8010701: Immutable nodes - final iteration

Fri, 19 Apr 2013 16:11:16 +0200

author
lagergren
date
Fri, 19 Apr 2013 16:11:16 +0200
changeset 211
3a209cbd1d8f
parent 210
c8460f668d0c
child 212
e599a1cad89a

8010701: Immutable nodes - final iteration
Reviewed-by: sundar, hannesw, jlaskey

bin/verbose_octane.sh file | annotate | diff | comparison | revisions
src/jdk/nashorn/api/scripting/NashornScriptEngine.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/Attr.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/ClassEmitter.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/CodeGenerator.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/CompilationPhase.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/Compiler.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/CompilerConstants.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/FieldObjectCreator.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/FinalizeTypes.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/FoldConstants.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/Frame.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/Lower.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/MethodEmitter.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/Namespace.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/ObjectCreator.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/Splitter.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/WeighNodes.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/AccessNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/BaseNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/BinaryNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/Block.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/BlockLexicalContext.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/BreakNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/BreakableNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/CallNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/CaseNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/CatchNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/ContinueNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/DoWhileNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/EmptyNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/ExecuteNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/Flags.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/ForNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/FunctionNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/IdentNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/IfNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/IndexNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/LabelNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/LabeledNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/LexicalContext.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/LexicalContextNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/LineNumberNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/LiteralNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/Location.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/LoopNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/Node.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/ObjectNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/PropertyNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/ReturnNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/RuntimeNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/SplitNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/SwitchNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/Symbol.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/TernaryNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/ThrowNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/TryNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/UnaryNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/VarNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/WhileNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/WithNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/annotations/Immutable.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/debug/ASTWriter.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/debug/JSONWriter.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/debug/PrintVisitor.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/lookup/MethodHandleFactory.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/objects/NativeString.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/parser/AbstractParser.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/parser/JSONParser.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/parser/Parser.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/parser/TokenType.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/Context.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/DebugLogger.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/StructureLoader.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/linker/ClassAndLoader.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/tools/Shell.java file | annotate | diff | comparison | revisions
test/script/basic/try2.js file | annotate | diff | comparison | revisions
test/script/basic/try2.js.EXPECTED file | annotate | diff | comparison | revisions
     1.1 --- a/bin/verbose_octane.sh	Fri Apr 19 18:23:00 2013 +0530
     1.2 +++ b/bin/verbose_octane.sh	Fri Apr 19 16:11:16 2013 +0200
     1.3 @@ -26,7 +26,7 @@
     1.4      ITERS=7
     1.5  fi
     1.6  NASHORN_JAR=dist/nashorn.jar
     1.7 -JVM_FLAGS="-XX:+UnlockDiagnosticVMOptions -Dnashorn.unstable.relink.threshold=8 -Xms2G -Xmx2G -XX:+TieredCompilation -server -jar ${NASHORN_JAR}"
     1.8 +JVM_FLAGS="-Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+UnlockDiagnosticVMOptions -Dnashorn.unstable.relink.threshold=8 -Xms2G -Xmx2G -XX:+TieredCompilation -server -jar ${NASHORN_JAR}"
     1.9  JVM_FLAGS7="-Xbootclasspath/p:${NASHORN_JAR} ${JVM_FLAGS}"
    1.10  OCTANE_ARGS="--verbose --iterations ${ITERS}"
    1.11  
     2.1 --- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Fri Apr 19 18:23:00 2013 +0530
     2.2 +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Fri Apr 19 16:11:16 2013 +0200
     2.3 @@ -397,10 +397,7 @@
     2.4              }
     2.5  
     2.6              setContextVariables(ctxt);
     2.7 -            final Object val = ctxt.getAttribute(ScriptEngine.FILENAME);
     2.8 -            final String fileName = (val != null) ? val.toString() : "<eval>";
     2.9 -            Object res = ScriptRuntime.apply(script, ctxtGlobal);
    2.10 -            return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(res, ctxtGlobal));
    2.11 +            return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal));
    2.12          } catch (final Exception e) {
    2.13              throwAsScriptException(e);
    2.14              throw new AssertionError("should not reach here");
     3.1 --- a/src/jdk/nashorn/internal/codegen/Attr.java	Fri Apr 19 18:23:00 2013 +0530
     3.2 +++ b/src/jdk/nashorn/internal/codegen/Attr.java	Fri Apr 19 16:11:16 2013 +0200
     3.3 @@ -25,14 +25,16 @@
     3.4  
     3.5  package jdk.nashorn.internal.codegen;
     3.6  
     3.7 +import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
     3.8  import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
     3.9  import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
    3.10  import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
    3.11 +import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
    3.12  import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
    3.13 -import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN;
    3.14  import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
    3.15  import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
    3.16  import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
    3.17 +import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF;
    3.18  import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
    3.19  import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
    3.20  import static jdk.nashorn.internal.ir.Symbol.IS_LET;
    3.21 @@ -42,18 +44,20 @@
    3.22  import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
    3.23  import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
    3.24  
    3.25 +import java.util.ArrayDeque;
    3.26  import java.util.ArrayList;
    3.27 +import java.util.Deque;
    3.28  import java.util.HashSet;
    3.29 +import java.util.IdentityHashMap;
    3.30  import java.util.Iterator;
    3.31  import java.util.List;
    3.32 -import java.util.ListIterator;
    3.33 +import java.util.Map;
    3.34  import java.util.Set;
    3.35  import jdk.nashorn.internal.codegen.types.Type;
    3.36  import jdk.nashorn.internal.ir.AccessNode;
    3.37  import jdk.nashorn.internal.ir.BinaryNode;
    3.38  import jdk.nashorn.internal.ir.Block;
    3.39  import jdk.nashorn.internal.ir.CallNode;
    3.40 -import jdk.nashorn.internal.ir.CallNode.EvalArgs;
    3.41  import jdk.nashorn.internal.ir.CaseNode;
    3.42  import jdk.nashorn.internal.ir.CatchNode;
    3.43  import jdk.nashorn.internal.ir.ForNode;
    3.44 @@ -62,6 +66,7 @@
    3.45  import jdk.nashorn.internal.ir.IdentNode;
    3.46  import jdk.nashorn.internal.ir.IndexNode;
    3.47  import jdk.nashorn.internal.ir.LexicalContext;
    3.48 +import jdk.nashorn.internal.ir.LexicalContextNode;
    3.49  import jdk.nashorn.internal.ir.LiteralNode;
    3.50  import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
    3.51  import jdk.nashorn.internal.ir.Node;
    3.52 @@ -86,7 +91,6 @@
    3.53  import jdk.nashorn.internal.runtime.JSType;
    3.54  import jdk.nashorn.internal.runtime.Property;
    3.55  import jdk.nashorn.internal.runtime.PropertyMap;
    3.56 -import jdk.nashorn.internal.runtime.ScriptFunction;
    3.57  import jdk.nashorn.internal.runtime.ScriptObject;
    3.58  
    3.59  /**
    3.60 @@ -105,21 +109,24 @@
    3.61   */
    3.62  
    3.63  final class Attr extends NodeOperatorVisitor {
    3.64 +
    3.65      /**
    3.66       * Local definitions in current block (to discriminate from function
    3.67       * declarations always defined in the function scope. This is for
    3.68       * "can be undefined" analysis.
    3.69       */
    3.70 -    private Set<String> localDefs;
    3.71 +    private final Deque<Set<String>> localDefs;
    3.72  
    3.73      /**
    3.74       * Local definitions in current block to guard against cases like
    3.75       * NASHORN-467 when things can be undefined as they are used before
    3.76       * their local var definition. *sigh* JavaScript...
    3.77       */
    3.78 -    private Set<String> localUses;
    3.79 +    private final Deque<Set<String>> localUses;
    3.80  
    3.81 -    private final LexicalContext lexicalContext = new LexicalContext();
    3.82 +    private final Deque<Type> returnTypes;
    3.83 +
    3.84 +    private final Map<Symbol, FunctionNode> selfSymbolToFunction = new IdentityHashMap<>();
    3.85  
    3.86      private static final DebugLogger LOG   = new DebugLogger("attr");
    3.87      private static final boolean     DEBUG = LOG.isEnabled();
    3.88 @@ -128,10 +135,13 @@
    3.89       * Constructor.
    3.90       */
    3.91      Attr() {
    3.92 +        localDefs = new ArrayDeque<>();
    3.93 +        localUses = new ArrayDeque<>();
    3.94 +        returnTypes = new ArrayDeque<>();
    3.95      }
    3.96  
    3.97      @Override
    3.98 -    protected Node enterDefault(final Node node) {
    3.99 +    protected boolean enterDefault(final Node node) {
   3.100          return start(node);
   3.101      }
   3.102  
   3.103 @@ -142,217 +152,44 @@
   3.104  
   3.105      @Override
   3.106      public Node leaveAccessNode(final AccessNode accessNode) {
   3.107 -        newTemporary(Type.OBJECT, accessNode);  //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this
   3.108 +        ensureSymbol(Type.OBJECT, accessNode);  //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this
   3.109          end(accessNode);
   3.110          return accessNode;
   3.111      }
   3.112  
   3.113 -    @Override
   3.114 -    public Node enterBlock(final Block block) {
   3.115 -        lexicalContext.push(block);
   3.116 -        start(block);
   3.117 +    private void enterFunctionBody() {
   3.118  
   3.119 -        final Set<String> savedLocalDefs = localDefs;
   3.120 -        final Set<String> savedLocalUses = localUses;
   3.121 -
   3.122 -        block.setFrame(getCurrentFunctionNode().pushFrame());
   3.123 -
   3.124 -        try {
   3.125 -            // a block starts out by copying the local defs and local uses
   3.126 -            // from the outer level. But we need the copies, as when we
   3.127 -            // leave the block the def and use sets given upon entry must
   3.128 -            // be restored
   3.129 -            localDefs = new HashSet<>(savedLocalDefs);
   3.130 -            localUses = new HashSet<>(savedLocalUses);
   3.131 -
   3.132 -            block.visitStatements(this);
   3.133 -        } finally {
   3.134 -            localDefs = savedLocalDefs;
   3.135 -            localUses = savedLocalUses;
   3.136 -
   3.137 -            getCurrentFunctionNode().popFrame();
   3.138 +        final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
   3.139 +        final Block body = getLexicalContext().getCurrentBlock();
   3.140 +        initCallee(body);
   3.141 +        initThis(body);
   3.142 +        if (functionNode.isVarArg()) {
   3.143 +            initVarArg(body, functionNode.needsArguments());
   3.144          }
   3.145  
   3.146 -        end(block);
   3.147 -
   3.148 -        lexicalContext.pop(block);
   3.149 -        return null;
   3.150 -    }
   3.151 -
   3.152 -    @Override
   3.153 -    public Node enterCallNode(final CallNode callNode) {
   3.154 -        start(callNode);
   3.155 -
   3.156 -        callNode.getFunction().accept(this);
   3.157 -
   3.158 -        final List<Node> acceptedArgs = new ArrayList<>(callNode.getArgs().size());
   3.159 -        for (final Node arg : callNode.getArgs()) {
   3.160 -            LOG.info("Doing call arg " + arg);
   3.161 -            acceptedArgs.add(arg.accept(this));
   3.162 -        }
   3.163 -        callNode.setArgs(acceptedArgs);
   3.164 -
   3.165 -        final EvalArgs evalArgs = callNode.getEvalArgs();
   3.166 -        if (evalArgs != null) {
   3.167 -            evalArgs.setCode(evalArgs.getCode().accept(this));
   3.168 -
   3.169 -            final IdentNode thisNode = new IdentNode(getCurrentFunctionNode().getThisNode());
   3.170 -            assert thisNode.getSymbol() != null; //should copy attributed symbol and that's it
   3.171 -            evalArgs.setThis(thisNode);
   3.172 -        }
   3.173 -
   3.174 -        newTemporary(callNode.getType(), callNode); // access specialization in FinalizeTypes may narrow it further later
   3.175 -
   3.176 -        end(callNode);
   3.177 -
   3.178 -        return null;
   3.179 -    }
   3.180 -
   3.181 -    @Override
   3.182 -    public Node enterCatchNode(final CatchNode catchNode) {
   3.183 -        final IdentNode exception = catchNode.getException();
   3.184 -        final Block     block     = getCurrentBlock();
   3.185 -
   3.186 -        start(catchNode);
   3.187 -
   3.188 -        // define block-local exception variable
   3.189 -        final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception);
   3.190 -        newType(def, Type.OBJECT);
   3.191 -        addLocalDef(exception.getName());
   3.192 -
   3.193 -        return catchNode;
   3.194 -    }
   3.195 -
   3.196 -    /**
   3.197 -     * Declare the definition of a new symbol.
   3.198 -     *
   3.199 -     * @param name         Name of symbol.
   3.200 -     * @param symbolFlags  Symbol flags.
   3.201 -     * @param node         Defining Node.
   3.202 -     *
   3.203 -     * @return Symbol for given name or null for redefinition.
   3.204 -     */
   3.205 -    private Symbol defineSymbol(final Block block, final String name, final int symbolFlags, final Node node) {
   3.206 -        int    flags  = symbolFlags;
   3.207 -        Symbol symbol = findSymbol(block, name); // Locate symbol.
   3.208 -
   3.209 -        if ((flags & KINDMASK) == IS_GLOBAL) {
   3.210 -            flags |= IS_SCOPE;
   3.211 -        }
   3.212 -
   3.213 -        final FunctionNode function = lexicalContext.getFunction(block);
   3.214 -        if (symbol != null) {
   3.215 -            // Symbol was already defined. Check if it needs to be redefined.
   3.216 -            if ((flags & KINDMASK) == IS_PARAM) {
   3.217 -                if (!isLocal(function, symbol)) {
   3.218 -                    // Not defined in this function. Create a new definition.
   3.219 -                    symbol = null;
   3.220 -                } else if (symbol.isParam()) {
   3.221 -                    // Duplicate parameter. Null return will force an error.
   3.222 -                    assert false : "duplicate parameter";
   3.223 -                    return null;
   3.224 -                }
   3.225 -            } else if ((flags & KINDMASK) == IS_VAR) {
   3.226 -                if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) {
   3.227 -                    assert !((flags & IS_LET) == IS_LET && symbol.getBlock() == block) : "duplicate let variable in block";
   3.228 -                    // Always create a new definition.
   3.229 -                    symbol = null;
   3.230 -                } else {
   3.231 -                    // Not defined in this function. Create a new definition.
   3.232 -                    if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
   3.233 -                        symbol = null;
   3.234 -                    }
   3.235 -                }
   3.236 -            }
   3.237 -        }
   3.238 -
   3.239 -        if (symbol == null) {
   3.240 -            // If not found, then create a new one.
   3.241 -            Block symbolBlock;
   3.242 -
   3.243 -            // Determine where to create it.
   3.244 -            if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
   3.245 -                symbolBlock = block;
   3.246 -            } else {
   3.247 -                symbolBlock = function;
   3.248 -            }
   3.249 -
   3.250 -            // Create and add to appropriate block.
   3.251 -            symbol = new Symbol(name, flags, node, symbolBlock);
   3.252 -            symbolBlock.putSymbol(name, symbol);
   3.253 -
   3.254 -            if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
   3.255 -                symbolBlock.getFrame().addSymbol(symbol);
   3.256 -                symbol.setNeedsSlot(true);
   3.257 -            }
   3.258 -        } else if (symbol.less(flags)) {
   3.259 -            symbol.setFlags(flags);
   3.260 -        }
   3.261 -
   3.262 -        if (node != null) {
   3.263 -            node.setSymbol(symbol);
   3.264 -        }
   3.265 -
   3.266 -        return symbol;
   3.267 -    }
   3.268 -
   3.269 -    @Override
   3.270 -    public Node enterFunctionNode(final FunctionNode functionNode) {
   3.271 -        start(functionNode, false);
   3.272 -        if (functionNode.isLazy()) {
   3.273 -            LOG.info("LAZY: " + functionNode.getName() + " => Promoting to OBJECT");
   3.274 -            newTemporary(lexicalContext.getCurrentFunction(), Type.OBJECT, functionNode);
   3.275 -            functionNode.setReturnType(Type.OBJECT);
   3.276 -            end(functionNode);
   3.277 -            return null;
   3.278 -        }
   3.279 -
   3.280 -        lexicalContext.push(functionNode);
   3.281 -
   3.282 -        clearLocalDefs();
   3.283 -        clearLocalUses();
   3.284 -
   3.285 -        functionNode.setFrame(functionNode.pushFrame());
   3.286 -
   3.287 -        initCallee(functionNode);
   3.288 -        initThis(functionNode);
   3.289 -        if (functionNode.isVarArg()) {
   3.290 -            initVarArg(functionNode);
   3.291 -        }
   3.292 -
   3.293 -        initParameters(functionNode);
   3.294 -        initScope(functionNode);
   3.295 -        initReturn(functionNode);
   3.296 -
   3.297 -        // Add all nested declared functions as symbols in this function
   3.298 -        for (final FunctionNode nestedFunction : functionNode.getDeclaredFunctions()) {
   3.299 -            final IdentNode ident = nestedFunction.getIdent();
   3.300 -            if (ident != null) {
   3.301 -                assert nestedFunction.isDeclared();
   3.302 -                final Symbol functionSymbol = defineSymbol(functionNode, ident.getName(), IS_VAR, nestedFunction);
   3.303 -                newType(functionSymbol, Type.typeFor(ScriptFunction.class));
   3.304 -            }
   3.305 -        }
   3.306 +        initParameters(functionNode, body);
   3.307 +        initScope(body);
   3.308 +        initReturn(body);
   3.309  
   3.310          if (functionNode.isProgram()) {
   3.311 -            initFromPropertyMap(functionNode);
   3.312 +            initFromPropertyMap(body);
   3.313          }
   3.314  
   3.315          // Add function name as local symbol
   3.316          if (!functionNode.isDeclared() && !functionNode.isProgram()) {
   3.317 -            if(functionNode.getSymbol() != null) {
   3.318 +            if (functionNode.getSymbol() != null) {
   3.319                  // a temporary left over from an earlier pass when the function was lazy
   3.320                  assert functionNode.getSymbol().isTemp();
   3.321                  // remove it
   3.322                  functionNode.setSymbol(null);
   3.323              }
   3.324              final Symbol selfSymbol;
   3.325 -            if(functionNode.isAnonymous()) {
   3.326 -                selfSymbol = newTemporary(functionNode, Type.OBJECT, functionNode);
   3.327 +            if (functionNode.isAnonymous()) {
   3.328 +                selfSymbol = ensureSymbol(functionNode, Type.OBJECT, functionNode);
   3.329              } else {
   3.330 -                selfSymbol = defineSymbol(functionNode, functionNode.getIdent().getName(), IS_VAR, functionNode);
   3.331 +                selfSymbol = defineSymbol(body, functionNode.getIdent().getName(), IS_VAR | IS_FUNCTION_SELF, functionNode);
   3.332                  newType(selfSymbol, Type.OBJECT);
   3.333 -                selfSymbol.setNode(functionNode);
   3.334 +                selfSymbolToFunction.put(selfSymbol, functionNode);
   3.335              }
   3.336          }
   3.337  
   3.338 @@ -373,73 +210,243 @@
   3.339           * @see NASHORN-73
   3.340           */
   3.341  
   3.342 -        final List<Symbol> declaredSymbols = new ArrayList<>();
   3.343          // This visitor will assign symbol to all declared variables, except function declarations (which are taken care
   3.344          // in a separate step above) and "var" declarations in for loop initializers.
   3.345 -        functionNode.accept(new NodeOperatorVisitor() {
   3.346 +        body.accept(new NodeOperatorVisitor() {
   3.347              @Override
   3.348 -            public Node enterFunctionNode(FunctionNode nestedFn) {
   3.349 -                // Don't descend into nested functions
   3.350 -                return nestedFn == functionNode ? nestedFn : null;
   3.351 +            public boolean enterFunctionNode(final FunctionNode nestedFn) {
   3.352 +                return false;
   3.353              }
   3.354 +
   3.355              @Override
   3.356 -            public Node enterVarNode(VarNode varNode) {
   3.357 -                if(varNode.isStatement() && !varNode.isFunctionDeclaration()) {
   3.358 +            public boolean enterVarNode(final VarNode varNode) {
   3.359 +
   3.360 +                // any declared symbols that aren't visited need to be typed as well, hence the list
   3.361 +
   3.362 +                if (varNode.isStatement()) {
   3.363 +
   3.364                      final IdentNode ident = varNode.getName();
   3.365 -                    // any declared symbols that aren't visited need to be typed as well, hence the list
   3.366 -                    declaredSymbols.add(defineSymbol(functionNode, ident.getName(), IS_VAR, new IdentNode(ident)));
   3.367 +                    final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR, new IdentNode(ident));
   3.368 +                    functionNode.addDeclaredSymbol(symbol);
   3.369 +                    if (varNode.isFunctionDeclaration()) {
   3.370 +                        newType(symbol, FunctionNode.FUNCTION_TYPE);
   3.371 +                    }
   3.372                  }
   3.373 -                return null;
   3.374 +                return false;
   3.375              }
   3.376          });
   3.377 +    }
   3.378  
   3.379 -        visitFunctionStatements(functionNode);
   3.380 +    @Override
   3.381 +    public boolean enterBlock(final Block block) {
   3.382 +        start(block);
   3.383 +
   3.384 +        if (getLexicalContext().isFunctionBody()) {
   3.385 +            enterFunctionBody();
   3.386 +        }
   3.387 +        pushLocalsBlock();
   3.388 +
   3.389 +        return true;
   3.390 +    }
   3.391 +
   3.392 +    @Override
   3.393 +    public Node leaveBlock(final Block block) {
   3.394 +        popLocals();
   3.395 +        return end(block);
   3.396 +    }
   3.397 +
   3.398 +    @Override
   3.399 +    public Node leaveCallNode(final CallNode callNode) {
   3.400 +        ensureSymbol(callNode.getType(), callNode);
   3.401 +        return end(callNode);
   3.402 +    }
   3.403 +
   3.404 +    @Override
   3.405 +    public boolean enterCallNode(final CallNode callNode) {
   3.406 +        return start(callNode);
   3.407 +    }
   3.408 +
   3.409 +    @Override
   3.410 +    public boolean enterCatchNode(final CatchNode catchNode) {
   3.411 +        final IdentNode exception = catchNode.getException();
   3.412 +        final Block     block     = getLexicalContext().getCurrentBlock();
   3.413 +
   3.414 +        start(catchNode);
   3.415 +
   3.416 +        // define block-local exception variable
   3.417 +        final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception);
   3.418 +        newType(def, Type.OBJECT);
   3.419 +        addLocalDef(exception.getName());
   3.420 +
   3.421 +        return true;
   3.422 +    }
   3.423 +
   3.424 +    /**
   3.425 +     * Declare the definition of a new symbol.
   3.426 +     *
   3.427 +     * @param name         Name of symbol.
   3.428 +     * @param symbolFlags  Symbol flags.
   3.429 +     * @param node         Defining Node.
   3.430 +     *
   3.431 +     * @return Symbol for given name or null for redefinition.
   3.432 +     */
   3.433 +    private Symbol defineSymbol(final Block block, final String name, final int symbolFlags, final Node node) {
   3.434 +        int    flags  = symbolFlags;
   3.435 +        Symbol symbol = findSymbol(block, name); // Locate symbol.
   3.436 +
   3.437 +        if ((flags & KINDMASK) == IS_GLOBAL) {
   3.438 +            flags |= IS_SCOPE;
   3.439 +        }
   3.440 +
   3.441 +        final FunctionNode function = getLexicalContext().getFunction(block);
   3.442 +        if (symbol != null) {
   3.443 +            // Symbol was already defined. Check if it needs to be redefined.
   3.444 +            if ((flags & KINDMASK) == IS_PARAM) {
   3.445 +                if (!isLocal(function, symbol)) {
   3.446 +                    // Not defined in this function. Create a new definition.
   3.447 +                    symbol = null;
   3.448 +                } else if (symbol.isParam()) {
   3.449 +                    // Duplicate parameter. Null return will force an error.
   3.450 +                    assert false : "duplicate parameter";
   3.451 +                    return null;
   3.452 +                }
   3.453 +            } else if ((flags & KINDMASK) == IS_VAR) {
   3.454 +                if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) {
   3.455 +                    // Always create a new definition.
   3.456 +                    symbol = null;
   3.457 +                } else {
   3.458 +                    // Not defined in this function. Create a new definition.
   3.459 +                    if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
   3.460 +                        symbol = null;
   3.461 +                    }
   3.462 +                }
   3.463 +            }
   3.464 +        }
   3.465 +
   3.466 +        if (symbol == null) {
   3.467 +            // If not found, then create a new one.
   3.468 +            Block symbolBlock;
   3.469 +
   3.470 +            // Determine where to create it.
   3.471 +            if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
   3.472 +                symbolBlock = block; //internal vars are always defined in the block closest to them
   3.473 +            } else {
   3.474 +                symbolBlock = getLexicalContext().getFunctionBody(function);
   3.475 +            }
   3.476 +
   3.477 +            // Create and add to appropriate block.
   3.478 +            symbol = new Symbol(name, flags);
   3.479 +            symbolBlock.putSymbol(name, symbol);
   3.480 +
   3.481 +            if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
   3.482 +                symbol.setNeedsSlot(true);
   3.483 +            }
   3.484 +        } else if (symbol.less(flags)) {
   3.485 +            symbol.setFlags(flags);
   3.486 +        }
   3.487 +
   3.488 +        if (node != null) {
   3.489 +            node.setSymbol(symbol);
   3.490 +        }
   3.491 +
   3.492 +        return symbol;
   3.493 +    }
   3.494 +
   3.495 +    @Override
   3.496 +    public boolean enterFunctionNode(final FunctionNode functionNode) {
   3.497 +        start(functionNode, false);
   3.498 +
   3.499 +        if (functionNode.isDeclared()) {
   3.500 +            final Iterator<Block> blocks = getLexicalContext().getBlocks();
   3.501 +            if (blocks.hasNext()) {
   3.502 +                defineSymbol(
   3.503 +                    blocks.next(),
   3.504 +                    functionNode.getIdent().getName(),
   3.505 +                    IS_VAR,
   3.506 +                    functionNode);
   3.507 +            } else {
   3.508 +                // Q: What's an outermost function in a lexical context that is not a program?
   3.509 +                // A: It's a function being compiled lazily!
   3.510 +                assert getLexicalContext().getOutermostFunction() == functionNode && !functionNode.isProgram();
   3.511 +            }
   3.512 +        }
   3.513 +
   3.514 +        if (functionNode.isLazy()) {
   3.515 +            LOG.info("LAZY: ", functionNode.getName(), " => Promoting to OBJECT");
   3.516 +            ensureSymbol(getLexicalContext().getCurrentFunction(), Type.OBJECT, functionNode);
   3.517 +            end(functionNode);
   3.518 +            return false;
   3.519 +        }
   3.520 +
   3.521 +        returnTypes.push(functionNode.getReturnType());
   3.522 +        pushLocalsFunction();
   3.523 +        return true;
   3.524 +    }
   3.525 +
   3.526 +    @Override
   3.527 +    public Node leaveFunctionNode(final FunctionNode functionNode) {
   3.528 +        FunctionNode newFunctionNode = functionNode;
   3.529 +
   3.530 +        final LexicalContext lc = getLexicalContext();
   3.531  
   3.532          //unknown parameters are promoted to object type.
   3.533 -        finalizeParameters(functionNode);
   3.534 -        finalizeTypes(functionNode);
   3.535 -        for (final Symbol symbol : declaredSymbols) {
   3.536 +        finalizeParameters(newFunctionNode);
   3.537 +        finalizeTypes(newFunctionNode);
   3.538 +        for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) {
   3.539              if (symbol.getSymbolType().isUnknown()) {
   3.540                  symbol.setType(Type.OBJECT);
   3.541                  symbol.setCanBeUndefined();
   3.542              }
   3.543          }
   3.544  
   3.545 -        if (functionNode.getReturnType().isUnknown()) {
   3.546 -            LOG.info("Unknown return type promoted to object");
   3.547 -            functionNode.setReturnType(Type.OBJECT);
   3.548 +        final Block body = newFunctionNode.getBody();
   3.549 +
   3.550 +        if (newFunctionNode.hasLazyChildren()) {
   3.551 +            //the final body has already been assigned as we have left the function node block body by now
   3.552 +            objectifySymbols(body);
   3.553          }
   3.554  
   3.555 -        if (functionNode.getSelfSymbolInit() != null) {
   3.556 -            LOG.info("Accepting self symbol init " + functionNode.getSelfSymbolInit() + " for " + functionNode.getName());
   3.557 -            final Node init = functionNode.getSelfSymbolInit();
   3.558 +        if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) {
   3.559 +            final IdentNode callee = compilerConstant(CALLEE);
   3.560 +            final VarNode selfInit =
   3.561 +                new VarNode(
   3.562 +                    newFunctionNode.getSource(),
   3.563 +                    newFunctionNode.getToken(),
   3.564 +                    newFunctionNode.getFinish(),
   3.565 +                    newFunctionNode.getIdent(),
   3.566 +                    callee);
   3.567 +
   3.568 +            LOG.info("Accepting self symbol init ", selfInit, " for ", newFunctionNode.getName());
   3.569 +
   3.570              final List<Node> newStatements = new ArrayList<>();
   3.571 -            newStatements.add(init);
   3.572 -            newStatements.addAll(functionNode.getStatements());
   3.573 -            functionNode.setStatements(newStatements);
   3.574 -            functionNode.setNeedsSelfSymbol(functionNode.getSelfSymbolInit().accept(this));
   3.575 +            newStatements.add(selfInit);
   3.576 +            assert callee.getSymbol() != null && callee.getSymbol().hasSlot();
   3.577 +
   3.578 +            final IdentNode name       = selfInit.getName();
   3.579 +            final Symbol    nameSymbol = body.getExistingSymbol(name.getName());
   3.580 +
   3.581 +            assert nameSymbol != null;
   3.582 +
   3.583 +            name.setSymbol(nameSymbol);
   3.584 +            selfInit.setSymbol(nameSymbol);
   3.585 +
   3.586 +            newStatements.addAll(body.getStatements());
   3.587 +            newFunctionNode = newFunctionNode.setBody(lc, body.setStatements(lc, newStatements));
   3.588          }
   3.589  
   3.590 -        if (functionNode.hasLazyChildren()) {
   3.591 -            objectifySymbols(functionNode);
   3.592 +        if (returnTypes.peek().isUnknown()) {
   3.593 +            LOG.info("Unknown return type promoted to object");
   3.594 +            newFunctionNode = newFunctionNode.setReturnType(lc, Type.OBJECT);
   3.595          }
   3.596 +        final Type returnType = returnTypes.pop();
   3.597 +        newFunctionNode = newFunctionNode.setReturnType(lc, returnType.isUnknown() ? Type.OBJECT : returnType);
   3.598 +        newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR);
   3.599  
   3.600 -        functionNode.popFrame();
   3.601 +        popLocals();
   3.602  
   3.603 -        functionNode.setState(CompilationState.ATTR);
   3.604 +        end(newFunctionNode, false);
   3.605  
   3.606 -        end(functionNode, false);
   3.607 -        lexicalContext.pop(functionNode);
   3.608 -
   3.609 -        return null;
   3.610 -    }
   3.611 -
   3.612 -    private void visitFunctionStatements(final FunctionNode functionNode) {
   3.613 -        final List<Node> newStatements = new ArrayList<>(functionNode.getStatements());
   3.614 -        for(ListIterator<Node> stmts = newStatements.listIterator(); stmts.hasNext();) {
   3.615 -            stmts.set(stmts.next().accept(this));
   3.616 -        }
   3.617 -        functionNode.setStatements(newStatements);
   3.618 +        return newFunctionNode; //.setFlag(lc, lc.getFlags(functionNode));
   3.619      }
   3.620  
   3.621      @Override
   3.622 @@ -450,7 +457,7 @@
   3.623      }
   3.624  
   3.625      @Override
   3.626 -    public Node enterIdentNode(final IdentNode identNode) {
   3.627 +    public boolean enterIdentNode(final IdentNode identNode) {
   3.628          final String name = identNode.getName();
   3.629  
   3.630          start(identNode);
   3.631 @@ -458,31 +465,28 @@
   3.632          if (identNode.isPropertyName()) {
   3.633              // assign a pseudo symbol to property name
   3.634              final Symbol pseudoSymbol = pseudoSymbol(name);
   3.635 -            LOG.info("IdentNode is property name -> assigning pseudo symbol " + pseudoSymbol);
   3.636 +            LOG.info("IdentNode is property name -> assigning pseudo symbol ", pseudoSymbol);
   3.637              LOG.unindent();
   3.638              identNode.setSymbol(pseudoSymbol);
   3.639 -            return null;
   3.640 +            return false;
   3.641          }
   3.642  
   3.643 -        final Block  block     = getCurrentBlock();
   3.644 -        final Symbol oldSymbol = identNode.getSymbol();
   3.645 +        final LexicalContext lc        = getLexicalContext();
   3.646 +        final Block          block     = lc.getCurrentBlock();
   3.647 +        final Symbol         oldSymbol = identNode.getSymbol();
   3.648  
   3.649          Symbol symbol = findSymbol(block, name);
   3.650  
   3.651          //If an existing symbol with the name is found, use that otherwise, declare a new one
   3.652          if (symbol != null) {
   3.653 -            LOG.info("Existing symbol = " + symbol);
   3.654 -            if (isFunctionExpressionSelfReference(symbol)) {
   3.655 -                final FunctionNode functionNode = (FunctionNode)symbol.getNode();
   3.656 -                assert functionNode.getCalleeNode() != null;
   3.657 -
   3.658 -                final VarNode var = new VarNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), functionNode.getIdent(), functionNode.getCalleeNode());
   3.659 -                //newTemporary(Type.OBJECT, var); //ScriptFunction? TODO
   3.660 -
   3.661 -                functionNode.setNeedsSelfSymbol(var);
   3.662 -            }
   3.663 -
   3.664 -            if (!identNode.isInitializedHere()) { // NASHORN-448
   3.665 +            LOG.info("Existing symbol = ", symbol);
   3.666 +            if (symbol.isFunctionSelf()) {
   3.667 +                final FunctionNode functionNode = lc.getDefiningFunction(symbol);
   3.668 +                assert functionNode != null;
   3.669 +                assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
   3.670 +                lc.setFlag(functionNode.getBody(), Block.NEEDS_SELF_SYMBOL);
   3.671 +                newType(symbol, FunctionNode.FUNCTION_TYPE);
   3.672 +            } else if (!identNode.isInitializedHere()) { // NASHORN-448
   3.673                  // here is a use outside the local def scope
   3.674                  if (!isLocalDef(name)) {
   3.675                      newType(symbol, Type.OBJECT);
   3.676 @@ -492,25 +496,19 @@
   3.677  
   3.678              identNode.setSymbol(symbol);
   3.679              // non-local: we need to put symbol in scope (if it isn't already)
   3.680 -            if (!isLocal(getCurrentFunctionNode(), symbol) && !symbol.isScope()) {
   3.681 -                symbol.setIsScope();
   3.682 +            if (!isLocal(lc.getCurrentFunction(), symbol) && !symbol.isScope()) {
   3.683 +                Symbol.setSymbolIsScope(lc, symbol);
   3.684              }
   3.685          } else {
   3.686 -            LOG.info("No symbol exists. Declare undefined: " + symbol);
   3.687 -            symbol = useSymbol(block, name, identNode);
   3.688 +            LOG.info("No symbol exists. Declare undefined: ", symbol);
   3.689 +            symbol = defineSymbol(block, name, IS_GLOBAL, identNode);
   3.690              // we have never seen this before, it can be undefined
   3.691              newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway?
   3.692              symbol.setCanBeUndefined();
   3.693 -            symbol.setIsScope();
   3.694 +            Symbol.setSymbolIsScope(lc, symbol);
   3.695          }
   3.696  
   3.697 -        assert symbol != null;
   3.698 -        if(symbol.isGlobal()) {
   3.699 -            setUsesGlobalSymbol();
   3.700 -        } else if(symbol.isScope()) {
   3.701 -            final Iterator<Block> blocks = lexicalContext.getBlocks();
   3.702 -            blocks.next().setUsesScopeSymbol(symbol, blocks);
   3.703 -        }
   3.704 +        setBlockScope(name, symbol);
   3.705  
   3.706          if (symbol != oldSymbol && !identNode.isInitializedHere()) {
   3.707              symbol.increaseUseCount();
   3.708 @@ -519,7 +517,37 @@
   3.709  
   3.710          end(identNode);
   3.711  
   3.712 -        return null;
   3.713 +        return false;
   3.714 +    }
   3.715 +
   3.716 +    private void setBlockScope(final String name, final Symbol symbol) {
   3.717 +        assert symbol != null;
   3.718 +        if (symbol.isGlobal()) {
   3.719 +            setUsesGlobalSymbol();
   3.720 +            return;
   3.721 +        }
   3.722 +
   3.723 +        if (symbol.isScope()) {
   3.724 +            final LexicalContext lc = getLexicalContext();
   3.725 +
   3.726 +            Block scopeBlock = null;
   3.727 +            for (final Iterator<LexicalContextNode> contextNodeIter = getLexicalContext().getAllNodes(); contextNodeIter.hasNext(); ) {
   3.728 +                final LexicalContextNode node = contextNodeIter.next();
   3.729 +                if (node instanceof Block) {
   3.730 +                    if (((Block)node).getExistingSymbol(name) != null) {
   3.731 +                        scopeBlock = (Block)node;
   3.732 +                        break;
   3.733 +                    }
   3.734 +                } else if (node instanceof FunctionNode) {
   3.735 +                    lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE);
   3.736 +                }
   3.737 +            }
   3.738 +
   3.739 +            if (scopeBlock != null) {
   3.740 +                assert getLexicalContext().contains(scopeBlock);
   3.741 +                lc.setFlag(scopeBlock, Block.NEEDS_SCOPE);
   3.742 +            }
   3.743 +        }
   3.744      }
   3.745  
   3.746      /**
   3.747 @@ -528,35 +556,12 @@
   3.748       * @see #needsParentScope()
   3.749       */
   3.750      private void setUsesGlobalSymbol() {
   3.751 -        for(final Iterator<FunctionNode> fns = lexicalContext.getFunctions(); fns.hasNext();) {
   3.752 -            fns.next().setUsesAncestorScope();
   3.753 +        for (final Iterator<FunctionNode> fns = getLexicalContext().getFunctions(); fns.hasNext();) {
   3.754 +            getLexicalContext().setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
   3.755          }
   3.756      }
   3.757  
   3.758      /**
   3.759 -     * Declare the use of a symbol in a block.
   3.760 -     *
   3.761 -     * @param block block in which the symbol is used
   3.762 -     * @param name Name of symbol.
   3.763 -     * @param node Using node
   3.764 -     *
   3.765 -     * @return Symbol for given name.
   3.766 -     */
   3.767 -    private Symbol useSymbol(final Block block, final String name, final Node node) {
   3.768 -        Symbol symbol = findSymbol(block, name);
   3.769 -
   3.770 -        if (symbol == null) {
   3.771 -            // If not found, declare as a free var.
   3.772 -            symbol = defineSymbol(block, name, IS_GLOBAL, node);
   3.773 -        } else {
   3.774 -            node.setSymbol(symbol);
   3.775 -        }
   3.776 -
   3.777 -        return symbol;
   3.778 -    }
   3.779 -
   3.780 -
   3.781 -    /**
   3.782       * Search for symbol in the lexical context starting from the given block.
   3.783       * @param name Symbol name.
   3.784       * @return Found symbol or null if not found.
   3.785 @@ -564,7 +569,7 @@
   3.786      private Symbol findSymbol(final Block block, final String name) {
   3.787          // Search up block chain to locate symbol.
   3.788  
   3.789 -        for(final Iterator<Block> blocks = lexicalContext.getBlocks(block); blocks.hasNext();) {
   3.790 +        for(final Iterator<Block> blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) {
   3.791              // Find name.
   3.792              final Symbol symbol = blocks.next().getExistingSymbol(name);
   3.793              // If found then we are good.
   3.794 @@ -577,13 +582,13 @@
   3.795  
   3.796      @Override
   3.797      public Node leaveIndexNode(final IndexNode indexNode) {
   3.798 -        newTemporary(Type.OBJECT, indexNode); //TODO
   3.799 +        ensureSymbol(Type.OBJECT, indexNode); //TODO
   3.800          return indexNode;
   3.801      }
   3.802  
   3.803      @SuppressWarnings("rawtypes")
   3.804      @Override
   3.805 -    public Node enterLiteralNode(final LiteralNode literalNode) {
   3.806 +    public boolean enterLiteralNode(final LiteralNode literalNode) {
   3.807          try {
   3.808              start(literalNode);
   3.809              assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens
   3.810 @@ -604,26 +609,33 @@
   3.811                  assert !(literalNode.getValue() instanceof Node) : "literals with Node values not supported";
   3.812              }
   3.813  
   3.814 -            getCurrentFunctionNode().newLiteral(literalNode);
   3.815 +            getLexicalContext().getCurrentFunction().newLiteral(literalNode);
   3.816          } finally {
   3.817              end(literalNode);
   3.818          }
   3.819 -        return null;
   3.820 +
   3.821 +        return false;
   3.822 +    }
   3.823 +
   3.824 +    @Override
   3.825 +    public boolean enterObjectNode(final ObjectNode objectNode) {
   3.826 +        return start(objectNode);
   3.827      }
   3.828  
   3.829      @Override
   3.830      public Node leaveObjectNode(final ObjectNode objectNode) {
   3.831 -        newTemporary(Type.OBJECT, objectNode);
   3.832 -        end(objectNode);
   3.833 -        return objectNode;
   3.834 +        ensureSymbol(Type.OBJECT, objectNode);
   3.835 +        return end(objectNode);
   3.836      }
   3.837  
   3.838 +    //TODO is this correct why not leave?
   3.839      @Override
   3.840 -    public Node enterPropertyNode(final PropertyNode propertyNode) {
   3.841 +    public boolean enterPropertyNode(final PropertyNode propertyNode) {
   3.842          // assign a pseudo symbol to property name, see NASHORN-710
   3.843 +        start(propertyNode);
   3.844          propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
   3.845          end(propertyNode);
   3.846 -        return propertyNode;
   3.847 +        return true;
   3.848      }
   3.849  
   3.850      @Override
   3.851 @@ -636,8 +648,10 @@
   3.852              if (expr.getType().isUnknown() && symbol.isParam()) {
   3.853                  symbol.setType(Type.OBJECT);
   3.854              }
   3.855 -            getCurrentFunctionNode().setReturnType(Type.widest(getCurrentFunctionNode().getReturnType(), symbol.getSymbolType()));
   3.856 -            LOG.info("Returntype is now " + getCurrentFunctionNode().getReturnType());
   3.857 +
   3.858 +            final Type returnType = Type.widest(returnTypes.pop(), symbol.getSymbolType());
   3.859 +            returnTypes.push(returnType);
   3.860 +            LOG.info("Returntype is now ", returnType);
   3.861          }
   3.862  
   3.863          end(returnNode);
   3.864 @@ -649,25 +663,29 @@
   3.865      public Node leaveSwitchNode(final SwitchNode switchNode) {
   3.866          Type type = Type.UNKNOWN;
   3.867  
   3.868 +        final List<CaseNode> newCases = new ArrayList<>();
   3.869          for (final CaseNode caseNode : switchNode.getCases()) {
   3.870              final Node test = caseNode.getTest();
   3.871 +
   3.872 +            CaseNode newCaseNode = caseNode;
   3.873              if (test != null) {
   3.874                  if (test instanceof LiteralNode) {
   3.875                      //go down to integers if we can
   3.876                      final LiteralNode<?> lit = (LiteralNode<?>)test;
   3.877                      if (lit.isNumeric() && !(lit.getValue() instanceof Integer)) {
   3.878                          if (JSType.isRepresentableAsInt(lit.getNumber())) {
   3.879 -                            caseNode.setTest(LiteralNode.newInstance(lit, lit.getInt32()).accept(this));
   3.880 +                            newCaseNode = caseNode.setTest(LiteralNode.newInstance(lit, lit.getInt32()).accept(this));
   3.881                          }
   3.882                      }
   3.883                  } else {
   3.884                      // the "all integer" case that CodeGenerator optimizes for currently assumes literals only
   3.885                      type = Type.OBJECT;
   3.886 -                    break;
   3.887                  }
   3.888  
   3.889 -                type = Type.widest(type, caseNode.getTest().getType());
   3.890 +                type = Type.widest(type, newCaseNode.getTest().getType());
   3.891              }
   3.892 +
   3.893 +            newCases.add(newCaseNode);
   3.894          }
   3.895  
   3.896          //only optimize for all integers
   3.897 @@ -675,11 +693,11 @@
   3.898              type = Type.OBJECT;
   3.899          }
   3.900  
   3.901 -        switchNode.setTag(newInternal(getCurrentFunctionNode().uniqueName(SWITCH_TAG_PREFIX.tag()), type));
   3.902 +        switchNode.setTag(newInternal(getLexicalContext().getCurrentFunction().uniqueName(SWITCH_TAG_PREFIX.symbolName()), type));
   3.903  
   3.904          end(switchNode);
   3.905  
   3.906 -        return switchNode;
   3.907 +        return switchNode.setCases(getLexicalContext(), newCases);
   3.908      }
   3.909  
   3.910      @Override
   3.911 @@ -696,25 +714,25 @@
   3.912      }
   3.913  
   3.914      @Override
   3.915 -    public Node enterVarNode(final VarNode varNode) {
   3.916 +    public boolean enterVarNode(final VarNode varNode) {
   3.917          start(varNode);
   3.918  
   3.919          final IdentNode ident = varNode.getName();
   3.920          final String    name  = ident.getName();
   3.921  
   3.922 -        final Symbol symbol = defineSymbol(getCurrentBlock(), name, IS_VAR, ident);
   3.923 +        final Symbol symbol = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR, ident);
   3.924          assert symbol != null;
   3.925  
   3.926 -        LOG.info("VarNode " + varNode + " set symbol " + symbol);
   3.927 +        LOG.info("VarNode ", varNode, " set symbol ", symbol);
   3.928          varNode.setSymbol(symbol);
   3.929  
   3.930          // NASHORN-467 - use before definition of vars - conservative
   3.931 -        if (localUses.contains(ident.getName())) {
   3.932 +        if (isLocalUse(ident.getName())) {
   3.933              newType(symbol, Type.OBJECT);
   3.934              symbol.setCanBeUndefined();
   3.935          }
   3.936  
   3.937 -        return varNode;
   3.938 +        return true;
   3.939      }
   3.940  
   3.941      @Override
   3.942 @@ -734,7 +752,7 @@
   3.943          addLocalDef(name);
   3.944  
   3.945          final Symbol  symbol   = varNode.getSymbol();
   3.946 -        final boolean isScript = lexicalContext.getFunction(symbol.getBlock()).isProgram(); //see NASHORN-56
   3.947 +        final boolean isScript = getLexicalContext().getDefiningFunction(symbol).isProgram(); //see NASHORN-56
   3.948          if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) {
   3.949              // Forbid integers as local vars for now as we have no way to treat them as undefined
   3.950              newType(symbol, init.getType());
   3.951 @@ -751,14 +769,14 @@
   3.952  
   3.953      @Override
   3.954      public Node leaveADD(final UnaryNode unaryNode) {
   3.955 -        newTemporary(arithType(), unaryNode);
   3.956 +        ensureSymbol(arithType(), unaryNode);
   3.957          end(unaryNode);
   3.958          return unaryNode;
   3.959      }
   3.960  
   3.961      @Override
   3.962      public Node leaveBIT_NOT(final UnaryNode unaryNode) {
   3.963 -        newTemporary(Type.INT, unaryNode);
   3.964 +        ensureSymbol(Type.INT, unaryNode);
   3.965          end(unaryNode);
   3.966          return unaryNode;
   3.967      }
   3.968 @@ -766,30 +784,29 @@
   3.969      @Override
   3.970      public Node leaveDECINC(final UnaryNode unaryNode) {
   3.971          // @see assignOffset
   3.972 -        ensureAssignmentSlots(getCurrentFunctionNode(), unaryNode.rhs());
   3.973 +        ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), unaryNode.rhs());
   3.974          final Type type = arithType();
   3.975          newType(unaryNode.rhs().getSymbol(), type);
   3.976 -        newTemporary(type, unaryNode);
   3.977 +        ensureSymbol(type, unaryNode);
   3.978          end(unaryNode);
   3.979          return unaryNode;
   3.980      }
   3.981  
   3.982      @Override
   3.983      public Node leaveDELETE(final UnaryNode unaryNode) {
   3.984 -        final FunctionNode   currentFunctionNode = getCurrentFunctionNode();
   3.985 -        final boolean        strictMode          = currentFunctionNode.isStrictMode();
   3.986 +        final FunctionNode   currentFunctionNode = getLexicalContext().getCurrentFunction();
   3.987 +        final boolean        strictMode          = currentFunctionNode.isStrict();
   3.988          final Node           rhs                 = unaryNode.rhs();
   3.989          final Node           strictFlagNode      = LiteralNode.newInstance(unaryNode, strictMode).accept(this);
   3.990  
   3.991          Request request = Request.DELETE;
   3.992 -        final RuntimeNode runtimeNode;
   3.993          final List<Node> args = new ArrayList<>();
   3.994  
   3.995          if (rhs instanceof IdentNode) {
   3.996              // If this is a declared variable or a function parameter, delete always fails (except for globals).
   3.997              final String name = ((IdentNode)rhs).getName();
   3.998  
   3.999 -            final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !rhs.getSymbol().isTopLevel());
  3.1000 +            final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !isProgramLevelSymbol(name));
  3.1001  
  3.1002              if (failDelete && rhs.getSymbol().isThis()) {
  3.1003                  return LiteralNode.newInstance(unaryNode, true).accept(this);
  3.1004 @@ -797,7 +814,7 @@
  3.1005              final Node literalNode = LiteralNode.newInstance(unaryNode, name).accept(this);
  3.1006  
  3.1007              if (!failDelete) {
  3.1008 -                args.add(currentFunctionNode.getScopeNode());
  3.1009 +                args.add(compilerConstant(SCOPE));
  3.1010              }
  3.1011              args.add(literalNode);
  3.1012              args.add(strictFlagNode);
  3.1013 @@ -825,42 +842,62 @@
  3.1014              return LiteralNode.newInstance(unaryNode, true).accept(this);
  3.1015          }
  3.1016  
  3.1017 -        runtimeNode = new RuntimeNode(unaryNode, request, args);
  3.1018 -        assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //clone constructor should do this
  3.1019 +        final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, request, args);
  3.1020 +        assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //unary parent constructor should do this
  3.1021  
  3.1022          return leaveRuntimeNode(runtimeNode);
  3.1023      }
  3.1024  
  3.1025 +    /**
  3.1026 +     * Is the symbol denoted by the specified name in the current lexical context defined in the program level
  3.1027 +     * @param name the name of the symbol
  3.1028 +     * @return true if the symbol denoted by the specified name in the current lexical context defined in the program level.
  3.1029 +     */
  3.1030 +    private boolean isProgramLevelSymbol(final String name) {
  3.1031 +        for(final Iterator<Block> it = getLexicalContext().getBlocks(); it.hasNext();) {
  3.1032 +            final Block next = it.next();
  3.1033 +            if(next.getExistingSymbol(name) != null) {
  3.1034 +                return next == getLexicalContext().getFunctionBody(getLexicalContext().getOutermostFunction());
  3.1035 +            }
  3.1036 +        }
  3.1037 +        throw new AssertionError("Couldn't find symbol " + name + " in the context");
  3.1038 +    }
  3.1039 +
  3.1040      @Override
  3.1041      public Node leaveNEW(final UnaryNode unaryNode) {
  3.1042 -        newTemporary(Type.OBJECT, unaryNode);
  3.1043 +        ensureSymbol(Type.OBJECT, unaryNode);
  3.1044          end(unaryNode);
  3.1045          return unaryNode;
  3.1046      }
  3.1047  
  3.1048      @Override
  3.1049      public Node leaveNOT(final UnaryNode unaryNode) {
  3.1050 -        newTemporary(Type.BOOLEAN, unaryNode);
  3.1051 +        ensureSymbol(Type.BOOLEAN, unaryNode);
  3.1052          end(unaryNode);
  3.1053          return unaryNode;
  3.1054      }
  3.1055  
  3.1056 +    private IdentNode compilerConstant(CompilerConstants cc) {
  3.1057 +        final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
  3.1058 +        final IdentNode node = new IdentNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
  3.1059 +        node.setSymbol(functionNode.compilerConstant(cc));
  3.1060 +        return node;
  3.1061 +    }
  3.1062 +
  3.1063      @Override
  3.1064      public Node leaveTYPEOF(final UnaryNode unaryNode) {
  3.1065 -        final Node rhs    = unaryNode.rhs();
  3.1066 -
  3.1067 -        RuntimeNode runtimeNode;
  3.1068 +        final Node rhs = unaryNode.rhs();
  3.1069  
  3.1070          List<Node> args = new ArrayList<>();
  3.1071          if (rhs instanceof IdentNode && !rhs.getSymbol().isParam() && !rhs.getSymbol().isVar()) {
  3.1072 -            args.add(getCurrentFunctionNode().getScopeNode());
  3.1073 +            args.add(compilerConstant(SCOPE));
  3.1074              args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null
  3.1075          } else {
  3.1076              args.add(rhs);
  3.1077              args.add(LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
  3.1078          }
  3.1079  
  3.1080 -        runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
  3.1081 +        RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
  3.1082          assert runtimeNode.getSymbol() == unaryNode.getSymbol();
  3.1083  
  3.1084          runtimeNode = (RuntimeNode)leaveRuntimeNode(runtimeNode);
  3.1085 @@ -872,21 +909,20 @@
  3.1086  
  3.1087      @Override
  3.1088      public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
  3.1089 -        newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode);
  3.1090 +        ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode);
  3.1091          return runtimeNode;
  3.1092      }
  3.1093  
  3.1094      @Override
  3.1095      public Node leaveSUB(final UnaryNode unaryNode) {
  3.1096 -        newTemporary(arithType(), unaryNode);
  3.1097 +        ensureSymbol(arithType(), unaryNode);
  3.1098          end(unaryNode);
  3.1099          return unaryNode;
  3.1100      }
  3.1101  
  3.1102      @Override
  3.1103      public Node leaveVOID(final UnaryNode unaryNode) {
  3.1104 -        final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.VOID);
  3.1105 -        runtimeNode.accept(this);
  3.1106 +        final RuntimeNode runtimeNode = (RuntimeNode)new RuntimeNode(unaryNode, Request.VOID).accept(this);
  3.1107          assert runtimeNode.getSymbol().getSymbolType().isObject();
  3.1108          end(unaryNode);
  3.1109          return runtimeNode;
  3.1110 @@ -903,7 +939,7 @@
  3.1111  
  3.1112          ensureTypeNotUnknown(lhs);
  3.1113          ensureTypeNotUnknown(rhs);
  3.1114 -        newTemporary(Type.widest(lhs.getType(), rhs.getType()), binaryNode);
  3.1115 +        ensureSymbol(Type.widest(lhs.getType(), rhs.getType()), binaryNode);
  3.1116  
  3.1117          end(binaryNode);
  3.1118  
  3.1119 @@ -912,7 +948,7 @@
  3.1120  
  3.1121      @Override
  3.1122      public Node leaveAND(final BinaryNode binaryNode) {
  3.1123 -        newTemporary(Type.OBJECT, binaryNode);
  3.1124 +        ensureSymbol(Type.OBJECT, binaryNode);
  3.1125          end(binaryNode);
  3.1126          return binaryNode;
  3.1127      }
  3.1128 @@ -921,39 +957,40 @@
  3.1129       * This is a helper called before an assignment.
  3.1130       * @param binaryNode assignment node
  3.1131       */
  3.1132 -    private Node enterAssignmentNode(final BinaryNode binaryNode) {
  3.1133 +    private boolean enterAssignmentNode(final BinaryNode binaryNode) {
  3.1134          start(binaryNode);
  3.1135  
  3.1136          final Node lhs = binaryNode.lhs();
  3.1137  
  3.1138          if (lhs instanceof IdentNode) {
  3.1139 -            final Block     block = getCurrentBlock();
  3.1140 -            final IdentNode ident = (IdentNode)lhs;
  3.1141 -            final String    name  = ident.getName();
  3.1142 +            final LexicalContext lc    = getLexicalContext();
  3.1143 +            final Block          block = lc.getCurrentBlock();
  3.1144 +            final IdentNode      ident = (IdentNode)lhs;
  3.1145 +            final String         name  = ident.getName();
  3.1146  
  3.1147 -            Symbol symbol = findSymbol(getCurrentBlock(), name);
  3.1148 +            Symbol symbol = findSymbol(block, name);
  3.1149  
  3.1150              if (symbol == null) {
  3.1151                  symbol = defineSymbol(block, name, IS_GLOBAL, ident);
  3.1152                  binaryNode.setSymbol(symbol);
  3.1153 -            } else if (!isLocal(getCurrentFunctionNode(), symbol)) {
  3.1154 -                symbol.setIsScope();
  3.1155 +            } else if (!isLocal(lc.getCurrentFunction(), symbol)) {
  3.1156 +                Symbol.setSymbolIsScope(lc, symbol);
  3.1157              }
  3.1158  
  3.1159              addLocalDef(name);
  3.1160          }
  3.1161  
  3.1162 -        return binaryNode;
  3.1163 +        return true;
  3.1164      }
  3.1165  
  3.1166      private boolean isLocal(FunctionNode function, Symbol symbol) {
  3.1167 -        final Block block = symbol.getBlock();
  3.1168 -        // some temp symbols have no block, so can be assumed local
  3.1169 -        return block == null || lexicalContext.getFunction(block) == function;
  3.1170 +        final FunctionNode definingFn = getLexicalContext().getDefiningFunction(symbol);
  3.1171 +        // Temp symbols are not assigned to a block, so their defining fn is null; those can be assumed local
  3.1172 +        return definingFn == null || definingFn == function;
  3.1173      }
  3.1174  
  3.1175      @Override
  3.1176 -    public Node enterASSIGN(final BinaryNode binaryNode) {
  3.1177 +    public boolean enterASSIGN(final BinaryNode binaryNode) {
  3.1178          return enterAssignmentNode(binaryNode);
  3.1179      }
  3.1180  
  3.1181 @@ -963,7 +1000,7 @@
  3.1182      }
  3.1183  
  3.1184      @Override
  3.1185 -    public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
  3.1186 +    public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
  3.1187          return enterAssignmentNode(binaryNode);
  3.1188      }
  3.1189  
  3.1190 @@ -978,7 +1015,7 @@
  3.1191      }
  3.1192  
  3.1193      @Override
  3.1194 -    public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
  3.1195 +    public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
  3.1196          return enterAssignmentNode(binaryNode);
  3.1197      }
  3.1198  
  3.1199 @@ -988,7 +1025,7 @@
  3.1200      }
  3.1201  
  3.1202      @Override
  3.1203 -    public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
  3.1204 +    public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
  3.1205          return enterAssignmentNode(binaryNode);
  3.1206      }
  3.1207  
  3.1208 @@ -998,7 +1035,7 @@
  3.1209      }
  3.1210  
  3.1211      @Override
  3.1212 -    public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
  3.1213 +    public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
  3.1214          return enterAssignmentNode(binaryNode);
  3.1215      }
  3.1216  
  3.1217 @@ -1008,7 +1045,7 @@
  3.1218      }
  3.1219  
  3.1220      @Override
  3.1221 -    public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
  3.1222 +    public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
  3.1223          return enterAssignmentNode(binaryNode);
  3.1224      }
  3.1225  
  3.1226 @@ -1018,7 +1055,7 @@
  3.1227      }
  3.1228  
  3.1229      @Override
  3.1230 -    public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
  3.1231 +    public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
  3.1232          return enterAssignmentNode(binaryNode);
  3.1233      }
  3.1234  
  3.1235 @@ -1028,7 +1065,7 @@
  3.1236      }
  3.1237  
  3.1238      @Override
  3.1239 -    public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
  3.1240 +    public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
  3.1241          return enterAssignmentNode(binaryNode);
  3.1242      }
  3.1243  
  3.1244 @@ -1038,7 +1075,7 @@
  3.1245      }
  3.1246  
  3.1247      @Override
  3.1248 -    public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
  3.1249 +    public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
  3.1250          return enterAssignmentNode(binaryNode);
  3.1251      }
  3.1252  
  3.1253 @@ -1048,7 +1085,7 @@
  3.1254      }
  3.1255  
  3.1256      @Override
  3.1257 -    public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
  3.1258 +    public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
  3.1259          return enterAssignmentNode(binaryNode);
  3.1260      }
  3.1261  
  3.1262 @@ -1058,7 +1095,7 @@
  3.1263      }
  3.1264  
  3.1265      @Override
  3.1266 -    public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
  3.1267 +    public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
  3.1268          return enterAssignmentNode(binaryNode);
  3.1269      }
  3.1270  
  3.1271 @@ -1068,7 +1105,7 @@
  3.1272      }
  3.1273  
  3.1274      @Override
  3.1275 -    public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
  3.1276 +    public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
  3.1277          return enterAssignmentNode(binaryNode);
  3.1278      }
  3.1279  
  3.1280 @@ -1094,13 +1131,13 @@
  3.1281  
  3.1282      @Override
  3.1283      public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
  3.1284 -        newTemporary(binaryNode.rhs().getType(), binaryNode);
  3.1285 +        ensureSymbol(binaryNode.rhs().getType(), binaryNode);
  3.1286          return binaryNode;
  3.1287      }
  3.1288  
  3.1289      @Override
  3.1290      public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
  3.1291 -        newTemporary(binaryNode.lhs().getType(), binaryNode);
  3.1292 +        ensureSymbol(binaryNode.lhs().getType(), binaryNode);
  3.1293          return binaryNode;
  3.1294      }
  3.1295  
  3.1296 @@ -1113,7 +1150,7 @@
  3.1297          final Node lhs = binaryNode.lhs();
  3.1298          final Node rhs = binaryNode.rhs();
  3.1299  
  3.1300 -        newTemporary(Type.BOOLEAN, binaryNode);
  3.1301 +        ensureSymbol(Type.BOOLEAN, binaryNode);
  3.1302          ensureTypeNotUnknown(lhs);
  3.1303          ensureTypeNotUnknown(rhs);
  3.1304  
  3.1305 @@ -1131,7 +1168,7 @@
  3.1306  
  3.1307          //newType(binaryNode.lhs().getSymbol(), operandType);
  3.1308          //newType(binaryNode.rhs().getSymbol(), operandType);
  3.1309 -        newTemporary(destType, binaryNode);
  3.1310 +        ensureSymbol(destType, binaryNode);
  3.1311          return binaryNode;
  3.1312      }
  3.1313  
  3.1314 @@ -1216,7 +1253,7 @@
  3.1315  
  3.1316      @Override
  3.1317      public Node leaveOR(final BinaryNode binaryNode) {
  3.1318 -        newTemporary(Type.OBJECT, binaryNode);
  3.1319 +        ensureSymbol(Type.OBJECT, binaryNode);
  3.1320          end(binaryNode);
  3.1321          return binaryNode;
  3.1322      }
  3.1323 @@ -1244,7 +1281,7 @@
  3.1324      @Override
  3.1325      public Node leaveForNode(final ForNode forNode) {
  3.1326          if (forNode.isForIn()) {
  3.1327 -            forNode.setIterator(newInternal(getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73
  3.1328 +            forNode.setIterator(newInternal(getLexicalContext().getCurrentFunction().uniqueName(ITERATOR_PREFIX.symbolName()), Type.OBJECT)); //NASHORN-73
  3.1329              /*
  3.1330               * Iterators return objects, so we need to widen the scope of the
  3.1331               * init variable if it, for example, has been assigned double type
  3.1332 @@ -1267,65 +1304,50 @@
  3.1333          ensureTypeNotUnknown(rhs);
  3.1334  
  3.1335          final Type type = Type.widest(lhs.getType(), rhs.getType());
  3.1336 -        newTemporary(type, ternaryNode);
  3.1337 +        ensureSymbol(type, ternaryNode);
  3.1338  
  3.1339          end(ternaryNode);
  3.1340 +        assert ternaryNode.getSymbol() != null;
  3.1341  
  3.1342          return ternaryNode;
  3.1343      }
  3.1344  
  3.1345 -    private void initThis(final FunctionNode functionNode) {
  3.1346 -        final Symbol thisSymbol = defineSymbol(functionNode, THIS.tag(), IS_PARAM | IS_THIS, null);
  3.1347 +    private void initThis(final Block block) {
  3.1348 +        final Symbol thisSymbol = defineSymbol(block, THIS.symbolName(), IS_PARAM | IS_THIS, null);
  3.1349          newType(thisSymbol, Type.OBJECT);
  3.1350          thisSymbol.setNeedsSlot(true);
  3.1351 -        functionNode.getThisNode().setSymbol(thisSymbol);
  3.1352 -        LOG.info("Initialized scope symbol: " + thisSymbol);
  3.1353      }
  3.1354  
  3.1355 -    private void initScope(final FunctionNode functionNode) {
  3.1356 -        final Symbol scopeSymbol = defineSymbol(functionNode, SCOPE.tag(), IS_VAR | IS_INTERNAL, null);
  3.1357 +    private void initScope(final Block block) {
  3.1358 +        final Symbol scopeSymbol = defineSymbol(block, SCOPE.symbolName(), IS_VAR | IS_INTERNAL, null);
  3.1359          newType(scopeSymbol, Type.typeFor(ScriptObject.class));
  3.1360          scopeSymbol.setNeedsSlot(true);
  3.1361 -        functionNode.getScopeNode().setSymbol(scopeSymbol);
  3.1362 -        LOG.info("Initialized scope symbol: " + scopeSymbol);
  3.1363      }
  3.1364  
  3.1365 -    private void initReturn(final FunctionNode functionNode) {
  3.1366 -        final Symbol returnSymbol = defineSymbol(functionNode, SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null);
  3.1367 +    private void initReturn(final Block block) {
  3.1368 +        final Symbol returnSymbol = defineSymbol(block, RETURN.symbolName(), IS_VAR | IS_INTERNAL, null);
  3.1369          newType(returnSymbol, Type.OBJECT);
  3.1370          returnSymbol.setNeedsSlot(true);
  3.1371 -        functionNode.getResultNode().setSymbol(returnSymbol);
  3.1372 -        LOG.info("Initialized return symbol: " + returnSymbol);
  3.1373          //return symbol is always object as it's the __return__ thing. What returnType is is another matter though
  3.1374      }
  3.1375  
  3.1376 -    private void initVarArg(final FunctionNode functionNode) {
  3.1377 -        if (functionNode.isVarArg()) {
  3.1378 -            final Symbol varArgsSymbol = defineSymbol(functionNode, VARARGS.tag(), IS_PARAM | IS_INTERNAL, null);
  3.1379 -            varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY);
  3.1380 -            varArgsSymbol.setNeedsSlot(true);
  3.1381 -            functionNode.getVarArgsNode().setSymbol(varArgsSymbol);
  3.1382 -            LOG.info("Initialized varargs symbol: " + varArgsSymbol);
  3.1383 +    private void initVarArg(final Block block, final boolean needsArguments) {
  3.1384 +        final Symbol varArgsSymbol = defineSymbol(block, VARARGS.symbolName(), IS_PARAM | IS_INTERNAL, null);
  3.1385 +        varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY);
  3.1386 +        varArgsSymbol.setNeedsSlot(true);
  3.1387  
  3.1388 -            if (functionNode.needsArguments()) {
  3.1389 -                final String    argumentsName   = functionNode.getArgumentsNode().getName();
  3.1390 -                final Symbol    argumentsSymbol = defineSymbol(functionNode, argumentsName, IS_VAR | IS_INTERNAL, null);
  3.1391 -                newType(argumentsSymbol, Type.typeFor(ScriptObject.class));
  3.1392 -                argumentsSymbol.setNeedsSlot(true);
  3.1393 -                functionNode.getArgumentsNode().setSymbol(argumentsSymbol);
  3.1394 -                addLocalDef(argumentsName);
  3.1395 -                LOG.info("Initialized vararg varArgsSymbol=" + varArgsSymbol + " argumentsSymbol=" + argumentsSymbol);
  3.1396 -            }
  3.1397 +        if (needsArguments) {
  3.1398 +            final Symbol    argumentsSymbol = defineSymbol(block, ARGUMENTS.symbolName(), IS_VAR | IS_INTERNAL, null);
  3.1399 +            newType(argumentsSymbol, Type.typeFor(ScriptObject.class));
  3.1400 +            argumentsSymbol.setNeedsSlot(true);
  3.1401 +            addLocalDef(ARGUMENTS.symbolName());
  3.1402          }
  3.1403      }
  3.1404  
  3.1405 -    private void initCallee(final FunctionNode functionNode) {
  3.1406 -        assert functionNode.getCalleeNode() != null : functionNode + " has no callee";
  3.1407 -        final Symbol calleeSymbol = defineSymbol(functionNode, CALLEE.tag(), IS_PARAM | IS_INTERNAL, null);
  3.1408 -        newType(calleeSymbol, Type.typeFor(ScriptFunction.class));
  3.1409 +    private void initCallee(final Block block) {
  3.1410 +        final Symbol calleeSymbol = defineSymbol(block, CALLEE.symbolName(), IS_PARAM | IS_INTERNAL, null);
  3.1411 +        newType(calleeSymbol, FunctionNode.FUNCTION_TYPE);
  3.1412          calleeSymbol.setNeedsSlot(true);
  3.1413 -        functionNode.getCalleeNode().setSymbol(calleeSymbol);
  3.1414 -        LOG.info("Initialized callee symbol " + calleeSymbol);
  3.1415      }
  3.1416  
  3.1417      /**
  3.1418 @@ -1334,25 +1356,19 @@
  3.1419       *
  3.1420       * @param functionNode the function node
  3.1421       */
  3.1422 -    private void initParameters(final FunctionNode functionNode) {
  3.1423 -        //If a function is specialized, we don't need to tag either it return
  3.1424 -        // type or its parameters with the widest (OBJECT) type for safety.
  3.1425 -        functionNode.setReturnType(Type.UNKNOWN);
  3.1426 -
  3.1427 +    private void initParameters(final FunctionNode functionNode, final Block body) {
  3.1428          for (final IdentNode param : functionNode.getParameters()) {
  3.1429              addLocalDef(param.getName());
  3.1430 -            final Symbol paramSymbol = defineSymbol(functionNode, param.getName(), IS_PARAM, param);
  3.1431 +            final Symbol paramSymbol = defineSymbol(body, param.getName(), IS_PARAM, param);
  3.1432              if (paramSymbol != null) {
  3.1433                  final Type callSiteParamType = functionNode.getSpecializedType(param);
  3.1434                  if (callSiteParamType != null) {
  3.1435 -                    LOG.info("Param " + paramSymbol + " has a callsite type " + callSiteParamType + ". Using that.");
  3.1436 -
  3.1437 -                    System.err.println("Param " + param + " has a callsite type " + callSiteParamType + ". Using that.");
  3.1438 +                    LOG.info("Param ", paramSymbol, " has a callsite type ", callSiteParamType, ". Using that.");
  3.1439                  }
  3.1440                  newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
  3.1441              }
  3.1442  
  3.1443 -            LOG.info("Initialized param " + paramSymbol);
  3.1444 +            LOG.info("Initialized param ", paramSymbol);
  3.1445          }
  3.1446      }
  3.1447  
  3.1448 @@ -1378,7 +1394,7 @@
  3.1449              // this function, we can tell the runtime system that no matter what the
  3.1450              // call site is, use this information. TODO
  3.1451              if (!paramSymbol.getSymbolType().isObject()) {
  3.1452 -                LOG.finest("Parameter " + ident + " could profit from specialization to " + paramSymbol.getSymbolType());
  3.1453 +                LOG.finest("Parameter ", ident, " could profit from specialization to ", paramSymbol.getSymbolType());
  3.1454              }
  3.1455  
  3.1456              newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType()));
  3.1457 @@ -1392,19 +1408,18 @@
  3.1458  
  3.1459      /**
  3.1460       * Move any properties from a global map into the scope of this method
  3.1461 -     * @param functionNode the function node for which to init scope vars
  3.1462 +     * @param block the function node body for which to init scope vars
  3.1463       */
  3.1464 -    private void initFromPropertyMap(final FunctionNode functionNode) {
  3.1465 +    private void initFromPropertyMap(final Block block) {
  3.1466          // For a script, add scope symbols as defined in the property map
  3.1467 -        assert functionNode.isProgram();
  3.1468  
  3.1469          final PropertyMap map = Context.getGlobalMap();
  3.1470  
  3.1471          for (final Property property : map.getProperties()) {
  3.1472              final String key    = property.getKey();
  3.1473 -            final Symbol symbol = defineSymbol(functionNode, key, IS_GLOBAL, null);
  3.1474 +            final Symbol symbol = defineSymbol(block, key, IS_GLOBAL, null);
  3.1475              newType(symbol, Type.OBJECT);
  3.1476 -            LOG.info("Added global symbol from property map " + symbol);
  3.1477 +            LOG.info("Added global symbol from property map ", symbol);
  3.1478          }
  3.1479      }
  3.1480  
  3.1481 @@ -1412,7 +1427,7 @@
  3.1482  
  3.1483          final Symbol symbol = node.getSymbol();
  3.1484  
  3.1485 -        LOG.info("Ensure type not unknown for: " + symbol);
  3.1486 +        LOG.info("Ensure type not unknown for: ", symbol);
  3.1487  
  3.1488          /*
  3.1489           * Note that not just unknowns, but params need to be blown
  3.1490 @@ -1452,7 +1467,7 @@
  3.1491      }
  3.1492  
  3.1493      private Symbol exceptionSymbol() {
  3.1494 -        return newInternal(getCurrentFunctionNode().uniqueName(EXCEPTION_PREFIX.tag()), Type.typeFor(ECMAException.class));
  3.1495 +        return newInternal(getLexicalContext().getCurrentFunction().uniqueName(EXCEPTION_PREFIX.symbolName()), Type.typeFor(ECMAException.class));
  3.1496      }
  3.1497  
  3.1498      /**
  3.1499 @@ -1512,15 +1527,15 @@
  3.1500                      }
  3.1501                      Type from = node.getType();
  3.1502                      if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) {
  3.1503 -                        LOG.fine("Had to post pass widen '" + node + "' " + Debug.id(node) + " from " + node.getType() + " to " + to);
  3.1504 +                        LOG.fine("Had to post pass widen '", node, "' " + Debug.id(node), " from ", node.getType(), " to ", to);
  3.1505                          newType(node.getSymbol(), to);
  3.1506                          changed.add(node);
  3.1507                      }
  3.1508                  }
  3.1509  
  3.1510                  @Override
  3.1511 -                public Node enterFunctionNode(final FunctionNode node) {
  3.1512 -                    return node.isLazy() ? null : node;
  3.1513 +                public boolean enterFunctionNode(final FunctionNode node) {
  3.1514 +                    return !node.isLazy();
  3.1515                  }
  3.1516  
  3.1517                  /**
  3.1518 @@ -1574,7 +1589,7 @@
  3.1519          } else {
  3.1520              type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too.
  3.1521          }
  3.1522 -        newTemporary(type, binaryNode);
  3.1523 +        ensureSymbol(type, binaryNode);
  3.1524          newType(lhs.getSymbol(), type);
  3.1525          end(binaryNode);
  3.1526          return binaryNode;
  3.1527 @@ -1589,32 +1604,25 @@
  3.1528          final Node lhs = binaryNode.lhs();
  3.1529  
  3.1530          newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType
  3.1531 -        newTemporary(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
  3.1532 +        ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
  3.1533  
  3.1534 -        ensureAssignmentSlots(getCurrentFunctionNode(), binaryNode);
  3.1535 +        ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), binaryNode);
  3.1536  
  3.1537          end(binaryNode);
  3.1538          return binaryNode;
  3.1539      }
  3.1540  
  3.1541 -    private static boolean isFunctionExpressionSelfReference(final Symbol symbol) {
  3.1542 -        if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) {
  3.1543 -            return ((FunctionNode)symbol.getNode()).getIdent().getName().equals(symbol.getName());
  3.1544 -        }
  3.1545 -        return false;
  3.1546 +    private Symbol ensureSymbol(final FunctionNode functionNode, final Type type, final Node node) {
  3.1547 +        LOG.info("New TEMPORARY added to ", functionNode.getName(), " type=", type);
  3.1548 +        return functionNode.ensureSymbol(getLexicalContext().getCurrentBlock(), type, node);
  3.1549      }
  3.1550  
  3.1551 -    private static Symbol newTemporary(final FunctionNode functionNode, final Type type, final Node node) {
  3.1552 -        LOG.info("New TEMPORARY added to " + functionNode.getName() + " type=" + type);
  3.1553 -        return functionNode.newTemporary(type, node);
  3.1554 -    }
  3.1555 -
  3.1556 -    private Symbol newTemporary(final Type type, final Node node) {
  3.1557 -        return newTemporary(getCurrentFunctionNode(), type, node);
  3.1558 +    private Symbol ensureSymbol(final Type type, final Node node) {
  3.1559 +        return ensureSymbol(getLexicalContext().getCurrentFunction(), type, node);
  3.1560      }
  3.1561  
  3.1562      private Symbol newInternal(final String name, final Type type) {
  3.1563 -        final Symbol iter = defineSymbol(getCurrentFunctionNode(), name, IS_VAR | IS_INTERNAL, null);
  3.1564 +        final Symbol iter = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR | IS_INTERNAL, null);
  3.1565          iter.setType(type); // NASHORN-73
  3.1566          return iter;
  3.1567      }
  3.1568 @@ -1624,40 +1632,51 @@
  3.1569          symbol.setType(type);
  3.1570  
  3.1571          if (symbol.getSymbolType() != oldType) {
  3.1572 -            LOG.info("New TYPE " + type + " for " + symbol + " (was " + oldType + ")");
  3.1573 +            LOG.info("New TYPE ", type, " for ", symbol," (was ", oldType, ")");
  3.1574          }
  3.1575  
  3.1576          if (symbol.isParam()) {
  3.1577              symbol.setType(type);
  3.1578 -            LOG.info("Param type change " + symbol);
  3.1579 +            LOG.info("Param type change ", symbol);
  3.1580          }
  3.1581      }
  3.1582  
  3.1583 -    private void clearLocalDefs() {
  3.1584 -        localDefs = new HashSet<>();
  3.1585 +    private void pushLocalsFunction() {
  3.1586 +        localDefs.push(new HashSet<String>());
  3.1587 +        localUses.push(new HashSet<String>());
  3.1588 +    }
  3.1589 +
  3.1590 +    private void pushLocalsBlock() {
  3.1591 +        localDefs.push(localDefs.isEmpty() ? new HashSet<String>() : new HashSet<>(localDefs.peek()));
  3.1592 +        localUses.push(localUses.isEmpty() ? new HashSet<String>() : new HashSet<>(localUses.peek()));
  3.1593 +    }
  3.1594 +
  3.1595 +    private void popLocals() {
  3.1596 +        localDefs.pop();
  3.1597 +        localUses.pop();
  3.1598      }
  3.1599  
  3.1600      private boolean isLocalDef(final String name) {
  3.1601 -        return localDefs.contains(name);
  3.1602 +        return localDefs.peek().contains(name);
  3.1603      }
  3.1604  
  3.1605      private void addLocalDef(final String name) {
  3.1606 -        LOG.info("Adding local def of symbol: '" + name + "'");
  3.1607 -        localDefs.add(name);
  3.1608 +        LOG.info("Adding local def of symbol: '", name, "'");
  3.1609 +        localDefs.peek().add(name);
  3.1610      }
  3.1611  
  3.1612      private void removeLocalDef(final String name) {
  3.1613 -        LOG.info("Removing local def of symbol: '" + name + "'");
  3.1614 -        localDefs.remove(name);
  3.1615 +        LOG.info("Removing local def of symbol: '", name, "'");
  3.1616 +        localDefs.peek().remove(name);
  3.1617      }
  3.1618  
  3.1619 -    private void clearLocalUses() {
  3.1620 -        localUses = new HashSet<>();
  3.1621 +    private boolean isLocalUse(final String name) {
  3.1622 +        return localUses.peek().contains(name);
  3.1623      }
  3.1624  
  3.1625      private void addLocalUse(final String name) {
  3.1626 -        LOG.info("Adding local use of symbol: '" + name + "'");
  3.1627 -        localUses.add(name);
  3.1628 +        LOG.info("Adding local use of symbol: '", name, "'");
  3.1629 +        localUses.peek().add(name);
  3.1630      }
  3.1631  
  3.1632      /**
  3.1633 @@ -1665,30 +1684,28 @@
  3.1634       * This is done when the function contains unevaluated black boxes such as
  3.1635       * lazy sub-function nodes that have not been compiled.
  3.1636       *
  3.1637 -     * @param functionNode function node in whose scope symbols should conservatively be made objects
  3.1638 +     * @param body body for the function node we are leaving
  3.1639       */
  3.1640 -    private static void objectifySymbols(final FunctionNode functionNode) {
  3.1641 -        functionNode.accept(new NodeVisitor() {
  3.1642 +    private static void objectifySymbols(final Block body) {
  3.1643 +        body.accept(new NodeVisitor() {
  3.1644              private void toObject(final Block block) {
  3.1645                  for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext();) {
  3.1646                      final Symbol symbol = iter.next();
  3.1647 -                    newType(symbol, Type.OBJECT);
  3.1648 +                    if (!symbol.isTemp()) {
  3.1649 +                        newType(symbol, Type.OBJECT);
  3.1650 +                    }
  3.1651                  }
  3.1652              }
  3.1653  
  3.1654              @Override
  3.1655 -            public Node enterBlock(final Block block) {
  3.1656 +            public boolean enterBlock(final Block block) {
  3.1657                  toObject(block);
  3.1658 -                return block;
  3.1659 +                return true;
  3.1660              }
  3.1661  
  3.1662              @Override
  3.1663 -            public Node enterFunctionNode(final FunctionNode node) {
  3.1664 -                toObject(node);
  3.1665 -                if (node.isLazy()) {
  3.1666 -                    return null;
  3.1667 -                }
  3.1668 -                return node;
  3.1669 +            public boolean enterFunctionNode(final FunctionNode node) {
  3.1670 +                return false;
  3.1671              }
  3.1672          });
  3.1673      }
  3.1674 @@ -1702,11 +1719,11 @@
  3.1675          return cn.substring(lastDot + 1);
  3.1676      }
  3.1677  
  3.1678 -    private Node start(final Node node) {
  3.1679 +    private boolean start(final Node node) {
  3.1680          return start(node, true);
  3.1681      }
  3.1682  
  3.1683 -    private Node start(final Node node, final boolean printNode) {
  3.1684 +    private boolean start(final Node node, final boolean printNode) {
  3.1685          if (DEBUG) {
  3.1686              final StringBuilder sb = new StringBuilder();
  3.1687  
  3.1688 @@ -1715,13 +1732,13 @@
  3.1689                  append("] ").
  3.1690                  append(printNode ? node.toString() : "").
  3.1691                  append(" in '").
  3.1692 -                append(getCurrentFunctionNode().getName()).
  3.1693 +                append(getLexicalContext().getCurrentFunction().getName()).
  3.1694                  append("'");
  3.1695 -            LOG.info(sb.toString());
  3.1696 +            LOG.info(sb);
  3.1697              LOG.indent();
  3.1698          }
  3.1699  
  3.1700 -        return node;
  3.1701 +        return true;
  3.1702      }
  3.1703  
  3.1704      private Node end(final Node node) {
  3.1705 @@ -1737,7 +1754,7 @@
  3.1706                  append("] ").
  3.1707                  append(printNode ? node.toString() : "").
  3.1708                  append(" in '").
  3.1709 -                append(getCurrentFunctionNode().getName());
  3.1710 +                append(getLexicalContext().getCurrentFunction().getName());
  3.1711  
  3.1712              if (node.getSymbol() == null) {
  3.1713                  sb.append(" <NO SYMBOL>");
  3.1714 @@ -1746,7 +1763,7 @@
  3.1715              }
  3.1716  
  3.1717              LOG.unindent();
  3.1718 -            LOG.info(sb.toString());
  3.1719 +            LOG.info(sb);
  3.1720          }
  3.1721  
  3.1722          return node;
     4.1 --- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Fri Apr 19 18:23:00 2013 +0530
     4.2 +++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Fri Apr 19 16:11:16 2013 +0200
     4.3 @@ -58,12 +58,14 @@
     4.4  import java.util.EnumSet;
     4.5  import java.util.HashSet;
     4.6  import java.util.Set;
     4.7 +
     4.8  import jdk.internal.org.objectweb.asm.ClassReader;
     4.9  import jdk.internal.org.objectweb.asm.ClassWriter;
    4.10  import jdk.internal.org.objectweb.asm.MethodVisitor;
    4.11  import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
    4.12  import jdk.nashorn.internal.codegen.types.Type;
    4.13  import jdk.nashorn.internal.ir.FunctionNode;
    4.14 +import jdk.nashorn.internal.ir.SplitNode;
    4.15  import jdk.nashorn.internal.runtime.PropertyMap;
    4.16  import jdk.nashorn.internal.runtime.ScriptEnvironment;
    4.17  import jdk.nashorn.internal.runtime.ScriptObject;
    4.18 @@ -219,14 +221,14 @@
    4.19      private void defineCommonStatics(final boolean strictMode) {
    4.20          // source - used to store the source data (text) for this script.  Shared across
    4.21          // compile units.  Set externally by the compiler.
    4.22 -        field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.tag(), Source.class);
    4.23 +        field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.symbolName(), Source.class);
    4.24  
    4.25          // constants - used to the constants array for this script.  Shared across
    4.26          // compile units.  Set externally by the compiler.
    4.27 -        field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.tag(), Object[].class);
    4.28 +        field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.symbolName(), Object[].class);
    4.29  
    4.30          // strictMode - was this script compiled in strict mode.  Set externally by the compiler.
    4.31 -        field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.tag(), boolean.class, strictMode);
    4.32 +        field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.symbolName(), boolean.class, strictMode);
    4.33      }
    4.34  
    4.35      /**
    4.36 @@ -238,9 +240,9 @@
    4.37  
    4.38          if (constantMethodNeeded.contains(String.class)) {
    4.39              // $getString - get the ith entry from the constants table and cast to String.
    4.40 -            final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.tag(), String.class, int.class);
    4.41 +            final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.symbolName(), String.class, int.class);
    4.42              getStringMethod.begin();
    4.43 -            getStringMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor())
    4.44 +            getStringMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
    4.45                          .load(Type.INT, 0)
    4.46                          .arrayload()
    4.47                          .checkcast(String.class)
    4.48 @@ -250,7 +252,7 @@
    4.49  
    4.50          if (constantMethodNeeded.contains(PropertyMap.class)) {
    4.51              // $getMap - get the ith entry from the constants table and cast to PropertyMap.
    4.52 -            final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.tag(), PropertyMap.class, int.class);
    4.53 +            final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.symbolName(), PropertyMap.class, int.class);
    4.54              getMapMethod.begin();
    4.55              getMapMethod.loadConstants()
    4.56                          .load(Type.INT, 0)
    4.57 @@ -260,7 +262,7 @@
    4.58              getMapMethod.end();
    4.59  
    4.60              // $setMap - overwrite an existing map.
    4.61 -            final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.tag(), void.class, int.class, PropertyMap.class);
    4.62 +            final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.symbolName(), void.class, int.class, PropertyMap.class);
    4.63              setMapMethod.begin();
    4.64              setMapMethod.loadConstants()
    4.65                          .load(Type.INT, 0)
    4.66 @@ -289,7 +291,7 @@
    4.67          final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, cls, int.class);
    4.68  
    4.69          getArrayMethod.begin();
    4.70 -        getArrayMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor())
    4.71 +        getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
    4.72                        .load(Type.INT, 0)
    4.73                        .arrayload()
    4.74                        .checkcast(cls)
    4.75 @@ -307,7 +309,7 @@
    4.76       */
    4.77      static String getArrayMethodName(final Class<?> cls) {
    4.78          assert cls.isArray();
    4.79 -        return GET_ARRAY_PREFIX.tag() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.tag();
    4.80 +        return GET_ARRAY_PREFIX.symbolName() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
    4.81      }
    4.82  
    4.83      /**
    4.84 @@ -409,6 +411,10 @@
    4.85          methodsStarted.remove(method);
    4.86      }
    4.87  
    4.88 +    SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
    4.89 +        return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode);
    4.90 +    }
    4.91 +
    4.92      /**
    4.93       * Add a new method to the class - defaults to public method
    4.94       *
    4.95 @@ -433,7 +439,7 @@
    4.96       * @return method emitter to use for weaving this method
    4.97       */
    4.98      MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
    4.99 -        return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, methodDescriptor(rtype, ptypes), null, null));
   4.100 +        return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes));
   4.101      }
   4.102  
   4.103      /**
   4.104 @@ -484,7 +490,7 @@
   4.105       * @return method emitter to use for weaving <clinit>
   4.106       */
   4.107      MethodEmitter clinit() {
   4.108 -        return method(EnumSet.of(Flag.STATIC), CLINIT.tag(), void.class);
   4.109 +        return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class);
   4.110      }
   4.111  
   4.112      /**
   4.113 @@ -493,7 +499,7 @@
   4.114       * @return method emitter to use for weaving <init>()V
   4.115       */
   4.116      MethodEmitter init() {
   4.117 -        return method(INIT.tag(), void.class);
   4.118 +        return method(INIT.symbolName(), void.class);
   4.119      }
   4.120  
   4.121      /**
   4.122 @@ -503,7 +509,7 @@
   4.123       * @return method emitter to use for weaving <init>()V
   4.124       */
   4.125      MethodEmitter init(final Class<?>... ptypes) {
   4.126 -        return method(INIT.tag(), void.class, ptypes);
   4.127 +        return method(INIT.symbolName(), void.class, ptypes);
   4.128      }
   4.129  
   4.130      /**
   4.131 @@ -515,7 +521,7 @@
   4.132       * @return method emitter to use for weaving <init>(...)V
   4.133       */
   4.134      MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) {
   4.135 -        return method(flags, INIT.tag(), void.class, ptypes);
   4.136 +        return method(flags, INIT.symbolName(), void.class, ptypes);
   4.137      }
   4.138  
   4.139      /**
   4.140 @@ -628,4 +634,9 @@
   4.141              return v;
   4.142          }
   4.143      }
   4.144 +
   4.145 +    private MethodVisitor methodVisitor(EnumSet<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
   4.146 +        return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null);
   4.147 +    }
   4.148 +
   4.149  }
     5.1 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Fri Apr 19 18:23:00 2013 +0530
     5.2 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Fri Apr 19 16:11:16 2013 +0200
     5.3 @@ -27,14 +27,18 @@
     5.4  
     5.5  import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE;
     5.6  import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC;
     5.7 +import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
     5.8 +import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
     5.9  import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
    5.10  import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
    5.11 -import static jdk.nashorn.internal.codegen.CompilerConstants.LEAF;
    5.12  import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX;
    5.13  import static jdk.nashorn.internal.codegen.CompilerConstants.REGEX_PREFIX;
    5.14 +import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
    5.15  import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
    5.16  import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_ARRAY_ARG;
    5.17  import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
    5.18 +import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
    5.19 +import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
    5.20  import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
    5.21  import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup;
    5.22  import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
    5.23 @@ -48,8 +52,10 @@
    5.24  import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
    5.25  
    5.26  import java.io.PrintWriter;
    5.27 +import java.util.ArrayDeque;
    5.28  import java.util.ArrayList;
    5.29  import java.util.Arrays;
    5.30 +import java.util.Deque;
    5.31  import java.util.EnumSet;
    5.32  import java.util.HashMap;
    5.33  import java.util.Iterator;
    5.34 @@ -67,11 +73,11 @@
    5.35  import jdk.nashorn.internal.ir.BinaryNode;
    5.36  import jdk.nashorn.internal.ir.Block;
    5.37  import jdk.nashorn.internal.ir.BreakNode;
    5.38 +import jdk.nashorn.internal.ir.BreakableNode;
    5.39  import jdk.nashorn.internal.ir.CallNode;
    5.40  import jdk.nashorn.internal.ir.CaseNode;
    5.41  import jdk.nashorn.internal.ir.CatchNode;
    5.42  import jdk.nashorn.internal.ir.ContinueNode;
    5.43 -import jdk.nashorn.internal.ir.DoWhileNode;
    5.44  import jdk.nashorn.internal.ir.EmptyNode;
    5.45  import jdk.nashorn.internal.ir.ExecuteNode;
    5.46  import jdk.nashorn.internal.ir.ForNode;
    5.47 @@ -85,6 +91,7 @@
    5.48  import jdk.nashorn.internal.ir.LiteralNode;
    5.49  import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
    5.50  import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
    5.51 +import jdk.nashorn.internal.ir.LoopNode;
    5.52  import jdk.nashorn.internal.ir.Node;
    5.53  import jdk.nashorn.internal.ir.ObjectNode;
    5.54  import jdk.nashorn.internal.ir.PropertyNode;
    5.55 @@ -107,6 +114,7 @@
    5.56  import jdk.nashorn.internal.parser.Lexer.RegexToken;
    5.57  import jdk.nashorn.internal.parser.TokenType;
    5.58  import jdk.nashorn.internal.runtime.Context;
    5.59 +import jdk.nashorn.internal.runtime.Debug;
    5.60  import jdk.nashorn.internal.runtime.DebugLogger;
    5.61  import jdk.nashorn.internal.runtime.ECMAException;
    5.62  import jdk.nashorn.internal.runtime.Property;
    5.63 @@ -157,14 +165,29 @@
    5.64      /** How many regexp fields have been emitted */
    5.65      private int regexFieldCount;
    5.66  
    5.67 -    /** Used for temporary signaling between enterCallNode and enterFunctionNode to handle the special case of calling
    5.68 -     * a just-defined anonymous function expression. */
    5.69 -    private boolean functionNodeIsCallee;
    5.70 -
    5.71      /** Map of shared scope call sites */
    5.72      private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>();
    5.73  
    5.74 -    private final LexicalContext lexicalContext = new LexicalContext();
    5.75 +    /** Compile unit stack - every time we start a sub method (e.g. a split) we push one */
    5.76 +    private final Deque<CompileUnit> compileUnits = new ArrayDeque<>();
    5.77 +
    5.78 +    /** Method emitter stack - every time we start a sub method (e.g. a split) we push one */
    5.79 +    private final Deque<MethodEmitter> methodEmitters = new ArrayDeque<>();
    5.80 +
    5.81 +    /** The discard stack - whenever we enter a discard node we keep track of its return value status -
    5.82 +     *  i.e. should we keep it or throw it away */
    5.83 +    private final Deque<Node> discard = new ArrayDeque<>();
    5.84 +
    5.85 +    // A stack tracking the next free local variable slot in the blocks. There's one entry for every block
    5.86 +    // currently on the lexical context stack.
    5.87 +    private int[] nextFreeSlots = new int[16];
    5.88 +    private int nextFreeSlotsSize = 0;
    5.89 +
    5.90 +    /** Current method emitter */
    5.91 +    private MethodEmitter method;
    5.92 +
    5.93 +    /** Current compile unit */
    5.94 +    private CompileUnit unit;
    5.95  
    5.96      /** When should we stop caching regexp expressions in fields to limit bytecode size? */
    5.97      private static final int MAX_REGEX_FIELDS = 2 * 1024;
    5.98 @@ -188,7 +211,37 @@
    5.99       * @return the correct flags for a call site in the current function
   5.100       */
   5.101      int getCallSiteFlags() {
   5.102 -        return getCurrentFunctionNode().isStrictMode() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
   5.103 +        return getLexicalContext().getCurrentFunction().isStrict() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
   5.104 +    }
   5.105 +
   5.106 +    private void pushMethodEmitter(final MethodEmitter newMethod) {
   5.107 +        methodEmitters.push(newMethod);
   5.108 +        this.method = newMethod;
   5.109 +    }
   5.110 +
   5.111 +    private void popMethodEmitter(final MethodEmitter oldMethod) {
   5.112 +        assert methodEmitters.peek() == oldMethod;
   5.113 +        methodEmitters.pop();
   5.114 +        if (!methodEmitters.isEmpty()) {
   5.115 +            this.method = methodEmitters.peek();
   5.116 +        } else {
   5.117 +            this.method = null;
   5.118 +        }
   5.119 +    }
   5.120 +
   5.121 +    private void push(final CompileUnit newUnit) {
   5.122 +        compileUnits.push(newUnit);
   5.123 +        this.unit = newUnit;
   5.124 +    }
   5.125 +
   5.126 +    private void pop(final CompileUnit oldUnit) {
   5.127 +        assert compileUnits.peek() == oldUnit;
   5.128 +        compileUnits.pop();
   5.129 +        if (!compileUnits.isEmpty()) {
   5.130 +            this.unit = compileUnits.peek();
   5.131 +        } else {
   5.132 +            this.unit = null;
   5.133 +        }
   5.134      }
   5.135  
   5.136      /**
   5.137 @@ -217,7 +270,7 @@
   5.138              assert identNode.getSymbol().isScope() : identNode + " is not in scope!";
   5.139  
   5.140              final int flags = CALLSITE_SCOPE | getCallSiteFlags();
   5.141 -            method.loadScope();
   5.142 +            method.loadCompilerConstant(SCOPE);
   5.143  
   5.144              if (isFastScope(symbol)) {
   5.145                  // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
   5.146 @@ -237,11 +290,11 @@
   5.147       * @return true if fast scope
   5.148       */
   5.149      private boolean isFastScope(final Symbol symbol) {
   5.150 -        if (!symbol.isScope() || !symbol.getBlock().needsScope()) {
   5.151 +        if (!symbol.isScope() || !getLexicalContext().getDefiningBlock(symbol).needsScope()) {
   5.152              return false;
   5.153          }
   5.154          // Allow fast scope access if no function contains with or eval
   5.155 -        for(final Iterator<FunctionNode> it = lexicalContext.getFunctions(getCurrentFunctionNode()); it.hasNext();) {
   5.156 +        for (final Iterator<FunctionNode> it = getLexicalContext().getFunctions(); it.hasNext();) {
   5.157              final FunctionNode func = it.next();
   5.158              if (func.hasWith() || func.hasEval()) {
   5.159                  return false;
   5.160 @@ -251,7 +304,7 @@
   5.161      }
   5.162  
   5.163      private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) {
   5.164 -        method.load(isFastScope(symbol) ? getScopeProtoDepth(getCurrentBlock(), symbol) : -1);
   5.165 +        method.load(isFastScope(symbol) ? getScopeProtoDepth(getLexicalContext().getCurrentBlock(), symbol) : -1);
   5.166          final SharedScopeCall scopeCall = getScopeGet(valueType, symbol, flags | CALLSITE_FAST_SCOPE);
   5.167          scopeCall.generateInvoke(method);
   5.168          return method;
   5.169 @@ -271,10 +324,10 @@
   5.170  
   5.171      private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) {
   5.172          int depth = 0;
   5.173 -        final Block definingBlock = symbol.getBlock();
   5.174 -        for(final Iterator<Block> blocks = lexicalContext.getBlocks(startingBlock); blocks.hasNext();) {
   5.175 +        final String name = symbol.getName();
   5.176 +        for(final Iterator<Block> blocks = getLexicalContext().getBlocks(startingBlock); blocks.hasNext();) {
   5.177              final Block currentBlock = blocks.next();
   5.178 -            if (currentBlock == definingBlock) {
   5.179 +            if (currentBlock.getExistingSymbol(name) == symbol) {
   5.180                  return depth;
   5.181              }
   5.182              if (currentBlock.needsScope()) {
   5.183 @@ -285,9 +338,9 @@
   5.184      }
   5.185  
   5.186      private void loadFastScopeProto(final Symbol symbol, final boolean swap) {
   5.187 -        final int depth = getScopeProtoDepth(getCurrentBlock(), symbol);
   5.188 +        final int depth = getScopeProtoDepth(getLexicalContext().getCurrentBlock(), symbol);
   5.189          assert depth != -1;
   5.190 -        if(depth > 0) {
   5.191 +        if (depth > 0) {
   5.192              if (swap) {
   5.193                  method.swap();
   5.194              }
   5.195 @@ -328,46 +381,46 @@
   5.196           */
   5.197          final CodeGenerator codegen = this;
   5.198  
   5.199 -        node.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
   5.200 +        node.accept(new NodeVisitor() {
   5.201              @Override
   5.202 -            public Node enterIdentNode(final IdentNode identNode) {
   5.203 +            public boolean enterIdentNode(final IdentNode identNode) {
   5.204                  loadIdent(identNode);
   5.205 -                return null;
   5.206 +                return false;
   5.207              }
   5.208  
   5.209              @Override
   5.210 -            public Node enterAccessNode(final AccessNode accessNode) {
   5.211 +            public boolean enterAccessNode(final AccessNode accessNode) {
   5.212                  if (!baseAlreadyOnStack) {
   5.213                      load(accessNode.getBase()).convert(Type.OBJECT);
   5.214                  }
   5.215                  assert method.peekType().isObject();
   5.216                  method.dynamicGet(node.getType(), accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction());
   5.217 -                return null;
   5.218 +                return false;
   5.219              }
   5.220  
   5.221              @Override
   5.222 -            public Node enterIndexNode(final IndexNode indexNode) {
   5.223 +            public boolean enterIndexNode(final IndexNode indexNode) {
   5.224                  if (!baseAlreadyOnStack) {
   5.225                      load(indexNode.getBase()).convert(Type.OBJECT);
   5.226                      load(indexNode.getIndex());
   5.227                  }
   5.228                  method.dynamicGetIndex(node.getType(), getCallSiteFlags(), indexNode.isFunction());
   5.229 -                return null;
   5.230 +                return false;
   5.231              }
   5.232  
   5.233              @Override
   5.234 -            public Node enterFunctionNode(FunctionNode functionNode) {
   5.235 +            public boolean enterFunctionNode(FunctionNode functionNode) {
   5.236                  // function nodes will always leave a constructed function object on stack, no need to load the symbol
   5.237                  // separately as in enterDefault()
   5.238                  functionNode.accept(codegen);
   5.239 -                return null;
   5.240 +                return false;
   5.241              }
   5.242  
   5.243              @Override
   5.244 -            public Node enterDefault(final Node otherNode) {
   5.245 +            public boolean enterDefault(final Node otherNode) {
   5.246                  otherNode.accept(codegen); // generate code for whatever we are looking at.
   5.247                  method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there)
   5.248 -                return null;
   5.249 +                return false;
   5.250              }
   5.251          });
   5.252  
   5.253 @@ -375,14 +428,9 @@
   5.254      }
   5.255  
   5.256      @Override
   5.257 -    public Node enterAccessNode(final AccessNode accessNode) {
   5.258 -        if (accessNode.testResolved()) {
   5.259 -            return null;
   5.260 -        }
   5.261 -
   5.262 +    public boolean enterAccessNode(final AccessNode accessNode) {
   5.263          load(accessNode);
   5.264 -
   5.265 -        return null;
   5.266 +        return false;
   5.267      }
   5.268  
   5.269      /**
   5.270 @@ -407,7 +455,7 @@
   5.271              final boolean isInternal = symbol.isParam() || symbol.isInternal() || symbol.isThis() || !symbol.canBeUndefined();
   5.272  
   5.273              if (symbol.hasSlot() && !isInternal) {
   5.274 -                assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getCurrentFunctionNode();
   5.275 +                assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getLexicalContext().getCurrentFunction();
   5.276                  if (symbol.getSymbolType().isNumber()) {
   5.277                      numbers.add(symbol);
   5.278                  } else if (symbol.getSymbolType().isObject()) {
   5.279 @@ -441,22 +489,20 @@
   5.280       * @param block block containing symbols.
   5.281       */
   5.282      private void symbolInfo(final Block block) {
   5.283 -        for (final Symbol symbol : block.getFrame().getSymbols()) {
   5.284 -            method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel());
   5.285 +        for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
   5.286 +            final Symbol symbol = iter.next();
   5.287 +            if (symbol.hasSlot()) {
   5.288 +                method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel());
   5.289 +            }
   5.290          }
   5.291      }
   5.292  
   5.293      @Override
   5.294 -    public Node enterBlock(final Block block) {
   5.295 -        if (block.testResolved()) {
   5.296 -            return null;
   5.297 -        }
   5.298 -        lexicalContext.push(block);
   5.299 -
   5.300 +    public boolean enterBlock(final Block block) {
   5.301          method.label(block.getEntryLabel());
   5.302          initLocals(block);
   5.303  
   5.304 -        return block;
   5.305 +        return true;
   5.306      }
   5.307  
   5.308      @Override
   5.309 @@ -464,10 +510,10 @@
   5.310          method.label(block.getBreakLabel());
   5.311          symbolInfo(block);
   5.312  
   5.313 -        if (block.needsScope()) {
   5.314 +        if (block.needsScope() && !block.isTerminal()) {
   5.315              popBlockScope(block);
   5.316          }
   5.317 -        lexicalContext.pop(block);
   5.318 +        --nextFreeSlotsSize;
   5.319          return block;
   5.320      }
   5.321  
   5.322 @@ -477,34 +523,30 @@
   5.323          final Label skipLabel     = new Label("skip_catch");
   5.324  
   5.325          /* pop scope a la try-finally */
   5.326 -        method.loadScope();
   5.327 +        method.loadCompilerConstant(SCOPE);
   5.328          method.invoke(ScriptObject.GET_PROTO);
   5.329 -        method.storeScope();
   5.330 +        method.storeCompilerConstant(SCOPE);
   5.331          method._goto(skipLabel);
   5.332          method.label(exitLabel);
   5.333  
   5.334          method._catch(recoveryLabel);
   5.335 -        method.loadScope();
   5.336 +        method.loadCompilerConstant(SCOPE);
   5.337          method.invoke(ScriptObject.GET_PROTO);
   5.338 -        method.storeScope();
   5.339 +        method.storeCompilerConstant(SCOPE);
   5.340          method.athrow();
   5.341          method.label(skipLabel);
   5.342          method._try(block.getEntryLabel(), exitLabel, recoveryLabel, Throwable.class);
   5.343      }
   5.344  
   5.345      @Override
   5.346 -    public Node enterBreakNode(final BreakNode breakNode) {
   5.347 -        if (breakNode.testResolved()) {
   5.348 -            return null;
   5.349 -        }
   5.350 -
   5.351 -        for (int i = 0; i < breakNode.getScopeNestingLevel(); i++) {
   5.352 +    public boolean enterBreakNode(final BreakNode breakNode) {
   5.353 +        final BreakableNode breakFrom = getLexicalContext().getBreakable(breakNode.getLabel());
   5.354 +        for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(breakFrom); i++) {
   5.355              closeWith();
   5.356          }
   5.357 -
   5.358 -        method.splitAwareGoto(breakNode.getTargetLabel());
   5.359 -
   5.360 -        return null;
   5.361 +        method.splitAwareGoto(getLexicalContext(), breakFrom.getBreakLabel());
   5.362 +
   5.363 +        return false;
   5.364      }
   5.365  
   5.366      private int loadArgs(final List<Node> args) {
   5.367 @@ -541,21 +583,17 @@
   5.368      }
   5.369  
   5.370      @Override
   5.371 -    public Node enterCallNode(final CallNode callNode) {
   5.372 -        if (callNode.testResolved()) {
   5.373 -            return null;
   5.374 -        }
   5.375 -
   5.376 +    public boolean enterCallNode(final CallNode callNode) {
   5.377          final List<Node>   args            = callNode.getArgs();
   5.378          final Node         function        = callNode.getFunction();
   5.379 -        final Block        currentBlock    = getCurrentBlock();
   5.380 -
   5.381 -        function.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
   5.382 +        final Block        currentBlock    = getLexicalContext().getCurrentBlock();
   5.383 +
   5.384 +        function.accept(new NodeVisitor() {
   5.385  
   5.386              private void sharedScopeCall(final IdentNode identNode, final int flags) {
   5.387                  final Symbol symbol = identNode.getSymbol();
   5.388                  int    scopeCallFlags = flags;
   5.389 -                method.loadScope();
   5.390 +                method.loadCompilerConstant(SCOPE);
   5.391                  if (isFastScope(symbol)) {
   5.392                      method.load(getScopeProtoDepth(currentBlock, symbol));
   5.393                      scopeCallFlags |= CALLSITE_FAST_SCOPE;
   5.394 @@ -591,7 +629,7 @@
   5.395                  // We don't need ScriptFunction object for 'eval'
   5.396                  method.pop();
   5.397  
   5.398 -                method.loadScope(); // Load up self (scope).
   5.399 +                method.loadCompilerConstant(SCOPE); // Load up self (scope).
   5.400  
   5.401                  final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
   5.402                  // load evaluated code
   5.403 @@ -618,7 +656,7 @@
   5.404              }
   5.405  
   5.406              @Override
   5.407 -            public Node enterIdentNode(final IdentNode node) {
   5.408 +            public boolean enterIdentNode(final IdentNode node) {
   5.409                  final Symbol symbol = node.getSymbol();
   5.410  
   5.411                  if (symbol.isScope()) {
   5.412 @@ -637,16 +675,16 @@
   5.413                      } else {
   5.414                          sharedScopeCall(node, flags);
   5.415                      }
   5.416 -                    assert method.peekType().equals(callNode.getType());
   5.417 +                    assert method.peekType().equals(callNode.getType()) : method.peekType() + "!=" + callNode.getType();
   5.418                  } else {
   5.419                      enterDefault(node);
   5.420                  }
   5.421  
   5.422 -                return null;
   5.423 +                return false;
   5.424              }
   5.425  
   5.426              @Override
   5.427 -            public Node enterAccessNode(final AccessNode node) {
   5.428 +            public boolean enterAccessNode(final AccessNode node) {
   5.429                  load(node.getBase());
   5.430                  method.convert(Type.OBJECT);
   5.431                  method.dup();
   5.432 @@ -655,35 +693,34 @@
   5.433                  method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags());
   5.434                  assert method.peekType().equals(callNode.getType());
   5.435  
   5.436 -                return null;
   5.437 +                return false;
   5.438              }
   5.439  
   5.440              @Override
   5.441 -            public Node enterFunctionNode(final FunctionNode callee) {
   5.442 +            public boolean enterFunctionNode(final FunctionNode origCallee) {
   5.443 +                // NOTE: visiting the callee will leave a constructed ScriptFunction object on the stack if
   5.444 +                // callee.needsCallee() == true
   5.445 +                final FunctionNode callee = (FunctionNode)origCallee.accept(CodeGenerator.this);
   5.446 +
   5.447                  final boolean      isVarArg = callee.isVarArg();
   5.448                  final int          argCount = isVarArg ? -1 : callee.getParameters().size();
   5.449  
   5.450                  final String signature = new FunctionSignature(true, callee.needsCallee(), callee.getReturnType(), isVarArg ? null : callee.getParameters()).toString();
   5.451  
   5.452 -                if (callee.needsCallee()) {
   5.453 -                    newFunctionObject(callee);
   5.454 -                }
   5.455 -
   5.456 -                if (callee.isStrictMode()) { // self is undefined
   5.457 +                if (callee.isStrict()) { // self is undefined
   5.458                      method.loadUndefined(Type.OBJECT);
   5.459                  } else { // get global from scope (which is the self)
   5.460                      globalInstance();
   5.461                  }
   5.462                  loadArgs(args, signature, isVarArg, argCount);
   5.463 +                assert callee.getCompileUnit() != null : "no compile unit for " + callee.getName() + " " + Debug.id(callee) + " " + callNode;
   5.464                  method.invokestatic(callee.getCompileUnit().getUnitClassName(), callee.getName(), signature);
   5.465                  assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType();
   5.466 -                functionNodeIsCallee = true;
   5.467 -                callee.accept(CodeGenerator.this);
   5.468 -                return null;
   5.469 +                return false;
   5.470              }
   5.471  
   5.472              @Override
   5.473 -            public Node enterIndexNode(final IndexNode node) {
   5.474 +            public boolean enterIndexNode(final IndexNode node) {
   5.475                  load(node.getBase());
   5.476                  method.convert(Type.OBJECT);
   5.477                  method.dup();
   5.478 @@ -697,11 +734,11 @@
   5.479                  method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags());
   5.480                  assert method.peekType().equals(callNode.getType());
   5.481  
   5.482 -                return null;
   5.483 +                return false;
   5.484              }
   5.485  
   5.486              @Override
   5.487 -            protected Node enterDefault(final Node node) {
   5.488 +            protected boolean enterDefault(final Node node) {
   5.489                  // Load up function.
   5.490                  load(function);
   5.491                  method.convert(Type.OBJECT); //TODO, e.g. booleans can be used as functions
   5.492 @@ -709,58 +746,41 @@
   5.493                  method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags() | CALLSITE_SCOPE);
   5.494                  assert method.peekType().equals(callNode.getType());
   5.495  
   5.496 -                return null;
   5.497 +                return false;
   5.498              }
   5.499          });
   5.500  
   5.501          method.store(callNode.getSymbol());
   5.502  
   5.503 -        return null;
   5.504 +        return false;
   5.505      }
   5.506  
   5.507      @Override
   5.508 -    public Node enterContinueNode(final ContinueNode continueNode) {
   5.509 -        if (continueNode.testResolved()) {
   5.510 -            return null;
   5.511 -        }
   5.512 -
   5.513 -        for (int i = 0; i < continueNode.getScopeNestingLevel(); i++) {
   5.514 +    public boolean enterContinueNode(final ContinueNode continueNode) {
   5.515 +        final LoopNode continueTo = getLexicalContext().getContinueTo(continueNode.getLabel());
   5.516 +        for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(continueTo); i++) {
   5.517              closeWith();
   5.518          }
   5.519 -
   5.520 -        method.splitAwareGoto(continueNode.getTargetLabel());
   5.521 -
   5.522 -        return null;
   5.523 +        method.splitAwareGoto(getLexicalContext(), continueTo.getContinueLabel());
   5.524 +
   5.525 +        return false;
   5.526      }
   5.527  
   5.528      @Override
   5.529 -    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
   5.530 -        return enterWhileNode(doWhileNode);
   5.531 +    public boolean enterEmptyNode(final EmptyNode emptyNode) {
   5.532 +        return false;
   5.533      }
   5.534  
   5.535      @Override
   5.536 -    public Node enterEmptyNode(final EmptyNode emptyNode) {
   5.537 -        return null;
   5.538 -    }
   5.539 -
   5.540 -    @Override
   5.541 -    public Node enterExecuteNode(final ExecuteNode executeNode) {
   5.542 -        if (executeNode.testResolved()) {
   5.543 -            return null;
   5.544 -        }
   5.545 -
   5.546 +    public boolean enterExecuteNode(final ExecuteNode executeNode) {
   5.547          final Node expression = executeNode.getExpression();
   5.548          expression.accept(this);
   5.549  
   5.550 -        return null;
   5.551 +        return false;
   5.552      }
   5.553  
   5.554      @Override
   5.555 -    public Node enterForNode(final ForNode forNode) {
   5.556 -        if (forNode.testResolved()) {
   5.557 -            return null;
   5.558 -        }
   5.559 -
   5.560 +    public boolean enterForNode(final ForNode forNode) {
   5.561          final Node  test   = forNode.getTest();
   5.562          final Block body   = forNode.getBody();
   5.563          final Node  modify = forNode.getModify();
   5.564 @@ -790,6 +810,10 @@
   5.565  
   5.566              new Store<Node>(init) {
   5.567                  @Override
   5.568 +                protected void storeNonDiscard() {
   5.569 +                    return;
   5.570 +                }
   5.571 +                @Override
   5.572                  protected void evaluate() {
   5.573                      method.load(iter);
   5.574                      method.invoke(interfaceCallNoLookup(Iterator.class, "next", Object.class));
   5.575 @@ -829,7 +853,19 @@
   5.576              method.label(breakLabel);
   5.577          }
   5.578  
   5.579 -        return null;
   5.580 +        return false;
   5.581 +    }
   5.582 +
   5.583 +    private static int assignSlots(final Block block, final int firstSlot) {
   5.584 +        int nextSlot = firstSlot;
   5.585 +        for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
   5.586 +            final Symbol next = iter.next();
   5.587 +            if (next.hasSlot()) {
   5.588 +                next.setSlot(nextSlot);
   5.589 +                nextSlot += next.slotCount();
   5.590 +            }
   5.591 +        }
   5.592 +        return nextSlot;
   5.593      }
   5.594  
   5.595      /**
   5.596 @@ -838,21 +874,26 @@
   5.597       * @param block block with local vars.
   5.598       */
   5.599      private void initLocals(final Block block) {
   5.600 -        final FunctionNode function       = lexicalContext.getFunction(block);
   5.601 -        final boolean      isFunctionNode = block == function;
   5.602 -
   5.603 -        /*
   5.604 -         * Get the symbols from the frame and realign the frame so that all
   5.605 -         * slots get correct numbers. The slot numbering is not fixed until
   5.606 -         * after initLocals has been run
   5.607 -         */
   5.608 -        final Frame        frame   = block.getFrame();
   5.609 -        final List<Symbol> symbols = frame.getSymbols();
   5.610 -
   5.611 -        /* Fix the predefined slots so they have numbers >= 0, like varargs. */
   5.612 -        frame.realign();
   5.613 -
   5.614 -        if (isFunctionNode) {
   5.615 +        final boolean isFunctionBody = getLexicalContext().isFunctionBody();
   5.616 +
   5.617 +        final int nextFreeSlot;
   5.618 +        if (isFunctionBody) {
   5.619 +            // On entry to function, start with slot 0
   5.620 +            nextFreeSlot = 0;
   5.621 +        } else {
   5.622 +            // Otherwise, continue from previous block's first free slot
   5.623 +            nextFreeSlot = nextFreeSlots[nextFreeSlotsSize - 1];
   5.624 +        }
   5.625 +        if(nextFreeSlotsSize == nextFreeSlots.length) {
   5.626 +            final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
   5.627 +            System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
   5.628 +            nextFreeSlots = newNextFreeSlots;
   5.629 +        }
   5.630 +        nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot);
   5.631 +
   5.632 +        final FunctionNode function = getLexicalContext().getCurrentFunction();
   5.633 +        if (isFunctionBody) {
   5.634 +            /* Fix the predefined slots so they have numbers >= 0, like varargs. */
   5.635              if (function.needsParentScope()) {
   5.636                  initParentScope();
   5.637              }
   5.638 @@ -876,14 +917,18 @@
   5.639              final List<String> nameList = new ArrayList<>();
   5.640              final List<Symbol> locals   = new ArrayList<>();
   5.641  
   5.642 -
   5.643              // Initalize symbols and values
   5.644              final List<Symbol> newSymbols = new ArrayList<>();
   5.645              final List<Symbol> values     = new ArrayList<>();
   5.646  
   5.647              final boolean hasArguments = function.needsArguments();
   5.648 -            for (final Symbol symbol : symbols) {
   5.649 -                if (symbol.isInternal() || symbol.isThis()) {
   5.650 +
   5.651 +            final Iterator<Symbol> symbols = block.symbolIterator();
   5.652 +
   5.653 +            while (symbols.hasNext()) {
   5.654 +                final Symbol symbol = symbols.next();
   5.655 +
   5.656 +                if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
   5.657                      continue;
   5.658                  }
   5.659  
   5.660 @@ -907,9 +952,6 @@
   5.661                  }
   5.662              }
   5.663  
   5.664 -            /* Correct slot numbering again */
   5.665 -            frame.realign();
   5.666 -
   5.667              // we may have locals that need to be initialized
   5.668              initSymbols(locals);
   5.669  
   5.670 @@ -931,7 +973,7 @@
   5.671                  @Override
   5.672                  protected void loadScope(MethodEmitter m) {
   5.673                      if(function.needsParentScope()) {
   5.674 -                        m.loadScope();
   5.675 +                        m.loadCompilerConstant(SCOPE);
   5.676                      } else {
   5.677                          m.loadNull();
   5.678                      }
   5.679 @@ -940,118 +982,102 @@
   5.680              foc.makeObject(method);
   5.681  
   5.682              // runScript(): merge scope into global
   5.683 -            if (isFunctionNode && function.isProgram()) {
   5.684 +            if (isFunctionBody && function.isProgram()) {
   5.685                  method.invoke(ScriptRuntime.MERGE_SCOPE);
   5.686              }
   5.687  
   5.688 -            method.storeScope();
   5.689 +            method.storeCompilerConstant(SCOPE);
   5.690          } else {
   5.691              // Since we don't have a scope, parameters didn't get assigned array indices by the FieldObjectCreator, so
   5.692              // we need to assign them separately here.
   5.693              int nextParam = 0;
   5.694 -            if (isFunctionNode && function.isVarArg()) {
   5.695 +            if (isFunctionBody && function.isVarArg()) {
   5.696                  for (final IdentNode param : function.getParameters()) {
   5.697                      param.getSymbol().setFieldIndex(nextParam++);
   5.698                  }
   5.699              }
   5.700 +
   5.701 +            final Iterator<Symbol> iter = block.symbolIterator();
   5.702 +            final List<Symbol> symbols = new ArrayList<>();
   5.703 +            while (iter.hasNext()) {
   5.704 +                symbols.add(iter.next());
   5.705 +            }
   5.706              initSymbols(symbols);
   5.707          }
   5.708  
   5.709          // Debugging: print symbols? @see --print-symbols flag
   5.710 -        printSymbols(block, (isFunctionNode ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName()));
   5.711 +        printSymbols(block, (isFunctionBody ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName()));
   5.712      }
   5.713  
   5.714      private void initArguments(final FunctionNode function) {
   5.715 -        method.loadVarArgs();
   5.716 +        method.loadCompilerConstant(VARARGS);
   5.717          if(function.needsCallee()) {
   5.718 -            method.loadCallee();
   5.719 +            method.loadCompilerConstant(CALLEE);
   5.720          } else {
   5.721              // If function is strict mode, "arguments.callee" is not populated, so we don't necessarily need the
   5.722              // caller.
   5.723 -            assert function.isStrictMode();
   5.724 +            assert function.isStrict();
   5.725              method.loadNull();
   5.726          }
   5.727          method.load(function.getParameters().size());
   5.728          globalAllocateArguments();
   5.729 -        method.storeArguments();
   5.730 +        method.storeCompilerConstant(ARGUMENTS);
   5.731      }
   5.732  
   5.733      private void initParentScope() {
   5.734 -        method.loadCallee();
   5.735 +        method.loadCompilerConstant(CALLEE);
   5.736          method.invoke(ScriptFunction.GET_SCOPE);
   5.737 -        method.storeScope();
   5.738 +        method.storeCompilerConstant(SCOPE);
   5.739      }
   5.740  
   5.741      @Override
   5.742 -    public Node enterFunctionNode(final FunctionNode functionNode) {
   5.743 -        final boolean isCallee = functionNodeIsCallee;
   5.744 -        functionNodeIsCallee = false;
   5.745 -
   5.746 -        if (functionNode.testResolved()) {
   5.747 -            return null;
   5.748 +    public boolean enterFunctionNode(final FunctionNode functionNode) {
   5.749 +        if (functionNode.isLazy()) {
   5.750 +            // Must do it now; can't postpone it until leaveFunctionNode()
   5.751 +            newFunctionObject(functionNode, functionNode);
   5.752 +            return false;
   5.753          }
   5.754  
   5.755 -        if(!(isCallee || functionNode == compiler.getFunctionNode())) {
   5.756 -            newFunctionObject(functionNode);
   5.757 -        }
   5.758 -
   5.759 -        if (functionNode.isLazy()) {
   5.760 -            return null;
   5.761 -        }
   5.762 -
   5.763 -        LOG.info("=== BEGIN " + functionNode.getName());
   5.764 -        lexicalContext.push(functionNode);
   5.765 -
   5.766 -        setCurrentCompileUnit(functionNode.getCompileUnit());
   5.767 -        assert getCurrentCompileUnit() != null;
   5.768 -
   5.769 -        setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(functionNode));
   5.770 -        functionNode.setMethodEmitter(method);
   5.771 +        LOG.info("=== BEGIN ", functionNode.getName());
   5.772 +
   5.773 +        assert functionNode.getCompileUnit() != null : "no compile unit for " + functionNode.getName() + " " + Debug.id(functionNode);
   5.774 +        push(functionNode.getCompileUnit());
   5.775 +        assert !compileUnits.isEmpty();
   5.776 +
   5.777 +        pushMethodEmitter(unit.getClassEmitter().method(functionNode));
   5.778          // Mark end for variable tables.
   5.779          method.begin();
   5.780 -        method.label(functionNode.getEntryLabel());
   5.781 -
   5.782 -        initLocals(functionNode);
   5.783 -        functionNode.setState(CompilationState.EMITTED);
   5.784 -
   5.785 -        return functionNode;
   5.786 +
   5.787 +        return true;
   5.788      }
   5.789  
   5.790      @Override
   5.791      public Node leaveFunctionNode(final FunctionNode functionNode) {
   5.792 -        // Mark end for variable tables.
   5.793 -        method.label(functionNode.getBreakLabel());
   5.794 -
   5.795 -        if (!functionNode.needsScope()) {
   5.796 -            method.markerVariable(LEAF.tag(), functionNode.getEntryLabel(), functionNode.getBreakLabel());
   5.797 -        }
   5.798 -
   5.799 -        symbolInfo(functionNode);
   5.800          try {
   5.801              method.end(); // wrap up this method
   5.802 +            pop(functionNode.getCompileUnit());
   5.803 +            popMethodEmitter(method);
   5.804 +            LOG.info("=== END ", functionNode.getName());
   5.805 +
   5.806 +            final FunctionNode newFunctionNode = functionNode.setState(getLexicalContext(), CompilationState.EMITTED);
   5.807 +
   5.808 +            newFunctionObject(newFunctionNode, functionNode);
   5.809 +            return newFunctionNode;
   5.810          } catch (final Throwable t) {
   5.811              Context.printStackTrace(t);
   5.812              final VerifyError e = new VerifyError("Code generation bug in \"" + functionNode.getName() + "\": likely stack misaligned: " + t + " " + functionNode.getSource().getName());
   5.813              e.initCause(t);
   5.814              throw e;
   5.815          }
   5.816 -
   5.817 -        lexicalContext.pop(functionNode);
   5.818 -        LOG.info("=== END " + functionNode.getName());
   5.819 -        return functionNode;
   5.820      }
   5.821  
   5.822      @Override
   5.823 -    public Node enterIdentNode(final IdentNode identNode) {
   5.824 -        return null;
   5.825 +    public boolean enterIdentNode(final IdentNode identNode) {
   5.826 +        return false;
   5.827      }
   5.828  
   5.829      @Override
   5.830 -    public Node enterIfNode(final IfNode ifNode) {
   5.831 -        if (ifNode.testResolved()) {
   5.832 -            return null;
   5.833 -        }
   5.834 -
   5.835 +    public boolean enterIfNode(final IfNode ifNode) {
   5.836          final Node  test = ifNode.getTest();
   5.837          final Block pass = ifNode.getPass();
   5.838          final Block fail = ifNode.getFail();
   5.839 @@ -1082,30 +1108,21 @@
   5.840              method.label(afterLabel);
   5.841          }
   5.842  
   5.843 -        return null;
   5.844 +        return false;
   5.845      }
   5.846  
   5.847      @Override
   5.848 -    public Node enterIndexNode(final IndexNode indexNode) {
   5.849 -        if (indexNode.testResolved()) {
   5.850 -            return null;
   5.851 -        }
   5.852 -
   5.853 +    public boolean enterIndexNode(final IndexNode indexNode) {
   5.854          load(indexNode);
   5.855 -
   5.856 -        return null;
   5.857 +        return false;
   5.858      }
   5.859  
   5.860      @Override
   5.861 -    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
   5.862 -        if (lineNumberNode.testResolved()) {
   5.863 -            return null;
   5.864 -        }
   5.865 -
   5.866 -        final Label label = new Label("line:" + lineNumberNode.getLineNumber() + " (" + getCurrentFunctionNode().getName() + ")");
   5.867 +    public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
   5.868 +        final Label label = new Label("line:" + lineNumberNode.getLineNumber() + " (" + getLexicalContext().getCurrentFunction().getName() + ")");
   5.869          method.label(label);
   5.870          method.lineNumber(lineNumberNode.getLineNumber(), label);
   5.871 -        return null;
   5.872 +        return false;
   5.873      }
   5.874  
   5.875      /**
   5.876 @@ -1131,43 +1148,43 @@
   5.877          final Type elementType = arrayType.getElementType();
   5.878  
   5.879          if (units != null) {
   5.880 -            final CompileUnit   savedCompileUnit = getCurrentCompileUnit();
   5.881 -            final MethodEmitter savedMethod      = getCurrentMethodEmitter();
   5.882 -
   5.883 -            try {
   5.884 -                for (final ArrayUnit unit : units) {
   5.885 -                    setCurrentCompileUnit(unit.getCompileUnit());
   5.886 -
   5.887 -                    final String className = getCurrentCompileUnit().getUnitClassName();
   5.888 -                    final String name      = getCurrentFunctionNode().uniqueName(SPLIT_PREFIX.tag());
   5.889 -                    final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
   5.890 -
   5.891 -                    setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature));
   5.892 -                    method.setFunctionNode(getCurrentFunctionNode());
   5.893 -                    method.begin();
   5.894 -
   5.895 -                    fixScopeSlot();
   5.896 -
   5.897 -                    method.load(arrayType, SPLIT_ARRAY_ARG.slot());
   5.898 -
   5.899 -                    for (int i = unit.getLo(); i < unit.getHi(); i++) {
   5.900 -                        storeElement(nodes, elementType, postsets[i]);
   5.901 -                    }
   5.902 -
   5.903 -                    method._return();
   5.904 -                    method.end();
   5.905 -
   5.906 -                    savedMethod.loadThis();
   5.907 -                    savedMethod.swap();
   5.908 -                    savedMethod.loadCallee();
   5.909 -                    savedMethod.swap();
   5.910 -                    savedMethod.loadScope();
   5.911 -                    savedMethod.swap();
   5.912 -                    savedMethod.invokestatic(className, name, signature);
   5.913 +            final MethodEmitter savedMethod = method;
   5.914 +
   5.915 +            for (final ArrayUnit arrayUnit : units) {
   5.916 +                push(arrayUnit.getCompileUnit());
   5.917 +
   5.918 +                final String className = unit.getUnitClassName();
   5.919 +                final String name      = getLexicalContext().getCurrentFunction().uniqueName(SPLIT_PREFIX.symbolName());
   5.920 +                final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
   5.921 +
   5.922 +                final MethodEmitter me = unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
   5.923 +                pushMethodEmitter(me);
   5.924 +
   5.925 +                method.setFunctionNode(getLexicalContext().getCurrentFunction());
   5.926 +                method.begin();
   5.927 +
   5.928 +                fixScopeSlot();
   5.929 +
   5.930 +                method.load(arrayType, SPLIT_ARRAY_ARG.slot());
   5.931 +
   5.932 +                for (int i = arrayUnit.getLo(); i < arrayUnit.getHi(); i++) {
   5.933 +                    storeElement(nodes, elementType, postsets[i]);
   5.934                  }
   5.935 -            } finally {
   5.936 -                setCurrentCompileUnit(savedCompileUnit);
   5.937 -                setCurrentMethodEmitter(savedMethod);
   5.938 +
   5.939 +                method._return();
   5.940 +                method.end();
   5.941 +                popMethodEmitter(me);
   5.942 +
   5.943 +                assert method == savedMethod;
   5.944 +                method.loadCompilerConstant(THIS);
   5.945 +                method.swap();
   5.946 +                method.loadCompilerConstant(CALLEE);
   5.947 +                method.swap();
   5.948 +                method.loadCompilerConstant(SCOPE);
   5.949 +                method.swap();
   5.950 +                method.invokestatic(className, name, signature);
   5.951 +
   5.952 +                pop(unit);
   5.953              }
   5.954  
   5.955              return method;
   5.956 @@ -1217,12 +1234,12 @@
   5.957       * @param string string to load
   5.958       */
   5.959      void loadConstant(final String string) {
   5.960 -        final String       unitClassName = getCurrentCompileUnit().getUnitClassName();
   5.961 -        final ClassEmitter classEmitter  = getCurrentCompileUnit().getClassEmitter();
   5.962 +        final String       unitClassName = unit.getUnitClassName();
   5.963 +        final ClassEmitter classEmitter  = unit.getClassEmitter();
   5.964          final int          index         = compiler.getConstantData().add(string);
   5.965  
   5.966          method.load(index);
   5.967 -        method.invokestatic(unitClassName, GET_STRING.tag(), methodDescriptor(String.class, int.class));
   5.968 +        method.invokestatic(unitClassName, GET_STRING.symbolName(), methodDescriptor(String.class, int.class));
   5.969          classEmitter.needGetConstantMethod(String.class);
   5.970      }
   5.971  
   5.972 @@ -1233,14 +1250,14 @@
   5.973       * @param object object to load
   5.974       */
   5.975      void loadConstant(final Object object) {
   5.976 -        final String       unitClassName = getCurrentCompileUnit().getUnitClassName();
   5.977 -        final ClassEmitter classEmitter  = getCurrentCompileUnit().getClassEmitter();
   5.978 +        final String       unitClassName = unit.getUnitClassName();
   5.979 +        final ClassEmitter classEmitter  = unit.getClassEmitter();
   5.980          final int          index         = compiler.getConstantData().add(object);
   5.981          final Class<?>     cls           = object.getClass();
   5.982  
   5.983          if (cls == PropertyMap.class) {
   5.984              method.load(index);
   5.985 -            method.invokestatic(unitClassName, GET_MAP.tag(), methodDescriptor(PropertyMap.class, int.class));
   5.986 +            method.invokestatic(unitClassName, GET_MAP.symbolName(), methodDescriptor(PropertyMap.class, int.class));
   5.987              classEmitter.needGetConstantMethod(PropertyMap.class);
   5.988          } else if (cls.isArray()) {
   5.989              method.load(index);
   5.990 @@ -1303,14 +1320,14 @@
   5.991              return loadRegexToken(regexToken);
   5.992          }
   5.993          // emit field
   5.994 -        final String       regexName    = getCurrentFunctionNode().uniqueName(REGEX_PREFIX.tag());
   5.995 -        final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter();
   5.996 +        final String       regexName    = getLexicalContext().getCurrentFunction().uniqueName(REGEX_PREFIX.symbolName());
   5.997 +        final ClassEmitter classEmitter = unit.getClassEmitter();
   5.998  
   5.999          classEmitter.field(EnumSet.of(PRIVATE, STATIC), regexName, Object.class);
  5.1000          regexFieldCount++;
  5.1001  
  5.1002          // get field, if null create new regex, finally clone regex object
  5.1003 -        method.getStatic(getCurrentCompileUnit().getUnitClassName(), regexName, typeDescriptor(Object.class));
  5.1004 +        method.getStatic(unit.getUnitClassName(), regexName, typeDescriptor(Object.class));
  5.1005          method.dup();
  5.1006          final Label cachedLabel = new Label("cached");
  5.1007          method.ifnonnull(cachedLabel);
  5.1008 @@ -1318,7 +1335,7 @@
  5.1009          method.pop();
  5.1010          loadRegexToken(regexToken);
  5.1011          method.dup();
  5.1012 -        method.putStatic(getCurrentCompileUnit().getUnitClassName(), regexName, typeDescriptor(Object.class));
  5.1013 +        method.putStatic(unit.getUnitClassName(), regexName, typeDescriptor(Object.class));
  5.1014  
  5.1015          method.label(cachedLabel);
  5.1016          globalRegExpCopy();
  5.1017 @@ -1328,18 +1345,14 @@
  5.1018  
  5.1019      @SuppressWarnings("rawtypes")
  5.1020      @Override
  5.1021 -    public Node enterLiteralNode(final LiteralNode literalNode) {
  5.1022 +    public boolean enterLiteralNode(final LiteralNode literalNode) {
  5.1023          assert literalNode.getSymbol() != null : literalNode + " has no symbol";
  5.1024          load(literalNode).store(literalNode.getSymbol());
  5.1025 -        return null;
  5.1026 +        return false;
  5.1027      }
  5.1028  
  5.1029      @Override
  5.1030 -    public Node enterObjectNode(final ObjectNode objectNode) {
  5.1031 -        if (objectNode.testResolved()) {
  5.1032 -            return null;
  5.1033 -        }
  5.1034 -
  5.1035 +    public boolean enterObjectNode(final ObjectNode objectNode) {
  5.1036          final List<Node> elements = objectNode.getElements();
  5.1037          final int        size     = elements.size();
  5.1038  
  5.1039 @@ -1404,14 +1417,14 @@
  5.1040  
  5.1041          if (!hasGettersSetters) {
  5.1042              method.store(objectNode.getSymbol());
  5.1043 -            return null;
  5.1044 +            return false;
  5.1045          }
  5.1046  
  5.1047          for (final Node element : elements) {
  5.1048              final PropertyNode propertyNode = (PropertyNode)element;
  5.1049              final Object       key          = propertyNode.getKey();
  5.1050 -            final FunctionNode getter       = (FunctionNode)propertyNode.getGetter();
  5.1051 -            final FunctionNode setter       = (FunctionNode)propertyNode.getSetter();
  5.1052 +            final FunctionNode getter       = propertyNode.getGetter();
  5.1053 +            final FunctionNode setter       = propertyNode.getSetter();
  5.1054  
  5.1055              if (getter == null && setter == null) {
  5.1056                  continue;
  5.1057 @@ -1436,35 +1449,25 @@
  5.1058  
  5.1059          method.store(objectNode.getSymbol());
  5.1060  
  5.1061 -        return null;
  5.1062 +        return false;
  5.1063      }
  5.1064  
  5.1065      @Override
  5.1066 -    public Node enterReturnNode(final ReturnNode returnNode) {
  5.1067 -        if (returnNode.testResolved()) {
  5.1068 -            return null;
  5.1069 -        }
  5.1070 -
  5.1071 -        // Set the split return flag in the scope if this is a split method fragment.
  5.1072 -        if (method.getSplitNode() != null) {
  5.1073 -            assert method.getSplitNode().hasReturn() : "unexpected return in split node";
  5.1074 -
  5.1075 -            method.loadScope();
  5.1076 -            method.checkcast(Scope.class);
  5.1077 -            method.load(0);
  5.1078 -            method.invoke(Scope.SET_SPLIT_STATE);
  5.1079 -        }
  5.1080 +    public boolean enterReturnNode(final ReturnNode returnNode) {
  5.1081 +        method.registerReturn();
  5.1082 +
  5.1083 +        final Type returnType = getLexicalContext().getCurrentFunction().getReturnType();
  5.1084  
  5.1085          final Node expression = returnNode.getExpression();
  5.1086          if (expression != null) {
  5.1087              load(expression);
  5.1088          } else {
  5.1089 -            method.loadUndefined(getCurrentFunctionNode().getReturnType());
  5.1090 +            method.loadUndefined(returnType);
  5.1091          }
  5.1092  
  5.1093 -        method._return(getCurrentFunctionNode().getReturnType());
  5.1094 -
  5.1095 -        return null;
  5.1096 +        method._return(returnType);
  5.1097 +
  5.1098 +        return false;
  5.1099      }
  5.1100  
  5.1101      private static boolean isNullLiteral(final Node node) {
  5.1102 @@ -1542,19 +1545,20 @@
  5.1103          }
  5.1104  
  5.1105          assert args.size() == 2;
  5.1106 -        final Node lhs = args.get(0);
  5.1107 -        final Node rhs = args.get(1);
  5.1108 -
  5.1109          final Type returnType = node.getType();
  5.1110 -        load(lhs);
  5.1111 -        load(rhs);
  5.1112 +
  5.1113 +        load(args.get(0));
  5.1114 +        load(args.get(1));
  5.1115  
  5.1116          Request finalRequest = request;
  5.1117  
  5.1118 +        //if the request is a comparison, i.e. one that can be reversed
  5.1119 +        //it keeps its semantic, but make sure that the object comes in
  5.1120 +        //last
  5.1121          final Request reverse = Request.reverse(request);
  5.1122 -        if (method.peekType().isObject() && reverse != null) {
  5.1123 -            if (!method.peekType(1).isObject()) {
  5.1124 -                method.swap();
  5.1125 +        if (method.peekType().isObject() && reverse != null) { //rhs is object
  5.1126 +            if (!method.peekType(1).isObject()) { //lhs is not object
  5.1127 +                method.swap(); //prefer object as lhs
  5.1128                  finalRequest = reverse;
  5.1129              }
  5.1130          }
  5.1131 @@ -1581,11 +1585,7 @@
  5.1132      }
  5.1133  
  5.1134      @Override
  5.1135 -    public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
  5.1136 -        if (runtimeNode.testResolved()) {
  5.1137 -            return null;
  5.1138 -        }
  5.1139 -
  5.1140 +    public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
  5.1141          /*
  5.1142           * First check if this should be something other than a runtime node
  5.1143           * AccessSpecializer might have changed the type
  5.1144 @@ -1624,7 +1624,7 @@
  5.1145                  method.add();
  5.1146                  method.convert(type);
  5.1147                  method.store(symbol);
  5.1148 -                return null;
  5.1149 +                return false;
  5.1150              default:
  5.1151                  // it's ok to send this one on with only primitive arguments, maybe INSTANCEOF(true, true) or similar
  5.1152                  // assert false : runtimeNode + " has all primitive arguments. This is an inconsistent state";
  5.1153 @@ -1636,11 +1636,11 @@
  5.1154          final List<Node> args = runtimeNode.getArgs();
  5.1155  
  5.1156          if (nullCheck(runtimeNode, args, new FunctionSignature(false, false, runtimeNode.getType(), args).toString())) {
  5.1157 -            return null;
  5.1158 +            return false;
  5.1159          }
  5.1160  
  5.1161          if (!runtimeNode.isFinal() && specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) {
  5.1162 -            return null;
  5.1163 +            return false;
  5.1164          }
  5.1165  
  5.1166          for (final Node arg : runtimeNode.getArgs()) {
  5.1167 @@ -1658,129 +1658,146 @@
  5.1168          method.convert(runtimeNode.getType());
  5.1169          method.store(runtimeNode.getSymbol());
  5.1170  
  5.1171 -        return null;
  5.1172 +        return false;
  5.1173      }
  5.1174  
  5.1175      @Override
  5.1176 -    public Node enterSplitNode(final SplitNode splitNode) {
  5.1177 -        if (splitNode.testResolved()) {
  5.1178 -            return null;
  5.1179 -        }
  5.1180 -
  5.1181 +    public boolean enterSplitNode(final SplitNode splitNode) {
  5.1182          final CompileUnit splitCompileUnit = splitNode.getCompileUnit();
  5.1183  
  5.1184 -        final FunctionNode fn   = getCurrentFunctionNode();
  5.1185 +        final FunctionNode fn   = getLexicalContext().getCurrentFunction();
  5.1186          final String className  = splitCompileUnit.getUnitClassName();
  5.1187          final String name       = splitNode.getName();
  5.1188  
  5.1189 -        final Class<?>   rtype  = fn.getReturnType().getTypeClass();
  5.1190 -        final boolean needsArguments = fn.needsArguments();
  5.1191 -        final Class<?>[] ptypes = needsArguments ?
  5.1192 +        final Class<?>   rtype          = fn.getReturnType().getTypeClass();
  5.1193 +        final boolean    needsArguments = fn.needsArguments();
  5.1194 +        final Class<?>[] ptypes         = needsArguments ?
  5.1195                  new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class, Object.class} :
  5.1196                  new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class};
  5.1197  
  5.1198 -        setCurrentCompileUnit(splitCompileUnit);
  5.1199 -        splitNode.setCompileUnit(splitCompileUnit);
  5.1200 +        final MethodEmitter caller = method;
  5.1201 +        push(splitCompileUnit);
  5.1202  
  5.1203          final Call splitCall = staticCallNoLookup(
  5.1204              className,
  5.1205              name,
  5.1206              methodDescriptor(rtype, ptypes));
  5.1207  
  5.1208 -        setCurrentMethodEmitter(
  5.1209 -            splitCompileUnit.getClassEmitter().method(
  5.1210 -                EnumSet.of(Flag.PUBLIC, Flag.STATIC),
  5.1211 -                name,
  5.1212 -                rtype,
  5.1213 -                ptypes));
  5.1214 +        final MethodEmitter splitEmitter =
  5.1215 +                splitCompileUnit.getClassEmitter().method(
  5.1216 +                        splitNode,
  5.1217 +                        name,
  5.1218 +                        rtype,
  5.1219 +                        ptypes);
  5.1220 +
  5.1221 +        pushMethodEmitter(splitEmitter);
  5.1222  
  5.1223          method.setFunctionNode(fn);
  5.1224 -        method.setSplitNode(splitNode);
  5.1225 -        splitNode.setMethodEmitter(method);
  5.1226 -
  5.1227 -        final MethodEmitter caller = splitNode.getCaller();
  5.1228 -        if(fn.needsCallee()) {
  5.1229 -            caller.loadCallee();
  5.1230 +
  5.1231 +        if (fn.needsCallee()) {
  5.1232 +            caller.loadCompilerConstant(CALLEE);
  5.1233          } else {
  5.1234              caller.loadNull();
  5.1235          }
  5.1236 -        caller.loadThis();
  5.1237 -        caller.loadScope();
  5.1238 +        caller.loadCompilerConstant(THIS);
  5.1239 +        caller.loadCompilerConstant(SCOPE);
  5.1240          if (needsArguments) {
  5.1241 -            caller.loadArguments();
  5.1242 +            caller.loadCompilerConstant(ARGUMENTS);
  5.1243          }
  5.1244          caller.invoke(splitCall);
  5.1245 -        caller.storeResult();
  5.1246 +        caller.storeCompilerConstant(RETURN);
  5.1247  
  5.1248          method.begin();
  5.1249  
  5.1250          method.loadUndefined(fn.getReturnType());
  5.1251 -        method.storeResult();
  5.1252 +        method.storeCompilerConstant(RETURN);
  5.1253  
  5.1254          fixScopeSlot();
  5.1255  
  5.1256 -        return splitNode;
  5.1257 +        return true;
  5.1258      }
  5.1259  
  5.1260      private void fixScopeSlot() {
  5.1261 -        if (getCurrentFunctionNode().getScopeNode().getSymbol().getSlot() != SCOPE.slot()) {
  5.1262 +        if (getLexicalContext().getCurrentFunction().compilerConstant(SCOPE).getSlot() != SCOPE.slot()) {
  5.1263              // TODO hack to move the scope to the expected slot (that's needed because split methods reuse the same slots as the root method)
  5.1264              method.load(Type.typeFor(ScriptObject.class), SCOPE.slot());
  5.1265 -            method.storeScope();
  5.1266 +            method.storeCompilerConstant(SCOPE);
  5.1267          }
  5.1268      }
  5.1269  
  5.1270      @Override
  5.1271      public Node leaveSplitNode(final SplitNode splitNode) {
  5.1272 +        assert method instanceof SplitMethodEmitter;
  5.1273 +        final boolean     hasReturn = method.hasReturn();
  5.1274 +        final List<Label> targets   = method.getExternalTargets();
  5.1275 +
  5.1276          try {
  5.1277              // Wrap up this method.
  5.1278 -            method.loadResult();
  5.1279 -            method._return(getCurrentFunctionNode().getReturnType());
  5.1280 +
  5.1281 +            method.loadCompilerConstant(RETURN);
  5.1282 +            method._return(getLexicalContext().getCurrentFunction().getReturnType());
  5.1283              method.end();
  5.1284 +
  5.1285 +            pop(splitNode.getCompileUnit());
  5.1286 +            popMethodEmitter(method);
  5.1287 +
  5.1288          } catch (final Throwable t) {
  5.1289              Context.printStackTrace(t);
  5.1290 -            final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getCurrentFunctionNode().getSource().getName());
  5.1291 +            final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getLexicalContext().getCurrentFunction().getSource().getName());
  5.1292              e.initCause(t);
  5.1293              throw e;
  5.1294          }
  5.1295  
  5.1296          // Handle return from split method if there was one.
  5.1297 -        final MethodEmitter caller      = splitNode.getCaller();
  5.1298 -        final List<Label>   targets     = splitNode.getExternalTargets();
  5.1299 -        final int           targetCount = targets.size();
  5.1300 -
  5.1301 -        if (splitNode.hasReturn() || targetCount > 0) {
  5.1302 -
  5.1303 -            caller.loadScope();
  5.1304 -            caller.checkcast(Scope.class);
  5.1305 -            caller.invoke(Scope.GET_SPLIT_STATE);
  5.1306 -
  5.1307 -            // Split state is -1 for no split state, 0 for return, 1..n+1 for break/continue
  5.1308 -            final Label   breakLabel = new Label("no_split_state");
  5.1309 -            final int     low        = splitNode.hasReturn() ? 0 : 1;
  5.1310 -            final int     labelCount = targetCount + 1 - low;
  5.1311 -            final Label[] labels     = new Label[labelCount];
  5.1312 +        final MethodEmitter caller = method;
  5.1313 +        final int     targetCount = targets.size();
  5.1314 +
  5.1315 +        //no external jump targets or return in switch node
  5.1316 +        if (!hasReturn && targets.isEmpty()) {
  5.1317 +            return splitNode;
  5.1318 +        }
  5.1319 +
  5.1320 +        caller.loadCompilerConstant(SCOPE);
  5.1321 +        caller.checkcast(Scope.class);
  5.1322 +        caller.invoke(Scope.GET_SPLIT_STATE);
  5.1323 +
  5.1324 +        final Label breakLabel = new Label("no_split_state");
  5.1325 +        // Split state is -1 for no split state, 0 for return, 1..n+1 for break/continue
  5.1326 +
  5.1327 +        //the common case is that we don't need a switch
  5.1328 +        if (targetCount == 0) {
  5.1329 +            assert hasReturn;
  5.1330 +            caller.ifne(breakLabel);
  5.1331 +            //has to be zero
  5.1332 +            caller.label(new Label("split_return"));
  5.1333 +            method.loadCompilerConstant(RETURN);
  5.1334 +            caller._return(getLexicalContext().getCurrentFunction().getReturnType());
  5.1335 +            caller.label(breakLabel);
  5.1336 +        } else {
  5.1337 +            assert !targets.isEmpty();
  5.1338 +
  5.1339 +            final int     low         = hasReturn ? 0 : 1;
  5.1340 +            final int     labelCount  = targetCount + 1 - low;
  5.1341 +            final Label[] labels      = new Label[labelCount];
  5.1342  
  5.1343              for (int i = 0; i < labelCount; i++) {
  5.1344 -                labels[i] = new Label("split_state_" + i);
  5.1345 +                labels[i] = new Label(i == 0 ? "split_return" : "split_" + targets.get(i - 1));
  5.1346              }
  5.1347 -
  5.1348              caller.tableswitch(low, targetCount, breakLabel, labels);
  5.1349              for (int i = low; i <= targetCount; i++) {
  5.1350                  caller.label(labels[i - low]);
  5.1351                  if (i == 0) {
  5.1352 -                    caller.loadResult();
  5.1353 -                    caller._return(getCurrentFunctionNode().getReturnType());
  5.1354 +                    caller.loadCompilerConstant(RETURN);
  5.1355 +                    caller._return(getLexicalContext().getCurrentFunction().getReturnType());
  5.1356                  } else {
  5.1357                      // Clear split state.
  5.1358 -                    caller.loadScope();
  5.1359 +                    caller.loadCompilerConstant(SCOPE);
  5.1360                      caller.checkcast(Scope.class);
  5.1361                      caller.load(-1);
  5.1362                      caller.invoke(Scope.SET_SPLIT_STATE);
  5.1363 -                    caller.splitAwareGoto(targets.get(i - 1));
  5.1364 +                    caller.splitAwareGoto(getLexicalContext(), targets.get(i - 1));
  5.1365                  }
  5.1366              }
  5.1367 -
  5.1368              caller.label(breakLabel);
  5.1369          }
  5.1370  
  5.1371 @@ -1788,11 +1805,7 @@
  5.1372      }
  5.1373  
  5.1374      @Override
  5.1375 -    public Node enterSwitchNode(final SwitchNode switchNode) {
  5.1376 -        if (switchNode.testResolved()) {
  5.1377 -            return null;
  5.1378 -        }
  5.1379 -
  5.1380 +    public boolean enterSwitchNode(final SwitchNode switchNode) {
  5.1381          final Node           expression  = switchNode.getExpression();
  5.1382          final Symbol         tag         = switchNode.getTag();
  5.1383          final boolean        allInteger  = tag.getSymbolType().isInteger();
  5.1384 @@ -1810,7 +1823,7 @@
  5.1385  
  5.1386          if (cases.isEmpty()) {
  5.1387              method.label(breakLabel);
  5.1388 -            return null;
  5.1389 +            return false;
  5.1390          }
  5.1391  
  5.1392          if (allInteger) {
  5.1393 @@ -1916,15 +1929,11 @@
  5.1394              method.label(breakLabel);
  5.1395          }
  5.1396  
  5.1397 -        return null;
  5.1398 +        return false;
  5.1399      }
  5.1400  
  5.1401      @Override
  5.1402 -    public Node enterThrowNode(final ThrowNode throwNode) {
  5.1403 -        if (throwNode.testResolved()) {
  5.1404 -            return null;
  5.1405 -        }
  5.1406 -
  5.1407 +    public boolean enterThrowNode(final ThrowNode throwNode) {
  5.1408          method._new(ECMAException.class).dup();
  5.1409  
  5.1410          final Node   expression = throwNode.getExpression();
  5.1411 @@ -1943,15 +1952,11 @@
  5.1412  
  5.1413          method.athrow();
  5.1414  
  5.1415 -        return null;
  5.1416 +        return false;
  5.1417      }
  5.1418  
  5.1419      @Override
  5.1420 -    public Node enterTryNode(final TryNode tryNode) {
  5.1421 -        if (tryNode.testResolved()) {
  5.1422 -            return null;
  5.1423 -        }
  5.1424 -
  5.1425 +    public boolean enterTryNode(final TryNode tryNode) {
  5.1426          final Block       body        = tryNode.getBody();
  5.1427          final List<Block> catchBlocks = tryNode.getCatchBlocks();
  5.1428          final Symbol      symbol      = tryNode.getException();
  5.1429 @@ -1974,74 +1979,68 @@
  5.1430          method.store(symbol);
  5.1431  
  5.1432          for (int i = 0; i < catchBlocks.size(); i++) {
  5.1433 -            final Block saveBlock = getCurrentBlock();
  5.1434              final Block catchBlock = catchBlocks.get(i);
  5.1435  
  5.1436 -            setCurrentBlock(catchBlock);
  5.1437 -
  5.1438 -            try {
  5.1439 -                enterBlock(catchBlock);
  5.1440 -
  5.1441 -                final CatchNode catchNode          = (CatchNode)catchBlocks.get(i).getStatements().get(0);
  5.1442 -                final IdentNode exception          = catchNode.getException();
  5.1443 -                final Node      exceptionCondition = catchNode.getExceptionCondition();
  5.1444 -                final Block     catchBody          = catchNode.getBody();
  5.1445 -
  5.1446 -                if (catchNode.isSyntheticRethrow()) {
  5.1447 -                    // Generate catch body (inlined finally) and rethrow exception
  5.1448 -                    catchBody.accept(this);
  5.1449 +            //TODO this is very ugly - try not to call enter/leave methods directly
  5.1450 +            //better to use the implicit lexical context scoping given by the visitor's
  5.1451 +            //accept method.
  5.1452 +            getLexicalContext().push(catchBlock);
  5.1453 +            enterBlock(catchBlock);
  5.1454 +
  5.1455 +            final CatchNode catchNode          = (CatchNode)catchBlocks.get(i).getStatements().get(0);
  5.1456 +            final IdentNode exception          = catchNode.getException();
  5.1457 +            final Node      exceptionCondition = catchNode.getExceptionCondition();
  5.1458 +            final Block     catchBody          = catchNode.getBody();
  5.1459 +
  5.1460 +            new Store<IdentNode>(exception) {
  5.1461 +                @Override
  5.1462 +                protected void storeNonDiscard() {
  5.1463 +                    return;
  5.1464 +                }
  5.1465 +                @Override
  5.1466 +                protected void evaluate() {
  5.1467 +                    /*
  5.1468 +                     * If caught object is an instance of ECMAException, then
  5.1469 +                     * bind obj.thrown to the script catch var. Or else bind the
  5.1470 +                     * caught object itself to the script catch var.
  5.1471 +                     */
  5.1472 +                    final Label notEcmaException = new Label("no_ecma_exception");
  5.1473 +
  5.1474 +                    method.load(symbol).dup()._instanceof(ECMAException.class).ifeq(notEcmaException);
  5.1475 +                    method.checkcast(ECMAException.class); //TODO is this necessary?
  5.1476 +                    method.getField(ECMAException.THROWN);
  5.1477 +                    method.label(notEcmaException);
  5.1478 +                }
  5.1479 +            }.store();
  5.1480 +
  5.1481 +            final Label next;
  5.1482 +
  5.1483 +            if (exceptionCondition != null) {
  5.1484 +                next = new Label("next");
  5.1485 +                load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next);
  5.1486 +            } else {
  5.1487 +                next = null;
  5.1488 +            }
  5.1489 +
  5.1490 +            catchBody.accept(this);
  5.1491 +
  5.1492 +            if (i + 1 != catchBlocks.size() && !catchBody.hasTerminalFlags()) {
  5.1493 +                method._goto(skip);
  5.1494 +            }
  5.1495 +
  5.1496 +            if (next != null) {
  5.1497 +                if (i + 1 == catchBlocks.size()) {
  5.1498 +                    // no next catch block - rethrow if condition failed
  5.1499 +                    method._goto(skip);
  5.1500 +                    method.label(next);
  5.1501                      method.load(symbol).athrow();
  5.1502 -                    lexicalContext.pop(catchBlock);
  5.1503 -                    continue;
  5.1504 +                } else {
  5.1505 +                    method.label(next);
  5.1506                  }
  5.1507 -
  5.1508 -                new Store<IdentNode>(exception) {
  5.1509 -                    @Override
  5.1510 -                    protected void evaluate() {
  5.1511 -                        /*
  5.1512 -                         * If caught object is an instance of ECMAException, then
  5.1513 -                         * bind obj.thrown to the script catch var. Or else bind the
  5.1514 -                         * caught object itself to the script catch var.
  5.1515 -                         */
  5.1516 -                        final Label notEcmaException = new Label("no_ecma_exception");
  5.1517 -
  5.1518 -                        method.load(symbol).dup()._instanceof(ECMAException.class).ifeq(notEcmaException);
  5.1519 -                        method.checkcast(ECMAException.class); //TODO is this necessary?
  5.1520 -                        method.getField(ECMAException.THROWN);
  5.1521 -                        method.label(notEcmaException);
  5.1522 -                    }
  5.1523 -                }.store();
  5.1524 -
  5.1525 -                final Label next;
  5.1526 -
  5.1527 -                if (exceptionCondition != null) {
  5.1528 -                    next = new Label("next");
  5.1529 -                    load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next);
  5.1530 -                } else {
  5.1531 -                    next = null;
  5.1532 -                }
  5.1533 -
  5.1534 -                catchBody.accept(this);
  5.1535 -
  5.1536 -                if (i + 1 != catchBlocks.size() && !catchBody.hasTerminalFlags()) {
  5.1537 -                    method._goto(skip);
  5.1538 -                }
  5.1539 -
  5.1540 -                if (next != null) {
  5.1541 -                    if (i + 1 == catchBlocks.size()) {
  5.1542 -                        // no next catch block - rethrow if condition failed
  5.1543 -                        method._goto(skip);
  5.1544 -                        method.label(next);
  5.1545 -                        method.load(symbol).athrow();
  5.1546 -                    } else {
  5.1547 -                        method.label(next);
  5.1548 -                    }
  5.1549 -                }
  5.1550 -
  5.1551 -                leaveBlock(catchBlock);
  5.1552 -            } finally {
  5.1553 -                setCurrentBlock(saveBlock);
  5.1554              }
  5.1555 +
  5.1556 +            leaveBlock(catchBlock);
  5.1557 +            getLexicalContext().pop(catchBlock);
  5.1558          }
  5.1559  
  5.1560          method.label(skip);
  5.1561 @@ -2049,15 +2048,15 @@
  5.1562  
  5.1563          // Finally body is always inlined elsewhere so it doesn't need to be emitted
  5.1564  
  5.1565 -        return null;
  5.1566 +        return false;
  5.1567      }
  5.1568  
  5.1569      @Override
  5.1570 -    public Node enterVarNode(final VarNode varNode) {
  5.1571 +    public boolean enterVarNode(final VarNode varNode) {
  5.1572          final Node init = varNode.getInit();
  5.1573  
  5.1574 -        if (varNode.testResolved() || init == null) {
  5.1575 -            return null;
  5.1576 +        if (init == null) {
  5.1577 +            return false;
  5.1578          }
  5.1579  
  5.1580          final Symbol varSymbol = varNode.getSymbol();
  5.1581 @@ -2067,7 +2066,7 @@
  5.1582  
  5.1583          final boolean needsScope = varSymbol.isScope();
  5.1584          if (needsScope) {
  5.1585 -            method.loadScope();
  5.1586 +            method.loadCompilerConstant(SCOPE);
  5.1587          }
  5.1588          load(init);
  5.1589  
  5.1590 @@ -2087,22 +2086,18 @@
  5.1591              method.store(varSymbol);
  5.1592          }
  5.1593  
  5.1594 -        return null;
  5.1595 +        return false;
  5.1596      }
  5.1597  
  5.1598      @Override
  5.1599 -    public Node enterWhileNode(final WhileNode whileNode) {
  5.1600 -        if (whileNode.testResolved()) {
  5.1601 -            return null;
  5.1602 -        }
  5.1603 -
  5.1604 +    public boolean enterWhileNode(final WhileNode whileNode) {
  5.1605          final Node  test          = whileNode.getTest();
  5.1606          final Block body          = whileNode.getBody();
  5.1607          final Label breakLabel    = whileNode.getBreakLabel();
  5.1608          final Label continueLabel = whileNode.getContinueLabel();
  5.1609          final Label loopLabel     = new Label("loop");
  5.1610  
  5.1611 -        if (!(whileNode instanceof DoWhileNode)) {
  5.1612 +        if (!whileNode.isDoWhile()) {
  5.1613              method._goto(continueLabel);
  5.1614          }
  5.1615  
  5.1616 @@ -2114,21 +2109,17 @@
  5.1617              method.label(breakLabel);
  5.1618          }
  5.1619  
  5.1620 -        return null;
  5.1621 +        return false;
  5.1622      }
  5.1623  
  5.1624      private void closeWith() {
  5.1625 -        method.loadScope();
  5.1626 +        method.loadCompilerConstant(SCOPE);
  5.1627          method.invoke(ScriptRuntime.CLOSE_WITH);
  5.1628 -        method.storeScope();
  5.1629 +        method.storeCompilerConstant(SCOPE);
  5.1630      }
  5.1631  
  5.1632      @Override
  5.1633 -    public Node enterWithNode(final WithNode withNode) {
  5.1634 -        if (withNode.testResolved()) {
  5.1635 -            return null;
  5.1636 -        }
  5.1637 -
  5.1638 +    public boolean enterWithNode(final WithNode withNode) {
  5.1639          final Node expression = withNode.getExpression();
  5.1640          final Node body       = withNode.getBody();
  5.1641  
  5.1642 @@ -2139,13 +2130,13 @@
  5.1643  
  5.1644          method.label(tryLabel);
  5.1645  
  5.1646 -        method.loadScope();
  5.1647 +        method.loadCompilerConstant(SCOPE);
  5.1648          load(expression);
  5.1649  
  5.1650          assert expression.getType().isObject() : "with expression needs to be object: " + expression;
  5.1651  
  5.1652          method.invoke(ScriptRuntime.OPEN_WITH);
  5.1653 -        method.storeScope();
  5.1654 +        method.storeCompilerConstant(SCOPE);
  5.1655  
  5.1656          body.accept(this);
  5.1657  
  5.1658 @@ -2164,40 +2155,27 @@
  5.1659  
  5.1660          method._try(tryLabel, endLabel, catchLabel);
  5.1661  
  5.1662 -        return null;
  5.1663 +        return false;
  5.1664      }
  5.1665  
  5.1666      @Override
  5.1667 -    public Node enterADD(final UnaryNode unaryNode) {
  5.1668 -        if (unaryNode.testResolved()) {
  5.1669 -            return null;
  5.1670 -        }
  5.1671 -
  5.1672 +    public boolean enterADD(final UnaryNode unaryNode) {
  5.1673          load(unaryNode.rhs());
  5.1674          assert unaryNode.rhs().getType().isNumber();
  5.1675          method.store(unaryNode.getSymbol());
  5.1676  
  5.1677 -        return null;
  5.1678 +        return false;
  5.1679      }
  5.1680  
  5.1681      @Override
  5.1682 -    public Node enterBIT_NOT(final UnaryNode unaryNode) {
  5.1683 -        if (unaryNode.testResolved()) {
  5.1684 -            return null;
  5.1685 -        }
  5.1686 -
  5.1687 +    public boolean enterBIT_NOT(final UnaryNode unaryNode) {
  5.1688          load(unaryNode.rhs()).convert(Type.INT).load(-1).xor().store(unaryNode.getSymbol());
  5.1689 -
  5.1690 -        return null;
  5.1691 +        return false;
  5.1692      }
  5.1693  
  5.1694      // do this better with convert calls to method. TODO
  5.1695      @Override
  5.1696 -    public Node enterCONVERT(final UnaryNode unaryNode) {
  5.1697 -        if (unaryNode.testResolved()) {
  5.1698 -            return null;
  5.1699 -        }
  5.1700 -
  5.1701 +    public boolean enterCONVERT(final UnaryNode unaryNode) {
  5.1702          final Node rhs = unaryNode.rhs();
  5.1703          final Type to  = unaryNode.getType();
  5.1704  
  5.1705 @@ -2230,15 +2208,11 @@
  5.1706  
  5.1707          method.store(unaryNode.getSymbol());
  5.1708  
  5.1709 -        return null;
  5.1710 +        return false;
  5.1711      }
  5.1712  
  5.1713      @Override
  5.1714 -    public Node enterDECINC(final UnaryNode unaryNode) {
  5.1715 -        if (unaryNode.testResolved()) {
  5.1716 -            return null;
  5.1717 -        }
  5.1718 -
  5.1719 +    public boolean enterDECINC(final UnaryNode unaryNode) {
  5.1720          final Node      rhs         = unaryNode.rhs();
  5.1721          final Type      type        = unaryNode.getType();
  5.1722          final TokenType tokenType   = unaryNode.tokenType();
  5.1723 @@ -2282,32 +2256,28 @@
  5.1724              }
  5.1725          }.store();
  5.1726  
  5.1727 -        return null;
  5.1728 +        return false;
  5.1729      }
  5.1730  
  5.1731      @Override
  5.1732 -    public Node enterDISCARD(final UnaryNode unaryNode) {
  5.1733 -        if (unaryNode.testResolved()) {
  5.1734 -            return null;
  5.1735 +    public boolean enterDISCARD(final UnaryNode unaryNode) {
  5.1736 +        final Node rhs = unaryNode.rhs();
  5.1737 +
  5.1738 +       // System.err.println("**** Enter discard " + unaryNode);
  5.1739 +        discard.push(rhs);
  5.1740 +        load(rhs);
  5.1741 +
  5.1742 +        if (discard.peek() == rhs) {
  5.1743 +            assert !rhs.isAssignment();
  5.1744 +            method.pop();
  5.1745 +            discard.pop();
  5.1746          }
  5.1747 -
  5.1748 -        final Node rhs = unaryNode.rhs();
  5.1749 -
  5.1750 -        load(rhs);
  5.1751 -
  5.1752 -        if (rhs.shouldDiscard()) {
  5.1753 -            method.pop();
  5.1754 -        }
  5.1755 -
  5.1756 -        return null;
  5.1757 +       // System.err.println("**** Leave discard " + unaryNode);
  5.1758 +        return false;
  5.1759      }
  5.1760  
  5.1761      @Override
  5.1762 -    public Node enterNEW(final UnaryNode unaryNode) {
  5.1763 -        if (unaryNode.testResolved()) {
  5.1764 -            return null;
  5.1765 -        }
  5.1766 -
  5.1767 +    public boolean enterNEW(final UnaryNode unaryNode) {
  5.1768          final CallNode callNode = (CallNode)unaryNode.rhs();
  5.1769          final List<Node> args   = callNode.getArgs();
  5.1770  
  5.1771 @@ -2317,15 +2287,11 @@
  5.1772          method.dynamicNew(1 + loadArgs(args), getCallSiteFlags());
  5.1773          method.store(unaryNode.getSymbol());
  5.1774  
  5.1775 -        return null;
  5.1776 +        return false;
  5.1777      }
  5.1778  
  5.1779      @Override
  5.1780 -    public Node enterNOT(final UnaryNode unaryNode) {
  5.1781 -        if (unaryNode.testResolved()) {
  5.1782 -            return null;
  5.1783 -        }
  5.1784 -
  5.1785 +    public boolean enterNOT(final UnaryNode unaryNode) {
  5.1786          final Node rhs = unaryNode.rhs();
  5.1787  
  5.1788          load(rhs);
  5.1789 @@ -2342,18 +2308,14 @@
  5.1790          method.label(afterLabel);
  5.1791          method.store(unaryNode.getSymbol());
  5.1792  
  5.1793 -        return null;
  5.1794 +        return false;
  5.1795      }
  5.1796  
  5.1797      @Override
  5.1798 -    public Node enterSUB(final UnaryNode unaryNode) {
  5.1799 -        if (unaryNode.testResolved()) {
  5.1800 -            return null;
  5.1801 -        }
  5.1802 -
  5.1803 +    public boolean enterSUB(final UnaryNode unaryNode) {
  5.1804          load(unaryNode.rhs()).neg().store(unaryNode.getSymbol());
  5.1805  
  5.1806 -        return null;
  5.1807 +        return false;
  5.1808      }
  5.1809  
  5.1810      private Node enterNumericAdd(final Node lhs, final Node rhs, final Type type, final Symbol symbol) {
  5.1811 @@ -2366,11 +2328,7 @@
  5.1812      }
  5.1813  
  5.1814      @Override
  5.1815 -    public Node enterADD(final BinaryNode binaryNode) {
  5.1816 -        if (binaryNode.testResolved()) {
  5.1817 -            return null;
  5.1818 -        }
  5.1819 -
  5.1820 +    public boolean enterADD(final BinaryNode binaryNode) {
  5.1821          final Node lhs = binaryNode.lhs();
  5.1822          final Node rhs = binaryNode.rhs();
  5.1823  
  5.1824 @@ -2384,14 +2342,10 @@
  5.1825              method.store(binaryNode.getSymbol());
  5.1826          }
  5.1827  
  5.1828 -        return null;
  5.1829 +        return false;
  5.1830      }
  5.1831  
  5.1832 -    private Node enterAND_OR(final BinaryNode binaryNode) {
  5.1833 -        if (binaryNode.testResolved()) {
  5.1834 -            return null;
  5.1835 -        }
  5.1836 -
  5.1837 +    private boolean enterAND_OR(final BinaryNode binaryNode) {
  5.1838          final Node lhs = binaryNode.lhs();
  5.1839          final Node rhs = binaryNode.rhs();
  5.1840  
  5.1841 @@ -2410,20 +2364,16 @@
  5.1842          method.label(skip);
  5.1843          method.store(binaryNode.getSymbol());
  5.1844  
  5.1845 -        return null;
  5.1846 +        return false;
  5.1847      }
  5.1848  
  5.1849      @Override
  5.1850 -    public Node enterAND(final BinaryNode binaryNode) {
  5.1851 +    public boolean enterAND(final BinaryNode binaryNode) {
  5.1852          return enterAND_OR(binaryNode);
  5.1853      }
  5.1854  
  5.1855      @Override
  5.1856 -    public Node enterASSIGN(final BinaryNode binaryNode) {
  5.1857 -        if (binaryNode.testResolved()) {
  5.1858 -            return null;
  5.1859 -        }
  5.1860 -
  5.1861 +    public boolean enterASSIGN(final BinaryNode binaryNode) {
  5.1862          final Node lhs = binaryNode.lhs();
  5.1863          final Node rhs = binaryNode.rhs();
  5.1864  
  5.1865 @@ -2442,7 +2392,7 @@
  5.1866              }
  5.1867          }.store();
  5.1868  
  5.1869 -        return null;
  5.1870 +        return false;
  5.1871      }
  5.1872  
  5.1873      /**
  5.1874 @@ -2473,14 +2423,6 @@
  5.1875              this.opType = opType;
  5.1876          }
  5.1877  
  5.1878 -        @Override
  5.1879 -        public void store() {
  5.1880 -            if (assignNode.testResolved()) {
  5.1881 -                return;
  5.1882 -            }
  5.1883 -            super.store();
  5.1884 -        }
  5.1885 -
  5.1886          protected abstract void op();
  5.1887  
  5.1888          @Override
  5.1889 @@ -2493,35 +2435,43 @@
  5.1890      }
  5.1891  
  5.1892      @Override
  5.1893 -    public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
  5.1894 +    public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
  5.1895          assert RuntimeNode.Request.ADD.canSpecialize();
  5.1896 +        final Type lhsType = binaryNode.lhs().getType();
  5.1897 +        final Type rhsType = binaryNode.rhs().getType();
  5.1898          final boolean specialize = binaryNode.getType() == Type.OBJECT;
  5.1899  
  5.1900          new AssignOp(binaryNode) {
  5.1901 -            @Override
  5.1902 -            protected boolean isSelfModifying() {
  5.1903 -                return !specialize;
  5.1904 -            }
  5.1905  
  5.1906              @Override
  5.1907              protected void op() {
  5.1908 -                method.add();
  5.1909 +                if (specialize) {
  5.1910 +                    method.dynamicRuntimeCall(
  5.1911 +                            new SpecializedRuntimeNode(
  5.1912 +                                Request.ADD,
  5.1913 +                                new Type[] {
  5.1914 +                                    lhsType,
  5.1915 +                                    rhsType,
  5.1916 +                                },
  5.1917 +                                Type.OBJECT).getInitialName(),
  5.1918 +                            Type.OBJECT,
  5.1919 +                            Request.ADD);
  5.1920 +                } else {
  5.1921 +                    method.add();
  5.1922 +                }
  5.1923              }
  5.1924  
  5.1925              @Override
  5.1926              protected void evaluate() {
  5.1927 -                if (specialize && specializationCheck(Request.ADD, assignNode, Arrays.asList(assignNode.lhs(), assignNode.rhs()))) {
  5.1928 -                    return;
  5.1929 -                }
  5.1930                  super.evaluate();
  5.1931              }
  5.1932          }.store();
  5.1933  
  5.1934 -        return null;
  5.1935 +        return false;
  5.1936      }
  5.1937  
  5.1938      @Override
  5.1939 -    public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
  5.1940 +    public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
  5.1941          new AssignOp(Type.INT, binaryNode) {
  5.1942              @Override
  5.1943              protected void op() {
  5.1944 @@ -2529,11 +2479,11 @@
  5.1945              }
  5.1946          }.store();
  5.1947  
  5.1948 -        return null;
  5.1949 +        return false;
  5.1950      }
  5.1951  
  5.1952      @Override
  5.1953 -    public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
  5.1954 +    public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
  5.1955          new AssignOp(Type.INT, binaryNode) {
  5.1956              @Override
  5.1957              protected void op() {
  5.1958 @@ -2541,11 +2491,11 @@
  5.1959              }
  5.1960          }.store();
  5.1961  
  5.1962 -        return null;
  5.1963 +        return false;
  5.1964      }
  5.1965  
  5.1966      @Override
  5.1967 -    public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
  5.1968 +    public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
  5.1969          new AssignOp(Type.INT, binaryNode) {
  5.1970              @Override
  5.1971              protected void op() {
  5.1972 @@ -2553,11 +2503,11 @@
  5.1973              }
  5.1974          }.store();
  5.1975  
  5.1976 -        return null;
  5.1977 +        return false;
  5.1978      }
  5.1979  
  5.1980      @Override
  5.1981 -    public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
  5.1982 +    public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
  5.1983          new AssignOp(binaryNode) {
  5.1984              @Override
  5.1985              protected void op() {
  5.1986 @@ -2565,11 +2515,11 @@
  5.1987              }
  5.1988          }.store();
  5.1989  
  5.1990 -        return null;
  5.1991 +        return false;
  5.1992      }
  5.1993  
  5.1994      @Override
  5.1995 -    public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
  5.1996 +    public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
  5.1997          new AssignOp(binaryNode) {
  5.1998              @Override
  5.1999              protected void op() {
  5.2000 @@ -2577,11 +2527,11 @@
  5.2001              }
  5.2002          }.store();
  5.2003  
  5.2004 -        return null;
  5.2005 +        return false;
  5.2006      }
  5.2007  
  5.2008      @Override
  5.2009 -    public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
  5.2010 +    public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
  5.2011          new AssignOp(binaryNode) {
  5.2012              @Override
  5.2013              protected void op() {
  5.2014 @@ -2589,11 +2539,11 @@
  5.2015              }
  5.2016          }.store();
  5.2017  
  5.2018 -        return null;
  5.2019 +        return false;
  5.2020      }
  5.2021  
  5.2022      @Override
  5.2023 -    public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
  5.2024 +    public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
  5.2025          new AssignOp(Type.INT, binaryNode) {
  5.2026              @Override
  5.2027              protected void op() {
  5.2028 @@ -2601,11 +2551,11 @@
  5.2029              }
  5.2030          }.store();
  5.2031  
  5.2032 -        return null;
  5.2033 +        return false;
  5.2034      }
  5.2035  
  5.2036      @Override
  5.2037 -    public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
  5.2038 +    public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
  5.2039          new AssignOp(Type.INT, binaryNode) {
  5.2040              @Override
  5.2041              protected void op() {
  5.2042 @@ -2613,11 +2563,11 @@
  5.2043              }
  5.2044          }.store();
  5.2045  
  5.2046 -        return null;
  5.2047 +        return false;
  5.2048      }
  5.2049  
  5.2050      @Override
  5.2051 -    public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
  5.2052 +    public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
  5.2053          new AssignOp(Type.INT, binaryNode) {
  5.2054              @Override
  5.2055              protected void op() {
  5.2056 @@ -2626,11 +2576,11 @@
  5.2057              }
  5.2058          }.store();
  5.2059  
  5.2060 -        return null;
  5.2061 +        return false;
  5.2062      }
  5.2063  
  5.2064      @Override
  5.2065 -    public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
  5.2066 +    public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
  5.2067          new AssignOp(binaryNode) {
  5.2068              @Override
  5.2069              protected void op() {
  5.2070 @@ -2638,7 +2588,7 @@
  5.2071              }
  5.2072          }.store();
  5.2073  
  5.2074 -        return null;
  5.2075 +        return false;
  5.2076      }
  5.2077  
  5.2078      /**
  5.2079 @@ -2649,9 +2599,6 @@
  5.2080          protected abstract void op();
  5.2081  
  5.2082          protected void evaluate(final BinaryNode node) {
  5.2083 -            if (node.testResolved()) {
  5.2084 -                return;
  5.2085 -            }
  5.2086              load(node.lhs());
  5.2087              load(node.rhs());
  5.2088              op();
  5.2089 @@ -2660,7 +2607,7 @@
  5.2090      }
  5.2091  
  5.2092      @Override
  5.2093 -    public Node enterBIT_AND(final BinaryNode binaryNode) {
  5.2094 +    public boolean enterBIT_AND(final BinaryNode binaryNode) {
  5.2095          new BinaryArith() {
  5.2096              @Override
  5.2097              protected void op() {
  5.2098 @@ -2668,11 +2615,11 @@
  5.2099              }
  5.2100          }.evaluate(binaryNode);
  5.2101  
  5.2102 -        return null;
  5.2103 +        return false;
  5.2104      }
  5.2105  
  5.2106      @Override
  5.2107 -    public Node enterBIT_OR(final BinaryNode binaryNode) {
  5.2108 +    public boolean enterBIT_OR(final BinaryNode binaryNode) {
  5.2109          new BinaryArith() {
  5.2110              @Override
  5.2111              protected void op() {
  5.2112 @@ -2680,11 +2627,11 @@
  5.2113              }
  5.2114          }.evaluate(binaryNode);
  5.2115  
  5.2116 -        return null;
  5.2117 +        return false;
  5.2118      }
  5.2119  
  5.2120      @Override
  5.2121 -    public Node enterBIT_XOR(final BinaryNode binaryNode) {
  5.2122 +    public boolean enterBIT_XOR(final BinaryNode binaryNode) {
  5.2123          new BinaryArith() {
  5.2124              @Override
  5.2125              protected void op() {
  5.2126 @@ -2692,14 +2639,10 @@
  5.2127              }
  5.2128          }.evaluate(binaryNode);
  5.2129  
  5.2130 -        return null;
  5.2131 +        return false;
  5.2132      }
  5.2133  
  5.2134 -    private Node enterComma(final BinaryNode binaryNode) {
  5.2135 -        if (binaryNode.testResolved()) {
  5.2136 -            return null;
  5.2137 -        }
  5.2138 -
  5.2139 +    private boolean enterComma(final BinaryNode binaryNode) {
  5.2140          final Node lhs = binaryNode.lhs();
  5.2141          final Node rhs = binaryNode.rhs();
  5.2142  
  5.2143 @@ -2707,21 +2650,21 @@
  5.2144          load(rhs);
  5.2145          method.store(binaryNode.getSymbol());
  5.2146  
  5.2147 -        return null;
  5.2148 +        return false;
  5.2149      }
  5.2150  
  5.2151      @Override
  5.2152 -    public Node enterCOMMARIGHT(final BinaryNode binaryNode) {
  5.2153 +    public boolean enterCOMMARIGHT(final BinaryNode binaryNode) {
  5.2154          return enterComma(binaryNode);
  5.2155      }
  5.2156  
  5.2157      @Override
  5.2158 -    public Node enterCOMMALEFT(final BinaryNode binaryNode) {
  5.2159 +    public boolean enterCOMMALEFT(final BinaryNode binaryNode) {
  5.2160          return enterComma(binaryNode);
  5.2161      }
  5.2162  
  5.2163      @Override
  5.2164 -    public Node enterDIV(final BinaryNode binaryNode) {
  5.2165 +    public boolean enterDIV(final BinaryNode binaryNode) {
  5.2166          new BinaryArith() {
  5.2167              @Override
  5.2168              protected void op() {
  5.2169 @@ -2729,10 +2672,10 @@
  5.2170              }
  5.2171          }.evaluate(binaryNode);
  5.2172  
  5.2173 -        return null;
  5.2174 +        return false;
  5.2175      }
  5.2176  
  5.2177 -    private Node enterCmp(final Node lhs, final Node rhs, final Condition cond, final Type type, final Symbol symbol) {
  5.2178 +    private boolean enterCmp(final Node lhs, final Node rhs, final Condition cond, final Type type, final Symbol symbol) {
  5.2179          final Type lhsType = lhs.getType();
  5.2180          final Type rhsType = rhs.getType();
  5.2181  
  5.2182 @@ -2758,48 +2701,45 @@
  5.2183          method.convert(type);
  5.2184          method.store(symbol);
  5.2185  
  5.2186 -        return null;
  5.2187 +        return false;
  5.2188      }
  5.2189  
  5.2190 -    private Node enterCmp(final BinaryNode binaryNode, final Condition cond) {
  5.2191 -        if (binaryNode.testResolved()) {
  5.2192 -            return null;
  5.2193 -        }
  5.2194 +    private boolean enterCmp(final BinaryNode binaryNode, final Condition cond) {
  5.2195          return enterCmp(binaryNode.lhs(), binaryNode.rhs(), cond, binaryNode.getType(), binaryNode.getSymbol());
  5.2196      }
  5.2197  
  5.2198      @Override
  5.2199 -    public Node enterEQ(final BinaryNode binaryNode) {
  5.2200 +    public boolean enterEQ(final BinaryNode binaryNode) {
  5.2201          return enterCmp(binaryNode, Condition.EQ);
  5.2202      }
  5.2203  
  5.2204      @Override
  5.2205 -    public Node enterEQ_STRICT(final BinaryNode binaryNode) {
  5.2206 +    public boolean enterEQ_STRICT(final BinaryNode binaryNode) {
  5.2207          return enterCmp(binaryNode, Condition.EQ);
  5.2208      }
  5.2209  
  5.2210      @Override
  5.2211 -    public Node enterGE(final BinaryNode binaryNode) {
  5.2212 +    public boolean enterGE(final BinaryNode binaryNode) {
  5.2213          return enterCmp(binaryNode, Condition.GE);
  5.2214      }
  5.2215  
  5.2216      @Override
  5.2217 -    public Node enterGT(final BinaryNode binaryNode) {
  5.2218 +    public boolean enterGT(final BinaryNode binaryNode) {
  5.2219          return enterCmp(binaryNode, Condition.GT);
  5.2220      }
  5.2221  
  5.2222      @Override
  5.2223 -    public Node enterLE(final BinaryNode binaryNode) {
  5.2224 +    public boolean enterLE(final BinaryNode binaryNode) {
  5.2225          return enterCmp(binaryNode, Condition.LE);
  5.2226      }
  5.2227  
  5.2228      @Override
  5.2229 -    public Node enterLT(final BinaryNode binaryNode) {
  5.2230 +    public boolean enterLT(final BinaryNode binaryNode) {
  5.2231          return enterCmp(binaryNode, Condition.LT);
  5.2232      }
  5.2233  
  5.2234      @Override
  5.2235 -    public Node enterMOD(final BinaryNode binaryNode) {
  5.2236 +    public boolean enterMOD(final BinaryNode binaryNode) {
  5.2237          new BinaryArith() {
  5.2238              @Override
  5.2239              protected void op() {
  5.2240 @@ -2807,11 +2747,11 @@
  5.2241              }
  5.2242          }.evaluate(binaryNode);
  5.2243  
  5.2244 -        return null;
  5.2245 +        return false;
  5.2246      }
  5.2247  
  5.2248      @Override
  5.2249 -    public Node enterMUL(final BinaryNode binaryNode) {
  5.2250 +    public boolean enterMUL(final BinaryNode binaryNode) {
  5.2251          new BinaryArith() {
  5.2252              @Override
  5.2253              protected void op() {
  5.2254 @@ -2819,26 +2759,26 @@
  5.2255              }
  5.2256          }.evaluate(binaryNode);
  5.2257  
  5.2258 -        return null;
  5.2259 +        return false;
  5.2260      }
  5.2261  
  5.2262      @Override
  5.2263 -    public Node enterNE(final BinaryNode binaryNode) {
  5.2264 +    public boolean enterNE(final BinaryNode binaryNode) {
  5.2265          return enterCmp(binaryNode, Condition.NE);
  5.2266      }
  5.2267  
  5.2268      @Override
  5.2269 -    public Node enterNE_STRICT(final BinaryNode binaryNode) {
  5.2270 +    public boolean enterNE_STRICT(final BinaryNode binaryNode) {
  5.2271          return enterCmp(binaryNode, Condition.NE);
  5.2272      }
  5.2273  
  5.2274      @Override
  5.2275 -    public Node enterOR(final BinaryNode binaryNode) {
  5.2276 +    public boolean enterOR(final BinaryNode binaryNode) {
  5.2277          return enterAND_OR(binaryNode);
  5.2278      }
  5.2279  
  5.2280      @Override
  5.2281 -    public Node enterSAR(final BinaryNode binaryNode) {
  5.2282 +    public boolean enterSAR(final BinaryNode binaryNode) {
  5.2283          new BinaryArith() {
  5.2284              @Override
  5.2285              protected void op() {
  5.2286 @@ -2846,11 +2786,11 @@
  5.2287              }
  5.2288          }.evaluate(binaryNode);
  5.2289  
  5.2290 -        return null;
  5.2291 +        return false;
  5.2292      }
  5.2293  
  5.2294      @Override
  5.2295 -    public Node enterSHL(final BinaryNode binaryNode) {
  5.2296 +    public boolean enterSHL(final BinaryNode binaryNode) {
  5.2297          new BinaryArith() {
  5.2298              @Override
  5.2299              protected void op() {
  5.2300 @@ -2858,11 +2798,11 @@
  5.2301              }
  5.2302          }.evaluate(binaryNode);
  5.2303  
  5.2304 -        return null;
  5.2305 +        return false;
  5.2306      }
  5.2307  
  5.2308      @Override
  5.2309 -    public Node enterSHR(final BinaryNode binaryNode) {
  5.2310 +    public boolean enterSHR(final BinaryNode binaryNode) {
  5.2311          new BinaryArith() {
  5.2312              @Override
  5.2313              protected void op() {
  5.2314 @@ -2871,11 +2811,11 @@
  5.2315              }
  5.2316          }.evaluate(binaryNode);
  5.2317  
  5.2318 -        return null;
  5.2319 +        return false;
  5.2320      }
  5.2321  
  5.2322      @Override
  5.2323 -    public Node enterSUB(final BinaryNode binaryNode) {
  5.2324 +    public boolean enterSUB(final BinaryNode binaryNode) {
  5.2325          new BinaryArith() {
  5.2326              @Override
  5.2327              protected void op() {
  5.2328 @@ -2883,18 +2823,11 @@
  5.2329              }
  5.2330          }.evaluate(binaryNode);
  5.2331  
  5.2332 -        return null;
  5.2333 +        return false;
  5.2334      }
  5.2335  
  5.2336 -    /*
  5.2337 -     * Ternary visits.
  5.2338 -     */
  5.2339      @Override
  5.2340 -    public Node enterTernaryNode(final TernaryNode ternaryNode) {
  5.2341 -        if (ternaryNode.testResolved()) {
  5.2342 -            return null;
  5.2343 -        }
  5.2344 -
  5.2345 +    public boolean enterTernaryNode(final TernaryNode ternaryNode) {
  5.2346          final Node lhs   = ternaryNode.lhs();
  5.2347          final Node rhs   = ternaryNode.rhs();
  5.2348          final Node third = ternaryNode.third();
  5.2349 @@ -2926,7 +2859,7 @@
  5.2350          method.label(exitLabel);
  5.2351          method.store(symbol);
  5.2352  
  5.2353 -        return null;
  5.2354 +        return false;
  5.2355      }
  5.2356  
  5.2357      /**
  5.2358 @@ -2955,7 +2888,7 @@
  5.2359          if (scopeCalls.containsKey(scopeCall)) {
  5.2360              return scopeCalls.get(scopeCall);
  5.2361          }
  5.2362 -        scopeCall.setClassAndName(getCurrentCompileUnit(), getCurrentFunctionNode().uniqueName("scopeCall"));
  5.2363 +        scopeCall.setClassAndName(unit, getLexicalContext().getCurrentFunction().uniqueName("scopeCall"));
  5.2364          scopeCalls.put(scopeCall, scopeCall);
  5.2365          return scopeCall;
  5.2366      }
  5.2367 @@ -2974,7 +2907,7 @@
  5.2368          if (scopeCalls.containsKey(scopeCall)) {
  5.2369              return scopeCalls.get(scopeCall);
  5.2370          }
  5.2371 -        scopeCall.setClassAndName(getCurrentCompileUnit(), getCurrentFunctionNode().uniqueName("scopeCall"));
  5.2372 +        scopeCall.setClassAndName(unit, getLexicalContext().getCurrentFunction().uniqueName("scopeCall"));
  5.2373          scopeCalls.put(scopeCall, scopeCall);
  5.2374          return scopeCall;
  5.2375      }
  5.2376 @@ -3037,9 +2970,6 @@
  5.2377          /** The target node to store to, e.g. x */
  5.2378          private final Node target;
  5.2379  
  5.2380 -        /** Should the result always be discarded, no matter what? */
  5.2381 -        private final boolean alwaysDiscard;
  5.2382 -
  5.2383          /** How deep on the stack do the arguments go if this generates an indy call */
  5.2384          private int depth;
  5.2385  
  5.2386 @@ -3055,7 +2985,6 @@
  5.2387          protected Store(final T assignNode, final Node target) {
  5.2388              this.assignNode = assignNode;
  5.2389              this.target = target;
  5.2390 -            this.alwaysDiscard = assignNode == target;
  5.2391          }
  5.2392  
  5.2393          /**
  5.2394 @@ -3077,21 +3006,21 @@
  5.2395  
  5.2396          private void prologue() {
  5.2397              final Symbol targetSymbol = target.getSymbol();
  5.2398 -            final Symbol scopeSymbol  = getCurrentFunctionNode().getScopeNode().getSymbol();
  5.2399 +            final Symbol scopeSymbol  = getLexicalContext().getCurrentFunction().compilerConstant(SCOPE);
  5.2400  
  5.2401              /**
  5.2402               * This loads the parts of the target, e.g base and index. they are kept
  5.2403               * on the stack throughout the store and used at the end to execute it
  5.2404               */
  5.2405  
  5.2406 -            target.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
  5.2407 +            target.accept(new NodeVisitor() {
  5.2408                  @Override
  5.2409 -                public Node enterIdentNode(final IdentNode node) {
  5.2410 +                public boolean enterIdentNode(final IdentNode node) {
  5.2411                      if (targetSymbol.isScope()) {
  5.2412                          method.load(scopeSymbol);
  5.2413                          depth++;
  5.2414                      }
  5.2415 -                    return null;
  5.2416 +                    return false;
  5.2417                  }
  5.2418  
  5.2419                  private void enterBaseNode() {
  5.2420 @@ -3109,13 +3038,13 @@
  5.2421                  }
  5.2422  
  5.2423                  @Override
  5.2424 -                public Node enterAccessNode(final AccessNode node) {
  5.2425 +                public boolean enterAccessNode(final AccessNode node) {
  5.2426                      enterBaseNode();
  5.2427 -                    return null;
  5.2428 +                    return false;
  5.2429                  }
  5.2430  
  5.2431                  @Override
  5.2432 -                public Node enterIndexNode(final IndexNode node) {
  5.2433 +                public boolean enterIndexNode(final IndexNode node) {
  5.2434                      enterBaseNode();
  5.2435  
  5.2436                      final Node index = node.getIndex();
  5.2437 @@ -3131,14 +3060,14 @@
  5.2438                          method.dup(1);
  5.2439                      }
  5.2440  
  5.2441 -                    return null;
  5.2442 +                    return false;
  5.2443                  }
  5.2444  
  5.2445              });
  5.2446          }
  5.2447  
  5.2448          private Symbol quickSymbol(final Type type) {
  5.2449 -            return quickSymbol(type, QUICK_PREFIX.tag());
  5.2450 +            return quickSymbol(type, QUICK_PREFIX.symbolName());
  5.2451          }
  5.2452  
  5.2453          /**
  5.2454 @@ -3151,22 +3080,28 @@
  5.2455           * @return the quick symbol
  5.2456           */
  5.2457          private Symbol quickSymbol(final Type type, final String prefix) {
  5.2458 -            final String name = getCurrentFunctionNode().uniqueName(prefix);
  5.2459 -            final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL, null, null);
  5.2460 +            final String name = getLexicalContext().getCurrentFunction().uniqueName(prefix);
  5.2461 +            final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL);
  5.2462  
  5.2463              symbol.setType(type);
  5.2464 -            symbol.setSlot(getCurrentBlock().getFrame().getSlotCount());
  5.2465 +            final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1];
  5.2466 +            nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount();
  5.2467 +            symbol.setSlot(quickSlot);
  5.2468  
  5.2469              return symbol;
  5.2470          }
  5.2471  
  5.2472          // store the result that "lives on" after the op, e.g. "i" in i++ postfix.
  5.2473          protected void storeNonDiscard() {
  5.2474 -            if (assignNode.shouldDiscard() || alwaysDiscard) {
  5.2475 -                assignNode.setDiscard(false);
  5.2476 +            if (discard.peek() == assignNode) {
  5.2477 +                assert assignNode.isAssignment();
  5.2478 +                discard.pop();
  5.2479                  return;
  5.2480              }
  5.2481  
  5.2482 +            //System.err.println("Store with out discard that shouldn't just return " + assignNode);
  5.2483 +            //new Throwable().printStackTrace();
  5.2484 +
  5.2485              final Symbol symbol = assignNode.getSymbol();
  5.2486              if (symbol.hasSlot()) {
  5.2487                  method.dup().store(symbol);
  5.2488 @@ -3191,22 +3126,22 @@
  5.2489               */
  5.2490              method.convert(target.getType());
  5.2491  
  5.2492 -            target.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
  5.2493 +            target.accept(new NodeVisitor() {
  5.2494                  @Override
  5.2495 -                protected Node enterDefault(Node node) {
  5.2496 +                protected boolean enterDefault(Node node) {
  5.2497                      throw new AssertionError("Unexpected node " + node + " in store epilogue");
  5.2498                  }
  5.2499  
  5.2500                  @Override
  5.2501 -                public Node enterUnaryNode(final UnaryNode node) {
  5.2502 -                    if(node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) {
  5.2503 +                public boolean enterUnaryNode(final UnaryNode node) {
  5.2504 +                    if (node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) {
  5.2505                          method.convert(node.rhs().getType());
  5.2506                      }
  5.2507 -                    return node;
  5.2508 +                    return true;
  5.2509                  }
  5.2510  
  5.2511                  @Override
  5.2512 -                public Node enterIdentNode(final IdentNode node) {
  5.2513 +                public boolean enterIdentNode(final IdentNode node) {
  5.2514                      final Symbol symbol = node.getSymbol();
  5.2515                      assert symbol != null;
  5.2516                      if (symbol.isScope()) {
  5.2517 @@ -3218,20 +3153,20 @@
  5.2518                      } else {
  5.2519                          method.store(symbol);
  5.2520                      }
  5.2521 -                    return null;
  5.2522 +                    return false;
  5.2523  
  5.2524                  }
  5.2525  
  5.2526                  @Override
  5.2527 -                public Node enterAccessNode(final AccessNode node) {
  5.2528 +                public boolean enterAccessNode(final AccessNode node) {
  5.2529                      method.dynamicSet(node.getProperty().getType(), node.getProperty().getName(), getCallSiteFlags());
  5.2530 -                    return null;
  5.2531 +                    return false;
  5.2532                  }
  5.2533  
  5.2534                  @Override
  5.2535 -                public Node enterIndexNode(final IndexNode node) {
  5.2536 +                public boolean enterIndexNode(final IndexNode node) {
  5.2537                      method.dynamicSetIndex(getCallSiteFlags());
  5.2538 -                    return null;
  5.2539 +                    return false;
  5.2540                  }
  5.2541              });
  5.2542  
  5.2543 @@ -3250,10 +3185,23 @@
  5.2544                  method.load(quick);
  5.2545              }
  5.2546          }
  5.2547 -
  5.2548      }
  5.2549  
  5.2550 -    private void newFunctionObject(final FunctionNode functionNode) {
  5.2551 +    private void newFunctionObject(final FunctionNode functionNode, final FunctionNode originalFunctionNode) {
  5.2552 +        final LexicalContext lc = getLexicalContext();
  5.2553 +        assert lc.peek() == functionNode;
  5.2554 +        // We don't emit a ScriptFunction on stack for:
  5.2555 +        // 1. the outermost compiled function (as there's no code being generated in its outer context that'd need it
  5.2556 +        //    as a callee), and
  5.2557 +        // 2. for functions that are immediately called upon definition and they don't need a callee, e.g. (function(){})().
  5.2558 +        //    Such immediately-called functions are invoked using INVOKESTATIC (see enterFunctionNode() of the embedded
  5.2559 +        //    visitor of enterCallNode() for details), and if they don't need a callee, they don't have it on their
  5.2560 +        //    static method's parameter list.
  5.2561 +        if(lc.getOutermostFunction() == functionNode ||
  5.2562 +                (!functionNode.needsCallee()) && lc.isFunctionDefinedInCurrentCall(originalFunctionNode)) {
  5.2563 +            return;
  5.2564 +        }
  5.2565 +
  5.2566          final boolean isLazy  = functionNode.isLazy();
  5.2567  
  5.2568          new ObjectCreator(this, new ArrayList<String>(), new ArrayList<Symbol>(), false, false) {
  5.2569 @@ -3265,7 +3213,7 @@
  5.2570                  loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), Compiler.binaryName(getClassName()), makeMap()));
  5.2571  
  5.2572                  if (isLazy || functionNode.needsParentScope()) {
  5.2573 -                    m.loadScope();
  5.2574 +                    m.loadCompilerConstant(SCOPE);
  5.2575                  } else {
  5.2576                      m.loadNull();
  5.2577                  }
     6.1 --- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Fri Apr 19 18:23:00 2013 +0530
     6.2 +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Fri Apr 19 16:11:16 2013 +0200
     6.3 @@ -15,6 +15,7 @@
     6.4  import java.util.HashSet;
     6.5  import java.util.Set;
     6.6  import jdk.nashorn.internal.codegen.types.Type;
     6.7 +import jdk.nashorn.internal.ir.Block;
     6.8  import jdk.nashorn.internal.ir.CallNode;
     6.9  import jdk.nashorn.internal.ir.FunctionNode;
    6.10  import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
    6.11 @@ -29,8 +30,8 @@
    6.12  import jdk.nashorn.internal.runtime.Timing;
    6.13  
    6.14  /**
    6.15 - * A compilation phase is a step in the processes of turning a JavaScript FunctionNode
    6.16 - * into bytecode. It has an optional return value.
    6.17 + * A compilation phase is a step in the processes of turning a JavaScript
    6.18 + * FunctionNode into bytecode. It has an optional return value.
    6.19   */
    6.20  enum CompilationPhase {
    6.21  
    6.22 @@ -41,77 +42,87 @@
    6.23       */
    6.24      LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
    6.25          @Override
    6.26 -        void transform(final Compiler compiler, final FunctionNode fn) {
    6.27 +        FunctionNode transform(final Compiler compiler, final FunctionNode fn0) {
    6.28  
    6.29              /*
    6.30 -             * For lazy compilation, we might be given a node previously marked as lazy
    6.31 -             * to compile as the outermost function node in the compiler. Unmark it
    6.32 -             * so it can be compiled and not cause recursion. Make sure the return type
    6.33 -             * is unknown so it can be correctly deduced. Return types are always
    6.34 -             * Objects in Lazy nodes as we haven't got a change to generate code for
    6.35 -             * them and decude its parameter specialization
    6.36 +             * For lazy compilation, we might be given a node previously marked
    6.37 +             * as lazy to compile as the outermost function node in the
    6.38 +             * compiler. Unmark it so it can be compiled and not cause
    6.39 +             * recursion. Make sure the return type is unknown so it can be
    6.40 +             * correctly deduced. Return types are always Objects in Lazy nodes
    6.41 +             * as we haven't got a change to generate code for them and decude
    6.42 +             * its parameter specialization
    6.43               *
    6.44 -             * TODO: in the future specializations from a callsite will be passed here
    6.45 -             * so we can generate a better non-lazy version of a function from a trampoline
    6.46 +             * TODO: in the future specializations from a callsite will be
    6.47 +             * passed here so we can generate a better non-lazy version of a
    6.48 +             * function from a trampoline
    6.49               */
    6.50 -            //compute the signature from the callsite - todo - now just clone object params
    6.51 +
    6.52              final FunctionNode outermostFunctionNode = compiler.getFunctionNode();
    6.53 -            outermostFunctionNode.setIsLazy(false);
    6.54 -            outermostFunctionNode.setReturnType(Type.UNKNOWN);
    6.55 +            assert outermostFunctionNode == fn0;
    6.56  
    6.57              final Set<FunctionNode> neverLazy = new HashSet<>();
    6.58 -            final Set<FunctionNode> lazy = new HashSet<>();
    6.59 +            final Set<FunctionNode> lazy      = new HashSet<>();
    6.60  
    6.61 -            outermostFunctionNode.accept(new NodeVisitor() {
    6.62 -                // self references are done with invokestatic and thus cannot have trampolines - never lazy
    6.63 +            FunctionNode newFunctionNode = outermostFunctionNode;
    6.64 +
    6.65 +            newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor() {
    6.66 +                // self references are done with invokestatic and thus cannot
    6.67 +                // have trampolines - never lazy
    6.68                  @Override
    6.69 -                public Node enterCallNode(final CallNode node) {
    6.70 +                public boolean enterCallNode(final CallNode node) {
    6.71                      final Node callee = node.getFunction();
    6.72                      if (callee instanceof FunctionNode) {
    6.73                          neverLazy.add(((FunctionNode)callee));
    6.74 -                        return null;
    6.75 +                        return false;
    6.76                      }
    6.77 -                    return node;
    6.78 +                    return true;
    6.79                  }
    6.80  
    6.81 +                //any function that isn't the outermost one must be marked as lazy
    6.82                  @Override
    6.83 -                public Node enterFunctionNode(final FunctionNode node) {
    6.84 -                    if (node == outermostFunctionNode) {
    6.85 -                        return node;
    6.86 -                    }
    6.87 +                public boolean enterFunctionNode(final FunctionNode node) {
    6.88                      assert compiler.isLazy();
    6.89                      lazy.add(node);
    6.90 -
    6.91 -                    //also needs scope, potentially needs arguments etc etc
    6.92 -
    6.93 -                    return node;
    6.94 +                    return true;
    6.95                  }
    6.96              });
    6.97  
    6.98 +            //at least one method is non lazy - the outermost one
    6.99 +            neverLazy.add(newFunctionNode);
   6.100 +
   6.101              for (final FunctionNode node : neverLazy) {
   6.102 -                Compiler.LOG.fine("Marking " + node.getName() + " as non lazy, as it's a self reference");
   6.103 -                node.setIsLazy(false);
   6.104 +                Compiler.LOG.fine(
   6.105 +                        "Marking ",
   6.106 +                        node.getName(),
   6.107 +                        " as non lazy, as it's a self reference");
   6.108                  lazy.remove(node);
   6.109              }
   6.110  
   6.111 -            outermostFunctionNode.accept(new NodeOperatorVisitor() {
   6.112 -                private final LexicalContext lexicalContext = new LexicalContext();
   6.113 +            newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeOperatorVisitor() {
   6.114                  @Override
   6.115 -                public Node enterFunctionNode(FunctionNode functionNode) {
   6.116 -                    lexicalContext.push(functionNode);
   6.117 -                    if(lazy.contains(functionNode)) {
   6.118 -                        Compiler.LOG.fine("Marking " + functionNode.getName() + " as lazy");
   6.119 -                        functionNode.setIsLazy(true);
   6.120 -                        lexicalContext.getParentFunction(functionNode).setHasLazyChildren();
   6.121 +                public Node leaveFunctionNode(final FunctionNode functionNode) {
   6.122 +                    final LexicalContext lc = getLexicalContext();
   6.123 +                    if (lazy.contains(functionNode)) {
   6.124 +                        Compiler.LOG.fine(
   6.125 +                                "Marking ",
   6.126 +                                functionNode.getName(),
   6.127 +                                " as lazy");
   6.128 +                        final FunctionNode parent = lc.getParentFunction(functionNode);
   6.129 +                        assert parent != null;
   6.130 +                        lc.setFlag(parent, FunctionNode.HAS_LAZY_CHILDREN);
   6.131 +                        lc.setFlag(parent.getBody(), Block.NEEDS_SCOPE);
   6.132 +                        lc.setFlag(functionNode, FunctionNode.IS_LAZY);
   6.133 +                        return functionNode;
   6.134                      }
   6.135 -                    return functionNode;
   6.136 -                }
   6.137 -                @Override
   6.138 -                public Node leaveFunctionNode(FunctionNode functionNode) {
   6.139 -                    lexicalContext.pop(functionNode);
   6.140 -                    return functionNode;
   6.141 +
   6.142 +                    return functionNode.
   6.143 +                        clearFlag(lc, FunctionNode.IS_LAZY).
   6.144 +                        setReturnType(lc, Type.UNKNOWN);
   6.145                  }
   6.146              });
   6.147 +
   6.148 +            return newFunctionNode;
   6.149          }
   6.150  
   6.151          @Override
   6.152 @@ -121,13 +132,13 @@
   6.153      },
   6.154  
   6.155      /*
   6.156 -     * Constant folding pass
   6.157 -     *   Simple constant folding that will make elementary constructs go away
   6.158 +     * Constant folding pass Simple constant folding that will make elementary
   6.159 +     * constructs go away
   6.160       */
   6.161      CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
   6.162          @Override
   6.163 -        void transform(final Compiler compiler, final FunctionNode fn) {
   6.164 -            fn.accept(new FoldConstants());
   6.165 +        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
   6.166 +            return (FunctionNode)fn.accept(new FoldConstants());
   6.167          }
   6.168  
   6.169          @Override
   6.170 @@ -137,18 +148,16 @@
   6.171      },
   6.172  
   6.173      /*
   6.174 -     * Lower (Control flow pass)
   6.175 -     *   Finalizes the control flow. Clones blocks for finally constructs and
   6.176 -     *   similar things. Establishes termination criteria for nodes
   6.177 -     *   Guarantee return instructions to method making sure control flow
   6.178 -     *   cannot fall off the end. Replacing high level nodes with lower such
   6.179 -     *   as runtime nodes where applicable.
   6.180 -     *
   6.181 +     * Lower (Control flow pass) Finalizes the control flow. Clones blocks for
   6.182 +     * finally constructs and similar things. Establishes termination criteria
   6.183 +     * for nodes Guarantee return instructions to method making sure control
   6.184 +     * flow cannot fall off the end. Replacing high level nodes with lower such
   6.185 +     * as runtime nodes where applicable.
   6.186       */
   6.187      LOWERING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED)) {
   6.188          @Override
   6.189 -        void transform(final Compiler compiler, final FunctionNode fn) {
   6.190 -            fn.accept(new Lower());
   6.191 +        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
   6.192 +            return (FunctionNode)fn.accept(new Lower());
   6.193          }
   6.194  
   6.195          @Override
   6.196 @@ -158,13 +167,27 @@
   6.197      },
   6.198  
   6.199      /*
   6.200 -     * Attribution
   6.201 -     *   Assign symbols and types to all nodes.
   6.202 +     * Attribution Assign symbols and types to all nodes.
   6.203       */
   6.204      ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
   6.205          @Override
   6.206 -        void transform(final Compiler compiler, final FunctionNode fn) {
   6.207 -            fn.accept(new Attr());
   6.208 +        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
   6.209 +            return (FunctionNode)initReturnTypes(fn).accept(new Attr());
   6.210 +        }
   6.211 +
   6.212 +        /**
   6.213 +         * Pessimistically set all lazy functions' return types to Object
   6.214 +         * @param functionNode node where to start iterating
   6.215 +         */
   6.216 +        private FunctionNode initReturnTypes(final FunctionNode functionNode) {
   6.217 +            return (FunctionNode)functionNode.accept(new NodeVisitor() {
   6.218 +                @Override
   6.219 +                public Node leaveFunctionNode(final FunctionNode node) {
   6.220 +                    return node.isLazy() ?
   6.221 +                           node.setReturnType(getLexicalContext(), Type.OBJECT) :
   6.222 +                           node.setReturnType(getLexicalContext(), Type.UNKNOWN);
   6.223 +                }
   6.224 +            });
   6.225          }
   6.226  
   6.227          @Override
   6.228 @@ -174,25 +197,35 @@
   6.229      },
   6.230  
   6.231      /*
   6.232 -     * Splitter
   6.233 -     *   Split the AST into several compile units based on a size heuristic
   6.234 -     *   Splitter needs attributed AST for weight calculations (e.g. is
   6.235 -     *   a + b a ScriptRuntime.ADD with call overhead or a dadd with much
   6.236 -     *   less). Split IR can lead to scope information being changed.
   6.237 +     * Splitter Split the AST into several compile units based on a size
   6.238 +     * heuristic Splitter needs attributed AST for weight calculations (e.g. is
   6.239 +     * a + b a ScriptRuntime.ADD with call overhead or a dadd with much less).
   6.240 +     * Split IR can lead to scope information being changed.
   6.241       */
   6.242      SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
   6.243          @Override
   6.244 -        void transform(final Compiler compiler, final FunctionNode fn) {
   6.245 +        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
   6.246              final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
   6.247  
   6.248 -            new Splitter(compiler, fn, outermostCompileUnit).split();
   6.249 +            final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn);
   6.250  
   6.251 -            assert fn.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + fn.getCompileUnit() + ") != " + outermostCompileUnit;
   6.252 +            assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
   6.253  
   6.254 -            if (fn.isStrictMode()) {
   6.255 +            if (newFunctionNode.isStrict()) {
   6.256                  assert compiler.getStrictMode();
   6.257                  compiler.setStrictMode(true);
   6.258              }
   6.259 +
   6.260 +            /*
   6.261 +            newFunctionNode.accept(new NodeVisitor() {
   6.262 +                @Override
   6.263 +                public boolean enterFunctionNode(final FunctionNode functionNode) {
   6.264 +                    assert functionNode.getCompileUnit() != null : functionNode.getName() + " " + Debug.id(functionNode) + " has no compile unit";
   6.265 +                    return true;
   6.266 +                }
   6.267 +            });*/
   6.268 +
   6.269 +            return newFunctionNode;
   6.270          }
   6.271  
   6.272          @Override
   6.273 @@ -204,30 +237,32 @@
   6.274      /*
   6.275       * FinalizeTypes
   6.276       *
   6.277 -     *   This pass finalizes the types for nodes. If Attr created wider types than
   6.278 -     *   known during the first pass, convert nodes are inserted or access nodes
   6.279 -     *   are specialized where scope accesses.
   6.280 +     * This pass finalizes the types for nodes. If Attr created wider types than
   6.281 +     * known during the first pass, convert nodes are inserted or access nodes
   6.282 +     * are specialized where scope accesses.
   6.283       *
   6.284 -     *   Runtime nodes may be removed and primitivized or reintroduced depending
   6.285 -     *   on information that was established in Attr.
   6.286 +     * Runtime nodes may be removed and primitivized or reintroduced depending
   6.287 +     * on information that was established in Attr.
   6.288       *
   6.289       * Contract: all variables must have slot assignments and scope assignments
   6.290       * before type finalization.
   6.291       */
   6.292      TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT)) {
   6.293          @Override
   6.294 -        void transform(final Compiler compiler, final FunctionNode fn) {
   6.295 +        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
   6.296              final ScriptEnvironment env = compiler.getEnv();
   6.297  
   6.298 -            fn.accept(new FinalizeTypes());
   6.299 +            final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes());
   6.300  
   6.301              if (env._print_lower_ast) {
   6.302 -                env.getErr().println(new ASTWriter(fn));
   6.303 +                env.getErr().println(new ASTWriter(newFunctionNode));
   6.304              }
   6.305  
   6.306              if (env._print_lower_parse) {
   6.307 -                env.getErr().println(new PrintVisitor(fn));
   6.308 -           }
   6.309 +                env.getErr().println(new PrintVisitor(newFunctionNode));
   6.310 +            }
   6.311 +
   6.312 +            return newFunctionNode;
   6.313          }
   6.314  
   6.315          @Override
   6.316 @@ -239,31 +274,21 @@
   6.317      /*
   6.318       * Bytecode generation:
   6.319       *
   6.320 -     *   Generate the byte code class(es) resulting from the compiled FunctionNode
   6.321 +     * Generate the byte code class(es) resulting from the compiled FunctionNode
   6.322       */
   6.323      BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED)) {
   6.324          @Override
   6.325 -        void transform(final Compiler compiler, final FunctionNode fn) {
   6.326 +        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
   6.327              final ScriptEnvironment env = compiler.getEnv();
   6.328 +            FunctionNode newFunctionNode = fn;
   6.329  
   6.330              try {
   6.331                  final CodeGenerator codegen = new CodeGenerator(compiler);
   6.332 -                fn.accept(codegen);
   6.333 +                newFunctionNode = (FunctionNode)newFunctionNode.accept(codegen);
   6.334                  codegen.generateScopeCalls();
   6.335 -                fn.accept(new NodeOperatorVisitor() {
   6.336 -                    @Override
   6.337 -                    public Node enterFunctionNode(FunctionNode functionNode) {
   6.338 -                        if(functionNode.isLazy()) {
   6.339 -                            functionNode.resetResolved();
   6.340 -                            return null;
   6.341 -                        }
   6.342 -                        return fn;
   6.343 -                    }
   6.344 -                });
   6.345 -
   6.346              } catch (final VerifyError e) {
   6.347                  if (env._verify_code || env._print_code) {
   6.348 -                    env.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
   6.349 +                    env.getErr().println(e.getClass().getSimpleName() + ": "  + e.getMessage());
   6.350                      if (env._dump_on_error) {
   6.351                          e.printStackTrace(env.getErr());
   6.352                      }
   6.353 @@ -283,25 +308,25 @@
   6.354  
   6.355                  compiler.addClass(className, bytecode);
   6.356  
   6.357 -                //should could be printed to stderr for generate class?
   6.358 +                // should could be printed to stderr for generate class?
   6.359                  if (env._print_code) {
   6.360                      final StringBuilder sb = new StringBuilder();
   6.361 -                    sb.append("class: " + className).
   6.362 -                        append('\n').
   6.363 -                        append(ClassEmitter.disassemble(bytecode)).
   6.364 -                        append("=====");
   6.365 +                    sb.append("class: " + className).append('\n')
   6.366 +                            .append(ClassEmitter.disassemble(bytecode))
   6.367 +                            .append("=====");
   6.368                      env.getErr().println(sb);
   6.369                  }
   6.370  
   6.371 -                //should we verify the generated code?
   6.372 +                // should we verify the generated code?
   6.373                  if (env._verify_code) {
   6.374                      compiler.getCodeInstaller().verify(bytecode);
   6.375                  }
   6.376  
   6.377 -                //should code be dumped to disk - only valid in compile_only mode?
   6.378 +                // should code be dumped to disk - only valid in compile_only
   6.379 +                // mode?
   6.380                  if (env._dest_dir != null && env._compile_only) {
   6.381                      final String fileName = className.replace('.', File.separatorChar) + ".class";
   6.382 -                    final int    index    = fileName.lastIndexOf(File.separatorChar);
   6.383 +                    final int index = fileName.lastIndexOf(File.separatorChar);
   6.384  
   6.385                      if (index != -1) {
   6.386                          final File dir = new File(fileName.substring(0, index));
   6.387 @@ -314,11 +339,18 @@
   6.388                                  fos.write(bytecode);
   6.389                              }
   6.390                          } catch (final IOException e) {
   6.391 -                            Compiler.LOG.warning("Skipping class dump for " + className + ": " + ECMAErrors.getMessage("io.error.cant.write", dir.toString()));
   6.392 +                            Compiler.LOG.warning("Skipping class dump for ",
   6.393 +                                    className,
   6.394 +                                    ": ",
   6.395 +                                    ECMAErrors.getMessage(
   6.396 +                                        "io.error.cant.write",
   6.397 +                                        dir.toString()));
   6.398                          }
   6.399                      }
   6.400                  }
   6.401              }
   6.402 +
   6.403 +            return newFunctionNode;
   6.404          }
   6.405  
   6.406          @Override
   6.407 @@ -340,26 +372,28 @@
   6.408          return functionNode.hasState(pre);
   6.409      }
   6.410  
   6.411 -    protected void begin(final FunctionNode functionNode) {
   6.412 +    protected FunctionNode begin(final FunctionNode functionNode) {
   6.413          if (pre != null) {
   6.414 -            //check that everything in pre is present
   6.415 +            // check that everything in pre is present
   6.416              for (final CompilationState state : pre) {
   6.417                  assert functionNode.hasState(state);
   6.418              }
   6.419 -            //check that nothing else is present
   6.420 +            // check that nothing else is present
   6.421              for (final CompilationState state : CompilationState.values()) {
   6.422                  assert !(functionNode.hasState(state) && !pre.contains(state));
   6.423              }
   6.424          }
   6.425  
   6.426          startTime = System.currentTimeMillis();
   6.427 +        return functionNode;
   6.428      }
   6.429  
   6.430 -    protected void end(final FunctionNode functionNode) {
   6.431 +    protected FunctionNode end(final FunctionNode functionNode) {
   6.432          endTime = System.currentTimeMillis();
   6.433          Timing.accumulateTime(toString(), endTime - startTime);
   6.434  
   6.435          isFinished = true;
   6.436 +        return functionNode;
   6.437      }
   6.438  
   6.439      boolean isFinished() {
   6.440 @@ -374,15 +408,13 @@
   6.441          return endTime;
   6.442      }
   6.443  
   6.444 -    abstract void transform(final Compiler compiler, final FunctionNode functionNode) throws CompilationException;
   6.445 +    abstract FunctionNode transform(final Compiler compiler, final FunctionNode functionNode) throws CompilationException;
   6.446  
   6.447 -    final void apply(final Compiler compiler, final FunctionNode functionNode) throws CompilationException {
   6.448 +    final FunctionNode apply(final Compiler compiler, final FunctionNode functionNode) throws CompilationException {
   6.449          if (!isApplicable(functionNode)) {
   6.450 -            throw new CompilationException("compile phase not applicable: " + this);
   6.451 +            throw new CompilationException("compile phase not applicable: " + this + " to " + functionNode.getName() + " state=" + functionNode.getState());
   6.452          }
   6.453 -        begin(functionNode);
   6.454 -        transform(compiler, functionNode);
   6.455 -        end(functionNode);
   6.456 +        return end(transform(compiler, begin(functionNode)));
   6.457      }
   6.458  
   6.459  }
     7.1 --- a/src/jdk/nashorn/internal/codegen/Compiler.java	Fri Apr 19 18:23:00 2013 +0530
     7.2 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java	Fri Apr 19 16:11:16 2013 +0200
     7.3 @@ -25,12 +25,16 @@
     7.4  
     7.5  package jdk.nashorn.internal.codegen;
     7.6  
     7.7 +import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
     7.8 +import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
     7.9  import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
    7.10  import static jdk.nashorn.internal.codegen.CompilerConstants.DEFAULT_SCRIPT_NAME;
    7.11  import static jdk.nashorn.internal.codegen.CompilerConstants.LAZY;
    7.12 +import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
    7.13  import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
    7.14  import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
    7.15  import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
    7.16 +import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
    7.17  
    7.18  import java.io.File;
    7.19  import java.lang.reflect.Field;
    7.20 @@ -46,13 +50,12 @@
    7.21  import java.util.Map.Entry;
    7.22  import java.util.Set;
    7.23  import java.util.logging.Level;
    7.24 +
    7.25  import jdk.internal.dynalink.support.NameCodec;
    7.26  import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
    7.27  import jdk.nashorn.internal.codegen.types.Type;
    7.28  import jdk.nashorn.internal.ir.FunctionNode;
    7.29  import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
    7.30 -import jdk.nashorn.internal.ir.Node;
    7.31 -import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    7.32  import jdk.nashorn.internal.runtime.CodeInstaller;
    7.33  import jdk.nashorn.internal.runtime.DebugLogger;
    7.34  import jdk.nashorn.internal.runtime.ScriptEnvironment;
    7.35 @@ -80,8 +83,6 @@
    7.36  
    7.37      private final ConstantData constantData;
    7.38  
    7.39 -    private final FunctionNode functionNode;
    7.40 -
    7.41      private final CompilationSequence sequence;
    7.42  
    7.43      private final ScriptEnvironment env;
    7.44 @@ -90,6 +91,8 @@
    7.45  
    7.46      private boolean strict;
    7.47  
    7.48 +    private FunctionNode functionNode;
    7.49 +
    7.50      private CodeInstaller<ScriptEnvironment> installer;
    7.51  
    7.52      /** logger for compiler, trampolines, splits and related code generation events
    7.53 @@ -103,8 +106,12 @@
    7.54       * during a compile.
    7.55       */
    7.56      private static String[] RESERVED_NAMES = {
    7.57 -        SCOPE.tag(),
    7.58 -        THIS.tag()
    7.59 +        SCOPE.symbolName(),
    7.60 +        THIS.symbolName(),
    7.61 +        RETURN.symbolName(),
    7.62 +        CALLEE.symbolName(),
    7.63 +        VARARGS.symbolName(),
    7.64 +        ARGUMENTS.symbolName()
    7.65      };
    7.66  
    7.67      /**
    7.68 @@ -186,7 +193,7 @@
    7.69  
    7.70      private static String lazyTag(final FunctionNode functionNode) {
    7.71          if (functionNode.isLazy()) {
    7.72 -            return '$' + LAZY.tag() + '$' + functionNode.getName();
    7.73 +            return '$' + LAZY.symbolName() + '$' + functionNode.getName();
    7.74          }
    7.75          return "";
    7.76      }
    7.77 @@ -205,13 +212,13 @@
    7.78          this.functionNode  = functionNode;
    7.79          this.sequence      = sequence;
    7.80          this.installer     = installer;
    7.81 -        this.strict        = strict || functionNode.isStrictMode();
    7.82 +        this.strict        = strict || functionNode.isStrict();
    7.83          this.constantData  = new ConstantData();
    7.84          this.compileUnits  = new HashSet<>();
    7.85          this.bytecode      = new HashMap<>();
    7.86  
    7.87          final StringBuilder sb = new StringBuilder();
    7.88 -        sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.tag() + lazyTag(functionNode))).
    7.89 +        sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.symbolName() + lazyTag(functionNode))).
    7.90                  append('$').
    7.91                  append(safeSourceName(functionNode.getSource()));
    7.92  
    7.93 @@ -253,9 +260,9 @@
    7.94       * Execute the compilation this Compiler was created with
    7.95       * @params param types if known, for specialization
    7.96       * @throws CompilationException if something goes wrong
    7.97 -     * @return this compiler, for possible chaining
    7.98 +     * @return function node that results from code transforms
    7.99       */
   7.100 -    public Compiler compile() throws CompilationException {
   7.101 +    public FunctionNode compile() throws CompilationException {
   7.102          return compile(null);
   7.103      }
   7.104  
   7.105 @@ -263,9 +270,9 @@
   7.106       * Execute the compilation this Compiler was created with
   7.107       * @param paramTypes param types if known, for specialization
   7.108       * @throws CompilationException if something goes wrong
   7.109 -     * @return this compiler, for possible chaining
   7.110 +     * @return function node that results from code transforms
   7.111       */
   7.112 -    public Compiler compile(final Class<?> paramTypes) throws CompilationException {
   7.113 +    public FunctionNode compile(final Class<?> paramTypes) throws CompilationException {
   7.114          for (final String reservedName : RESERVED_NAMES) {
   7.115              functionNode.uniqueName(reservedName);
   7.116          }
   7.117 @@ -276,7 +283,7 @@
   7.118          long time = 0L;
   7.119  
   7.120          for (final CompilationPhase phase : sequence) {
   7.121 -            phase.apply(this, functionNode);
   7.122 +            this.functionNode = phase.apply(this, functionNode);
   7.123  
   7.124              final long duration = Timing.isEnabled() ? (phase.getEndTime() - phase.getStartTime()) : 0L;
   7.125              time += duration;
   7.126 @@ -295,7 +302,7 @@
   7.127                          append(" ms ");
   7.128                  }
   7.129  
   7.130 -                LOG.fine(sb.toString());
   7.131 +                LOG.fine(sb);
   7.132              }
   7.133          }
   7.134  
   7.135 @@ -311,14 +318,14 @@
   7.136                      append(" ms");
   7.137              }
   7.138  
   7.139 -            LOG.info(sb.toString());
   7.140 +            LOG.info(sb);
   7.141          }
   7.142  
   7.143 -        return this;
   7.144 +        return functionNode;
   7.145      }
   7.146  
   7.147      private Class<?> install(final String className, final byte[] code) {
   7.148 -        LOG.fine("Installing class " + className);
   7.149 +        LOG.fine("Installing class ", className);
   7.150  
   7.151          final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
   7.152  
   7.153 @@ -330,8 +337,8 @@
   7.154                  @Override
   7.155                  public Void run() throws Exception {
   7.156                      //use reflection to write source and constants table to installed classes
   7.157 -                    final Field sourceField    = clazz.getDeclaredField(SOURCE.tag());
   7.158 -                    final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag());
   7.159 +                    final Field sourceField    = clazz.getDeclaredField(SOURCE.symbolName());
   7.160 +                    final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
   7.161                      sourceField.setAccessible(true);
   7.162                      constantsField.setAccessible(true);
   7.163                      sourceField.set(null, source);
   7.164 @@ -380,17 +387,6 @@
   7.165              unit.setCode(installedClasses.get(unit.getUnitClassName()));
   7.166          }
   7.167  
   7.168 -        functionNode.accept(new NodeVisitor() {
   7.169 -            @Override
   7.170 -            public Node enterFunctionNode(final FunctionNode node) {
   7.171 -                if (node.isLazy()) {
   7.172 -                    return null;
   7.173 -                }
   7.174 -                node.setState(CompilationState.INSTALLED);
   7.175 -                return node;
   7.176 -            }
   7.177 -        });
   7.178 -
   7.179          final StringBuilder sb;
   7.180          if (LOG.isEnabled()) {
   7.181              sb = new StringBuilder();
   7.182 @@ -416,7 +412,7 @@
   7.183          }
   7.184  
   7.185          if (sb != null) {
   7.186 -            LOG.info(sb.toString());
   7.187 +            LOG.info(sb);
   7.188          }
   7.189  
   7.190          return rootClass;
   7.191 @@ -495,7 +491,7 @@
   7.192      private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
   7.193          final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
   7.194          compileUnits.add(compileUnit);
   7.195 -        LOG.fine("Added compile unit " + compileUnit);
   7.196 +        LOG.fine("Added compile unit ", compileUnit);
   7.197          return compileUnit;
   7.198      }
   7.199  
     8.1 --- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Fri Apr 19 18:23:00 2013 +0530
     8.2 +++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Fri Apr 19 16:11:16 2013 +0200
     8.3 @@ -52,9 +52,6 @@
     8.4      /** lazy prefix for classes of jitted methods */
     8.5      LAZY("Lazy"),
     8.6  
     8.7 -    /** leaf tag used for functions that require no scope */
     8.8 -    LEAF("__leaf__"),
     8.9 -
    8.10      /** constructor name */
    8.11      INIT("<init>"),
    8.12  
    8.13 @@ -96,7 +93,7 @@
    8.14      SCOPE("__scope__", ScriptObject.class, 2),
    8.15  
    8.16      /** the return value variable name were intermediate results are stored for scripts */
    8.17 -    SCRIPT_RETURN("__return__"),
    8.18 +    RETURN("__return__"),
    8.19  
    8.20      /** the callee value variable when necessary */
    8.21      CALLEE("__callee__", ScriptFunction.class),
    8.22 @@ -167,30 +164,30 @@
    8.23      /** get array suffix */
    8.24      GET_ARRAY_SUFFIX("$array");
    8.25  
    8.26 -    private final String tag;
    8.27 +    private final String symbolName;
    8.28      private final Class<?> type;
    8.29      private final int slot;
    8.30  
    8.31      private CompilerConstants() {
    8.32 -        this.tag = name();
    8.33 +        this.symbolName = name();
    8.34          this.type = null;
    8.35          this.slot = -1;
    8.36      }
    8.37  
    8.38 -    private CompilerConstants(final String tag) {
    8.39 -        this(tag, -1);
    8.40 +    private CompilerConstants(final String symbolName) {
    8.41 +        this(symbolName, -1);
    8.42      }
    8.43  
    8.44 -    private CompilerConstants(final String tag, final int slot) {
    8.45 -        this(tag, null, slot);
    8.46 +    private CompilerConstants(final String symbolName, final int slot) {
    8.47 +        this(symbolName, null, slot);
    8.48      }
    8.49  
    8.50 -    private CompilerConstants(final String tag, final Class<?> type) {
    8.51 -        this(tag, type, -1);
    8.52 +    private CompilerConstants(final String symbolName, final Class<?> type) {
    8.53 +        this(symbolName, type, -1);
    8.54      }
    8.55  
    8.56 -    private CompilerConstants(final String tag, final Class<?> type, final int slot) {
    8.57 -        this.tag  = tag;
    8.58 +    private CompilerConstants(final String symbolName, final Class<?> type, final int slot) {
    8.59 +        this.symbolName  = symbolName;
    8.60          this.type = type;
    8.61          this.slot = slot;
    8.62      }
    8.63 @@ -202,8 +199,8 @@
    8.64       *
    8.65       * @return the tag
    8.66       */
    8.67 -    public final String tag() {
    8.68 -        return tag;
    8.69 +    public final String symbolName() {
    8.70 +        return symbolName;
    8.71      }
    8.72  
    8.73      /**
    8.74 @@ -277,7 +274,7 @@
    8.75       * @return Call representing void constructor for type
    8.76       */
    8.77      public static Call constructorNoLookup(final Class<?> clazz) {
    8.78 -        return specialCallNoLookup(clazz, INIT.tag(), void.class);
    8.79 +        return specialCallNoLookup(clazz, INIT.symbolName(), void.class);
    8.80      }
    8.81  
    8.82      /**
    8.83 @@ -290,7 +287,7 @@
    8.84       * @return Call representing constructor for type
    8.85       */
    8.86      public static Call constructorNoLookup(final String className, final Class<?>... ptypes) {
    8.87 -        return specialCallNoLookup(className, INIT.tag(), methodDescriptor(void.class, ptypes));
    8.88 +        return specialCallNoLookup(className, INIT.symbolName(), methodDescriptor(void.class, ptypes));
    8.89      }
    8.90  
    8.91      /**
    8.92 @@ -303,7 +300,7 @@
    8.93       * @return Call representing constructor for type
    8.94       */
    8.95      public static Call constructorNoLookup(final Class<?> clazz, final Class<?>... ptypes) {
    8.96 -        return specialCallNoLookup(clazz, INIT.tag(), void.class, ptypes);
    8.97 +        return specialCallNoLookup(clazz, INIT.symbolName(), void.class, ptypes);
    8.98      }
    8.99  
   8.100      /**
     9.1 --- a/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java	Fri Apr 19 18:23:00 2013 +0530
     9.2 +++ b/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java	Fri Apr 19 16:11:16 2013 +0200
     9.3 @@ -26,6 +26,7 @@
     9.4  package jdk.nashorn.internal.codegen;
     9.5  
     9.6  import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
     9.7 +import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
     9.8  import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
     9.9  import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
    9.10  import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
    9.11 @@ -86,7 +87,7 @@
    9.12       * @param method the method emitter to use
    9.13       */
    9.14      protected void loadScope(final MethodEmitter method) {
    9.15 -        method.loadScope();
    9.16 +        method.loadCompilerConstant(SCOPE);
    9.17      }
    9.18  
    9.19      /**
    9.20 @@ -105,7 +106,7 @@
    9.21              loadScope(method);
    9.22  
    9.23              if (hasArguments()) {
    9.24 -                method.loadArguments();
    9.25 +                method.loadCompilerConstant(ARGUMENTS);
    9.26                  method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class, ARGUMENTS.type()));
    9.27              } else {
    9.28                  method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class));
    10.1 --- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Fri Apr 19 18:23:00 2013 +0530
    10.2 +++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Fri Apr 19 16:11:16 2013 +0200
    10.3 @@ -25,18 +25,22 @@
    10.4  
    10.5  package jdk.nashorn.internal.codegen;
    10.6  
    10.7 +import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
    10.8 +import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
    10.9 +
   10.10 +import java.util.ArrayList;
   10.11  import java.util.HashSet;
   10.12 +import java.util.Iterator;
   10.13  import java.util.List;
   10.14 +
   10.15  import jdk.nashorn.internal.codegen.types.Type;
   10.16  import jdk.nashorn.internal.ir.AccessNode;
   10.17  import jdk.nashorn.internal.ir.Assignment;
   10.18  import jdk.nashorn.internal.ir.BinaryNode;
   10.19  import jdk.nashorn.internal.ir.Block;
   10.20  import jdk.nashorn.internal.ir.CallNode;
   10.21 -import jdk.nashorn.internal.ir.CallNode.EvalArgs;
   10.22  import jdk.nashorn.internal.ir.CaseNode;
   10.23  import jdk.nashorn.internal.ir.CatchNode;
   10.24 -import jdk.nashorn.internal.ir.DoWhileNode;
   10.25  import jdk.nashorn.internal.ir.ExecuteNode;
   10.26  import jdk.nashorn.internal.ir.ForNode;
   10.27  import jdk.nashorn.internal.ir.FunctionNode;
   10.28 @@ -85,18 +89,11 @@
   10.29  
   10.30      private static final DebugLogger LOG = new DebugLogger("finalize");
   10.31  
   10.32 -    private final LexicalContext lexicalContext = new LexicalContext();
   10.33 -
   10.34      FinalizeTypes() {
   10.35      }
   10.36  
   10.37      @Override
   10.38      public Node leaveCallNode(final CallNode callNode) {
   10.39 -        final EvalArgs evalArgs = callNode.getEvalArgs();
   10.40 -        if (evalArgs != null) {
   10.41 -            evalArgs.setCode(evalArgs.getCode().accept(this));
   10.42 -        }
   10.43 -
   10.44          // AccessSpecializer - call return type may change the access for this location
   10.45          final Node function = callNode.getFunction();
   10.46          if (function instanceof FunctionNode) {
   10.47 @@ -133,8 +130,7 @@
   10.48      @Override
   10.49      public Node leaveNEW(final UnaryNode unaryNode) {
   10.50          assert unaryNode.getSymbol() != null && unaryNode.getSymbol().getSymbolType().isObject();
   10.51 -        ((CallNode)unaryNode.rhs()).setIsNew();
   10.52 -        return unaryNode;
   10.53 +        return unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew());
   10.54      }
   10.55  
   10.56      @Override
   10.57 @@ -254,7 +250,7 @@
   10.58      @Override
   10.59      public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
   10.60          assert binaryNode.getSymbol() != null;
   10.61 -        final BinaryNode newBinaryNode = (BinaryNode)binaryNode.setRHS(discard(binaryNode.rhs()));
   10.62 +        final BinaryNode newBinaryNode = binaryNode.setRHS(discard(binaryNode.rhs()));
   10.63          // AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed
   10.64          // in that case, update the node type as well
   10.65          propagateType(newBinaryNode, newBinaryNode.lhs().getType());
   10.66 @@ -354,41 +350,30 @@
   10.67      }
   10.68  
   10.69      @Override
   10.70 -    public Node enterBlock(final Block block) {
   10.71 -        lexicalContext.push(block);
   10.72 +    public boolean enterBlock(final Block block) {
   10.73          updateSymbols(block);
   10.74 -        return block;
   10.75 +        return true;
   10.76      }
   10.77  
   10.78 +    /*
   10.79      @Override
   10.80 -    public Node leaveBlock(Block block) {
   10.81 -        lexicalContext.pop(block);
   10.82 -        return super.leaveBlock(block);
   10.83 -    }
   10.84 +    public Node leaveBlock(final Block block) {
   10.85 +        final LexicalContext lc = getLexicalContext();
   10.86 +        return block;//.setFlag(lc, lc.getFlags(block));
   10.87 +    }*/
   10.88  
   10.89      @Override
   10.90      public Node leaveCatchNode(final CatchNode catchNode) {
   10.91          final Node exceptionCondition = catchNode.getExceptionCondition();
   10.92          if (exceptionCondition != null) {
   10.93 -            catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
   10.94 +            return catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
   10.95          }
   10.96          return catchNode;
   10.97      }
   10.98  
   10.99      @Override
  10.100 -    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
  10.101 -        return enterWhileNode(doWhileNode);
  10.102 -    }
  10.103 -
  10.104 -    @Override
  10.105 -    public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
  10.106 -        return leaveWhileNode(doWhileNode);
  10.107 -    }
  10.108 -
  10.109 -    @Override
  10.110      public Node leaveExecuteNode(final ExecuteNode executeNode) {
  10.111 -        executeNode.setExpression(discard(executeNode.getExpression()));
  10.112 -        return executeNode;
  10.113 +        return executeNode.setExpression(discard(executeNode.getExpression()));
  10.114      }
  10.115  
  10.116      @Override
  10.117 @@ -397,69 +382,54 @@
  10.118          final Node test   = forNode.getTest();
  10.119          final Node modify = forNode.getModify();
  10.120  
  10.121 +        final LexicalContext lc = getLexicalContext();
  10.122 +
  10.123          if (forNode.isForIn()) {
  10.124 -            forNode.setModify(convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
  10.125 -            return forNode;
  10.126 +            return forNode.setModify(lc, convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
  10.127          }
  10.128 +        assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getLexicalContext().getCurrentFunction();
  10.129  
  10.130 -        if (init != null) {
  10.131 -            forNode.setInit(discard(init));
  10.132 -        }
  10.133 -
  10.134 -        if (test != null) {
  10.135 -            forNode.setTest(convert(test, Type.BOOLEAN));
  10.136 -        } else {
  10.137 -            assert forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getCurrentFunctionNode();
  10.138 -        }
  10.139 -
  10.140 -        if (modify != null) {
  10.141 -            forNode.setModify(discard(modify));
  10.142 -        }
  10.143 -
  10.144 -        return forNode;
  10.145 +        return forNode.
  10.146 +            setInit(lc, init == null ? null : discard(init)).
  10.147 +            setTest(lc, test == null ? null : convert(test, Type.BOOLEAN)).
  10.148 +            setModify(lc, modify == null ? null : discard(modify));
  10.149      }
  10.150  
  10.151      @Override
  10.152 -    public Node enterFunctionNode(final FunctionNode functionNode) {
  10.153 +    public boolean enterFunctionNode(final FunctionNode functionNode) {
  10.154          if (functionNode.isLazy()) {
  10.155 -            return null;
  10.156 +            return false;
  10.157          }
  10.158  
  10.159 -        lexicalContext.push(functionNode);
  10.160          // If the function doesn't need a callee, we ensure its __callee__ symbol doesn't get a slot. We can't do
  10.161          // this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the
  10.162          // need for the callee.
  10.163          if (!functionNode.needsCallee()) {
  10.164 -            functionNode.getCalleeNode().getSymbol().setNeedsSlot(false);
  10.165 +            functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
  10.166          }
  10.167          // Similar reasoning applies to __scope__ symbol: if the function doesn't need either parent scope or its
  10.168          // own scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope earlier than
  10.169          // this phase.
  10.170 -        if (!(functionNode.needsScope() || functionNode.needsParentScope())) {
  10.171 -            functionNode.getScopeNode().getSymbol().setNeedsSlot(false);
  10.172 +        if (!(functionNode.getBody().needsScope() || functionNode.needsParentScope())) {
  10.173 +            functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
  10.174          }
  10.175  
  10.176 -        updateSymbols(functionNode);
  10.177 -        functionNode.setState(CompilationState.FINALIZED);
  10.178 -
  10.179 -        return functionNode;
  10.180 +        return true;
  10.181      }
  10.182  
  10.183      @Override
  10.184 -    public Node leaveFunctionNode(FunctionNode functionNode) {
  10.185 -        lexicalContext.pop(functionNode);
  10.186 -        return super.leaveFunctionNode(functionNode);
  10.187 +    public Node leaveFunctionNode(final FunctionNode functionNode) {
  10.188 +        return functionNode.setState(getLexicalContext(), CompilationState.FINALIZED);
  10.189      }
  10.190  
  10.191      @Override
  10.192      public Node leaveIfNode(final IfNode ifNode) {
  10.193 -        ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
  10.194 -        return ifNode;
  10.195 +        return ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
  10.196      }
  10.197  
  10.198      @SuppressWarnings("rawtypes")
  10.199      @Override
  10.200 -    public Node enterLiteralNode(final LiteralNode literalNode) {
  10.201 +    public boolean enterLiteralNode(final LiteralNode literalNode) {
  10.202          if (literalNode instanceof ArrayLiteralNode) {
  10.203              final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
  10.204              final Node[]           array            = arrayLiteralNode.getValue();
  10.205 @@ -473,14 +443,14 @@
  10.206              }
  10.207          }
  10.208  
  10.209 -        return null;
  10.210 +        return false;
  10.211      }
  10.212  
  10.213      @Override
  10.214      public Node leaveReturnNode(final ReturnNode returnNode) {
  10.215          final Node expr = returnNode.getExpression();
  10.216          if (expr != null) {
  10.217 -            returnNode.setExpression(convert(expr, getCurrentFunctionNode().getReturnType()));
  10.218 +            return returnNode.setExpression(convert(expr, getLexicalContext().getCurrentFunction().getReturnType()));
  10.219          }
  10.220          return returnNode;
  10.221      }
  10.222 @@ -496,21 +466,24 @@
  10.223  
  10.224      @Override
  10.225      public Node leaveSwitchNode(final SwitchNode switchNode) {
  10.226 +        final boolean allInteger = switchNode.getTag().getSymbolType().isInteger();
  10.227 +
  10.228 +        if (allInteger) {
  10.229 +            return switchNode;
  10.230 +        }
  10.231 +
  10.232          final Node           expression  = switchNode.getExpression();
  10.233          final List<CaseNode> cases       = switchNode.getCases();
  10.234 -        final boolean        allInteger  = switchNode.getTag().getSymbolType().isInteger();
  10.235 +        final List<CaseNode> newCases    = new ArrayList<>();
  10.236  
  10.237 -        if (!allInteger) {
  10.238 -            switchNode.setExpression(convert(expression, Type.OBJECT));
  10.239 -            for (final CaseNode caseNode : cases) {
  10.240 -                final Node test = caseNode.getTest();
  10.241 -                if (test != null) {
  10.242 -                    caseNode.setTest(convert(test, Type.OBJECT));
  10.243 -                }
  10.244 -            }
  10.245 +        for (final CaseNode caseNode : cases) {
  10.246 +            final Node test = caseNode.getTest();
  10.247 +            newCases.add(test != null ? caseNode.setTest(convert(test, Type.OBJECT)) : caseNode);
  10.248          }
  10.249  
  10.250 -        return switchNode;
  10.251 +        return switchNode.
  10.252 +            setExpression(getLexicalContext(), convert(expression, Type.OBJECT)).
  10.253 +            setCases(getLexicalContext(), newCases);
  10.254      }
  10.255  
  10.256      @Override
  10.257 @@ -520,8 +493,7 @@
  10.258  
  10.259      @Override
  10.260      public Node leaveThrowNode(final ThrowNode throwNode) {
  10.261 -        throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
  10.262 -        return throwNode;
  10.263 +        return throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
  10.264      }
  10.265  
  10.266      @Override
  10.267 @@ -544,23 +516,24 @@
  10.268      public Node leaveWhileNode(final WhileNode whileNode) {
  10.269          final Node test = whileNode.getTest();
  10.270          if (test != null) {
  10.271 -            whileNode.setTest(convert(test, Type.BOOLEAN));
  10.272 +            return whileNode.setTest(getLexicalContext(), convert(test, Type.BOOLEAN));
  10.273          }
  10.274          return whileNode;
  10.275      }
  10.276  
  10.277      @Override
  10.278      public Node leaveWithNode(final WithNode withNode) {
  10.279 -        withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT));
  10.280 -        return withNode;
  10.281 +        return withNode.setExpression(getLexicalContext(), convert(withNode.getExpression(), Type.OBJECT));
  10.282      }
  10.283  
  10.284      private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) {
  10.285 -        if (!symbol.isScope()) {
  10.286 -            LOG.finest("updateSymbols: " + symbol + " => scope, because all vars in " + functionNode.getName() + " are in scope");
  10.287 -        }
  10.288 -        if (loseSlot && symbol.hasSlot()) {
  10.289 -            LOG.finest("updateSymbols: " + symbol + " => no slot, because all vars in " + functionNode.getName() + " are in scope");
  10.290 +        if (LOG.isEnabled()) {
  10.291 +            if (!symbol.isScope()) {
  10.292 +                LOG.finest("updateSymbols: ", symbol, " => scope, because all vars in ", functionNode.getName(), " are in scope");
  10.293 +            }
  10.294 +            if (loseSlot && symbol.hasSlot()) {
  10.295 +                LOG.finest("updateSymbols: ", symbol, " => no slot, because all vars in ", functionNode.getName(), " are in scope");
  10.296 +            }
  10.297          }
  10.298      }
  10.299  
  10.300 @@ -574,29 +547,28 @@
  10.301              return; // nothing to do
  10.302          }
  10.303  
  10.304 -        final FunctionNode functionNode = lexicalContext.getFunction(block);
  10.305 -        assert !(block instanceof FunctionNode) || functionNode == block;
  10.306 +        final LexicalContext lc             = getLexicalContext();
  10.307 +        final FunctionNode   functionNode   = lc.getFunction(block);
  10.308 +        final boolean        allVarsInScope = functionNode.allVarsInScope();
  10.309 +        final boolean        isVarArg       = functionNode.isVarArg();
  10.310  
  10.311 -        final List<Symbol> symbols        = block.getFrame().getSymbols();
  10.312 -        final boolean      allVarsInScope = functionNode.allVarsInScope();
  10.313 -        final boolean      isVarArg       = functionNode.isVarArg();
  10.314 -
  10.315 -        for (final Symbol symbol : symbols) {
  10.316 -            if (symbol.isInternal() || symbol.isThis()) {
  10.317 +        for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
  10.318 +            final Symbol symbol = iter.next();
  10.319 +            if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
  10.320                  continue;
  10.321              }
  10.322  
  10.323              if (symbol.isVar()) {
  10.324                  if (allVarsInScope || symbol.isScope()) {
  10.325                      updateSymbolsLog(functionNode, symbol, true);
  10.326 -                    symbol.setIsScope();
  10.327 +                    Symbol.setSymbolIsScope(lc, symbol);
  10.328                      symbol.setNeedsSlot(false);
  10.329                  } else {
  10.330                      assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
  10.331                  }
  10.332              } else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) {
  10.333                  updateSymbolsLog(functionNode, symbol, isVarArg);
  10.334 -                symbol.setIsScope();
  10.335 +                Symbol.setSymbolIsScope(lc, symbol);
  10.336                  symbol.setNeedsSlot(!isVarArg);
  10.337              }
  10.338          }
  10.339 @@ -636,11 +608,7 @@
  10.340              //fallthru
  10.341          default:
  10.342              if (newRuntimeNode || widest.isObject()) {
  10.343 -                final RuntimeNode runtimeNode = new RuntimeNode(binaryNode, request);
  10.344 -                if (finalized) {
  10.345 -                    runtimeNode.setIsFinal();
  10.346 -                }
  10.347 -                return runtimeNode;
  10.348 +                return new RuntimeNode(binaryNode, request).setIsFinal(finalized);
  10.349              }
  10.350              break;
  10.351          }
  10.352 @@ -667,7 +635,8 @@
  10.353      }
  10.354  
  10.355      private Node leaveBinary(final BinaryNode binaryNode, final Type lhsType, final Type rhsType) {
  10.356 -        return binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType));
  10.357 +        Node b =  binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType));
  10.358 +        return b;
  10.359      }
  10.360  
  10.361      /**
  10.362 @@ -683,28 +652,28 @@
  10.363  
  10.364          node.accept(new NodeVisitor() {
  10.365              private void setCanBePrimitive(final Symbol symbol) {
  10.366 -                LOG.info("*** can be primitive symbol " + symbol + " " + Debug.id(symbol));
  10.367 +                LOG.info("*** can be primitive symbol ", symbol, " ", Debug.id(symbol));
  10.368                  symbol.setCanBePrimitive(to);
  10.369              }
  10.370  
  10.371              @Override
  10.372 -            public Node enterIdentNode(final IdentNode identNode) {
  10.373 +            public boolean enterIdentNode(final IdentNode identNode) {
  10.374                  if (!exclude.contains(identNode)) {
  10.375                      setCanBePrimitive(identNode.getSymbol());
  10.376                  }
  10.377 -                return null;
  10.378 +                return false;
  10.379              }
  10.380  
  10.381              @Override
  10.382 -            public Node enterAccessNode(final AccessNode accessNode) {
  10.383 +            public boolean enterAccessNode(final AccessNode accessNode) {
  10.384                  setCanBePrimitive(accessNode.getProperty().getSymbol());
  10.385 -                return null;
  10.386 +                return false;
  10.387              }
  10.388  
  10.389              @Override
  10.390 -            public Node enterIndexNode(final IndexNode indexNode) {
  10.391 +            public boolean enterIndexNode(final IndexNode indexNode) {
  10.392                  exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine
  10.393 -                return indexNode;
  10.394 +                return true;
  10.395              }
  10.396          });
  10.397      }
  10.398 @@ -785,12 +754,12 @@
  10.399      private static <T extends Node> T setTypeOverride(final T node, final Type to) {
  10.400          final Type from = node.getType();
  10.401          if (!node.getType().equals(to)) {
  10.402 -            LOG.info("Changing call override type for '" + node + "' from " + node.getType() + " to " + to);
  10.403 +            LOG.info("Changing call override type for '", node, "' from ", node.getType(), " to ", to);
  10.404              if (!to.isObject() && from.isObject()) {
  10.405                  setCanBePrimitive(node, to);
  10.406              }
  10.407          }
  10.408 -        LOG.info("Type override for lhs in '" + node + "' => " + to);
  10.409 +        LOG.info("Type override for lhs in '", node, "' => ", to);
  10.410          return ((TypeOverride<T>)node).setType(to);
  10.411      }
  10.412  
  10.413 @@ -814,8 +783,8 @@
  10.414      private Node convert(final Node node, final Type to) {
  10.415          assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass();
  10.416          assert node != null : "node is null";
  10.417 -        assert node.getSymbol() != null : "node " + node + " has no symbol!";
  10.418 -        assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getCurrentFunctionNode();
  10.419 +        assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + getLexicalContext().getCurrentFunction() + " " + node.getSource();
  10.420 +        assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getLexicalContext().getCurrentFunction();
  10.421  
  10.422          final Type from = node.getType();
  10.423  
  10.424 @@ -842,23 +811,23 @@
  10.425              resultNode = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.CONVERT), node);
  10.426          }
  10.427  
  10.428 -        LOG.info("CONVERT('" + node + "', " + to + ") => '" + resultNode + "'");
  10.429 +        LOG.info("CONVERT('", node, "', ", to, ") => '", resultNode, "'");
  10.430  
  10.431 +        final LexicalContext lc = getLexicalContext();
  10.432          //This is the only place in this file that can create new temporaries
  10.433          //FinalizeTypes may not introduce ANY node that is not a conversion.
  10.434 -        getCurrentFunctionNode().newTemporary(getCurrentBlock().getFrame(), to, resultNode);
  10.435 -        resultNode.copyTerminalFlags(node);
  10.436 +        lc.getCurrentFunction().ensureSymbol(lc.getCurrentBlock(), to, resultNode);
  10.437 +
  10.438 +        assert !node.isTerminal();
  10.439  
  10.440          return resultNode;
  10.441      }
  10.442  
  10.443      private static Node discard(final Node node) {
  10.444 -        node.setDiscard(true);
  10.445 -
  10.446          if (node.getSymbol() != null) {
  10.447              final Node discard = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.DISCARD), node);
  10.448              //discard never has a symbol in the discard node - then it would be a nop
  10.449 -            discard.copyTerminalFlags(node);
  10.450 +            assert !node.isTerminal();
  10.451              return discard;
  10.452          }
  10.453  
  10.454 @@ -883,7 +852,7 @@
  10.455          final Symbol symbol = node.getSymbol();
  10.456          if (symbol.isTemp()) {
  10.457              symbol.setTypeOverride(to);
  10.458 -            LOG.info("Type override for temporary in '" + node + "' => " + to);
  10.459 +            LOG.info("Type override for temporary in '", node, "' => ", to);
  10.460          }
  10.461      }
  10.462  
    11.1 --- a/src/jdk/nashorn/internal/codegen/FoldConstants.java	Fri Apr 19 18:23:00 2013 +0530
    11.2 +++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java	Fri Apr 19 16:11:16 2013 +0200
    11.3 @@ -57,7 +57,7 @@
    11.4      public Node leaveUnaryNode(final UnaryNode unaryNode) {
    11.5          final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval();
    11.6          if (literalNode != null) {
    11.7 -            LOG.info("Unary constant folded " + unaryNode + " to " + literalNode);
    11.8 +            LOG.info("Unary constant folded ", unaryNode, " to ", literalNode);
    11.9              return literalNode;
   11.10          }
   11.11          return unaryNode;
   11.12 @@ -67,24 +67,20 @@
   11.13      public Node leaveBinaryNode(final BinaryNode binaryNode) {
   11.14          final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval();
   11.15          if (literalNode != null) {
   11.16 -            LOG.info("Binary constant folded " + binaryNode + " to " + literalNode);
   11.17 +            LOG.info("Binary constant folded ", binaryNode, " to ", literalNode);
   11.18              return literalNode;
   11.19          }
   11.20          return binaryNode;
   11.21      }
   11.22  
   11.23      @Override
   11.24 -    public Node enterFunctionNode(final FunctionNode functionNode) {
   11.25 -        if (functionNode.isLazy()) {
   11.26 -            return null;
   11.27 -        }
   11.28 -        return functionNode;
   11.29 +    public boolean enterFunctionNode(final FunctionNode functionNode) {
   11.30 +        return !functionNode.isLazy();
   11.31      }
   11.32  
   11.33      @Override
   11.34      public Node leaveFunctionNode(final FunctionNode functionNode) {
   11.35 -        functionNode.setState(CompilationState.CONSTANT_FOLDED);
   11.36 -        return functionNode;
   11.37 +        return functionNode.setState(getLexicalContext(), CompilationState.CONSTANT_FOLDED);
   11.38      }
   11.39  
   11.40      @Override
    12.1 --- a/src/jdk/nashorn/internal/codegen/Frame.java	Fri Apr 19 18:23:00 2013 +0530
    12.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.3 @@ -1,196 +0,0 @@
    12.4 -/*
    12.5 - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    12.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    12.7 - *
    12.8 - * This code is free software; you can redistribute it and/or modify it
    12.9 - * under the terms of the GNU General Public License version 2 only, as
   12.10 - * published by the Free Software Foundation.  Oracle designates this
   12.11 - * particular file as subject to the "Classpath" exception as provided
   12.12 - * by Oracle in the LICENSE file that accompanied this code.
   12.13 - *
   12.14 - * This code is distributed in the hope that it will be useful, but WITHOUT
   12.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   12.16 - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   12.17 - * version 2 for more details (a copy is included in the LICENSE file that
   12.18 - * accompanied this code).
   12.19 - *
   12.20 - * You should have received a copy of the GNU General Public License version
   12.21 - * 2 along with this work; if not, write to the Free Software Foundation,
   12.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   12.23 - *
   12.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   12.25 - * or visit www.oracle.com if you need additional information or have any
   12.26 - * questions.
   12.27 - */
   12.28 -
   12.29 -package jdk.nashorn.internal.codegen;
   12.30 -
   12.31 -import java.util.ArrayList;
   12.32 -import java.util.Collections;
   12.33 -import java.util.List;
   12.34 -import jdk.nashorn.internal.ir.Symbol;
   12.35 -
   12.36 -/**
   12.37 - * Tracks the variable area state.
   12.38 - *
   12.39 - */
   12.40 -public final class Frame {
   12.41 -    /** Previous frame. */
   12.42 -    private Frame previous;
   12.43 -
   12.44 -    /** Current variables. */
   12.45 -    private final ArrayList<Symbol> symbols;
   12.46 -
   12.47 -    /** Number of slots in previous frame. */
   12.48 -    private int baseCount;
   12.49 -
   12.50 -    /** Number of slots in this frame. */
   12.51 -    private int count;
   12.52 -
   12.53 -    /**
   12.54 -     * Constructor.
   12.55 -     *
   12.56 -     * @param previous frame, the parent variable frame
   12.57 -     */
   12.58 -    public Frame(final Frame previous) {
   12.59 -        this.previous  = previous;
   12.60 -        this.symbols   = new ArrayList<>();
   12.61 -        this.baseCount = getBaseCount();
   12.62 -        this.count     = 0;
   12.63 -    }
   12.64 -
   12.65 -    /**
   12.66 -     * Copy constructor
   12.67 -     * @param frame
   12.68 -     * @param symbols
   12.69 -     */
   12.70 -    private Frame(final Frame frame, final List<Symbol> symbols) {
   12.71 -        this.previous  = frame.getPrevious() == null ? null : new Frame(frame.getPrevious(), frame.getPrevious().getSymbols());
   12.72 -        this.symbols   = new ArrayList<>(frame.getSymbols());
   12.73 -        this.baseCount = frame.getBaseCount();
   12.74 -        this.count     = frame.getCount();
   12.75 -    }
   12.76 -
   12.77 -    /**
   12.78 -     * Copy the frame
   12.79 -     *
   12.80 -     * @return a new frame with the identical contents
   12.81 -     */
   12.82 -    public Frame copy() {
   12.83 -        return new Frame(this, getSymbols());
   12.84 -    }
   12.85 -
   12.86 -    /**
   12.87 -     * Add a new variable to the frame.
   12.88 -     * @param symbol Symbol representing variable.
   12.89 -     */
   12.90 -    public void addSymbol(final Symbol symbol) {
   12.91 -        final int slot = symbol.getSlot();
   12.92 -        if (slot < 0) {
   12.93 -            symbols.add(symbol);
   12.94 -            count += symbol.slotCount();
   12.95 -        }
   12.96 -    }
   12.97 -
   12.98 -    /**
   12.99 -     * Realign slot numbering prior to code generation.
  12.100 -     * @return Number of slots in frame.
  12.101 -     */
  12.102 -    public int realign() {
  12.103 -        baseCount = getBaseCount();
  12.104 -        count     = 0;
  12.105 -
  12.106 -        for (final Symbol symbol : symbols) {
  12.107 -            if (symbol.hasSlot()) {
  12.108 -                symbol.setSlot(baseCount + count);
  12.109 -                count += symbol.slotCount();
  12.110 -            }
  12.111 -        }
  12.112 -
  12.113 -        return count;
  12.114 -    }
  12.115 -
  12.116 -    /**
  12.117 -     * Return the slot count of previous frames.
  12.118 -     * @return Number of slots in previous frames.
  12.119 -     */
  12.120 -    private int getBaseCount() {
  12.121 -        return previous != null ? previous.getSlotCount() : 0;
  12.122 -    }
  12.123 -
  12.124 -    /**
  12.125 -     * Determine the number of slots to top of frame.
  12.126 -     * @return Number of slots in total.
  12.127 -     */
  12.128 -    public int getSlotCount() {
  12.129 -        return baseCount + count;
  12.130 -    }
  12.131 -
  12.132 -    @Override
  12.133 -    public String toString() {
  12.134 -        final StringBuilder sb = new StringBuilder();
  12.135 -        Frame f = this;
  12.136 -        boolean hasPrev = false;
  12.137 -        int pos = 0;
  12.138 -
  12.139 -        do {
  12.140 -            if (hasPrev) {
  12.141 -                sb.append("\n");
  12.142 -            }
  12.143 -
  12.144 -            sb.append("#").
  12.145 -                append(pos++).
  12.146 -                append(" {baseCount:").
  12.147 -                append(baseCount).
  12.148 -                append(", ").
  12.149 -                append("count:").
  12.150 -                append(count).
  12.151 -                append("} ");
  12.152 -
  12.153 -            for (final Symbol var : f.getSymbols()) {
  12.154 -                sb.append('[').
  12.155 -                    append(var.toString()).
  12.156 -                    append(' ').
  12.157 -                    append(var.hashCode()).
  12.158 -                    append("] ");
  12.159 -            }
  12.160 -
  12.161 -            f = f.getPrevious();
  12.162 -            hasPrev = true;
  12.163 -        } while (f != null);
  12.164 -
  12.165 -        return sb.toString();
  12.166 -    }
  12.167 -
  12.168 -    /**
  12.169 -     * Get variable count for this frame
  12.170 -     * @return variable count
  12.171 -     */
  12.172 -    public int getCount() {
  12.173 -        return count;
  12.174 -    }
  12.175 -
  12.176 -    /**
  12.177 -     * Get previous frame
  12.178 -     * @return previous frame
  12.179 -     */
  12.180 -    public Frame getPrevious() {
  12.181 -        return previous;
  12.182 -    }
  12.183 -
  12.184 -    /**
  12.185 -     * Set previous frame
  12.186 -     * @param previous previous frame
  12.187 -     */
  12.188 -    public void setPrevious(final Frame previous) {
  12.189 -        this.previous = previous;
  12.190 -    }
  12.191 -
  12.192 -    /**
  12.193 -     * Get symbols in frame
  12.194 -     * @return a list of symbols in this frame
  12.195 -     */
  12.196 -    public List<Symbol> getSymbols() {
  12.197 -        return Collections.unmodifiableList(symbols);
  12.198 -    }
  12.199 - }
    13.1 --- a/src/jdk/nashorn/internal/codegen/Lower.java	Fri Apr 19 18:23:00 2013 +0530
    13.2 +++ b/src/jdk/nashorn/internal/codegen/Lower.java	Fri Apr 19 16:11:16 2013 +0200
    13.3 @@ -25,29 +25,21 @@
    13.4  
    13.5  package jdk.nashorn.internal.codegen;
    13.6  
    13.7 -import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
    13.8 -import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
    13.9  import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL;
   13.10 -import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
   13.11 -import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN;
   13.12 +import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
   13.13  import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
   13.14 -import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
   13.15  
   13.16 -import java.util.ArrayDeque;
   13.17  import java.util.ArrayList;
   13.18  import java.util.Arrays;
   13.19 -import java.util.Deque;
   13.20 -import java.util.Iterator;
   13.21  import java.util.List;
   13.22  import jdk.nashorn.internal.ir.BaseNode;
   13.23  import jdk.nashorn.internal.ir.BinaryNode;
   13.24  import jdk.nashorn.internal.ir.Block;
   13.25 +import jdk.nashorn.internal.ir.BlockLexicalContext;
   13.26  import jdk.nashorn.internal.ir.BreakNode;
   13.27  import jdk.nashorn.internal.ir.CallNode;
   13.28 -import jdk.nashorn.internal.ir.CaseNode;
   13.29  import jdk.nashorn.internal.ir.CatchNode;
   13.30  import jdk.nashorn.internal.ir.ContinueNode;
   13.31 -import jdk.nashorn.internal.ir.DoWhileNode;
   13.32  import jdk.nashorn.internal.ir.EmptyNode;
   13.33  import jdk.nashorn.internal.ir.ExecuteNode;
   13.34  import jdk.nashorn.internal.ir.ForNode;
   13.35 @@ -56,10 +48,10 @@
   13.36  import jdk.nashorn.internal.ir.IdentNode;
   13.37  import jdk.nashorn.internal.ir.IfNode;
   13.38  import jdk.nashorn.internal.ir.LabelNode;
   13.39 -import jdk.nashorn.internal.ir.LabeledNode;
   13.40  import jdk.nashorn.internal.ir.LexicalContext;
   13.41  import jdk.nashorn.internal.ir.LineNumberNode;
   13.42  import jdk.nashorn.internal.ir.LiteralNode;
   13.43 +import jdk.nashorn.internal.ir.LoopNode;
   13.44  import jdk.nashorn.internal.ir.Node;
   13.45  import jdk.nashorn.internal.ir.ReturnNode;
   13.46  import jdk.nashorn.internal.ir.SwitchNode;
   13.47 @@ -90,356 +82,167 @@
   13.48  
   13.49  final class Lower extends NodeOperatorVisitor {
   13.50  
   13.51 -    /**
   13.52 -     * Nesting level stack. Currently just used for loops to avoid the problem
   13.53 -     * with terminal bodies that end with throw/return but still do continues to
   13.54 -     * outer loops or same loop.
   13.55 -     */
   13.56 -    private final Deque<Node> nesting;
   13.57 -
   13.58      private static final DebugLogger LOG = new DebugLogger("lower");
   13.59  
   13.60 -    private Node lastStatement;
   13.61 -
   13.62 -    private List<Node> statements;
   13.63 -
   13.64 -    private LexicalContext lexicalContext = new LexicalContext();
   13.65 -
   13.66      /**
   13.67       * Constructor.
   13.68       *
   13.69       * @param compiler the compiler
   13.70       */
   13.71      Lower() {
   13.72 -        this.nesting    = new ArrayDeque<>();
   13.73 -        this.statements = new ArrayList<>();
   13.74 +        super(new BlockLexicalContext() {
   13.75 +
   13.76 +            @Override
   13.77 +            public List<Node> popStatements() {
   13.78 +                List<Node> newStatements = new ArrayList<>();
   13.79 +                boolean terminated = false;
   13.80 +
   13.81 +                final List<Node> statements = super.popStatements();
   13.82 +                for (final Node statement : statements) {
   13.83 +                    if (!terminated) {
   13.84 +                        newStatements.add(statement);
   13.85 +                        if (statement.isTerminal()) {
   13.86 +                            terminated = true;
   13.87 +                        }
   13.88 +                    } else {
   13.89 +                        if (statement instanceof VarNode) {
   13.90 +                            newStatements.add(((VarNode)statement).setInit(null));
   13.91 +                        }
   13.92 +                    }
   13.93 +                }
   13.94 +                return newStatements;
   13.95 +            }
   13.96 +        });
   13.97      }
   13.98  
   13.99      @Override
  13.100 -    public Node enterBlock(final Block block) {
  13.101 -        final Node       savedLastStatement = lastStatement;
  13.102 -        final List<Node> savedStatements    = statements;
  13.103 -        lexicalContext.push(block);
  13.104 -        try {
  13.105 -            this.statements = new ArrayList<>();
  13.106 -            NodeVisitor visitor = this;
  13.107 -            for (final Node statement : block.getStatements()) {
  13.108 -                statement.accept(visitor);
  13.109 -                /*
  13.110 -                 * This is slightly unsound, for example if we have a loop with
  13.111 -                 * a guarded statement like if (x) continue in the body and the
  13.112 -                 * body ends with TERMINAL, e.g. return; we removed the continue
  13.113 -                 * before we had the loop stack, as all we cared about was a
  13.114 -                 * return last in the loop.
  13.115 -                 *
  13.116 -                 * @see NASHORN-285
  13.117 -                 */
  13.118 -                if (lastStatement != null && lastStatement.isTerminal()) {
  13.119 -                    copyTerminal(block, lastStatement);
  13.120 -                    visitor = new DeadCodeVarDeclarationVisitor();
  13.121 -                }
  13.122 -            }
  13.123 -            block.setStatements(statements);
  13.124 -
  13.125 -        } finally {
  13.126 -            this.statements = savedStatements;
  13.127 -            this.lastStatement = savedLastStatement;
  13.128 -            lexicalContext.pop(block);
  13.129 +    public boolean enterBlock(final Block block) {
  13.130 +        final LexicalContext lc = getLexicalContext();
  13.131 +        if (lc.isFunctionBody() && lc.getCurrentFunction().isProgram() && !lc.getCurrentFunction().hasDeclaredFunctions()) {
  13.132 +            new ExecuteNode(block.getSource(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this);
  13.133          }
  13.134 -
  13.135 -        return null;
  13.136 +        return true;
  13.137      }
  13.138  
  13.139      @Override
  13.140 -    public Node enterBreakNode(final BreakNode breakNode) {
  13.141 -        return enterBreakOrContinue(breakNode);
  13.142 +    public Node leaveBlock(final Block block) {
  13.143 +        //now we have committed the entire statement list to the block, but we need to truncate
  13.144 +        //whatever is after the last terminal. block append won't append past it
  13.145 +
  13.146 +        final BlockLexicalContext lc = (BlockLexicalContext)getLexicalContext();
  13.147 +
  13.148 +        Node last = lc.getLastStatement();
  13.149 +
  13.150 +        if (lc.isFunctionBody()) {
  13.151 +            final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
  13.152 +            final boolean isProgram = currentFunction.isProgram();
  13.153 +            final ReturnNode returnNode = new ReturnNode(
  13.154 +                currentFunction.getSource(),
  13.155 +                currentFunction.getToken(),
  13.156 +                currentFunction.getFinish(),
  13.157 +                isProgram ?
  13.158 +                    compilerConstant(RETURN) :
  13.159 +                    LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED));
  13.160 +
  13.161 +            last = returnNode.accept(this);
  13.162 +        }
  13.163 +
  13.164 +        if (last != null && last.isTerminal()) {
  13.165 +            return block.setIsTerminal(lc, true);
  13.166 +        }
  13.167 +
  13.168 +        return block;
  13.169      }
  13.170  
  13.171      @Override
  13.172 -    public Node enterCallNode(final CallNode callNode) {
  13.173 -        final Node function = markerFunction(callNode.getFunction());
  13.174 -        callNode.setFunction(function);
  13.175 -        checkEval(callNode); //check if this is an eval call and store the information
  13.176 -        return callNode;
  13.177 +    public boolean enterBreakNode(final BreakNode breakNode) {
  13.178 +        addStatement(breakNode);
  13.179 +        return false;
  13.180      }
  13.181  
  13.182      @Override
  13.183 -    public Node leaveCaseNode(final CaseNode caseNode) {
  13.184 -        caseNode.copyTerminalFlags(caseNode.getBody());
  13.185 -        return caseNode;
  13.186 +    public Node leaveCallNode(final CallNode callNode) {
  13.187 +        return checkEval(callNode.setFunction(markerFunction(callNode.getFunction())));
  13.188      }
  13.189  
  13.190      @Override
  13.191      public Node leaveCatchNode(final CatchNode catchNode) {
  13.192 -        catchNode.copyTerminalFlags(catchNode.getBody());
  13.193 -        addStatement(catchNode);
  13.194 -        return catchNode;
  13.195 +        return addStatement(catchNode);
  13.196      }
  13.197  
  13.198      @Override
  13.199 -    public Node enterContinueNode(final ContinueNode continueNode) {
  13.200 -        return enterBreakOrContinue(continueNode);
  13.201 +    public boolean enterContinueNode(final ContinueNode continueNode) {
  13.202 +        addStatement(continueNode);
  13.203 +        return false;
  13.204      }
  13.205  
  13.206      @Override
  13.207 -    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
  13.208 -        return enterWhileNode(doWhileNode);
  13.209 -    }
  13.210 -
  13.211 -    @Override
  13.212 -    public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
  13.213 -        return leaveWhileNode(doWhileNode);
  13.214 -    }
  13.215 -
  13.216 -    @Override
  13.217 -    public Node enterEmptyNode(final EmptyNode emptyNode) {
  13.218 -        return null;
  13.219 +    public boolean enterEmptyNode(final EmptyNode emptyNode) {
  13.220 +        return false;
  13.221      }
  13.222  
  13.223      @Override
  13.224      public Node leaveExecuteNode(final ExecuteNode executeNode) {
  13.225          final Node expr = executeNode.getExpression();
  13.226 +        ExecuteNode node = executeNode;
  13.227  
  13.228 -        if (getCurrentFunctionNode().isProgram()) {
  13.229 +        final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
  13.230 +
  13.231 +        if (currentFunction.isProgram()) {
  13.232              if (!(expr instanceof Block) || expr instanceof FunctionNode) { // it's not a block, but can be a function
  13.233                  if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) {
  13.234 -                    executeNode.setExpression(new BinaryNode(executeNode.getSource(), Token.recast(executeNode.getToken(), TokenType.ASSIGN),
  13.235 -                            getCurrentFunctionNode().getResultNode(),
  13.236 -                            expr));
  13.237 +                    node = executeNode.setExpression(
  13.238 +                        new BinaryNode(
  13.239 +                            executeNode.getSource(),
  13.240 +                            Token.recast(
  13.241 +                                executeNode.getToken(),
  13.242 +                                TokenType.ASSIGN),
  13.243 +                            compilerConstant(RETURN),
  13.244 +                        expr));
  13.245                  }
  13.246              }
  13.247          }
  13.248  
  13.249 -        copyTerminal(executeNode, executeNode.getExpression());
  13.250 -        addStatement(executeNode);
  13.251 -
  13.252 -        return executeNode;
  13.253 -    }
  13.254 -
  13.255 -    @Override
  13.256 -    public Node enterForNode(final ForNode forNode) {
  13.257 -        nest(forNode);
  13.258 -        return forNode;
  13.259 +        return addStatement(node);
  13.260      }
  13.261  
  13.262      @Override
  13.263      public Node leaveForNode(final ForNode forNode) {
  13.264 +        ForNode newForNode = forNode;
  13.265 +
  13.266          final Node  test = forNode.getTest();
  13.267 -        final Block body = forNode.getBody();
  13.268 -
  13.269 -        if (!forNode.isForIn() && test == null) {
  13.270 -            setHasGoto(forNode);
  13.271 +        if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
  13.272 +            newForNode = forNode.setTest(getLexicalContext(), null);
  13.273          }
  13.274  
  13.275 -        final boolean escapes = controlFlowEscapes(body);
  13.276 -        if (escapes) {
  13.277 -            setTerminal(body, false);
  13.278 -        }
  13.279 -
  13.280 -        // pop the loop from the loop context
  13.281 -        unnest(forNode);
  13.282 -
  13.283 -        if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
  13.284 -            forNode.setTest(null);
  13.285 -            setHasGoto(forNode);
  13.286 -            setTerminal(forNode, !escapes);
  13.287 -        }
  13.288 -
  13.289 -        addStatement(forNode);
  13.290 -
  13.291 -        return forNode;
  13.292 +        return addStatement(checkEscape(newForNode));
  13.293      }
  13.294  
  13.295      @Override
  13.296 -    public Node enterFunctionNode(final FunctionNode functionNode) {
  13.297 -        LOG.info("START FunctionNode: " + functionNode.getName());
  13.298 -
  13.299 -        if (functionNode.isLazy()) {
  13.300 -            LOG.info("LAZY: " + functionNode.getName());
  13.301 -            return null;
  13.302 -        }
  13.303 -        lexicalContext.push(functionNode);
  13.304 -        initFunctionNode(functionNode);
  13.305 -
  13.306 -        nest(functionNode);
  13.307 -
  13.308 -        /*
  13.309 -         * As we are evaluating a nested structure, we need to store the
  13.310 -         * statement list for the surrounding block and restore it when the
  13.311 -         * function is done
  13.312 -         */
  13.313 -        final List<Node> savedStatements = statements;
  13.314 -        final Node savedLastStatement = lastStatement;
  13.315 -
  13.316 -        statements    = new ArrayList<>();
  13.317 -        lastStatement = null;
  13.318 -
  13.319 -        if (functionNode.needsSelfSymbol()) {
  13.320 -            //function needs to start with var funcIdent = __callee_;
  13.321 -            statements.add(functionNode.getSelfSymbolInit().accept(this));
  13.322 -        }
  13.323 -
  13.324 -        NodeVisitor visitor = this;
  13.325 -        try {
  13.326 -            //do the statements - this fills the block with code
  13.327 -            boolean needsInitialEvalResult = functionNode.isProgram();
  13.328 -            for (final Node statement : functionNode.getStatements()) {
  13.329 -                // If this function is a program, then insert an assignment to the initial eval result after all
  13.330 -                // function declarations.
  13.331 -                if(needsInitialEvalResult && !(statement instanceof LineNumberNode || (statement instanceof VarNode && ((VarNode)statement).isFunctionDeclaration()))) {
  13.332 -                    addInitialEvalResult(functionNode);
  13.333 -                    needsInitialEvalResult = false;
  13.334 -                }
  13.335 -                statement.accept(visitor);
  13.336 -                //If there are unused terminated endpoints in the function, we need
  13.337 -                // to add a "return undefined" in those places for correct semantics
  13.338 -                LOG.info("Checking lastStatement="+lastStatement+" for terminal flags");
  13.339 -                if (lastStatement != null && lastStatement.hasTerminalFlags()) {
  13.340 -                    copyTerminal(functionNode, lastStatement);
  13.341 -                    assert !needsInitialEvalResult;
  13.342 -                    visitor = new DeadCodeVarDeclarationVisitor();
  13.343 -                }
  13.344 -            }
  13.345 -            if(needsInitialEvalResult) {
  13.346 -                addInitialEvalResult(functionNode);
  13.347 -            }
  13.348 -            functionNode.setStatements(statements);
  13.349 -
  13.350 -            if (!functionNode.isTerminal()) {
  13.351 -                guaranteeReturn(functionNode);
  13.352 -            }
  13.353 -        } finally {
  13.354 -            statements    = savedStatements;
  13.355 -            lastStatement = savedLastStatement;
  13.356 -        }
  13.357 -
  13.358 -        LOG.info("END FunctionNode: " + functionNode.getName());
  13.359 -        unnest(functionNode);
  13.360 -        lexicalContext.pop(functionNode);
  13.361 -
  13.362 -        functionNode.setState(CompilationState.LOWERED);
  13.363 -
  13.364 -        return null;
  13.365 -    }
  13.366 -
  13.367 -    /**
  13.368 -     * This visitor is used to go over statements after a terminal statement. Those statements are dead code, but the
  13.369 -     * var declarations in them still have the effect of declaring a local variable on the function level. Therefore,
  13.370 -     * they aren't really dead code and must be preserved. Note that they're only preserved as no-op declarations; their
  13.371 -     * initializers are wiped out as those are, in fact, dead code.
  13.372 -     */
  13.373 -    private class DeadCodeVarDeclarationVisitor extends NodeOperatorVisitor {
  13.374 -        DeadCodeVarDeclarationVisitor() {
  13.375 -        }
  13.376 -
  13.377 -        @Override
  13.378 -        public Node enterVarNode(VarNode varNode) {
  13.379 -            // Can't ever see a function declaration, as this visitor is only ever used after a terminal statement was
  13.380 -            // encountered, and all function declarations precede any terminal statements.
  13.381 -            assert !varNode.isFunctionDeclaration();
  13.382 -            if(varNode.getInit() == null) {
  13.383 -                // No initializer, just pass it to Lower.
  13.384 -                return varNode.accept(Lower.this);
  13.385 -            }
  13.386 -            // Wipe out the initializer and then pass it to Lower.
  13.387 -            return varNode.setInit(null).accept(Lower.this);
  13.388 -        }
  13.389 -    }
  13.390 -
  13.391 -    private void addInitialEvalResult(final FunctionNode functionNode) {
  13.392 -        new ExecuteNode(functionNode.getSource(), functionNode.getFirstToken(), functionNode.getFinish(),
  13.393 -                getInitialEvalResult(functionNode)).accept(this);
  13.394 -    }
  13.395 -
  13.396 -    /**
  13.397 -     * Result of initial result of evaluating a particular program, which is either the last function it declares, or
  13.398 -     * undefined if it doesn't declare any functions.
  13.399 -     * @param program
  13.400 -     * @return the initial result of evaluating the program
  13.401 -     */
  13.402 -    private static Node getInitialEvalResult(final FunctionNode program) {
  13.403 -        IdentNode lastFnName = null;
  13.404 -        for (final FunctionNode fn : program.getDeclaredFunctions()) {
  13.405 -            assert fn.isDeclared();
  13.406 -            final IdentNode fnName = fn.getIdent();
  13.407 -            if(fnName != null) {
  13.408 -                lastFnName = fnName;
  13.409 -            }
  13.410 -        }
  13.411 -        return lastFnName != null ? new IdentNode(lastFnName) : LiteralNode.newInstance(program, ScriptRuntime.UNDEFINED);
  13.412 +    public boolean enterFunctionNode(final FunctionNode functionNode) {
  13.413 +        return !functionNode.isLazy();
  13.414      }
  13.415  
  13.416      @Override
  13.417 -    public Node enterIfNode(final IfNode ifNode) {
  13.418 -        return nest(ifNode);
  13.419 +    public Node leaveFunctionNode(final FunctionNode functionNode) {
  13.420 +        LOG.info("END FunctionNode: ", functionNode.getName());
  13.421 +        return functionNode.setState(getLexicalContext(), CompilationState.LOWERED);
  13.422      }
  13.423  
  13.424      @Override
  13.425      public Node leaveIfNode(final IfNode ifNode) {
  13.426 -        final Node pass = ifNode.getPass();
  13.427 -        final Node fail = ifNode.getFail();
  13.428 -
  13.429 -        if (pass.isTerminal() && fail != null && fail.isTerminal()) {
  13.430 -            setTerminal(ifNode,  true);
  13.431 -        }
  13.432 -
  13.433 -        addStatement(ifNode);
  13.434 -        unnest(ifNode);
  13.435 -
  13.436 -        return ifNode;
  13.437 +        return addStatement(ifNode);
  13.438      }
  13.439  
  13.440      @Override
  13.441 -    public Node enterLabelNode(LabelNode labelNode) {
  13.442 -        final Block body = labelNode.getBody();
  13.443 -        body.accept(this);
  13.444 -        copyTerminal(labelNode, body);
  13.445 -        addStatement(labelNode);
  13.446 -        return null;
  13.447 +    public Node leaveLabelNode(final LabelNode labelNode) {
  13.448 +        return addStatement(labelNode);
  13.449      }
  13.450  
  13.451      @Override
  13.452 -    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
  13.453 -        addStatement(lineNumberNode, false); // don't put it in lastStatement cache
  13.454 -        return null;
  13.455 -    }
  13.456 -
  13.457 -    @Override
  13.458 -    public Node enterReturnNode(final ReturnNode returnNode) {
  13.459 -        final TryNode tryNode = returnNode.getTryChain();
  13.460 -        final Node    expr    = returnNode.getExpression();
  13.461 -
  13.462 -        if (tryNode != null) {
  13.463 -            //we are inside a try block - we don't necessarily have a result node yet. attr will do that.
  13.464 -            if (expr != null) {
  13.465 -                final Source source = getCurrentFunctionNode().getSource();
  13.466 -
  13.467 -                //we need to evaluate the result of the return in case it is complex while
  13.468 -                //still in the try block, store it in a result value and return it afterwards
  13.469 -                final long token        = returnNode.getToken();
  13.470 -                final Node resultNode   = new IdentNode(getCurrentFunctionNode().getResultNode());
  13.471 -                final Node assignResult = new BinaryNode(source, Token.recast(token, TokenType.ASSIGN), resultNode, expr);
  13.472 -
  13.473 -                //add return_in_try = expr; to try block
  13.474 -                new ExecuteNode(source, token, Token.descPosition(token), assignResult).accept(this);
  13.475 -
  13.476 -                //splice in the finally code, inlining it here
  13.477 -                if (copyFinally(tryNode, null)) {
  13.478 -                    return null;
  13.479 -                }
  13.480 -
  13.481 -                //make sure that the return node now returns 'return_in_try'
  13.482 -                returnNode.setExpression(resultNode);
  13.483 -            } else if (copyFinally(tryNode, null)) {
  13.484 -                return null;
  13.485 -            }
  13.486 -        } else if (expr != null) {
  13.487 -            returnNode.setExpression(expr.accept(this));
  13.488 -        }
  13.489 -
  13.490 -        addStatement(returnNode);
  13.491 -
  13.492 -        return null;
  13.493 +    public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
  13.494 +        addStatement(lineNumberNode); // don't put it in lastStatement cache
  13.495 +        return false;
  13.496      }
  13.497  
  13.498      @Override
  13.499 @@ -448,31 +251,10 @@
  13.500          return returnNode;
  13.501      }
  13.502  
  13.503 -    @Override
  13.504 -    public Node enterSwitchNode(final SwitchNode switchNode) {
  13.505 -        nest(switchNode);
  13.506 -        return switchNode;
  13.507 -    }
  13.508  
  13.509      @Override
  13.510      public Node leaveSwitchNode(final SwitchNode switchNode) {
  13.511 -        unnest(switchNode);
  13.512 -
  13.513 -        final List<CaseNode> cases       = switchNode.getCases();
  13.514 -        final CaseNode       defaultCase = switchNode.getDefaultCase();
  13.515 -
  13.516 -        boolean allTerminal = !cases.isEmpty();
  13.517 -        for (final CaseNode caseNode : switchNode.getCases()) {
  13.518 -            allTerminal &= caseNode.isTerminal();
  13.519 -        }
  13.520 -
  13.521 -        if (allTerminal && defaultCase != null && defaultCase.isTerminal()) {
  13.522 -            setTerminal(switchNode, true);
  13.523 -        }
  13.524 -
  13.525 -        addStatement(switchNode);
  13.526 -
  13.527 -        return switchNode;
  13.528 +        return addStatement(switchNode);
  13.529      }
  13.530  
  13.531      @Override
  13.532 @@ -481,208 +263,234 @@
  13.533          return throwNode;
  13.534      }
  13.535  
  13.536 -    @Override
  13.537 -    public Node enterTryNode(final TryNode tryNode) {
  13.538 -        final Block  finallyBody = tryNode.getFinallyBody();
  13.539 -        final long   token       = tryNode.getToken();
  13.540 -        final int    finish      = tryNode.getFinish();
  13.541 +    private static Node ensureUniqueLabelsIn(final Node node) {
  13.542 +        return node.accept(new NodeVisitor() {
  13.543 +           @Override
  13.544 +           public Node leaveDefault(final Node labelledNode) {
  13.545 +               return labelledNode.ensureUniqueLabels(getLexicalContext());
  13.546 +           }
  13.547 +        });
  13.548 +    }
  13.549  
  13.550 -        nest(tryNode);
  13.551 +    private static List<Node> copyFinally(final Block finallyBody) {
  13.552 +        final List<Node> newStatements = new ArrayList<>();
  13.553 +        for (final Node statement : finallyBody.getStatements()) {
  13.554 +            newStatements.add(ensureUniqueLabelsIn(statement));
  13.555 +            if (statement.hasTerminalFlags()) {
  13.556 +                return newStatements;
  13.557 +            }
  13.558 +        }
  13.559 +        return newStatements;
  13.560 +    }
  13.561  
  13.562 -        if (finallyBody == null) {
  13.563 -            //do nothing if no finally exists
  13.564 -            return tryNode;
  13.565 +    private Block catchAllBlock(final TryNode tryNode) {
  13.566 +        final Source source = tryNode.getSource();
  13.567 +        final long   token  = tryNode.getToken();
  13.568 +        final int    finish = tryNode.getFinish();
  13.569 +
  13.570 +        final IdentNode exception = new IdentNode(source, token, finish, getLexicalContext().getCurrentFunction().uniqueName("catch_all"));
  13.571 +
  13.572 +        final Block catchBody = new Block(source, token, finish, new ThrowNode(source, token, finish, new IdentNode(exception))).
  13.573 +                setIsTerminal(getLexicalContext(), true); //ends with throw, so terminal
  13.574 +
  13.575 +        final CatchNode catchAllNode  = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody);
  13.576 +        final Block     catchAllBlock = new Block(source, token, finish, catchAllNode);
  13.577 +
  13.578 +        //catchallblock -> catchallnode (catchnode) -> exception -> throw
  13.579 +
  13.580 +        return (Block)catchAllBlock.accept(this); //not accepted. has to be accepted by lower
  13.581 +    }
  13.582 +
  13.583 +    private IdentNode compilerConstant(final CompilerConstants cc) {
  13.584 +        final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
  13.585 +        return new IdentNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
  13.586 +    }
  13.587 +
  13.588 +    private static boolean isTerminal(final List<Node> statements) {
  13.589 +        return !statements.isEmpty() && statements.get(statements.size() - 1).hasTerminalFlags();
  13.590 +    }
  13.591 +
  13.592 +    /**
  13.593 +     * Splice finally code into all endpoints of a trynode
  13.594 +     * @param tryNode the try node
  13.595 +     * @param list of rethrowing throw nodes from synthetic catch blocks
  13.596 +     * @param finallyBody the code in the original finally block
  13.597 +     * @return new try node after splicing finally code (same if nop)
  13.598 +     */
  13.599 +    private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) {
  13.600 +        final Source source = tryNode.getSource();
  13.601 +        final int    finish = tryNode.getFinish();
  13.602 +
  13.603 +        assert tryNode.getFinallyBody() == null;
  13.604 +
  13.605 +        final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor() {
  13.606 +            final List<Node> insideTry = new ArrayList<>();
  13.607 +
  13.608 +            @Override
  13.609 +            public boolean enterDefault(final Node node) {
  13.610 +                insideTry.add(node);
  13.611 +                return true;
  13.612 +            }
  13.613 +
  13.614 +            @Override
  13.615 +            public boolean enterFunctionNode(final FunctionNode functionNode) {
  13.616 +                // do not enter function nodes - finally code should not be inlined into them
  13.617 +                return false;
  13.618 +            }
  13.619 +
  13.620 +            @Override
  13.621 +            public Node leaveThrowNode(final ThrowNode throwNode) {
  13.622 +                if (rethrows.contains(throwNode)) {
  13.623 +                    final List<Node> newStatements = copyFinally(finallyBody);
  13.624 +                    if (!isTerminal(newStatements)) {
  13.625 +                        newStatements.add(throwNode);
  13.626 +                    }
  13.627 +                    return new Block(source, throwNode.getToken(), throwNode.getFinish(), newStatements);
  13.628 +                }
  13.629 +                return throwNode;
  13.630 +            }
  13.631 +
  13.632 +            @Override
  13.633 +            public Node leaveBreakNode(final BreakNode breakNode) {
  13.634 +                return copy(breakNode, Lower.this.getLexicalContext().getBreakable(breakNode.getLabel()));
  13.635 +            }
  13.636 +
  13.637 +            @Override
  13.638 +            public Node leaveContinueNode(final ContinueNode continueNode) {
  13.639 +                return copy(continueNode, Lower.this.getLexicalContext().getContinueTo(continueNode.getLabel()));
  13.640 +            }
  13.641 +
  13.642 +            @Override
  13.643 +            public Node leaveReturnNode(final ReturnNode returnNode) {
  13.644 +                final Node  expr  = returnNode.getExpression();
  13.645 +                final List<Node> newStatements = new ArrayList<>();
  13.646 +
  13.647 +                final Node resultNode;
  13.648 +                if (expr != null) {
  13.649 +                    //we need to evaluate the result of the return in case it is complex while
  13.650 +                    //still in the try block, store it in a result value and return it afterwards
  13.651 +                    resultNode = new IdentNode(Lower.this.compilerConstant(RETURN));
  13.652 +                    newStatements.add(new ExecuteNode(new BinaryNode(source, Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
  13.653 +                } else {
  13.654 +                    resultNode = null;
  13.655 +                }
  13.656 +
  13.657 +                newStatements.addAll(copyFinally(finallyBody));
  13.658 +                if (!isTerminal(newStatements)) {
  13.659 +                    newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode));
  13.660 +                }
  13.661 +
  13.662 +                return new ExecuteNode(new Block(source, returnNode.getToken(), getLexicalContext().getCurrentBlock().getFinish(), newStatements));
  13.663 +            }
  13.664 +
  13.665 +            private Node copy(final Node endpoint, final Node targetNode) {
  13.666 +                if (!insideTry.contains(targetNode)) {
  13.667 +                    final List<Node> newStatements = copyFinally(finallyBody);
  13.668 +                    if (!isTerminal(newStatements)) {
  13.669 +                        newStatements.add(endpoint);
  13.670 +                    }
  13.671 +                    return new ExecuteNode(new Block(source, endpoint.getToken(), finish, newStatements));
  13.672 +                }
  13.673 +                return endpoint;
  13.674 +            }
  13.675 +        });
  13.676 +
  13.677 +        addStatement(newTryNode);
  13.678 +        for (final Node statement : finallyBody.getStatements()) {
  13.679 +            addStatement(statement);
  13.680          }
  13.681  
  13.682 -        /*
  13.683 -         * We have a finally clause.
  13.684 -         *
  13.685 -         * Transform to do finally tail duplication as follows:
  13.686 -         *
  13.687 -         * <pre>
  13.688 -         *  try {
  13.689 -         *    try_body
  13.690 -         *  } catch e1 {
  13.691 -         *    catchbody_1
  13.692 -         *  }
  13.693 -         *  ...
  13.694 -         *  } catch en {
  13.695 -         *    catchbody_n
  13.696 -         *  } finally {
  13.697 -         *    finally_body
  13.698 -         *  }
  13.699 -         *
  13.700 -         *  (where e1 ... en are optional)
  13.701 -         *
  13.702 -         *  turns into
  13.703 -         *
  13.704 -         *  try {
  13.705 -         *    try {
  13.706 -         *      try_body
  13.707 -         *    } catch e1 {
  13.708 -         *      catchbody1
  13.709 -         *      //nothing inlined explicitly here, return, break other
  13.710 -         *      //terminals may inline the finally body
  13.711 -         *      ...
  13.712 -         *    } catch en {
  13.713 -         *      catchbody2
  13.714 -         *      //nothing inlined explicitly here, return, break other
  13.715 -         *      //terminals may inline the finally body
  13.716 -         *    }
  13.717 -         *  } catch all ex {
  13.718 -         *      finally_body_inlined
  13.719 -         *      rethrow ex
  13.720 -         *  }
  13.721 -         *  finally_body_inlined
  13.722 -         * </pre>
  13.723 -         *
  13.724 -         * If tries are catches are terminal, visitors for return, break &
  13.725 -         * continue will handle the tail duplications. Throw needs to be
  13.726 -         * treated specially with the catchall as described in the above
  13.727 -         * ASCII art.
  13.728 -         *
  13.729 -         * If the try isn't terminal we do the finally_body_inlined at the
  13.730 -         * end. If the try is terminated with continue/break/return the
  13.731 -         * existing visitor logic will inline the finally before that
  13.732 -         * operation. if the try is terminated with a throw, the catches e1
  13.733 -         * ... en will have a chance to process the exception. If the
  13.734 -         * appropriate catch e1..en is non terminal we fall through to the
  13.735 -         * last finally_body_inlined. if the catch e1...en IS terminal with
  13.736 -         * continue/break/return existing visitor logic will fix it. If they
  13.737 -         * are terminal with another throw it goes to the catchall and the
  13.738 -         * finally_body_inlined marked (*) will fix it before rethrowing
  13.739 -         * whatever problem there was for identical semantic.
  13.740 -         */
  13.741 -        final Source source = getCurrentFunctionNode().getSource();
  13.742 -
  13.743 -        // if try node does not contain a catch we can skip creation of a new
  13.744 -        // try node and just append our synthetic catch to the existing try node.
  13.745 -        if (!tryNode.getCatchBlocks().isEmpty()) {
  13.746 -            // insert an intermediate try-catch* node, where we move the body and all catch blocks.
  13.747 -            // the original try node become a try-finally container for the new try-catch* node.
  13.748 -            // because we don't clone (to avoid deep copy), we have to fix the block chain in the end.
  13.749 -            final TryNode innerTryNode;
  13.750 -            innerTryNode = new TryNode(source, token, finish, tryNode.getNext());
  13.751 -            innerTryNode.setBody(tryNode.getBody());
  13.752 -            innerTryNode.setCatchBlocks(tryNode.getCatchBlocks());
  13.753 -
  13.754 -            // set outer tryNode's body to innerTryNode
  13.755 -            final Block outerBody;
  13.756 -            outerBody = new Block(source, token, finish);
  13.757 -            outerBody.setStatements(new ArrayList<Node>(Arrays.asList(innerTryNode)));
  13.758 -            tryNode.setBody(outerBody);
  13.759 -            tryNode.setCatchBlocks(null);
  13.760 -        }
  13.761 -
  13.762 -        // create a catch-all that inlines finally and rethrows
  13.763 -
  13.764 -        final Block catchBlock      = new Block(source, token, finish);
  13.765 -        //this catch block should get define symbol
  13.766 -
  13.767 -        final Block catchBody       = new Block(source, token, finish);
  13.768 -        final Node  catchAllFinally = finallyBody.copy();
  13.769 -
  13.770 -        catchBody.addStatement(new ExecuteNode(source, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally));
  13.771 -        setTerminal(catchBody, true);
  13.772 -
  13.773 -        final CatchNode catchAllNode;
  13.774 -        final IdentNode exception;
  13.775 -
  13.776 -        exception    = new IdentNode(source, token, finish, getCurrentFunctionNode().uniqueName("catch_all"));
  13.777 -        catchAllNode = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody);
  13.778 -        catchAllNode.setIsSyntheticRethrow();
  13.779 -
  13.780 -        catchBlock.addStatement(catchAllNode);
  13.781 -
  13.782 -        // replace all catches of outer tryNode with the catch-all
  13.783 -        tryNode.setCatchBlocks(new ArrayList<>(Arrays.asList(catchBlock)));
  13.784 -
  13.785 -        /*
  13.786 -         * We leave the finally block for the original try in place for now
  13.787 -         * so that children visitations will work. It is removed and placed
  13.788 -         * afterwards in the else case below, after all children are visited
  13.789 -         */
  13.790 -
  13.791 -        return tryNode;
  13.792 +        return newTryNode;
  13.793      }
  13.794  
  13.795      @Override
  13.796      public Node leaveTryNode(final TryNode tryNode) {
  13.797 -        final Block finallyBody   = tryNode.getFinallyBody();
  13.798 +        final Block finallyBody = tryNode.getFinallyBody();
  13.799  
  13.800 -        boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal());
  13.801 -
  13.802 -        for (final Block catchBlock : tryNode.getCatchBlocks()) {
  13.803 -            allTerminal &= catchBlock.isTerminal();
  13.804 +        if (finallyBody == null) {
  13.805 +            return addStatement(tryNode);
  13.806          }
  13.807  
  13.808 -        tryNode.setIsTerminal(allTerminal);
  13.809 +        /*
  13.810 +         * create a new trynode
  13.811 +         *    if we have catches:
  13.812 +         *
  13.813 +         *    try            try
  13.814 +         *       x              try
  13.815 +         *    catch               x
  13.816 +         *       y              catch
  13.817 +         *    finally z           y
  13.818 +         *                   catchall
  13.819 +         *                        rethrow
  13.820 +         *
  13.821 +         *   otheriwse
  13.822 +         *
  13.823 +         *   try              try
  13.824 +         *      x               x
  13.825 +         *   finally          catchall
  13.826 +         *      y               rethrow
  13.827 +         *
  13.828 +         *
  13.829 +         *   now splice in finally code wherever needed
  13.830 +         *
  13.831 +         */
  13.832 +        TryNode newTryNode;
  13.833  
  13.834 -        addStatement(tryNode);
  13.835 -        unnest(tryNode);
  13.836 +        final Block catchAll = catchAllBlock(tryNode);
  13.837  
  13.838 -        // if finally body is present, place it after the tryNode
  13.839 -        if (finallyBody != null) {
  13.840 -            tryNode.setFinallyBody(null);
  13.841 -            addStatement(finallyBody);
  13.842 +        final List<ThrowNode> rethrows = new ArrayList<>();
  13.843 +        catchAll.accept(new NodeVisitor() {
  13.844 +            @Override
  13.845 +            public boolean enterThrowNode(final ThrowNode throwNode) {
  13.846 +                rethrows.add(throwNode);
  13.847 +                return true;
  13.848 +            }
  13.849 +        });
  13.850 +        assert rethrows.size() == 1;
  13.851 +
  13.852 +        if (tryNode.getCatchBlocks().isEmpty()) {
  13.853 +            newTryNode = tryNode.setFinallyBody(null);
  13.854 +        } else {
  13.855 +            Block outerBody = new Block(tryNode.getSource(), tryNode.getToken(), tryNode.getFinish(), new ArrayList<Node>(Arrays.asList(tryNode.setFinallyBody(null))));
  13.856 +            newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null);
  13.857          }
  13.858  
  13.859 -        return tryNode;
  13.860 +        newTryNode = newTryNode.setCatchBlocks(Arrays.asList(catchAll)).setFinallyBody(null);
  13.861 +
  13.862 +        /*
  13.863 +         * Now that the transform is done, we have to go into the try and splice
  13.864 +         * the finally block in front of any statement that is outside the try
  13.865 +         */
  13.866 +        return spliceFinally(newTryNode, rethrows, finallyBody);
  13.867      }
  13.868  
  13.869      @Override
  13.870      public Node leaveVarNode(final VarNode varNode) {
  13.871          addStatement(varNode);
  13.872 +        if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION) && getLexicalContext().getCurrentFunction().isProgram()) {
  13.873 +            new ExecuteNode(varNode.getSource(), varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this);
  13.874 +        }
  13.875          return varNode;
  13.876      }
  13.877  
  13.878      @Override
  13.879 -    public Node enterWhileNode(final WhileNode whileNode) {
  13.880 -        return nest(whileNode);
  13.881 -    }
  13.882 -
  13.883 -    @Override
  13.884      public Node leaveWhileNode(final WhileNode whileNode) {
  13.885          final Node test = whileNode.getTest();
  13.886 +        final Block body = whileNode.getBody();
  13.887  
  13.888 -        if (test == null) {
  13.889 -            setHasGoto(whileNode);
  13.890 +        if (conservativeAlwaysTrue(test)) {
  13.891 +            //turn it into a for node without a test.
  13.892 +            final ForNode forNode = (ForNode)new ForNode(whileNode.getSource(), whileNode.getToken(), whileNode.getFinish(), null, null, body, null, ForNode.IS_FOR).accept(this);
  13.893 +            getLexicalContext().replace(whileNode, forNode);
  13.894 +            return forNode;
  13.895          }
  13.896  
  13.897 -        final Block   body    = whileNode.getBody();
  13.898 -        final boolean escapes = controlFlowEscapes(body);
  13.899 -        if (escapes) {
  13.900 -            setTerminal(body, false);
  13.901 -        }
  13.902 -
  13.903 -        Node node = whileNode;
  13.904 -
  13.905 -        if (body.isTerminal()) {
  13.906 -            if (whileNode instanceof DoWhileNode) {
  13.907 -                setTerminal(whileNode, true);
  13.908 -            } else if (conservativeAlwaysTrue(test)) {
  13.909 -                node = new ForNode(whileNode.getSource(), whileNode.getToken(), whileNode.getFinish());
  13.910 -                ((ForNode)node).setBody(body);
  13.911 -                node.accept(this);
  13.912 -                setTerminal(node, !escapes);
  13.913 -            }
  13.914 -        }
  13.915 -
  13.916 -        // pop the loop from the loop context
  13.917 -        unnest(whileNode);
  13.918 -        addStatement(node);
  13.919 -
  13.920 -        return node;
  13.921 +         return addStatement(checkEscape(whileNode));
  13.922      }
  13.923  
  13.924      @Override
  13.925      public Node leaveWithNode(final WithNode withNode) {
  13.926 -        if (withNode.getBody().isTerminal()) {
  13.927 -            setTerminal(withNode,  true);
  13.928 -        }
  13.929 -        addStatement(withNode);
  13.930 -
  13.931 -        return withNode;
  13.932 +        return addStatement(withNode);
  13.933      }
  13.934  
  13.935      @Override
  13.936 @@ -741,23 +549,25 @@
  13.937       *
  13.938       * @param callNode call node to check if it's an eval
  13.939       */
  13.940 -    private void checkEval(final CallNode callNode) {
  13.941 +    private CallNode checkEval(final CallNode callNode) {
  13.942          if (callNode.getFunction() instanceof IdentNode) {
  13.943  
  13.944              final List<Node> args   = callNode.getArgs();
  13.945              final IdentNode  callee = (IdentNode)callNode.getFunction();
  13.946  
  13.947              // 'eval' call with at least one argument
  13.948 -            if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) {
  13.949 -                final CallNode.EvalArgs evalArgs =
  13.950 +            if (args.size() >= 1 && EVAL.symbolName().equals(callee.getName())) {
  13.951 +                final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
  13.952 +                return callNode.setEvalArgs(
  13.953                      new CallNode.EvalArgs(
  13.954 -                        args.get(0).copy().accept(this), //clone as we use this for the "is eval case". original evaluated separately for "is not eval case"
  13.955 -                        getCurrentFunctionNode().getThisNode(),
  13.956 +                        ensureUniqueLabelsIn(args.get(0)).accept(this),
  13.957 +                        compilerConstant(THIS),
  13.958                          evalLocation(callee),
  13.959 -                        getCurrentFunctionNode().isStrictMode());
  13.960 -                callNode.setEvalArgs(evalArgs);
  13.961 +                        currentFunction.isStrict()));
  13.962              }
  13.963          }
  13.964 +
  13.965 +        return callNode;
  13.966      }
  13.967  
  13.968      private static boolean conservativeAlwaysTrue(final Node node) {
  13.969 @@ -773,7 +583,7 @@
  13.970       * @param loopBody the loop body to check
  13.971       * @return true if control flow may escape the loop
  13.972       */
  13.973 -    private boolean controlFlowEscapes(final Node loopBody) {
  13.974 +    private static boolean controlFlowEscapes(final LexicalContext lex, final Block loopBody) {
  13.975          final List<Node> escapes = new ArrayList<>();
  13.976  
  13.977          loopBody.accept(new NodeVisitor() {
  13.978 @@ -786,7 +596,7 @@
  13.979              @Override
  13.980              public Node leaveContinueNode(final ContinueNode node) {
  13.981                  // all inner loops have been popped.
  13.982 -                if (nesting.contains(node.getTargetNode())) {
  13.983 +                if (lex.contains(lex.getContinueTo(node.getLabel()))) {
  13.984                      escapes.add(node);
  13.985                  }
  13.986                  return node;
  13.987 @@ -796,135 +606,23 @@
  13.988          return !escapes.isEmpty();
  13.989      }
  13.990  
  13.991 -    private void guaranteeReturn(final FunctionNode functionNode) {
  13.992 -        Node resultNode;
  13.993 -
  13.994 -        if (functionNode.isProgram()) {
  13.995 -            resultNode = functionNode.getResultNode(); // the eval result, symbol assigned in Attr
  13.996 -        } else {
  13.997 -            if (lastStatement != null && lastStatement.isTerminal() || lastStatement instanceof ReturnNode) {
  13.998 -                return; //already in place or not needed, as it should be for a non-undefined returning function
  13.999 -            }
 13.1000 -            resultNode = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED);
 13.1001 +    private LoopNode checkEscape(final LoopNode loopNode) {
 13.1002 +        final LexicalContext lc = getLexicalContext();
 13.1003 +        final boolean escapes = controlFlowEscapes(lc, loopNode.getBody());
 13.1004 +        if (escapes) {
 13.1005 +            return loopNode.
 13.1006 +                setBody(lc, loopNode.getBody().setIsTerminal(lc, false)).
 13.1007 +                setControlFlowEscapes(lc, escapes);
 13.1008          }
 13.1009 -
 13.1010 -        //create a return statement
 13.1011 -        final Node returnNode = new ReturnNode(functionNode.getSource(), functionNode.getLastToken(), functionNode.getFinish(), resultNode, null);
 13.1012 -        returnNode.accept(this);
 13.1013 +        return loopNode;
 13.1014      }
 13.1015  
 13.1016  
 13.1017 -    private Node nest(final Node node) {
 13.1018 -        LOG.info("Nesting: " + node);
 13.1019 -        LOG.indent();
 13.1020 -        nesting.push(node);
 13.1021 -        return node;
 13.1022 +    private Node addStatement(final Node statement) {
 13.1023 +        ((BlockLexicalContext)getLexicalContext()).appendStatement(statement);
 13.1024 +        return statement;
 13.1025      }
 13.1026  
 13.1027 -    private void unnest(final Node node) {
 13.1028 -        LOG.unindent();
 13.1029 -        assert nesting.getFirst() == node : "inconsistent nesting order : " + nesting.getFirst() + " != " + node;
 13.1030 -        LOG.info("Unnesting: " + nesting);
 13.1031 -        nesting.pop();
 13.1032 -    }
 13.1033 -
 13.1034 -    private static void setTerminal(final Node node, final boolean isTerminal) {
 13.1035 -        LOG.info("terminal = " + isTerminal + " for " + node);
 13.1036 -        node.setIsTerminal(isTerminal);
 13.1037 -    }
 13.1038 -
 13.1039 -    private static void setHasGoto(final Node node) { //, final boolean hasGoto) {
 13.1040 -        LOG.info("hasGoto = true for " + node);
 13.1041 -        node.setHasGoto();
 13.1042 -    }
 13.1043 -
 13.1044 -    private static void copyTerminal(final Node node, final Node sourceNode) {
 13.1045 -        LOG.info("copy terminal flags " + sourceNode + " -> " + node);
 13.1046 -        node.copyTerminalFlags(sourceNode);
 13.1047 -    }
 13.1048 -
 13.1049 -    private void addStatement(final Node statement, final boolean storeInLastStatement) {
 13.1050 -        LOG.info("add statement = " + statement + " (lastStatement = " + lastStatement + ")");
 13.1051 -        statements.add(statement);
 13.1052 -        if (storeInLastStatement) {
 13.1053 -            lastStatement = statement;
 13.1054 -        }
 13.1055 -    }
 13.1056 -
 13.1057 -    private void addStatement(final Node statement) {
 13.1058 -        addStatement(statement, true);
 13.1059 -    }
 13.1060 -
 13.1061 -    /**
 13.1062 -     * Determine if Try block is inside target block.
 13.1063 -     *
 13.1064 -     * @param tryNode Try node to test.
 13.1065 -     * @param target  Target block.
 13.1066 -     *
 13.1067 -     * @return true if try block is inside the target, false otherwise.
 13.1068 -     */
 13.1069 -    private boolean isNestedTry(final TryNode tryNode, final Block target) {
 13.1070 -        for(Iterator<Block> blocks = lexicalContext.getBlocks(getCurrentBlock()); blocks.hasNext();) {
 13.1071 -            final Block block = blocks.next();
 13.1072 -            if(block == target) {
 13.1073 -                return false;
 13.1074 -            }
 13.1075 -            if(tryNode.isChildBlock(block)) {
 13.1076 -                return true;
 13.1077 -            }
 13.1078 -        }
 13.1079 -        return false;
 13.1080 -    }
 13.1081 -
 13.1082 -    /**
 13.1083 -     * Clones the body of the try finallys up to the target block.
 13.1084 -     *
 13.1085 -     * @param node       first try node in the chain.
 13.1086 -     * @param targetNode target block of the break/continue statement or null for return
 13.1087 -     *
 13.1088 -     * @return true if terminates.
 13.1089 -     */
 13.1090 -    private boolean copyFinally(final TryNode node, final Node targetNode) {
 13.1091 -        Block target = null;
 13.1092 -
 13.1093 -        if (targetNode instanceof Block) {
 13.1094 -            target = (Block)targetNode;
 13.1095 -        }
 13.1096 -
 13.1097 -        for (TryNode tryNode = node; tryNode != null; tryNode = tryNode.getNext()) {
 13.1098 -            if (target != null && !isNestedTry(tryNode, target)) {
 13.1099 -                return false;
 13.1100 -            }
 13.1101 -
 13.1102 -            Block finallyBody = tryNode.getFinallyBody();
 13.1103 -            if (finallyBody == null) {
 13.1104 -                continue;
 13.1105 -            }
 13.1106 -
 13.1107 -            finallyBody = (Block)finallyBody.copy();
 13.1108 -            final boolean hasTerminalFlags = finallyBody.hasTerminalFlags();
 13.1109 -
 13.1110 -            new ExecuteNode(finallyBody.getSource(), finallyBody.getToken(), finallyBody.getFinish(), finallyBody).accept(this);
 13.1111 -
 13.1112 -            if (hasTerminalFlags) {
 13.1113 -                getCurrentBlock().copyTerminalFlags(finallyBody);
 13.1114 -                return true;
 13.1115 -            }
 13.1116 -        }
 13.1117 -
 13.1118 -        return false;
 13.1119 -    }
 13.1120 -
 13.1121 -    private Node enterBreakOrContinue(final LabeledNode labeledNode) {
 13.1122 -        final TryNode tryNode = labeledNode.getTryChain();
 13.1123 -        if (tryNode != null && copyFinally(tryNode, labeledNode.getTargetNode())) {
 13.1124 -            return null;
 13.1125 -        }
 13.1126 -        addStatement(labeledNode);
 13.1127 -        return null;
 13.1128 -    }
 13.1129 -
 13.1130 -
 13.1131      /**
 13.1132       * An internal expression has a symbol that is tagged internal. Check if
 13.1133       * this is such a node
 13.1134 @@ -939,40 +637,21 @@
 13.1135  
 13.1136      /**
 13.1137       * Is this an assignment to the special variable that hosts scripting eval
 13.1138 -     * results?
 13.1139 +     * results, i.e. __return__?
 13.1140       *
 13.1141       * @param expression expression to check whether it is $evalresult = X
 13.1142       * @return true if an assignment to eval result, false otherwise
 13.1143       */
 13.1144 -    private boolean isEvalResultAssignment(final Node expression) {
 13.1145 +    private static boolean isEvalResultAssignment(final Node expression) {
 13.1146          Node e = expression;
 13.1147 -        if (e.tokenType() == TokenType.DISCARD) {
 13.1148 -            e = ((UnaryNode)expression).rhs();
 13.1149 -        }
 13.1150 -        final Node resultNode = getCurrentFunctionNode().getResultNode();
 13.1151 -        return e instanceof BinaryNode && ((BinaryNode)e).lhs().equals(resultNode);
 13.1152 -    }
 13.1153 -
 13.1154 -    /**
 13.1155 -     * Prepare special function nodes.
 13.1156 -     * TODO : only create those that are needed.
 13.1157 -     * TODO : make sure slot numbering is not hardcoded in {@link CompilerConstants} - now creation order is significant
 13.1158 -     */
 13.1159 -    private static void initFunctionNode(final FunctionNode functionNode) {
 13.1160 -        final Source source = functionNode.getSource();
 13.1161 -        final long token    = functionNode.getToken();
 13.1162 -        final int  finish   = functionNode.getFinish();
 13.1163 -
 13.1164 -        functionNode.setThisNode(new IdentNode(source, token, finish, THIS.tag()));
 13.1165 -        functionNode.setScopeNode(new IdentNode(source, token, finish, SCOPE.tag()));
 13.1166 -        functionNode.setResultNode(new IdentNode(source, token, finish, SCRIPT_RETURN.tag()));
 13.1167 -        functionNode.setCalleeNode(new IdentNode(source, token, finish, CALLEE.tag()));
 13.1168 -        if (functionNode.isVarArg()) {
 13.1169 -            functionNode.setVarArgsNode(new IdentNode(source, token, finish, VARARGS.tag()));
 13.1170 -            if (functionNode.needsArguments()) {
 13.1171 -                functionNode.setArgumentsNode(new IdentNode(source, token, finish, ARGUMENTS.tag()));
 13.1172 +        assert e.tokenType() != TokenType.DISCARD; //there are no discards this early anymore
 13.1173 +        if (e instanceof BinaryNode) {
 13.1174 +            final Node lhs = ((BinaryNode)e).lhs();
 13.1175 +            if (lhs instanceof IdentNode) {
 13.1176 +                return ((IdentNode)lhs).getName().equals(RETURN.symbolName());
 13.1177              }
 13.1178          }
 13.1179 +        return false;
 13.1180      }
 13.1181  
 13.1182  }
    14.1 --- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java	Fri Apr 19 18:23:00 2013 +0530
    14.2 +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java	Fri Apr 19 16:11:16 2013 +0200
    14.3 @@ -53,9 +53,12 @@
    14.4  import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
    14.5  import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
    14.6  import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
    14.7 +import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
    14.8  import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
    14.9 +import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
   14.10  import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
   14.11  import static jdk.nashorn.internal.codegen.CompilerConstants.THIS_DEBUGGER;
   14.12 +import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
   14.13  import static jdk.nashorn.internal.codegen.CompilerConstants.className;
   14.14  import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
   14.15  import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
   14.16 @@ -67,6 +70,8 @@
   14.17  import java.util.ArrayDeque;
   14.18  import java.util.EnumSet;
   14.19  import java.util.Iterator;
   14.20 +import java.util.List;
   14.21 +
   14.22  import jdk.internal.dynalink.support.NameCodec;
   14.23  import jdk.internal.org.objectweb.asm.Handle;
   14.24  import jdk.internal.org.objectweb.asm.MethodVisitor;
   14.25 @@ -79,14 +84,14 @@
   14.26  import jdk.nashorn.internal.codegen.types.Type;
   14.27  import jdk.nashorn.internal.ir.FunctionNode;
   14.28  import jdk.nashorn.internal.ir.IdentNode;
   14.29 +import jdk.nashorn.internal.ir.LexicalContext;
   14.30  import jdk.nashorn.internal.ir.LiteralNode;
   14.31  import jdk.nashorn.internal.ir.RuntimeNode;
   14.32 -import jdk.nashorn.internal.ir.SplitNode;
   14.33  import jdk.nashorn.internal.ir.Symbol;
   14.34  import jdk.nashorn.internal.runtime.ArgumentSetter;
   14.35 +import jdk.nashorn.internal.runtime.Debug;
   14.36  import jdk.nashorn.internal.runtime.DebugLogger;
   14.37  import jdk.nashorn.internal.runtime.JSType;
   14.38 -import jdk.nashorn.internal.runtime.Scope;
   14.39  import jdk.nashorn.internal.runtime.ScriptEnvironment;
   14.40  import jdk.nashorn.internal.runtime.ScriptObject;
   14.41  import jdk.nashorn.internal.runtime.linker.Bootstrap;
   14.42 @@ -116,10 +121,10 @@
   14.43      private final ClassEmitter classEmitter;
   14.44  
   14.45      /** FunctionNode representing this method, or null if none exists */
   14.46 -    private FunctionNode functionNode;
   14.47 +    protected FunctionNode functionNode;
   14.48  
   14.49 -    /** SplitNode representing the current split, or null if none exists */
   14.50 -    private SplitNode splitNode;
   14.51 +    /** Check whether this emitter ever has a function return point */
   14.52 +    private boolean hasReturn;
   14.53  
   14.54      /** The script environment */
   14.55      private final ScriptEnvironment env;
   14.56 @@ -203,7 +208,7 @@
   14.57  
   14.58      @Override
   14.59      public String toString() {
   14.60 -        return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + stack;
   14.61 +        return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + Debug.id(this);
   14.62      }
   14.63  
   14.64      /**
   14.65 @@ -476,8 +481,8 @@
   14.66  
   14.67          String name = symbol.getName();
   14.68  
   14.69 -        if (name.equals(THIS.tag())) {
   14.70 -            name = THIS_DEBUGGER.tag();
   14.71 +        if (name.equals(THIS.symbolName())) {
   14.72 +            name = THIS_DEBUGGER.symbolName();
   14.73          }
   14.74  
   14.75          method.visitLocalVariable(name, symbol.getSymbolType().getDescriptor(), null, start, end, symbol.getSlot());
   14.76 @@ -654,7 +659,7 @@
   14.77       * @return this method emitter
   14.78       */
   14.79      MethodEmitter loadConstants() {
   14.80 -        getStatic(classEmitter.getUnitClassName(), CONSTANTS.tag(), CONSTANTS.descriptor());
   14.81 +        getStatic(classEmitter.getUnitClassName(), CONSTANTS.symbolName(), CONSTANTS.descriptor());
   14.82          assert peekType().isArray() : peekType();
   14.83          return this;
   14.84      }
   14.85 @@ -835,13 +840,13 @@
   14.86              if (functionNode.needsArguments()) {
   14.87                  // ScriptObject.getArgument(int) on arguments
   14.88                  debug("load symbol", symbol.getName(), " arguments index=", index);
   14.89 -                loadArguments();
   14.90 +                loadCompilerConstant(ARGUMENTS);
   14.91                  load(index);
   14.92                  ScriptObject.GET_ARGUMENT.invoke(this);
   14.93              } else {
   14.94                  // array load from __varargs__
   14.95                  debug("load symbol", symbol.getName(), " array index=", index);
   14.96 -                loadVarArgs();
   14.97 +                loadCompilerConstant(VARARGS);
   14.98                  load(symbol.getFieldIndex());
   14.99                  arrayload();
  14.100              }
  14.101 @@ -870,48 +875,13 @@
  14.102          if(functionNode == null) {
  14.103              return slot == CompilerConstants.JAVA_THIS.slot();
  14.104          }
  14.105 -        final int thisSlot = functionNode.getThisNode().getSymbol().getSlot();
  14.106 +        final int thisSlot = compilerConstant(THIS).getSlot();
  14.107          assert !functionNode.needsCallee() || thisSlot == 1; // needsCallee -> thisSlot == 1
  14.108          assert functionNode.needsCallee() || thisSlot == 0; // !needsCallee -> thisSlot == 0
  14.109          return slot == thisSlot;
  14.110      }
  14.111  
  14.112      /**
  14.113 -     * Push the this object to the stack.
  14.114 -     *
  14.115 -     * @return the method emitter
  14.116 -     */
  14.117 -    MethodEmitter loadThis() {
  14.118 -        load(functionNode.getThisNode().getSymbol());
  14.119 -        return this;
  14.120 -    }
  14.121 -
  14.122 -    /**
  14.123 -     * Push the scope object to the stack.
  14.124 -     *
  14.125 -     * @return the method emitter
  14.126 -     */
  14.127 -    MethodEmitter loadScope() {
  14.128 -        if (peekType() == Type.SCOPE) {
  14.129 -            dup();
  14.130 -            return this;
  14.131 -        }
  14.132 -        load(functionNode.getScopeNode().getSymbol());
  14.133 -        return this;
  14.134 -    }
  14.135 -
  14.136 -    /**
  14.137 -     * Push the return object to the stack.
  14.138 -     *
  14.139 -     * @return the method emitter
  14.140 -     */
  14.141 -    MethodEmitter loadResult() {
  14.142 -        load(functionNode.getResultNode().getSymbol());
  14.143 -        return this;
  14.144 -    }
  14.145 -
  14.146 -
  14.147 -    /**
  14.148       * Push a method handle to the stack
  14.149       *
  14.150       * @param className  class name
  14.151 @@ -927,62 +897,24 @@
  14.152          return this;
  14.153      }
  14.154  
  14.155 -    /**
  14.156 -     * Push the varargs object to the stack
  14.157 -     *
  14.158 -     * @return the method emitter
  14.159 -     */
  14.160 -    MethodEmitter loadVarArgs() {
  14.161 -        debug("load var args " + functionNode.getVarArgsNode().getSymbol());
  14.162 -        return load(functionNode.getVarArgsNode().getSymbol());
  14.163 +    private Symbol compilerConstant(final CompilerConstants cc) {
  14.164 +        return functionNode.getBody().getExistingSymbol(cc.symbolName());
  14.165      }
  14.166  
  14.167 -    /**
  14.168 -     * Push the arguments array to the stack
  14.169 -     *
  14.170 -     * @return the method emitter
  14.171 -     */
  14.172 -    MethodEmitter loadArguments() {
  14.173 -        debug("load arguments ", functionNode.getArgumentsNode().getSymbol());
  14.174 -        assert functionNode.getArgumentsNode().getSymbol().getSlot() != 0;
  14.175 -        return load(functionNode.getArgumentsNode().getSymbol());
  14.176 +    MethodEmitter loadCompilerConstant(final CompilerConstants cc) {
  14.177 +        final Symbol symbol = compilerConstant(cc);
  14.178 +        if (cc == SCOPE && peekType() == Type.SCOPE) {
  14.179 +            dup();
  14.180 +            return this;
  14.181 +        }
  14.182 +        debug("load compiler constant ", symbol);
  14.183 +        return load(symbol);
  14.184      }
  14.185  
  14.186 -    /**
  14.187 -     * Push the callee object to the stack
  14.188 -     *
  14.189 -     * @return the method emitter
  14.190 -     */
  14.191 -    MethodEmitter loadCallee() {
  14.192 -        final Symbol calleeSymbol = functionNode.getCalleeNode().getSymbol();
  14.193 -        debug("load callee ", calleeSymbol);
  14.194 -        assert calleeSymbol.getSlot() == 0 : "callee has wrong slot " + calleeSymbol.getSlot() + " in " + functionNode.getName();
  14.195 -
  14.196 -        return load(calleeSymbol);
  14.197 -    }
  14.198 -
  14.199 -    /**
  14.200 -     * Pop the scope from the stack and store it in its predefined slot
  14.201 -     */
  14.202 -    void storeScope() {
  14.203 -        debug("store scope");
  14.204 -        store(functionNode.getScopeNode().getSymbol());
  14.205 -    }
  14.206 -
  14.207 -    /**
  14.208 -     * Pop the return from the stack and store it in its predefined slot
  14.209 -     */
  14.210 -    void storeResult() {
  14.211 -        debug("store result");
  14.212 -        store(functionNode.getResultNode().getSymbol());
  14.213 -    }
  14.214 -
  14.215 -    /**
  14.216 -     * Pop the arguments array from the stack and store it in its predefined slot
  14.217 -     */
  14.218 -    void storeArguments() {
  14.219 -        debug("store arguments");
  14.220 -        store(functionNode.getArgumentsNode().getSymbol());
  14.221 +    void storeCompilerConstant(final CompilerConstants cc) {
  14.222 +        final Symbol symbol = compilerConstant(cc);
  14.223 +        debug("store compiler constant ", symbol);
  14.224 +        store(symbol);
  14.225      }
  14.226  
  14.227      /**
  14.228 @@ -1030,13 +962,13 @@
  14.229              final int index = symbol.getFieldIndex();
  14.230              if (functionNode.needsArguments()) {
  14.231                  debug("store symbol", symbol.getName(), " arguments index=", index);
  14.232 -                loadArguments();
  14.233 +                loadCompilerConstant(ARGUMENTS);
  14.234                  load(index);
  14.235                  ArgumentSetter.SET_ARGUMENT.invoke(this);
  14.236              } else {
  14.237                  // varargs without arguments object - just do array store to __varargs__
  14.238                  debug("store symbol", symbol.getName(), " array index=", index);
  14.239 -                loadVarArgs();
  14.240 +                loadCompilerConstant(VARARGS);
  14.241                  load(index);
  14.242                  ArgumentSetter.SET_ARRAY_ELEMENT.invoke(this);
  14.243              }
  14.244 @@ -1345,6 +1277,11 @@
  14.245          }
  14.246      }
  14.247  
  14.248 +    MethodEmitter registerReturn() {
  14.249 +        this.hasReturn = true;
  14.250 +        return this;
  14.251 +    }
  14.252 +
  14.253      /**
  14.254       * Perform a non void return, popping the type from the stack
  14.255       *
  14.256 @@ -1385,22 +1322,7 @@
  14.257       *
  14.258       * @param label destination label
  14.259       */
  14.260 -    void splitAwareGoto(final Label label) {
  14.261 -
  14.262 -        if (splitNode != null) {
  14.263 -            final int index = splitNode.getExternalTargets().indexOf(label);
  14.264 -
  14.265 -            if (index > -1) {
  14.266 -                loadScope();
  14.267 -                checkcast(Scope.class);
  14.268 -                load(index + 1);
  14.269 -                invoke(Scope.SET_SPLIT_STATE);
  14.270 -                loadUndefined(Type.OBJECT);
  14.271 -                _return(functionNode.getReturnType());
  14.272 -                return;
  14.273 -            }
  14.274 -        }
  14.275 -
  14.276 +    void splitAwareGoto(final LexicalContext lc, final Label label) {
  14.277          _goto(label);
  14.278      }
  14.279  
  14.280 @@ -2237,7 +2159,7 @@
  14.281              }
  14.282  
  14.283              if (env != null) { //early bootstrap code doesn't have inited context yet
  14.284 -                LOG.info(sb.toString());
  14.285 +                LOG.info(sb);
  14.286                  if (DEBUG_TRACE_LINE == linePrefix) {
  14.287                      new Throwable().printStackTrace(LOG.getOutputStream());
  14.288                  }
  14.289 @@ -2254,21 +2176,12 @@
  14.290          this.functionNode = functionNode;
  14.291      }
  14.292  
  14.293 -    /**
  14.294 -     * Get the split node for this method emitter, if this is code
  14.295 -     * generation due to splitting large methods
  14.296 -     *
  14.297 -     * @return split node
  14.298 -     */
  14.299 -    SplitNode getSplitNode() {
  14.300 -        return splitNode;
  14.301 +    boolean hasReturn() {
  14.302 +        return hasReturn;
  14.303      }
  14.304  
  14.305 -    /**
  14.306 -     * Set the split node for this method emitter
  14.307 -     * @param splitNode split node
  14.308 -     */
  14.309 -    void setSplitNode(final SplitNode splitNode) {
  14.310 -        this.splitNode = splitNode;
  14.311 +    List<Label> getExternalTargets() {
  14.312 +        return null;
  14.313      }
  14.314 +
  14.315  }
    15.1 --- a/src/jdk/nashorn/internal/codegen/Namespace.java	Fri Apr 19 18:23:00 2013 +0530
    15.2 +++ b/src/jdk/nashorn/internal/codegen/Namespace.java	Fri Apr 19 16:11:16 2013 +0200
    15.3 @@ -53,7 +53,7 @@
    15.4       */
    15.5      public Namespace(final Namespace parent) {
    15.6          this.parent    = parent;
    15.7 -        directory = new HashMap<>();
    15.8 +        this.directory = new HashMap<>();
    15.9      }
   15.10  
   15.11      /**
   15.12 @@ -65,10 +65,6 @@
   15.13          return parent;
   15.14      }
   15.15  
   15.16 -    private HashMap<String, Integer> getDirectory() {
   15.17 -        return directory;
   15.18 -    }
   15.19 -
   15.20      /**
   15.21       * Create a uniqueName name in the namespace in the form base$n where n varies
   15.22       * .
   15.23 @@ -78,7 +74,7 @@
   15.24       */
   15.25      public String uniqueName(final String base) {
   15.26          for (Namespace namespace = this; namespace != null; namespace = namespace.getParent()) {
   15.27 -            final HashMap<String, Integer> namespaceDirectory = namespace.getDirectory();
   15.28 +            final HashMap<String, Integer> namespaceDirectory = namespace.directory;
   15.29              final Integer                  counter            = namespaceDirectory.get(base);
   15.30  
   15.31              if (counter != null) {
    16.1 --- a/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java	Fri Apr 19 18:23:00 2013 +0530
    16.2 +++ b/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java	Fri Apr 19 16:11:16 2013 +0200
    16.3 @@ -204,8 +204,8 @@
    16.4       * @return The class name.
    16.5       */
    16.6      public static String getClassName(final int fieldCount) {
    16.7 -        return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag() + fieldCount :
    16.8 -                                 SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag();
    16.9 +        return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount :
   16.10 +                                 SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName();
   16.11      }
   16.12  
   16.13      /**
   16.14 @@ -218,7 +218,7 @@
   16.15       * @return The class name.
   16.16       */
   16.17      public static String getClassName(final int fieldCount, final int paramCount) {
   16.18 -        return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag() + fieldCount + SCOPE_MARKER + paramCount;
   16.19 +        return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount + SCOPE_MARKER + paramCount;
   16.20      }
   16.21  
   16.22      /**
   16.23 @@ -449,7 +449,7 @@
   16.24       * @param className    Name of JavaScript class.
   16.25       */
   16.26      private static void newAllocate(final ClassEmitter classEmitter, final String className) {
   16.27 -        final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.tag(), ScriptObject.class, PropertyMap.class);
   16.28 +        final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.symbolName(), ScriptObject.class, PropertyMap.class);
   16.29          allocate.begin();
   16.30          allocate._new(className);
   16.31          allocate.dup();
    17.1 --- a/src/jdk/nashorn/internal/codegen/ObjectCreator.java	Fri Apr 19 18:23:00 2013 +0530
    17.2 +++ b/src/jdk/nashorn/internal/codegen/ObjectCreator.java	Fri Apr 19 16:11:16 2013 +0200
    17.3 @@ -36,7 +36,7 @@
    17.4  public abstract class ObjectCreator {
    17.5  
    17.6      /** Compile unit for this ObjectCreator, see CompileUnit */
    17.7 -    protected final CompileUnit   compileUnit;
    17.8 +    //protected final CompileUnit   compileUnit;
    17.9  
   17.10      /** List of keys to initiate in this ObjectCreator */
   17.11      protected final List<String>  keys;
   17.12 @@ -66,7 +66,6 @@
   17.13       */
   17.14      protected ObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final boolean isScope, final boolean hasArguments) {
   17.15          this.codegen       = codegen;
   17.16 -        this.compileUnit   = codegen.getCurrentCompileUnit();
   17.17          this.keys          = keys;
   17.18          this.symbols       = symbols;
   17.19          this.isScope       = isScope;
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java	Fri Apr 19 16:11:16 2013 +0200
    18.3 @@ -0,0 +1,100 @@
    18.4 +/*
    18.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    18.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    18.7 + *
    18.8 + * This code is free software; you can redistribute it and/or modify it
    18.9 + * under the terms of the GNU General Public License version 2 only, as
   18.10 + * published by the Free Software Foundation.  Oracle designates this
   18.11 + * particular file as subject to the "Classpath" exception as provided
   18.12 + * by Oracle in the LICENSE file that accompanied this code.
   18.13 + *
   18.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   18.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   18.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   18.17 + * version 2 for more details (a copy is included in the LICENSE file that
   18.18 + * accompanied this code).
   18.19 + *
   18.20 + * You should have received a copy of the GNU General Public License version
   18.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   18.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   18.23 + *
   18.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   18.25 + * or visit www.oracle.com if you need additional information or have any
   18.26 + * questions.
   18.27 + */
   18.28 +
   18.29 +package jdk.nashorn.internal.codegen;
   18.30 +
   18.31 +import java.util.ArrayList;
   18.32 +import java.util.List;
   18.33 +
   18.34 +import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
   18.35 +
   18.36 +import jdk.internal.org.objectweb.asm.MethodVisitor;
   18.37 +import jdk.nashorn.internal.codegen.types.Type;
   18.38 +import jdk.nashorn.internal.ir.LexicalContext;
   18.39 +import jdk.nashorn.internal.ir.SplitNode;
   18.40 +import jdk.nashorn.internal.runtime.Scope;
   18.41 +
   18.42 +/**
   18.43 + * Emitter used for splitting methods. Needs to keep track of if there are jump targets
   18.44 + * outside the current split node. All external jump targets encountered at method
   18.45 + * emission are logged, and {@code CodeGenerator#leaveSplitNode(SplitNode)} creates
   18.46 + * an appropriate jump table when the SplitNode has been iterated through
   18.47 + */
   18.48 +public class SplitMethodEmitter extends MethodEmitter {
   18.49 +
   18.50 +    private final SplitNode splitNode;
   18.51 +
   18.52 +    private final List<Label> externalTargets = new ArrayList<>();
   18.53 +
   18.54 +    SplitMethodEmitter(final ClassEmitter classEmitter, final MethodVisitor mv, SplitNode splitNode) {
   18.55 +        super(classEmitter, mv);
   18.56 +        this.splitNode = splitNode;
   18.57 +    }
   18.58 +
   18.59 +    @Override
   18.60 +    void splitAwareGoto(final LexicalContext lc, final Label label) {
   18.61 +        assert splitNode != null;
   18.62 +        final int index = findExternalTarget(lc, label);
   18.63 +        if (index >= 0) {
   18.64 +            loadCompilerConstant(SCOPE);
   18.65 +            checkcast(Scope.class);
   18.66 +            load(index + 1);
   18.67 +            invoke(Scope.SET_SPLIT_STATE);
   18.68 +            loadUndefined(Type.OBJECT);
   18.69 +            _return(functionNode.getReturnType());
   18.70 +            return;
   18.71 +        }
   18.72 +        super.splitAwareGoto(lc, label);
   18.73 +    }
   18.74 +
   18.75 +    private int findExternalTarget(final LexicalContext lc, final Label label) {
   18.76 +        final int index = externalTargets.indexOf(label);
   18.77 +
   18.78 +        if (index >= 0) {
   18.79 +            return index;
   18.80 +        }
   18.81 +
   18.82 +        if (lc.isExternalTarget(splitNode, label)) {
   18.83 +             externalTargets.add(label);
   18.84 +             return externalTargets.size() - 1;
   18.85 +         }
   18.86 +         return -1;
   18.87 +    }
   18.88 +
   18.89 +    @Override
   18.90 +    MethodEmitter registerReturn() {
   18.91 +        super.registerReturn();
   18.92 +        loadCompilerConstant(SCOPE);
   18.93 +        checkcast(Scope.class);
   18.94 +        load(0);
   18.95 +        invoke(Scope.SET_SPLIT_STATE);
   18.96 +        return this;
   18.97 +    }
   18.98 +
   18.99 +    @Override
  18.100 +    final List<Label> getExternalTargets() {
  18.101 +        return externalTargets;
  18.102 +    }
  18.103 +}
    19.1 --- a/src/jdk/nashorn/internal/codegen/Splitter.java	Fri Apr 19 18:23:00 2013 +0530
    19.2 +++ b/src/jdk/nashorn/internal/codegen/Splitter.java	Fri Apr 19 16:11:16 2013 +0200
    19.3 @@ -28,29 +28,18 @@
    19.4  import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
    19.5  
    19.6  import java.util.ArrayList;
    19.7 -import java.util.Deque;
    19.8  import java.util.HashMap;
    19.9 -import java.util.LinkedList;
   19.10  import java.util.List;
   19.11  import java.util.Map;
   19.12  import jdk.nashorn.internal.ir.Block;
   19.13 -import jdk.nashorn.internal.ir.BreakNode;
   19.14 -import jdk.nashorn.internal.ir.ContinueNode;
   19.15 -import jdk.nashorn.internal.ir.DoWhileNode;
   19.16 -import jdk.nashorn.internal.ir.ForNode;
   19.17  import jdk.nashorn.internal.ir.FunctionNode;
   19.18  import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
   19.19 -import jdk.nashorn.internal.ir.LabelNode;
   19.20  import jdk.nashorn.internal.ir.LexicalContext;
   19.21  import jdk.nashorn.internal.ir.LiteralNode;
   19.22  import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
   19.23  import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
   19.24  import jdk.nashorn.internal.ir.Node;
   19.25 -import jdk.nashorn.internal.ir.ReturnNode;
   19.26  import jdk.nashorn.internal.ir.SplitNode;
   19.27 -import jdk.nashorn.internal.ir.SwitchNode;
   19.28 -import jdk.nashorn.internal.ir.WhileNode;
   19.29 -import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
   19.30  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   19.31  import jdk.nashorn.internal.runtime.DebugLogger;
   19.32  import jdk.nashorn.internal.runtime.Source;
   19.33 @@ -64,7 +53,7 @@
   19.34      private final Compiler compiler;
   19.35  
   19.36      /** IR to be broken down. */
   19.37 -    private final FunctionNode functionNode;
   19.38 +    private FunctionNode outermost;
   19.39  
   19.40      /** Compile unit for the main script. */
   19.41      private final CompileUnit outermostCompileUnit;
   19.42 @@ -72,8 +61,6 @@
   19.43      /** Cache for calculated block weights. */
   19.44      private final Map<Node, Long> weightCache = new HashMap<>();
   19.45  
   19.46 -    private final LexicalContext lexicalContext = new LexicalContext();
   19.47 -
   19.48      /** Weight threshold for when to start a split. */
   19.49      public static final long SPLIT_THRESHOLD = Options.getIntProperty("nashorn.compiler.splitter.threshold", 32 * 1024);
   19.50  
   19.51 @@ -88,70 +75,92 @@
   19.52       */
   19.53      public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit outermostCompileUnit) {
   19.54          this.compiler             = compiler;
   19.55 -        this.functionNode         = functionNode;
   19.56 +        this.outermost = functionNode;
   19.57          this.outermostCompileUnit = outermostCompileUnit;
   19.58      }
   19.59  
   19.60      /**
   19.61       * Execute the split
   19.62       */
   19.63 -    void split() {
   19.64 +    FunctionNode split(final FunctionNode fn) {
   19.65 +        FunctionNode functionNode = fn;
   19.66 +
   19.67          if (functionNode.isLazy()) {
   19.68 -            LOG.finest("Postponing split of '" + functionNode.getName() + "' as it's lazy");
   19.69 -            return;
   19.70 +            LOG.finest("Postponing split of '", functionNode.getName(), "' as it's lazy");
   19.71 +            return functionNode;
   19.72          }
   19.73  
   19.74 -        LOG.finest("Initiating split of '" + functionNode.getName() + "'");
   19.75 +        LOG.finest("Initiating split of '", functionNode.getName(), "'");
   19.76 +
   19.77 +        final LexicalContext lc = getLexicalContext();
   19.78  
   19.79          long weight = WeighNodes.weigh(functionNode);
   19.80 +        final boolean top = compiler.getFunctionNode() == outermost;
   19.81  
   19.82          if (weight >= SPLIT_THRESHOLD) {
   19.83 -            LOG.finest("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
   19.84 -
   19.85 -            functionNode.accept(this);
   19.86 +            LOG.finest("Splitting '", functionNode.getName(), "' as its weight ", weight, " exceeds split threshold ", SPLIT_THRESHOLD);
   19.87 +            functionNode = (FunctionNode)functionNode.accept(this);
   19.88  
   19.89              if (functionNode.isSplit()) {
   19.90                  // Weight has changed so weigh again, this time using block weight cache
   19.91                  weight = WeighNodes.weigh(functionNode, weightCache);
   19.92 +                functionNode = functionNode.setBody(lc, functionNode.getBody().setNeedsScope(lc));
   19.93              }
   19.94  
   19.95              if (weight >= SPLIT_THRESHOLD) {
   19.96 -                weight = splitBlock(functionNode, functionNode);
   19.97 -            }
   19.98 -
   19.99 -            if (functionNode.isSplit()) {
  19.100 -                functionNode.accept(new SplitFlowAnalyzer());
  19.101 +                functionNode = functionNode.setBody(lc, splitBlock(functionNode.getBody(), functionNode));
  19.102 +                weight = WeighNodes.weigh(functionNode.getBody(), weightCache);
  19.103              }
  19.104          }
  19.105  
  19.106 -        assert functionNode.getCompileUnit() == null : "compile unit already set";
  19.107 +        assert functionNode.getCompileUnit() == null : "compile unit already set for " + functionNode.getName();
  19.108  
  19.109 -        if (compiler.getFunctionNode() == functionNode) { //functionNode.isScript()) {
  19.110 +        if (top) {
  19.111              assert outermostCompileUnit != null : "outermost compile unit is null";
  19.112 -
  19.113 -            functionNode.setCompileUnit(outermostCompileUnit);
  19.114 +            functionNode = functionNode.setCompileUnit(lc, outermostCompileUnit);
  19.115              outermostCompileUnit.addWeight(weight + WeighNodes.FUNCTION_WEIGHT);
  19.116          } else {
  19.117 -            functionNode.setCompileUnit(findUnit(weight));
  19.118 +            functionNode = functionNode.setCompileUnit(lc, findUnit(weight));
  19.119          }
  19.120  
  19.121 -        // Recursively split nested functions
  19.122 -        functionNode.accept(new NodeOperatorVisitor() {
  19.123 +        final Block body = functionNode.getBody();
  19.124 +        final List<FunctionNode> dc = directChildren(functionNode);
  19.125 +
  19.126 +        final Block newBody = (Block)body.accept(new NodeVisitor() {
  19.127 +                @Override
  19.128 +                public boolean enterFunctionNode(final FunctionNode nestedFunction) {
  19.129 +                    return dc.contains(nestedFunction);
  19.130 +                }
  19.131 +
  19.132 +                @Override
  19.133 +                public Node leaveFunctionNode(final FunctionNode nestedFunction) {
  19.134 +                    FunctionNode split = new Splitter(compiler, nestedFunction, outermostCompileUnit).split(nestedFunction);
  19.135 +                    getLexicalContext().replace(nestedFunction, split);
  19.136 +                    return split;
  19.137 +                }
  19.138 +            });
  19.139 +        functionNode = functionNode.setBody(lc, newBody);
  19.140 +
  19.141 +        assert functionNode.getCompileUnit() != null;
  19.142 +
  19.143 +        return functionNode.setState(lc, CompilationState.SPLIT);
  19.144 +    }
  19.145 +
  19.146 +    private static List<FunctionNode> directChildren(final FunctionNode functionNode) {
  19.147 +        final List<FunctionNode> dc = new ArrayList<>();
  19.148 +        functionNode.accept(new NodeVisitor() {
  19.149              @Override
  19.150 -            public Node enterFunctionNode(FunctionNode function) {
  19.151 -                if(function == functionNode) {
  19.152 -                    // Don't process outermost function (it was already processed) but descend into it to find nested
  19.153 -                    // functions.
  19.154 -                    return function;
  19.155 +            public boolean enterFunctionNode(final FunctionNode child) {
  19.156 +                if (child == functionNode) {
  19.157 +                    return true;
  19.158                  }
  19.159 -                // Process a nested function
  19.160 -                new Splitter(compiler, function, outermostCompileUnit).split();
  19.161 -                // Don't descend into a a nested function; Splitter.split() has taken care of nested-in-nested functions.
  19.162 -                return null;
  19.163 +                if (getLexicalContext().getParentFunction(child) == functionNode) {
  19.164 +                    dc.add(child);
  19.165 +                }
  19.166 +                return false;
  19.167              }
  19.168          });
  19.169 -
  19.170 -        functionNode.setState(CompilationState.SPLIT);
  19.171 +        return dc;
  19.172      }
  19.173  
  19.174      /**
  19.175 @@ -170,8 +179,8 @@
  19.176       *
  19.177       * @return new weight for the resulting block.
  19.178       */
  19.179 -    private long splitBlock(final Block block, final FunctionNode function) {
  19.180 -        functionNode.setIsSplit();
  19.181 +    private Block splitBlock(final Block block, final FunctionNode function) {
  19.182 +        getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
  19.183  
  19.184          final List<Node> splits = new ArrayList<>();
  19.185          List<Node> statements = new ArrayList<>();
  19.186 @@ -186,7 +195,6 @@
  19.187                      statements = new ArrayList<>();
  19.188                      statementsWeight = 0;
  19.189                  }
  19.190 -
  19.191              }
  19.192  
  19.193              if (statement.isTerminal()) {
  19.194 @@ -201,9 +209,7 @@
  19.195              splits.add(createBlockSplitNode(block, function, statements, statementsWeight));
  19.196          }
  19.197  
  19.198 -        block.setStatements(splits);
  19.199 -
  19.200 -        return WeighNodes.weigh(block, weightCache);
  19.201 +        return block.setStatements(getLexicalContext(), splits);
  19.202      }
  19.203  
  19.204      /**
  19.205 @@ -218,51 +224,44 @@
  19.206          final Source source = parent.getSource();
  19.207          final long   token  = parent.getToken();
  19.208          final int    finish = parent.getFinish();
  19.209 -        final String name   = function.uniqueName(SPLIT_PREFIX.tag());
  19.210 +        final String name   = function.uniqueName(SPLIT_PREFIX.symbolName());
  19.211  
  19.212 -        final Block newBlock = new Block(source, token, finish);
  19.213 -        newBlock.setFrame(new Frame(parent.getFrame()));
  19.214 -        newBlock.setStatements(statements);
  19.215 +        final Block newBlock = new Block(source, token, finish, statements);
  19.216  
  19.217 -        final SplitNode splitNode = new SplitNode(name, functionNode, newBlock);
  19.218 -
  19.219 -        splitNode.setCompileUnit(compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT));
  19.220 -
  19.221 -        return splitNode;
  19.222 +        return new SplitNode(name, newBlock, compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT));
  19.223      }
  19.224  
  19.225      @Override
  19.226 -    public Node enterBlock(final Block block) {
  19.227 +    public boolean enterBlock(final Block block) {
  19.228          if (block.isCatchBlock()) {
  19.229 -            return null;
  19.230 +            return false;
  19.231          }
  19.232 -        lexicalContext.push(block);
  19.233  
  19.234          final long weight = WeighNodes.weigh(block, weightCache);
  19.235  
  19.236          if (weight < SPLIT_THRESHOLD) {
  19.237              weightCache.put(block, weight);
  19.238 -            lexicalContext.pop(block);
  19.239 -            return null;
  19.240 +            return false;
  19.241          }
  19.242  
  19.243 -        return block;
  19.244 +        return true;
  19.245      }
  19.246  
  19.247      @Override
  19.248      public Node leaveBlock(final Block block) {
  19.249          assert !block.isCatchBlock();
  19.250  
  19.251 +        Block newBlock = block;
  19.252 +
  19.253          // Block was heavier than SLIT_THRESHOLD in enter, but a sub-block may have
  19.254          // been split already, so weigh again before splitting.
  19.255          long weight = WeighNodes.weigh(block, weightCache);
  19.256          if (weight >= SPLIT_THRESHOLD) {
  19.257 -            weight = splitBlock(block, lexicalContext.getFunction(block));
  19.258 +            newBlock = splitBlock(block, getLexicalContext().getFunction(block));
  19.259 +            weight   = WeighNodes.weigh(newBlock, weightCache);
  19.260          }
  19.261 -        weightCache.put(block, weight);
  19.262 -
  19.263 -        lexicalContext.pop(block);
  19.264 -        return block;
  19.265 +        weightCache.put(newBlock, weight);
  19.266 +        return newBlock;
  19.267      }
  19.268  
  19.269      @SuppressWarnings("rawtypes")
  19.270 @@ -274,7 +273,7 @@
  19.271              return literal;
  19.272          }
  19.273  
  19.274 -        functionNode.setIsSplit();
  19.275 +        getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
  19.276  
  19.277          if (literal instanceof ArrayLiteralNode) {
  19.278              final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal;
  19.279 @@ -312,123 +311,12 @@
  19.280      }
  19.281  
  19.282      @Override
  19.283 -    public Node enterFunctionNode(final FunctionNode node) {
  19.284 -        if(node == functionNode && !node.isLazy()) {
  19.285 -            lexicalContext.push(node);
  19.286 -            node.visitStatements(this);
  19.287 -            lexicalContext.pop(node);
  19.288 +    public boolean enterFunctionNode(final FunctionNode node) {
  19.289 +        //only go into the function node for this splitter. any subfunctions are rejected
  19.290 +        if (node == outermost && !node.isLazy()) {
  19.291 +            return true;
  19.292          }
  19.293 -        return null;
  19.294 -    }
  19.295 -
  19.296 -    static class SplitFlowAnalyzer extends NodeVisitor {
  19.297 -
  19.298 -        /** Stack of visited Split nodes, deepest node first. */
  19.299 -        private final Deque<SplitNode> splitStack;
  19.300 -
  19.301 -        /** Map of possible jump targets to containing split node */
  19.302 -        private final Map<Node,SplitNode> targetNodes = new HashMap<>();
  19.303 -
  19.304 -        SplitFlowAnalyzer() {
  19.305 -            this.splitStack = new LinkedList<>();
  19.306 -        }
  19.307 -
  19.308 -        @Override
  19.309 -        public Node enterLabelNode(final LabelNode labelNode) {
  19.310 -            registerJumpTarget(labelNode.getBreakNode());
  19.311 -            registerJumpTarget(labelNode.getContinueNode());
  19.312 -            return labelNode;
  19.313 -        }
  19.314 -
  19.315 -        @Override
  19.316 -        public Node enterWhileNode(final WhileNode whileNode) {
  19.317 -            registerJumpTarget(whileNode);
  19.318 -            return whileNode;
  19.319 -        }
  19.320 -
  19.321 -        @Override
  19.322 -        public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
  19.323 -            registerJumpTarget(doWhileNode);
  19.324 -            return doWhileNode;
  19.325 -        }
  19.326 -
  19.327 -        @Override
  19.328 -        public Node enterForNode(final ForNode forNode) {
  19.329 -            registerJumpTarget(forNode);
  19.330 -            return forNode;
  19.331 -        }
  19.332 -
  19.333 -        @Override
  19.334 -        public Node enterSwitchNode(final SwitchNode switchNode) {
  19.335 -            registerJumpTarget(switchNode);
  19.336 -            return switchNode;
  19.337 -        }
  19.338 -
  19.339 -        @Override
  19.340 -        public Node enterReturnNode(final ReturnNode returnNode) {
  19.341 -            for (final SplitNode split : splitStack) {
  19.342 -                split.setHasReturn(true);
  19.343 -            }
  19.344 -            return returnNode;
  19.345 -        }
  19.346 -
  19.347 -        @Override
  19.348 -        public Node enterContinueNode(final ContinueNode continueNode) {
  19.349 -            searchJumpTarget(continueNode.getTargetNode(), continueNode.getTargetLabel());
  19.350 -            return continueNode;
  19.351 -        }
  19.352 -
  19.353 -        @Override
  19.354 -        public Node enterBreakNode(final BreakNode breakNode) {
  19.355 -            searchJumpTarget(breakNode.getTargetNode(), breakNode.getTargetLabel());
  19.356 -            return breakNode;
  19.357 -        }
  19.358 -
  19.359 -        @Override
  19.360 -        public Node enterSplitNode(final SplitNode splitNode) {
  19.361 -            splitStack.addFirst(splitNode);
  19.362 -            return splitNode;
  19.363 -        }
  19.364 -
  19.365 -        @Override
  19.366 -        public Node leaveSplitNode(final SplitNode splitNode) {
  19.367 -            assert splitNode == splitStack.peekFirst();
  19.368 -            splitStack.removeFirst();
  19.369 -            return splitNode;
  19.370 -        }
  19.371 -
  19.372 -        /**
  19.373 -         * Register the split node containing a potential jump target.
  19.374 -         * @param targetNode a potential target node.
  19.375 -         */
  19.376 -        private void registerJumpTarget(final Node targetNode) {
  19.377 -            final SplitNode splitNode = splitStack.peekFirst();
  19.378 -            if (splitNode != null) {
  19.379 -                targetNodes.put(targetNode, splitNode);
  19.380 -            }
  19.381 -        }
  19.382 -
  19.383 -        /**
  19.384 -         * Check if a jump target is outside the current split node and its parent split nodes.
  19.385 -         * @param targetNode the jump target node.
  19.386 -         * @param targetLabel the jump target label.
  19.387 -         */
  19.388 -        private void searchJumpTarget(final Node targetNode, final Label targetLabel) {
  19.389 -
  19.390 -            final SplitNode targetSplit = targetNodes.get(targetNode);
  19.391 -            // Note that targetSplit may be null, indicating that targetNode is in top level method.
  19.392 -            // In this case we have to add the external jump target to all split nodes.
  19.393 -
  19.394 -            for (final SplitNode split : splitStack) {
  19.395 -                if (split == targetSplit) {
  19.396 -                    break;
  19.397 -                }
  19.398 -                final List<Label> externalTargets = split.getExternalTargets();
  19.399 -                if (!externalTargets.contains(targetLabel)) {
  19.400 -                    split.addExternalTarget(targetLabel);
  19.401 -                }
  19.402 -            }
  19.403 -        }
  19.404 +        return false;
  19.405      }
  19.406  }
  19.407  
    20.1 --- a/src/jdk/nashorn/internal/codegen/WeighNodes.java	Fri Apr 19 18:23:00 2013 +0530
    20.2 +++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java	Fri Apr 19 16:11:16 2013 +0200
    20.3 @@ -35,7 +35,6 @@
    20.4  import jdk.nashorn.internal.ir.CallNode;
    20.5  import jdk.nashorn.internal.ir.CatchNode;
    20.6  import jdk.nashorn.internal.ir.ContinueNode;
    20.7 -import jdk.nashorn.internal.ir.DoWhileNode;
    20.8  import jdk.nashorn.internal.ir.ExecuteNode;
    20.9  import jdk.nashorn.internal.ir.ForNode;
   20.10  import jdk.nashorn.internal.ir.FunctionNode;
   20.11 @@ -101,7 +100,7 @@
   20.12       * @param weightCache cache of already calculated block weights
   20.13       */
   20.14      private WeighNodes(FunctionNode topFunction, final Map<Node, Long> weightCache) {
   20.15 -        super(null, null);
   20.16 +        super();
   20.17          this.topFunction = topFunction;
   20.18          this.weightCache = weightCache;
   20.19      }
   20.20 @@ -123,13 +122,13 @@
   20.21      }
   20.22  
   20.23      @Override
   20.24 -    public Node enterBlock(final Block block) {
   20.25 +    public boolean enterBlock(final Block block) {
   20.26          if (weightCache != null && weightCache.containsKey(block)) {
   20.27              weight += weightCache.get(block);
   20.28 -            return null;
   20.29 +            return false;
   20.30          }
   20.31  
   20.32 -        return block;
   20.33 +        return true;
   20.34      }
   20.35  
   20.36      @Override
   20.37 @@ -157,12 +156,6 @@
   20.38      }
   20.39  
   20.40      @Override
   20.41 -    public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
   20.42 -        weight += LOOP_WEIGHT;
   20.43 -        return doWhileNode;
   20.44 -    }
   20.45 -
   20.46 -    @Override
   20.47      public Node leaveExecuteNode(final ExecuteNode executeNode) {
   20.48          return executeNode;
   20.49      }
   20.50 @@ -174,15 +167,15 @@
   20.51      }
   20.52  
   20.53      @Override
   20.54 -    public Node enterFunctionNode(final FunctionNode functionNode) {
   20.55 -        if(functionNode == topFunction) {
   20.56 +    public boolean enterFunctionNode(final FunctionNode functionNode) {
   20.57 +        if (functionNode == topFunction) {
   20.58              // the function being weighted; descend into its statements
   20.59 -            functionNode.visitStatements(this);
   20.60 -        } else {
   20.61 -            // just a reference to inner function from outer function
   20.62 -            weight += FUNC_EXPR_WEIGHT;
   20.63 +            return true;
   20.64 +//            functionNode.visitStatements(this);
   20.65          }
   20.66 -        return null;
   20.67 +        // just a reference to inner function from outer function
   20.68 +        weight += FUNC_EXPR_WEIGHT;
   20.69 +        return false;
   20.70      }
   20.71  
   20.72      @Override
   20.73 @@ -205,7 +198,7 @@
   20.74  
   20.75      @SuppressWarnings("rawtypes")
   20.76      @Override
   20.77 -    public Node enterLiteralNode(final LiteralNode literalNode) {
   20.78 +    public boolean enterLiteralNode(final LiteralNode literalNode) {
   20.79          weight += LITERAL_WEIGHT;
   20.80  
   20.81          if (literalNode instanceof ArrayLiteralNode) {
   20.82 @@ -224,10 +217,10 @@
   20.83                  }
   20.84              }
   20.85  
   20.86 -            return null;
   20.87 +            return false;
   20.88          }
   20.89  
   20.90 -        return literalNode;
   20.91 +        return true;
   20.92      }
   20.93  
   20.94      @Override
   20.95 @@ -249,9 +242,9 @@
   20.96      }
   20.97  
   20.98      @Override
   20.99 -    public Node enterSplitNode(final SplitNode splitNode) {
  20.100 +    public boolean enterSplitNode(final SplitNode splitNode) {
  20.101          weight += SPLIT_WEIGHT;
  20.102 -        return null;
  20.103 +        return false;
  20.104      }
  20.105  
  20.106      @Override
    21.1 --- a/src/jdk/nashorn/internal/ir/AccessNode.java	Fri Apr 19 18:23:00 2013 +0530
    21.2 +++ b/src/jdk/nashorn/internal/ir/AccessNode.java	Fri Apr 19 16:11:16 2013 +0200
    21.3 @@ -25,23 +25,18 @@
    21.4  
    21.5  package jdk.nashorn.internal.ir;
    21.6  
    21.7 -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
    21.8 -
    21.9 -import jdk.nashorn.internal.codegen.ObjectClassGenerator;
   21.10  import jdk.nashorn.internal.codegen.types.Type;
   21.11 +import jdk.nashorn.internal.ir.annotations.Immutable;
   21.12  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   21.13  import jdk.nashorn.internal.runtime.Source;
   21.14  
   21.15  /**
   21.16   * IR representation of a property access (period operator.)
   21.17 - *
   21.18   */
   21.19 -public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
   21.20 +@Immutable
   21.21 +public final class AccessNode extends BaseNode {
   21.22      /** Property ident. */
   21.23 -    private IdentNode property;
   21.24 -
   21.25 -    /** Does this node have a type override */
   21.26 -    private boolean hasCallSiteType;
   21.27 +    private final IdentNode property;
   21.28  
   21.29      /**
   21.30       * Constructor
   21.31 @@ -53,49 +48,13 @@
   21.32       * @param property  property
   21.33       */
   21.34      public AccessNode(final Source source, final long token, final int finish, final Node base, final IdentNode property) {
   21.35 -        super(source, token, finish, base);
   21.36 -
   21.37 -        this.start    = base.getStart();
   21.38 +        super(source, token, finish, base, false, false);
   21.39          this.property = property.setIsPropertyName();
   21.40      }
   21.41  
   21.42 -    /**
   21.43 -     * Copy constructor
   21.44 -     *
   21.45 -     * @param accessNode  source node
   21.46 -     */
   21.47 -    public AccessNode(final AccessNode accessNode) {
   21.48 -        this(accessNode, new CopyState());
   21.49 -    }
   21.50 -
   21.51 -    /**
   21.52 -     * Internal copy constructor
   21.53 -     *
   21.54 -     * @param accessNode  source node
   21.55 -     * @param cs          copy state
   21.56 -     */
   21.57 -    protected AccessNode(final AccessNode accessNode, final CopyState cs) {
   21.58 -        super(accessNode, cs);
   21.59 -        this.property = (IdentNode)cs.existingOrCopy(accessNode.getProperty());
   21.60 -    }
   21.61 -
   21.62 -    @Override
   21.63 -    protected Node copy(final CopyState cs) {
   21.64 -        return new AccessNode(this, cs);
   21.65 -    }
   21.66 -
   21.67 -    @Override
   21.68 -    public boolean equals(final Object other) {
   21.69 -        if (!super.equals(other)) {
   21.70 -            return false;
   21.71 -        }
   21.72 -        final AccessNode accessNode = (AccessNode)other;
   21.73 -        return property.equals(accessNode.getProperty());
   21.74 -    }
   21.75 -
   21.76 -    @Override
   21.77 -    public int hashCode() {
   21.78 -        return super.hashCode() ^ property.hashCode();
   21.79 +    private AccessNode(final AccessNode accessNode, final Node base, final IdentNode property, final boolean isFunction, final boolean hasCallSiteType) {
   21.80 +        super(accessNode, base, isFunction, hasCallSiteType);
   21.81 +        this.property = property;
   21.82      }
   21.83  
   21.84      /**
   21.85 @@ -104,12 +63,11 @@
   21.86       */
   21.87      @Override
   21.88      public Node accept(final NodeVisitor visitor) {
   21.89 -        if (visitor.enterAccessNode(this) != null) {
   21.90 -            base = base.accept(visitor);
   21.91 -            property = (IdentNode)property.accept(visitor);
   21.92 -            return visitor.leaveAccessNode(this);
   21.93 +        if (visitor.enterAccessNode(this)) {
   21.94 +            return visitor.leaveAccessNode(
   21.95 +                setBase(base.accept(visitor)).
   21.96 +                setProperty((IdentNode)property.accept(visitor)));
   21.97          }
   21.98 -
   21.99          return this;
  21.100      }
  21.101  
  21.102 @@ -117,7 +75,7 @@
  21.103      public void toString(final StringBuilder sb) {
  21.104          final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true);
  21.105  
  21.106 -        if (hasCallSiteType) {
  21.107 +        if (hasCallSiteType()) {
  21.108              sb.append('{');
  21.109              final String desc = getType().getDescriptor();
  21.110              sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
  21.111 @@ -147,19 +105,34 @@
  21.112          return property;
  21.113      }
  21.114  
  21.115 -    @Override
  21.116 -    public AccessNode setType(final Type type) {
  21.117 -        if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
  21.118 -            ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
  21.119 +    private AccessNode setBase(final Node base) {
  21.120 +        if (this.base == base) {
  21.121 +            return this;
  21.122          }
  21.123 -        property = property.setType(type);
  21.124 -        getSymbol().setTypeOverride(type); //always a temp so this is fine.
  21.125 -        hasCallSiteType = true;
  21.126 -        return this;
  21.127 +        return new AccessNode(this, base, property, isFunction(), hasCallSiteType());
  21.128 +    }
  21.129 +
  21.130 +
  21.131 +    private AccessNode setProperty(final IdentNode property) {
  21.132 +        if (this.property == property) {
  21.133 +            return this;
  21.134 +        }
  21.135 +        return new AccessNode(this, base, property, isFunction(), hasCallSiteType());
  21.136      }
  21.137  
  21.138      @Override
  21.139 -    public boolean canHaveCallSiteType() {
  21.140 -        return true; //carried by the symbol and always the same nodetype==symboltype
  21.141 +    public AccessNode setType(final Type type) {
  21.142 +        logTypeChange(type);
  21.143 +        getSymbol().setTypeOverride(type); //always a temp so this is fine.
  21.144 +        return new AccessNode(this, base, property.setType(type), isFunction(), hasCallSiteType());
  21.145      }
  21.146 +
  21.147 +    @Override
  21.148 +    public BaseNode setIsFunction() {
  21.149 +        if (isFunction()) {
  21.150 +            return this;
  21.151 +        }
  21.152 +        return new AccessNode(this, base, property, true, hasCallSiteType());
  21.153 +    }
  21.154 +
  21.155  }
    22.1 --- a/src/jdk/nashorn/internal/ir/BaseNode.java	Fri Apr 19 18:23:00 2013 +0530
    22.2 +++ b/src/jdk/nashorn/internal/ir/BaseNode.java	Fri Apr 19 16:11:16 2013 +0200
    22.3 @@ -25,6 +25,10 @@
    22.4  
    22.5  package jdk.nashorn.internal.ir;
    22.6  
    22.7 +import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
    22.8 +import jdk.nashorn.internal.codegen.ObjectClassGenerator;
    22.9 +import jdk.nashorn.internal.codegen.types.Type;
   22.10 +import jdk.nashorn.internal.ir.annotations.Immutable;
   22.11  import jdk.nashorn.internal.runtime.Source;
   22.12  
   22.13  /**
   22.14 @@ -33,12 +37,15 @@
   22.15   * @see AccessNode
   22.16   * @see IndexNode
   22.17   */
   22.18 -public abstract class BaseNode extends Node implements FunctionCall {
   22.19 +@Immutable
   22.20 +public abstract class BaseNode extends Node implements FunctionCall, TypeOverride<BaseNode> {
   22.21  
   22.22      /** Base Node. */
   22.23 -    protected Node base;
   22.24 +    protected final Node base;
   22.25  
   22.26 -    private boolean function;
   22.27 +    private final boolean isFunction;
   22.28 +
   22.29 +    private final boolean hasCallSiteType;
   22.30  
   22.31      /**
   22.32       * Constructor
   22.33 @@ -47,37 +54,28 @@
   22.34       * @param token  token
   22.35       * @param finish finish
   22.36       * @param base   base node
   22.37 +     * @param isFunction is this a function
   22.38 +     * @param hasCallSiteType does this access have a callsite type
   22.39       */
   22.40 -    public BaseNode(final Source source, final long token, final int finish, final Node base) {
   22.41 -        super(source, token, finish);
   22.42 -        this.base = base;
   22.43 -        setStart(base.getStart());
   22.44 +    public BaseNode(final Source source, final long token, final int finish, final Node base, final boolean isFunction, final boolean hasCallSiteType) {
   22.45 +        super(source, token, base.getStart(), finish);
   22.46 +        this.base            = base;
   22.47 +        this.isFunction      = isFunction;
   22.48 +        this.hasCallSiteType = hasCallSiteType;
   22.49      }
   22.50  
   22.51      /**
   22.52 -     * Copy constructor
   22.53 -     *
   22.54 -     * @param baseNode the base node
   22.55 -     * @param cs       a copy state
   22.56 +     * Copy constructor for immutable nodes
   22.57 +     * @param baseNode node to inherit from
   22.58 +     * @param base base
   22.59 +     * @param isFunction is this a function
   22.60 +     * @param hasCallSiteType does this access have a callsite type
   22.61       */
   22.62 -    protected BaseNode(final BaseNode baseNode, final CopyState cs) {
   22.63 +    protected BaseNode(final BaseNode baseNode, final Node base, final boolean isFunction, final boolean hasCallSiteType) {
   22.64          super(baseNode);
   22.65 -        this.base = cs.existingOrCopy(baseNode.getBase());
   22.66 -        setStart(base.getStart());
   22.67 -    }
   22.68 -
   22.69 -    @Override
   22.70 -    public boolean equals(final Object other) {
   22.71 -        if (!super.equals(other)) {
   22.72 -            return false;
   22.73 -        }
   22.74 -        final BaseNode baseNode = (BaseNode)other;
   22.75 -        return base.equals(baseNode.getBase());
   22.76 -    }
   22.77 -
   22.78 -    @Override
   22.79 -    public int hashCode() {
   22.80 -        return base.hashCode();
   22.81 +        this.base            = base;
   22.82 +        this.isFunction      = isFunction;
   22.83 +        this.hasCallSiteType = hasCallSiteType;
   22.84      }
   22.85  
   22.86      /**
   22.87 @@ -88,25 +86,37 @@
   22.88          return base;
   22.89      }
   22.90  
   22.91 -    /**
   22.92 -     * Reset the base node for this access
   22.93 -     * @param base new base node
   22.94 -     */
   22.95 -    public void setBase(final Node base) {
   22.96 -        this.base = base;
   22.97 -    }
   22.98 -
   22.99      @Override
  22.100      public boolean isFunction() {
  22.101 -        return function;
  22.102 +        return isFunction;
  22.103      }
  22.104  
  22.105      /**
  22.106       * Mark this node as being the callee operand of a {@link CallNode}.
  22.107       * @return a base node identical to this one in all aspects except with its function flag set.
  22.108       */
  22.109 -    public BaseNode setIsFunction() {
  22.110 -        function = true;
  22.111 -        return this;
  22.112 +    public abstract BaseNode setIsFunction();
  22.113 +
  22.114 +    @Override
  22.115 +    public boolean canHaveCallSiteType() {
  22.116 +        return true; //carried by the symbol and always the same nodetype==symboltype
  22.117 +    }
  22.118 +
  22.119 +    /**
  22.120 +     * Does the access have a call site type override?
  22.121 +     * @return true if overridden
  22.122 +     */
  22.123 +    protected boolean hasCallSiteType() {
  22.124 +        return hasCallSiteType;
  22.125 +    }
  22.126 +
  22.127 +    /**
  22.128 +     * Debug type change
  22.129 +     * @param type new type
  22.130 +     */
  22.131 +    protected final void logTypeChange(final Type type) {
  22.132 +        if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
  22.133 +            ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType());
  22.134 +        }
  22.135      }
  22.136  }
    23.1 --- a/src/jdk/nashorn/internal/ir/BinaryNode.java	Fri Apr 19 18:23:00 2013 +0530
    23.2 +++ b/src/jdk/nashorn/internal/ir/BinaryNode.java	Fri Apr 19 16:11:16 2013 +0200
    23.3 @@ -26,6 +26,7 @@
    23.4  package jdk.nashorn.internal.ir;
    23.5  
    23.6  import jdk.nashorn.internal.codegen.types.Type;
    23.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    23.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    23.9  import jdk.nashorn.internal.parser.TokenType;
   23.10  import jdk.nashorn.internal.runtime.Source;
   23.11 @@ -33,9 +34,12 @@
   23.12  /**
   23.13   * BinaryNode nodes represent two operand operations.
   23.14   */
   23.15 -public class BinaryNode extends UnaryNode {
   23.16 +@Immutable
   23.17 +public final class BinaryNode extends Node implements Assignment<Node> {
   23.18      /** Left hand side argument. */
   23.19 -    private Node lhs;
   23.20 +    private final Node lhs;
   23.21 +
   23.22 +    private final Node rhs;
   23.23  
   23.24      /**
   23.25       * Constructor
   23.26 @@ -46,28 +50,15 @@
   23.27       * @param rhs    right hand side
   23.28       */
   23.29      public BinaryNode(final Source source, final long token, final Node lhs, final Node rhs) {
   23.30 -        super(source, token, rhs);
   23.31 -
   23.32 -        start  = lhs.getStart();
   23.33 -        finish = rhs.getFinish();
   23.34 -
   23.35 -        this.lhs = lhs;
   23.36 +        super(source, token, lhs.getStart(), rhs.getFinish());
   23.37 +        this.lhs   = lhs;
   23.38 +        this.rhs   = rhs;
   23.39      }
   23.40  
   23.41 -    /**
   23.42 -     * Copy constructor
   23.43 -     *
   23.44 -     * @param binaryNode the binary node
   23.45 -     * @param cs         copy state
   23.46 -     */
   23.47 -    protected BinaryNode(final BinaryNode binaryNode, final CopyState cs) {
   23.48 -        super(binaryNode, cs);
   23.49 -        lhs = cs.existingOrCopy(binaryNode.lhs);
   23.50 -    }
   23.51 -
   23.52 -    @Override
   23.53 -    protected Node copy(final CopyState cs) {
   23.54 -        return new BinaryNode(this, cs);
   23.55 +    private BinaryNode(final BinaryNode binaryNode, final Node lhs, final Node rhs) {
   23.56 +        super(binaryNode);
   23.57 +        this.lhs = lhs;
   23.58 +        this.rhs = rhs;
   23.59      }
   23.60  
   23.61      /**
   23.62 @@ -149,28 +140,14 @@
   23.63          return rhs();
   23.64      }
   23.65  
   23.66 -    @Override
   23.67 -    public boolean equals(final Object other) {
   23.68 -        if (!super.equals(other)) {
   23.69 -            return false;
   23.70 -        }
   23.71 -        return lhs.equals(((BinaryNode)other).lhs());
   23.72 -    }
   23.73 -
   23.74 -    @Override
   23.75 -    public int hashCode() {
   23.76 -        return super.hashCode() ^ lhs().hashCode();
   23.77 -    }
   23.78 -
   23.79      /**
   23.80       * Assist in IR navigation.
   23.81       * @param visitor IR navigating visitor.
   23.82       */
   23.83      @Override
   23.84      public Node accept(final NodeVisitor visitor) {
   23.85 -        if (visitor.enterBinaryNode(this) != null) {
   23.86 -            // TODO: good cause for a separate visitMembers: we could delegate to UnaryNode.visitMembers
   23.87 -            return visitor.leaveBinaryNode((BinaryNode)setLHS(lhs.accept(visitor)).setRHS(rhs().accept(visitor)));
   23.88 +        if (visitor.enterBinaryNode(this)) {
   23.89 +            return visitor.leaveBinaryNode(setLHS(lhs.accept(visitor)).setRHS(rhs.accept(visitor)));
   23.90          }
   23.91  
   23.92          return this;
   23.93 @@ -231,14 +208,35 @@
   23.94      }
   23.95  
   23.96      /**
   23.97 +     * Get the right hand side expression for this node
   23.98 +     * @return the left hand side expression
   23.99 +     */
  23.100 +    public Node rhs() {
  23.101 +        return rhs;
  23.102 +    }
  23.103 +
  23.104 +    /**
  23.105       * Set the left hand side expression for this node
  23.106       * @param lhs new left hand side expression
  23.107       * @return a node equivalent to this one except for the requested change.
  23.108       */
  23.109      public BinaryNode setLHS(final Node lhs) {
  23.110 -        if(this.lhs == lhs) return this;
  23.111 -        final BinaryNode n = (BinaryNode)clone();
  23.112 -        n.lhs = lhs;
  23.113 -        return n;
  23.114 +        if (this.lhs == lhs) {
  23.115 +            return this;
  23.116 +        }
  23.117 +        return new BinaryNode(this, lhs, rhs);
  23.118      }
  23.119 +
  23.120 +    /**
  23.121 +     * Set the right hand side expression for this node
  23.122 +     * @param rhs new left hand side expression
  23.123 +     * @return a node equivalent to this one except for the requested change.
  23.124 +     */
  23.125 +    public BinaryNode setRHS(final Node rhs) {
  23.126 +        if (this.rhs == rhs) {
  23.127 +            return this;
  23.128 +        }
  23.129 +        return new BinaryNode(this, lhs, rhs);
  23.130 +    }
  23.131 +
  23.132  }
    24.1 --- a/src/jdk/nashorn/internal/ir/Block.java	Fri Apr 19 18:23:00 2013 +0530
    24.2 +++ b/src/jdk/nashorn/internal/ir/Block.java	Fri Apr 19 16:11:16 2013 +0200
    24.3 @@ -27,14 +27,15 @@
    24.4  
    24.5  import java.io.PrintWriter;
    24.6  import java.util.ArrayList;
    24.7 +import java.util.Arrays;
    24.8  import java.util.Collections;
    24.9  import java.util.Comparator;
   24.10 -import java.util.HashMap;
   24.11  import java.util.Iterator;
   24.12 +import java.util.LinkedHashMap;
   24.13  import java.util.List;
   24.14 -import java.util.ListIterator;
   24.15 -import jdk.nashorn.internal.codegen.Frame;
   24.16 +import java.util.Map;
   24.17  import jdk.nashorn.internal.codegen.Label;
   24.18 +import jdk.nashorn.internal.ir.annotations.Immutable;
   24.19  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   24.20  import jdk.nashorn.internal.runtime.Source;
   24.21  
   24.22 @@ -42,97 +43,79 @@
   24.23   * IR representation for a list of statements and functions. All provides the
   24.24   * basis for script body.
   24.25   */
   24.26 -public class Block extends Node {
   24.27 +@Immutable
   24.28 +public class Block extends BreakableNode implements Flags<Block> {
   24.29      /** List of statements */
   24.30 -    protected List<Node> statements;
   24.31 +    protected final List<Node> statements;
   24.32  
   24.33 -    /** Symbol table. */
   24.34 -    protected final HashMap<String, Symbol> symbols;
   24.35 -
   24.36 -    /** Variable frame. */
   24.37 -    protected Frame frame;
   24.38 +    /** Symbol table - keys must be returned in the order they were put in. */
   24.39 +    protected final Map<String, Symbol> symbols;
   24.40  
   24.41      /** Entry label. */
   24.42      protected final Label entryLabel;
   24.43  
   24.44 -    /** Break label. */
   24.45 -    protected final Label breakLabel;
   24.46 +    /** Does the block/function need a new scope? */
   24.47 +    protected final int flags;
   24.48  
   24.49 -    /** Does the block/function need a new scope? */
   24.50 -    protected boolean needsScope;
   24.51 +    /** Flag indicating that this block needs scope */
   24.52 +    public static final int NEEDS_SCOPE = 1 << 0;
   24.53 +
   24.54 +    /**
   24.55 +     * Flag indicating whether this block needs
   24.56 +     * self symbol assignment at the start. This is used only for
   24.57 +     * blocks that are the bodies of function nodes who refer to themselves
   24.58 +     * by name. It causes codegen to insert a var [fn_name] = __callee__
   24.59 +     * at the start of the body
   24.60 +     */
   24.61 +    public static final int NEEDS_SELF_SYMBOL = 1 << 1;
   24.62 +
   24.63 +    /**
   24.64 +     * Is this block tagged as terminal based on its contents
   24.65 +     * (usually the last statement)
   24.66 +     */
   24.67 +    public static final int IS_TERMINAL = 1 << 2;
   24.68  
   24.69      /**
   24.70       * Constructor
   24.71       *
   24.72 -     * @param source   source code
   24.73 -     * @param token    token
   24.74 -     * @param finish   finish
   24.75 +     * @param source     source code
   24.76 +     * @param token      token
   24.77 +     * @param finish     finish
   24.78 +     * @param statements statements
   24.79       */
   24.80 -    public Block(final Source source, final long token, final int finish) {
   24.81 -        super(source, token, finish);
   24.82 +    public Block(final Source source, final long token, final int finish, final Node... statements) {
   24.83 +        super(source, token, finish, new Label("block_break"));
   24.84  
   24.85 -        this.statements = new ArrayList<>();
   24.86 -        this.symbols    = new HashMap<>();
   24.87 +        this.statements = Arrays.asList(statements);
   24.88 +        this.symbols    = new LinkedHashMap<>();
   24.89          this.entryLabel = new Label("block_entry");
   24.90 -        this.breakLabel = new Label("block_break");
   24.91 +        this.flags     =  0;
   24.92      }
   24.93  
   24.94      /**
   24.95 -     * Internal copy constructor
   24.96 +     * Constructor
   24.97       *
   24.98 -     * @param block the source block
   24.99 -     * @param cs    the copy state
  24.100 +     * @param source     source code
  24.101 +     * @param token      token
  24.102 +     * @param finish     finish
  24.103 +     * @param statements statements
  24.104       */
  24.105 -    protected Block(final Block block, final CopyState cs) {
  24.106 +    public Block(final Source source, final long token, final int finish, final List<Node> statements) {
  24.107 +        this(source, token, finish, statements.toArray(new Node[statements.size()]));
  24.108 +    }
  24.109 +
  24.110 +    private Block(final Block block, final int finish, final List<Node> statements, final int flags) {
  24.111          super(block);
  24.112 -
  24.113 -        this.statements = new ArrayList<>();
  24.114 -        for (final Node statement : block.getStatements()) {
  24.115 -            statements.add(cs.existingOrCopy(statement));
  24.116 -        }
  24.117 -        this.symbols    = new HashMap<>();
  24.118 -        this.frame      = block.frame == null ? null : block.frame.copy();
  24.119 +        this.statements = statements;
  24.120 +        this.flags      = flags;
  24.121 +        this.symbols    = block.symbols; //todo - symbols have no dependencies on any IR node and can as far as we understand it be shallow copied now
  24.122          this.entryLabel = new Label(block.entryLabel);
  24.123 -        this.breakLabel = new Label(block.breakLabel);
  24.124 -
  24.125 -        assert block.symbols.isEmpty() : "must not clone with symbols";
  24.126 +        this.finish = finish;
  24.127      }
  24.128  
  24.129      @Override
  24.130 -    protected Node copy(final CopyState cs) {
  24.131 -        return new Block(this, cs);
  24.132 -    }
  24.133 -
  24.134 -    /**
  24.135 -     * Add a new statement to the statement list.
  24.136 -     *
  24.137 -     * @param statement Statement node to add.
  24.138 -     */
  24.139 -    public void addStatement(final Node statement) {
  24.140 -        if (statement != null) {
  24.141 -            statements.add(statement);
  24.142 -            if (getFinish() < statement.getFinish()) {
  24.143 -                setFinish(statement.getFinish());
  24.144 -            }
  24.145 -        }
  24.146 -    }
  24.147 -
  24.148 -    /**
  24.149 -     * Prepend statements to the statement list
  24.150 -     *
  24.151 -     * @param prepended statement to add
  24.152 -     */
  24.153 -    public void prependStatements(final List<Node> prepended) {
  24.154 -        statements.addAll(0, prepended);
  24.155 -    }
  24.156 -
  24.157 -    /**
  24.158 -     * Add a list of statements to the statement list.
  24.159 -     *
  24.160 -     * @param statementList Statement nodes to add.
  24.161 -     */
  24.162 -    public void addStatements(final List<Node> statementList) {
  24.163 -        statements.addAll(statementList);
  24.164 +    public Node ensureUniqueLabels(final LexicalContext lc) {
  24.165 +        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags));
  24.166      }
  24.167  
  24.168      /**
  24.169 @@ -142,19 +125,9 @@
  24.170       * @return new or same node
  24.171       */
  24.172      @Override
  24.173 -    public Node accept(final NodeVisitor visitor) {
  24.174 -        final Block saveBlock = visitor.getCurrentBlock();
  24.175 -        visitor.setCurrentBlock(this);
  24.176 -
  24.177 -        try {
  24.178 -            // Ignore parent to avoid recursion.
  24.179 -
  24.180 -            if (visitor.enterBlock(this) != null) {
  24.181 -                visitStatements(visitor);
  24.182 -                return visitor.leaveBlock(this);
  24.183 -            }
  24.184 -        } finally {
  24.185 -            visitor.setCurrentBlock(saveBlock);
  24.186 +    public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
  24.187 +        if (visitor.enterBlock(this)) {
  24.188 +            return visitor.leaveBlock(setStatements(lc, Node.accept(visitor, Node.class, statements)));
  24.189          }
  24.190  
  24.191          return this;
  24.192 @@ -222,11 +195,18 @@
  24.193      }
  24.194  
  24.195      /**
  24.196 -     * Get the break label for this block
  24.197 -     * @return the break label
  24.198 +     * Tag block as terminal or non terminal
  24.199 +     * @param lc          lexical context
  24.200 +     * @param isTerminal is block terminal
  24.201 +     * @return same block, or new if flag changed
  24.202       */
  24.203 -    public Label getBreakLabel() {
  24.204 -        return breakLabel;
  24.205 +    public Block setIsTerminal(final LexicalContext lc, final boolean isTerminal) {
  24.206 +        return isTerminal ? setFlag(lc, IS_TERMINAL) : clearFlag(lc, IS_TERMINAL);
  24.207 +    }
  24.208 +
  24.209 +    @Override
  24.210 +    public boolean isTerminal() {
  24.211 +        return getFlag(IS_TERMINAL);
  24.212      }
  24.213  
  24.214      /**
  24.215 @@ -238,23 +218,6 @@
  24.216      }
  24.217  
  24.218      /**
  24.219 -     * Get the frame for this block
  24.220 -     * @return the frame
  24.221 -     */
  24.222 -    public Frame getFrame() {
  24.223 -        return frame;
  24.224 -    }
  24.225 -
  24.226 -    /**
  24.227 -     * Reset the frame for this block
  24.228 -     *
  24.229 -     * @param frame  the new frame
  24.230 -     */
  24.231 -    public void setFrame(final Frame frame) {
  24.232 -        this.frame = frame;
  24.233 -    }
  24.234 -
  24.235 -    /**
  24.236       * Get the list of statements in this block
  24.237       *
  24.238       * @return a list of statements
  24.239 @@ -264,21 +227,21 @@
  24.240      }
  24.241  
  24.242      /**
  24.243 -     * Applies the specified visitor to all statements in the block.
  24.244 -     * @param visitor the visitor.
  24.245 -     */
  24.246 -    public void visitStatements(NodeVisitor visitor) {
  24.247 -        for (ListIterator<Node> stmts = statements.listIterator(); stmts.hasNext();) {
  24.248 -            stmts.set(stmts.next().accept(visitor));
  24.249 -        }
  24.250 -    }
  24.251 -    /**
  24.252       * Reset the statement list for this block
  24.253       *
  24.254 -     * @param statements  new statement list
  24.255 +     * @param lc lexical context
  24.256 +     * @param statements new statement list
  24.257 +     * @return new block if statements changed, identity of statements == block.statements
  24.258       */
  24.259 -    public void setStatements(final List<Node> statements) {
  24.260 -        this.statements = statements;
  24.261 +    public Block setStatements(final LexicalContext lc, final List<Node> statements) {
  24.262 +        if (this.statements == statements) {
  24.263 +            return this;
  24.264 +        }
  24.265 +        int lastFinish = 0;
  24.266 +        if (!statements.isEmpty()) {
  24.267 +            lastFinish = statements.get(statements.size() - 1).getFinish();
  24.268 +        }
  24.269 +        return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags));
  24.270      }
  24.271  
  24.272      /**
  24.273 @@ -297,39 +260,65 @@
  24.274       * @return true if this function needs a scope
  24.275       */
  24.276      public boolean needsScope() {
  24.277 -        return needsScope;
  24.278 +        return (flags & NEEDS_SCOPE) == NEEDS_SCOPE;
  24.279 +    }
  24.280 +
  24.281 +    @Override
  24.282 +    public Block setFlags(final LexicalContext lc, int flags) {
  24.283 +        if (this.flags == flags) {
  24.284 +            return this;
  24.285 +        }
  24.286 +        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags));
  24.287 +    }
  24.288 +
  24.289 +    @Override
  24.290 +    public Block clearFlag(final LexicalContext lc, int flag) {
  24.291 +        return setFlags(lc, flags & ~flag);
  24.292 +    }
  24.293 +
  24.294 +    @Override
  24.295 +    public Block setFlag(final LexicalContext lc, int flag) {
  24.296 +        return setFlags(lc, flags | flag);
  24.297 +    }
  24.298 +
  24.299 +    @Override
  24.300 +    public boolean getFlag(final int flag) {
  24.301 +        return (flags & flag) == flag;
  24.302      }
  24.303  
  24.304      /**
  24.305       * Set the needs scope flag.
  24.306 +     * @param lc lexicalContext
  24.307 +     * @return new block if state changed, otherwise this
  24.308       */
  24.309 -    public void setNeedsScope() {
  24.310 -        needsScope = true;
  24.311 +    public Block setNeedsScope(final LexicalContext lc) {
  24.312 +        if (needsScope()) {
  24.313 +            return this;
  24.314 +        }
  24.315 +
  24.316 +        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE));
  24.317      }
  24.318  
  24.319      /**
  24.320 -     * Marks this block as using a specified scoped symbol. The block and its parent blocks up to but not
  24.321 -     * including the block defining the symbol will be marked as needing parent scope. The block defining the symbol
  24.322 -     * will be marked as one that needs to have its own scope.
  24.323 -     * @param symbol the symbol being used.
  24.324 -     * @param ancestors the iterator over block's containing lexical context
  24.325 +     * Computationally determine the next slot for this block,
  24.326 +     * indexed from 0. Use this as a relative base when computing
  24.327 +     * frames
  24.328 +     * @return next slot
  24.329       */
  24.330 -    public void setUsesScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) {
  24.331 -        if(symbol.getBlock() == this) {
  24.332 -            setNeedsScope();
  24.333 -        } else {
  24.334 -            setUsesParentScopeSymbol(symbol, ancestors);
  24.335 +    public int nextSlot() {
  24.336 +        final Iterator<Symbol> iter = symbolIterator();
  24.337 +        int next = 0;
  24.338 +        while (iter.hasNext()) {
  24.339 +        final Symbol symbol = iter.next();
  24.340 +        if (symbol.hasSlot()) {
  24.341 +            next += symbol.slotCount();
  24.342          }
  24.343 +        }
  24.344 +        return next;
  24.345      }
  24.346  
  24.347 -    /**
  24.348 -     * Invoked when this block uses a scope symbol defined in one of its ancestors.
  24.349 -     * @param symbol the scope symbol being used
  24.350 -     * @param ancestors iterator over ancestor blocks
  24.351 -     */
  24.352 -    void setUsesParentScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) {
  24.353 -        if(ancestors.hasNext()) {
  24.354 -            ancestors.next().setUsesScopeSymbol(symbol, ancestors);
  24.355 -        }
  24.356 +    @Override
  24.357 +    protected boolean isBreakableWithoutLabel() {
  24.358 +        return false;
  24.359      }
  24.360  }
    25.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.2 +++ b/src/jdk/nashorn/internal/ir/BlockLexicalContext.java	Fri Apr 19 16:11:16 2013 +0200
    25.3 @@ -0,0 +1,120 @@
    25.4 +/*
    25.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    25.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    25.7 + *
    25.8 + * This code is free software; you can redistribute it and/or modify it
    25.9 + * under the terms of the GNU General Public License version 2 only, as
   25.10 + * published by the Free Software Foundation.  Oracle designates this
   25.11 + * particular file as subject to the "Classpath" exception as provided
   25.12 + * by Oracle in the LICENSE file that accompanied this code.
   25.13 + *
   25.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   25.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   25.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   25.17 + * version 2 for more details (a copy is included in the LICENSE file that
   25.18 + * accompanied this code).
   25.19 + *
   25.20 + * You should have received a copy of the GNU General Public License version
   25.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   25.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   25.23 + *
   25.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   25.25 + * or visit www.oracle.com if you need additional information or have any
   25.26 + * questions.
   25.27 + */
   25.28 +
   25.29 +package jdk.nashorn.internal.ir;
   25.30 +
   25.31 +import java.util.ArrayDeque;
   25.32 +import java.util.ArrayList;
   25.33 +import java.util.Deque;
   25.34 +import java.util.List;
   25.35 +import java.util.ListIterator;
   25.36 +
   25.37 +/**
   25.38 + * This is a subclass of lexical context used for filling
   25.39 + * blocks (and function nodes) with statements. When popping
   25.40 + * a block from the lexical context, any statements that have
   25.41 + * been generated in it are commited to the block. This saves
   25.42 + * unnecessary object mutations and lexical context replacement
   25.43 + */
   25.44 +public class BlockLexicalContext extends LexicalContext {
   25.45 +    /** statement stack, each block on the lexical context maintains one of these, which is
   25.46 +     *  committed to the block on pop */
   25.47 +    private Deque<List<Node>> sstack = new ArrayDeque<>();
   25.48 +
   25.49 +    /** Last non debug statement emitted in this context */
   25.50 +    protected Node lastStatement;
   25.51 +
   25.52 +    @Override
   25.53 +    public <T extends LexicalContextNode> T push(final T node) {
   25.54 +        T pushed = super.push(node);
   25.55 +        if (node instanceof Block) {
   25.56 +            sstack.push(new ArrayList<Node>());
   25.57 +        }
   25.58 +        return pushed;
   25.59 +    }
   25.60 +
   25.61 +    /**
   25.62 +     * Get the statement list from the stack, possibly filtered
   25.63 +     * @return statement list
   25.64 +     */
   25.65 +    protected List<Node> popStatements() {
   25.66 +        return sstack.pop();
   25.67 +    }
   25.68 +
   25.69 +    @Override
   25.70 +    @SuppressWarnings("unchecked")
   25.71 +    public <T extends LexicalContextNode> T pop(final T node) {
   25.72 +        T expected = node;
   25.73 +        if (node instanceof Block) {
   25.74 +            final List<Node> newStatements = popStatements();
   25.75 +            expected = (T)((Block)node).setStatements(this, newStatements);
   25.76 +            if (!sstack.isEmpty()) {
   25.77 +                lastStatement = lastStatement(sstack.peek());
   25.78 +            }
   25.79 +        }
   25.80 +        return super.pop(expected);
   25.81 +    }
   25.82 +
   25.83 +    /**
   25.84 +     * Append a statement to the block being generated
   25.85 +     * @param statement statement to add
   25.86 +     */
   25.87 +    public void appendStatement(final Node statement) {
   25.88 +        assert statement != null;
   25.89 +        sstack.peek().add(statement);
   25.90 +        if (!statement.isDebug()) {
   25.91 +            lastStatement = statement;
   25.92 +        }
   25.93 +    }
   25.94 +
   25.95 +    /**
   25.96 +     * Prepend a statement to the block being generated
   25.97 +     * @param statement statement to prepend
   25.98 +     * @return the prepended statement
   25.99 +     */
  25.100 +    public Node prependStatement(final Node statement) {
  25.101 +        assert statement != null;
  25.102 +        sstack.peek().add(0, statement);
  25.103 +        return statement;
  25.104 +    }
  25.105 +
  25.106 +    /**
  25.107 +     * Get the last (non debug) statement that was emitted into a block
  25.108 +     * @return the last statement emitted
  25.109 +     */
  25.110 +    public Node getLastStatement() {
  25.111 +        return lastStatement;
  25.112 +    }
  25.113 +
  25.114 +    private static Node lastStatement(final List<Node> statements) {
  25.115 +        for (final ListIterator<Node> iter = statements.listIterator(statements.size()); iter.hasPrevious(); ) {
  25.116 +            final Node node = iter.previous();
  25.117 +            if (!node.isDebug()) {
  25.118 +                return node;
  25.119 +            }
  25.120 +        }
  25.121 +        return null;
  25.122 +    }
  25.123 +}
    26.1 --- a/src/jdk/nashorn/internal/ir/BreakNode.java	Fri Apr 19 18:23:00 2013 +0530
    26.2 +++ b/src/jdk/nashorn/internal/ir/BreakNode.java	Fri Apr 19 16:11:16 2013 +0200
    26.3 @@ -25,37 +25,34 @@
    26.4  
    26.5  package jdk.nashorn.internal.ir;
    26.6  
    26.7 -import jdk.nashorn.internal.codegen.Label;
    26.8 +import jdk.nashorn.internal.ir.annotations.Immutable;
    26.9  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   26.10  import jdk.nashorn.internal.runtime.Source;
   26.11  
   26.12  /**
   26.13   * IR representation for {@code break} statements.
   26.14   */
   26.15 -public class BreakNode extends LabeledNode {
   26.16 +@Immutable
   26.17 +public final class BreakNode extends Node {
   26.18  
   26.19 -     /**
   26.20 +    private final IdentNode label;
   26.21 +
   26.22 +    /**
   26.23       * Constructor
   26.24       *
   26.25 -     * @param source     source code
   26.26 -     * @param token      token
   26.27 -     * @param finish     finish
   26.28 -     * @param labelNode  break label
   26.29 -     * @param targetNode node to break to
   26.30 -     * @param tryChain   surrounding try chain
   26.31 +     * @param source source code
   26.32 +     * @param token  token
   26.33 +     * @param finish finish
   26.34 +     * @param label  label for break or null if none
   26.35       */
   26.36 -    public BreakNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) {
   26.37 -        super(source, token, finish, labelNode, targetNode, tryChain);
   26.38 -        setHasGoto();
   26.39 -    }
   26.40 -
   26.41 -    private BreakNode(final BreakNode breakNode, final CopyState cs) {
   26.42 -        super(breakNode, cs);
   26.43 +    public BreakNode(final Source source, final long token, final int finish, final IdentNode label) {
   26.44 +        super(source, token, finish);
   26.45 +        this.label = label;
   26.46      }
   26.47  
   26.48      @Override
   26.49 -    protected Node copy(final CopyState cs) {
   26.50 -        return new BreakNode(this, cs);
   26.51 +    public boolean hasGoto() {
   26.52 +        return true;
   26.53      }
   26.54  
   26.55      /**
   26.56 @@ -64,7 +61,7 @@
   26.57       */
   26.58      @Override
   26.59      public Node accept(final NodeVisitor visitor) {
   26.60 -        if (visitor.enterBreakNode(this) != null) {
   26.61 +        if (visitor.enterBreakNode(this)) {
   26.62              return visitor.leaveBreakNode(this);
   26.63          }
   26.64  
   26.65 @@ -72,26 +69,20 @@
   26.66      }
   26.67  
   26.68      /**
   26.69 -     * Return the target label of this break node.
   26.70 -     * @return the target label.
   26.71 +     * Get the label for this break node
   26.72 +     * @return label, or null if none
   26.73       */
   26.74 -    public Label getTargetLabel() {
   26.75 -        if (targetNode instanceof BreakableNode) {
   26.76 -            return ((BreakableNode)targetNode).getBreakLabel();
   26.77 -        } else if (targetNode instanceof Block) {
   26.78 -            return ((Block)targetNode).getBreakLabel();
   26.79 -        }
   26.80 -
   26.81 -        throw new AssertionError("Invalid break target " + targetNode.getClass());
   26.82 +    public IdentNode getLabel() {
   26.83 +        return label;
   26.84      }
   26.85  
   26.86      @Override
   26.87      public void toString(final StringBuilder sb) {
   26.88          sb.append("break");
   26.89  
   26.90 -        if (labelNode != null) {
   26.91 +        if (label != null) {
   26.92              sb.append(' ');
   26.93 -            labelNode.getLabel().toString(sb);
   26.94 +            label.toString(sb);
   26.95          }
   26.96      }
   26.97  }
    27.1 --- a/src/jdk/nashorn/internal/ir/BreakableNode.java	Fri Apr 19 18:23:00 2013 +0530
    27.2 +++ b/src/jdk/nashorn/internal/ir/BreakableNode.java	Fri Apr 19 16:11:16 2013 +0200
    27.3 @@ -25,27 +25,34 @@
    27.4  
    27.5  package jdk.nashorn.internal.ir;
    27.6  
    27.7 +import java.util.Arrays;
    27.8 +import java.util.List;
    27.9 +
   27.10  import jdk.nashorn.internal.codegen.Label;
   27.11 +import jdk.nashorn.internal.ir.annotations.Immutable;
   27.12  import jdk.nashorn.internal.runtime.Source;
   27.13  
   27.14  /**
   27.15   * This class represents a node from which control flow can execute
   27.16   * a {@code break} statement
   27.17   */
   27.18 -public abstract class BreakableNode extends Node {
   27.19 +@Immutable
   27.20 +public abstract class BreakableNode extends LexicalContextNode {
   27.21  
   27.22      /** break label. */
   27.23 -    protected Label breakLabel;
   27.24 +    protected final Label breakLabel;
   27.25  
   27.26      /**
   27.27       * Constructor
   27.28       *
   27.29 -     * @param source   source code
   27.30 -     * @param token    token
   27.31 -     * @param finish   finish
   27.32 +     * @param source     source code
   27.33 +     * @param token      token
   27.34 +     * @param finish     finish
   27.35 +     * @param breakLabel break label
   27.36       */
   27.37 -    public BreakableNode(final Source source, final long token, final int finish) {
   27.38 +    protected BreakableNode(final Source source, final long token, final int finish, final Label breakLabel) {
   27.39          super(source, token, finish);
   27.40 +        this.breakLabel = breakLabel;
   27.41      }
   27.42  
   27.43      /**
   27.44 @@ -55,6 +62,19 @@
   27.45       */
   27.46      protected BreakableNode(final BreakableNode breakableNode) {
   27.47          super(breakableNode);
   27.48 +        this.breakLabel = new Label(breakableNode.getBreakLabel());
   27.49 +    }
   27.50 +
   27.51 +    @Override
   27.52 +    public abstract Node ensureUniqueLabels(final LexicalContext lc);
   27.53 +
   27.54 +    /**
   27.55 +     * Check whether this can be broken out from without using a label,
   27.56 +     * e.g. everything but Blocks, basically
   27.57 +     * @return true if breakable without label
   27.58 +     */
   27.59 +    protected boolean isBreakableWithoutLabel() {
   27.60 +        return true;
   27.61      }
   27.62  
   27.63      /**
   27.64 @@ -65,4 +85,14 @@
   27.65          return breakLabel;
   27.66      }
   27.67  
   27.68 +    /**
   27.69 +     * Return the labels associated with this node. Breakable nodes that
   27.70 +     * aren't LoopNodes only have a break label -> the location immediately
   27.71 +     * afterwards the node in code
   27.72 +     * @return list of labels representing locations around this node
   27.73 +     */
   27.74 +    public List<Label> getLabels() {
   27.75 +        return Arrays.asList(breakLabel);
   27.76 +    }
   27.77 +
   27.78  }
    28.1 --- a/src/jdk/nashorn/internal/ir/CallNode.java	Fri Apr 19 18:23:00 2013 +0530
    28.2 +++ b/src/jdk/nashorn/internal/ir/CallNode.java	Fri Apr 19 16:11:16 2013 +0200
    28.3 @@ -25,49 +25,51 @@
    28.4  
    28.5  package jdk.nashorn.internal.ir;
    28.6  
    28.7 -import java.util.ArrayList;
    28.8  import java.util.Collections;
    28.9  import java.util.List;
   28.10  import jdk.nashorn.internal.codegen.types.Type;
   28.11  import jdk.nashorn.internal.ir.annotations.Ignore;
   28.12 +import jdk.nashorn.internal.ir.annotations.Immutable;
   28.13  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   28.14  import jdk.nashorn.internal.runtime.Source;
   28.15  
   28.16  /**
   28.17   * IR representation for a function call.
   28.18 - *
   28.19   */
   28.20 -public class CallNode extends Node implements TypeOverride<CallNode> {
   28.21 +@Immutable
   28.22 +public final class CallNode extends LexicalContextNode implements TypeOverride<CallNode> {
   28.23  
   28.24 -    private Type type;
   28.25 +    private final Type type;
   28.26  
   28.27      /** Function identifier or function body. */
   28.28 -    private Node function;
   28.29 +    private final Node function;
   28.30  
   28.31      /** Call arguments. */
   28.32 -    private List<Node> args;
   28.33 +    private final List<Node> args;
   28.34  
   28.35 -    /** flag - is new expression */
   28.36 -    private boolean isNew;
   28.37 +    /** Is this a "new" operation */
   28.38 +    public static final int IS_NEW        = 0x1;
   28.39  
   28.40 -    /** flag - is in with block */
   28.41 -    private boolean inWithBlock;
   28.42 +    /** Is this call tagged as inside a with block */
   28.43 +    public static final int IN_WITH_BLOCK = 0x2;
   28.44 +
   28.45 +    private final int flags;
   28.46  
   28.47      /**
   28.48       * Arguments to be passed to builtin {@code eval} function
   28.49       */
   28.50      public static class EvalArgs {
   28.51          /** evaluated code */
   28.52 -        private Node code;
   28.53 +        private final Node code;
   28.54  
   28.55          /** 'this' passed to evaluated code */
   28.56 -        private IdentNode evalThis;
   28.57 +        private final IdentNode evalThis;
   28.58  
   28.59          /** location string for the eval call */
   28.60 -        final private String location;
   28.61 +        private final String location;
   28.62  
   28.63          /** is this call from a strict context? */
   28.64 -        final private boolean strictMode;
   28.65 +        private final boolean strictMode;
   28.66  
   28.67          /**
   28.68           * Constructor
   28.69 @@ -92,12 +94,11 @@
   28.70              return code;
   28.71          }
   28.72  
   28.73 -        /**
   28.74 -         * Set the code that is to be eval.ed by this eval function
   28.75 -         * @param code the code as an AST node
   28.76 -         */
   28.77 -        public void setCode(final Node code) {
   28.78 -            this.code = code;
   28.79 +        private EvalArgs setCode(final Node code) {
   28.80 +            if (this.code == code) {
   28.81 +                return this;
   28.82 +            }
   28.83 +            return new EvalArgs(code, evalThis, location, strictMode);
   28.84          }
   28.85  
   28.86          /**
   28.87 @@ -108,12 +109,11 @@
   28.88              return this.evalThis;
   28.89          }
   28.90  
   28.91 -        /**
   28.92 -         * Set the {@code this} symbol used to invoke this eval call
   28.93 -         * @param evalThis the {@code this} symbol
   28.94 -         */
   28.95 -        public void setThis(final IdentNode evalThis) {
   28.96 -            this.evalThis = evalThis;
   28.97 +        private EvalArgs setThis(final IdentNode evalThis) {
   28.98 +            if (this.evalThis == evalThis) {
   28.99 +                return this;
  28.100 +            }
  28.101 +            return new EvalArgs(code, evalThis, location, strictMode);
  28.102          }
  28.103  
  28.104          /**
  28.105 @@ -135,7 +135,7 @@
  28.106  
  28.107      /** arguments for 'eval' call. Non-null only if this call node is 'eval' */
  28.108      @Ignore
  28.109 -    private EvalArgs evalArgs;
  28.110 +    private final EvalArgs evalArgs;
  28.111  
  28.112      /**
  28.113       * Constructors
  28.114 @@ -145,32 +145,27 @@
  28.115       * @param finish   finish
  28.116       * @param function the function to call
  28.117       * @param args     args to the call
  28.118 +     * @param flags    flags
  28.119       */
  28.120 -    public CallNode(final Source source, final long token, final int finish, final Node function, final List<Node> args) {
  28.121 +    public CallNode(final Source source, final long token, final int finish, final Node function, final List<Node> args, final int flags) {
  28.122          super(source, token, finish);
  28.123  
  28.124 -        setStart(function.getStart());
  28.125 -
  28.126 -        this.function     = function;
  28.127 -        this.args         = args;
  28.128 +        this.function = function;
  28.129 +        this.args     = args;
  28.130 +        this.flags    = flags;
  28.131 +        this.type     = null;
  28.132 +        this.evalArgs = null;
  28.133      }
  28.134  
  28.135 -    private CallNode(final CallNode callNode, final CopyState cs) {
  28.136 +    private CallNode(final CallNode callNode, final Node function, final List<Node> args, final int flags, final Type type, final EvalArgs evalArgs) {
  28.137          super(callNode);
  28.138 -
  28.139 -        final List<Node> newArgs = new ArrayList<>();
  28.140 -
  28.141 -        for (final Node arg : callNode.args) {
  28.142 -            newArgs.add(cs.existingOrCopy(arg));
  28.143 -        }
  28.144 -
  28.145 -        this.function     = cs.existingOrCopy(callNode.function);     //TODO existing or same?
  28.146 -        this.args         = newArgs;
  28.147 -        this.isNew        = callNode.isNew;
  28.148 -        this.inWithBlock  = callNode.inWithBlock;
  28.149 +        this.function = function;
  28.150 +        this.args = args;
  28.151 +        this.flags = flags;
  28.152 +        this.type = type;
  28.153 +        this.evalArgs = evalArgs;
  28.154      }
  28.155  
  28.156 -
  28.157      @Override
  28.158      public Type getType() {
  28.159          if (hasCallSiteType()) {
  28.160 @@ -181,8 +176,10 @@
  28.161  
  28.162      @Override
  28.163      public CallNode setType(final Type type) {
  28.164 -        this.type = type;
  28.165 -        return this;
  28.166 +        if (this.type == type) {
  28.167 +            return this;
  28.168 +        }
  28.169 +        return new CallNode(this, function, args, flags, type, evalArgs);
  28.170      }
  28.171  
  28.172      private boolean hasCallSiteType() {
  28.173 @@ -194,11 +191,6 @@
  28.174          return true;
  28.175      }
  28.176  
  28.177 -    @Override
  28.178 -    protected Node copy(final CopyState cs) {
  28.179 -        return new CallNode(this, cs);
  28.180 -    }
  28.181 -
  28.182      /**
  28.183       * Assist in IR navigation.
  28.184       *
  28.185 @@ -207,15 +199,22 @@
  28.186       * @return node or replacement
  28.187       */
  28.188      @Override
  28.189 -    public Node accept(final NodeVisitor visitor) {
  28.190 -        if (visitor.enterCallNode(this) != null) {
  28.191 -            function = function.accept(visitor);
  28.192 -
  28.193 -            for (int i = 0, count = args.size(); i < count; i++) {
  28.194 -                args.set(i, args.get(i).accept(visitor));
  28.195 +    public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
  28.196 +        if (visitor.enterCallNode(this)) {
  28.197 +            final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
  28.198 +                    setFunction(function.accept(visitor)).
  28.199 +                    setArgs(Node.accept(visitor, Node.class, args)).
  28.200 +                    setFlags(flags).
  28.201 +                    setType(type).
  28.202 +                    setEvalArgs(evalArgs == null ?
  28.203 +                            null :
  28.204 +                            evalArgs.setCode(evalArgs.getCode().accept(visitor)).
  28.205 +                                setThis((IdentNode)evalArgs.getThis().accept(visitor))));
  28.206 +            // Theoretically, we'd need to instead pass lc to every setter and do a replacement on each. In practice,
  28.207 +            // setType from TypeOverride can't accept a lc, and we don't necessarily want to go there now.
  28.208 +            if(this != newCallNode) {
  28.209 +                return Node.replaceInLexicalContext(lc, this, newCallNode);
  28.210              }
  28.211 -
  28.212 -            return visitor.leaveCallNode(this);
  28.213          }
  28.214  
  28.215          return this;
  28.216 @@ -226,7 +225,7 @@
  28.217          if (hasCallSiteType()) {
  28.218              sb.append('{');
  28.219              final String desc = getType().getDescriptor();
  28.220 -            sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
  28.221 +            sb.append(desc.charAt(desc.length() - 1) == ';' ? 'O' : getType().getDescriptor());
  28.222              sb.append('}');
  28.223          }
  28.224  
  28.225 @@ -261,8 +260,11 @@
  28.226       * Reset the arguments for the call
  28.227       * @param args new arguments list
  28.228       */
  28.229 -    public void setArgs(final List<Node> args) {
  28.230 -        this.args = args;
  28.231 +    private CallNode setArgs(final List<Node> args) {
  28.232 +        if (this.args == args) {
  28.233 +            return this;
  28.234 +        }
  28.235 +        return new CallNode(this, function, args, flags, type, evalArgs);
  28.236      }
  28.237  
  28.238      /**
  28.239 @@ -278,9 +280,13 @@
  28.240       * {@code eval}
  28.241       *
  28.242       * @param evalArgs eval args
  28.243 +     * @return same node or new one on state change
  28.244       */
  28.245 -    public void setEvalArgs(final EvalArgs evalArgs) {
  28.246 -        this.evalArgs = evalArgs;
  28.247 +    public CallNode setEvalArgs(final EvalArgs evalArgs) {
  28.248 +        if (this.evalArgs == evalArgs) {
  28.249 +            return this;
  28.250 +        }
  28.251 +        return new CallNode(this, function, args, flags, type, evalArgs);
  28.252      }
  28.253  
  28.254      /**
  28.255 @@ -301,10 +307,14 @@
  28.256  
  28.257      /**
  28.258       * Reset the function expression that this call invokes
  28.259 -     * @param node the function
  28.260 +     * @param function the function
  28.261 +     * @return same node or new one on state change
  28.262       */
  28.263 -    public void setFunction(final Node node) {
  28.264 -        function = node;
  28.265 +    public CallNode setFunction(final Node function) {
  28.266 +        if (this.function == function) {
  28.267 +            return this;
  28.268 +        }
  28.269 +        return new CallNode(this, function, args, flags, type, evalArgs);
  28.270      }
  28.271  
  28.272      /**
  28.273 @@ -312,14 +322,15 @@
  28.274       * @return true if this a new operation
  28.275       */
  28.276      public boolean isNew() {
  28.277 -        return isNew;
  28.278 +        return (flags & IS_NEW) == IS_NEW;
  28.279      }
  28.280  
  28.281      /**
  28.282       * Flag this call as a new operation
  28.283 +     * @return same node or new one on state change
  28.284       */
  28.285 -    public void setIsNew() {
  28.286 -        this.isNew = true;
  28.287 +    public CallNode setIsNew() {
  28.288 +        return setFlags(IS_NEW);
  28.289      }
  28.290  
  28.291      /**
  28.292 @@ -327,13 +338,13 @@
  28.293       * @return true if the call is inside a {@code with} block
  28.294       */
  28.295      public boolean inWithBlock() {
  28.296 -        return inWithBlock;
  28.297 +        return (flags & IN_WITH_BLOCK) == IN_WITH_BLOCK;
  28.298      }
  28.299  
  28.300 -    /**
  28.301 -     * Flag this call to be inside a {@code with} block
  28.302 -     */
  28.303 -    public void setInWithBlock() {
  28.304 -        this.inWithBlock = true;
  28.305 +    private CallNode setFlags(final int flags) {
  28.306 +        if (this.flags == flags) {
  28.307 +            return this;
  28.308 +        }
  28.309 +        return new CallNode(this, function, args, flags, type, evalArgs);
  28.310      }
  28.311  }
    29.1 --- a/src/jdk/nashorn/internal/ir/CaseNode.java	Fri Apr 19 18:23:00 2013 +0530
    29.2 +++ b/src/jdk/nashorn/internal/ir/CaseNode.java	Fri Apr 19 16:11:16 2013 +0200
    29.3 @@ -26,19 +26,21 @@
    29.4  package jdk.nashorn.internal.ir;
    29.5  
    29.6  import jdk.nashorn.internal.codegen.Label;
    29.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    29.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    29.9  import jdk.nashorn.internal.runtime.Source;
   29.10  
   29.11  /**
   29.12   * IR representation of CASE clause.
   29.13 - *
   29.14 + * Case nodes are not BreakableNodes, but the SwitchNode is
   29.15   */
   29.16 -public class CaseNode extends BreakableNode {
   29.17 +@Immutable
   29.18 +public final class CaseNode extends Node {
   29.19      /** Test expression. */
   29.20 -    private Node test;
   29.21 +    private final Node test;
   29.22  
   29.23      /** Statements. */
   29.24 -    private Block body;
   29.25 +    private final Block body;
   29.26  
   29.27      /** Case entry label. */
   29.28      private final Label entry;
   29.29 @@ -60,17 +62,17 @@
   29.30          this.entry = new Label("entry");
   29.31      }
   29.32  
   29.33 -    private CaseNode(final CaseNode caseNode, final CopyState cs) {
   29.34 +    CaseNode(final CaseNode caseNode, final Node test, final Block body) {
   29.35          super(caseNode);
   29.36  
   29.37 -        this.test  = cs.existingOrCopy(caseNode.test);
   29.38 -        this.body  = (Block)cs.existingOrCopy(caseNode.body);
   29.39 +        this.test  = test;
   29.40 +        this.body  = body;
   29.41          this.entry = new Label(caseNode.entry);
   29.42      }
   29.43  
   29.44      @Override
   29.45 -    protected Node copy(final CopyState cs) {
   29.46 -        return new CaseNode(this, cs);
   29.47 +    public boolean isTerminal() {
   29.48 +        return body.isTerminal();
   29.49      }
   29.50  
   29.51      /**
   29.52 @@ -79,15 +81,11 @@
   29.53       */
   29.54      @Override
   29.55      public Node accept(final NodeVisitor visitor) {
   29.56 -        if (visitor.enterCaseNode(this) != null) {
   29.57 -            if (test != null) {
   29.58 -                test = test.accept(visitor);
   29.59 -            }
   29.60 -            if (body != null) {
   29.61 -                body = (Block)body.accept(visitor);
   29.62 -            }
   29.63 +        if (visitor.enterCaseNode(this)) {
   29.64 +            final Node  newTest = test == null ? null : test.accept(visitor);
   29.65 +            final Block newBody = body == null ? null : (Block)body.accept(visitor);
   29.66  
   29.67 -            return visitor.leaveCaseNode(this);
   29.68 +            return visitor.leaveCaseNode(setTest(newTest).setBody(newBody));
   29.69          }
   29.70  
   29.71          return this;
   29.72 @@ -131,8 +129,19 @@
   29.73      /**
   29.74       * Reset the test expression for this case node
   29.75       * @param test new test expression
   29.76 +     * @return new or same CaseNode
   29.77       */
   29.78 -    public void setTest(final Node test) {
   29.79 -        this.test = test;
   29.80 +    public CaseNode setTest(final Node test) {
   29.81 +        if (this.test == test) {
   29.82 +            return this;
   29.83 +        }
   29.84 +        return new CaseNode(this, test, body);
   29.85 +    }
   29.86 +
   29.87 +    private CaseNode setBody(final Block body) {
   29.88 +        if (this.body == body) {
   29.89 +            return this;
   29.90 +        }
   29.91 +        return new CaseNode(this, test, body);
   29.92      }
   29.93  }
    30.1 --- a/src/jdk/nashorn/internal/ir/CatchNode.java	Fri Apr 19 18:23:00 2013 +0530
    30.2 +++ b/src/jdk/nashorn/internal/ir/CatchNode.java	Fri Apr 19 16:11:16 2013 +0200
    30.3 @@ -25,26 +25,23 @@
    30.4  
    30.5  package jdk.nashorn.internal.ir;
    30.6  
    30.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    30.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    30.9  import jdk.nashorn.internal.runtime.Source;
   30.10  
   30.11  /**
   30.12   * IR representation of a catch clause.
   30.13 - *
   30.14   */
   30.15 -public class CatchNode extends Node {
   30.16 +@Immutable
   30.17 +public final class CatchNode extends Node {
   30.18      /** Exception identifier. */
   30.19 -    private IdentNode exception;
   30.20 +    private final IdentNode exception;
   30.21  
   30.22      /** Exception condition. */
   30.23 -    private Node exceptionCondition;
   30.24 +    private final Node exceptionCondition;
   30.25  
   30.26      /** Catch body. */
   30.27 -    private Block body;
   30.28 -
   30.29 -    /** Is rethrow - e.g. synthetic catch block for e.g. finallies, the parser case where
   30.30 -     * there has to be at least on catch for syntactic validity */
   30.31 -    private boolean isSyntheticRethrow;
   30.32 +    private final Block body;
   30.33  
   30.34      /**
   30.35       * Constructors
   30.36 @@ -64,18 +61,12 @@
   30.37          this.body               = body;
   30.38      }
   30.39  
   30.40 -    private CatchNode(final CatchNode catchNode, final CopyState cs) {
   30.41 +    private CatchNode(final CatchNode catchNode, final IdentNode exception, final Node exceptionCondition, final Block body) {
   30.42          super(catchNode);
   30.43  
   30.44 -        this.exception          = (IdentNode)cs.existingOrCopy(catchNode.exception);
   30.45 -        this.exceptionCondition = cs.existingOrCopy(catchNode.exceptionCondition);
   30.46 -        this.body               = (Block)cs.existingOrCopy(catchNode.body);
   30.47 -        this.isSyntheticRethrow = catchNode.isSyntheticRethrow;
   30.48 -     }
   30.49 -
   30.50 -    @Override
   30.51 -    protected Node copy(final CopyState cs) {
   30.52 -        return new CatchNode(this, cs);
   30.53 +        this.exception          = exception;
   30.54 +        this.exceptionCondition = exceptionCondition;
   30.55 +        this.body               = body;
   30.56      }
   30.57  
   30.58      /**
   30.59 @@ -84,21 +75,22 @@
   30.60       */
   30.61      @Override
   30.62      public Node accept(final NodeVisitor visitor) {
   30.63 -        if (visitor.enterCatchNode(this) != null) {
   30.64 -            exception = (IdentNode)exception.accept(visitor);
   30.65 -
   30.66 -            if (exceptionCondition != null) {
   30.67 -                exceptionCondition = exceptionCondition.accept(visitor);
   30.68 -            }
   30.69 -
   30.70 -            body = (Block)body.accept(visitor);
   30.71 -            return visitor.leaveCatchNode(this);
   30.72 +        if (visitor.enterCatchNode(this)) {
   30.73 +            return visitor.leaveCatchNode(
   30.74 +                setException((IdentNode)exception.accept(visitor)).
   30.75 +                setExceptionCondition(exceptionCondition == null ? null : exceptionCondition.accept(visitor)).
   30.76 +                setBody((Block)body.accept(visitor)));
   30.77          }
   30.78  
   30.79          return this;
   30.80      }
   30.81  
   30.82      @Override
   30.83 +    public boolean isTerminal() {
   30.84 +        return body.isTerminal();
   30.85 +    }
   30.86 +
   30.87 +    @Override
   30.88      public void toString(final StringBuilder sb) {
   30.89          sb.append(" catch (");
   30.90          exception.toString(sb);
   30.91 @@ -111,23 +103,6 @@
   30.92      }
   30.93  
   30.94      /**
   30.95 -     * Check if this catch is a synthetic rethrow
   30.96 -     * @return true if this is a synthetic rethrow
   30.97 -     */
   30.98 -    public boolean isSyntheticRethrow() {
   30.99 -        return isSyntheticRethrow;
  30.100 -    }
  30.101 -
  30.102 -    /**
  30.103 -     * Flag this as deliberatly generated catch all that rethrows the
  30.104 -     * caught exception. This is used for example for generating finally
  30.105 -     * expressions
  30.106 -     */
  30.107 -    public void setIsSyntheticRethrow() {
  30.108 -        this.isSyntheticRethrow = true;
  30.109 -    }
  30.110 -
  30.111 -    /**
  30.112       * Get the identifier representing the exception thrown
  30.113       * @return the exception identifier
  30.114       */
  30.115 @@ -146,9 +121,13 @@
  30.116      /**
  30.117       * Reset the exception condition for this catch block
  30.118       * @param exceptionCondition the new exception condition
  30.119 +     * @return new or same CatchNode
  30.120       */
  30.121 -    public void setExceptionCondition(final Node exceptionCondition) {
  30.122 -        this.exceptionCondition = exceptionCondition;
  30.123 +    public CatchNode setExceptionCondition(final Node exceptionCondition) {
  30.124 +        if (this.exceptionCondition == exceptionCondition) {
  30.125 +            return this;
  30.126 +        }
  30.127 +        return new CatchNode(this, exception, exceptionCondition, body);
  30.128      }
  30.129  
  30.130      /**
  30.131 @@ -158,4 +137,18 @@
  30.132      public Block getBody() {
  30.133          return body;
  30.134      }
  30.135 +
  30.136 +    private CatchNode setException(final IdentNode exception) {
  30.137 +        if (this.exception == exception) {
  30.138 +            return this;
  30.139 +        }
  30.140 +        return new CatchNode(this, exception, exceptionCondition, body);
  30.141 +    }
  30.142 +
  30.143 +    private CatchNode setBody(final Block body) {
  30.144 +        if (this.body == body) {
  30.145 +            return this;
  30.146 +        }
  30.147 +        return new CatchNode(this, exception, exceptionCondition, body);
  30.148 +    }
  30.149  }
    31.1 --- a/src/jdk/nashorn/internal/ir/ContinueNode.java	Fri Apr 19 18:23:00 2013 +0530
    31.2 +++ b/src/jdk/nashorn/internal/ir/ContinueNode.java	Fri Apr 19 16:11:16 2013 +0200
    31.3 @@ -25,43 +25,39 @@
    31.4  
    31.5  package jdk.nashorn.internal.ir;
    31.6  
    31.7 -import jdk.nashorn.internal.codegen.Label;
    31.8 +import jdk.nashorn.internal.ir.annotations.Immutable;
    31.9  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   31.10  import jdk.nashorn.internal.runtime.Source;
   31.11  
   31.12  /**
   31.13   * IR representation for CONTINUE statements.
   31.14 - *
   31.15   */
   31.16 -public class ContinueNode extends LabeledNode {
   31.17 +@Immutable
   31.18 +public class ContinueNode extends Node {
   31.19 +
   31.20 +    private IdentNode label;
   31.21  
   31.22      /**
   31.23       * Constructor
   31.24       *
   31.25 -     * @param source     the source
   31.26 -     * @param token      token
   31.27 -     * @param finish     finish
   31.28 -     * @param labelNode  the continue label
   31.29 -     * @param targetNode node to continue to
   31.30 -     * @param tryChain   surrounding try chain
   31.31 +     * @param source source code
   31.32 +     * @param token  token
   31.33 +     * @param finish finish
   31.34 +     * @param label  label for break or null if none
   31.35       */
   31.36 -    public ContinueNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) {
   31.37 -        super(source, token, finish, labelNode, targetNode, tryChain);
   31.38 -        setHasGoto();
   31.39 -    }
   31.40 -
   31.41 -    private ContinueNode(final ContinueNode continueNode, final CopyState cs) {
   31.42 -        super(continueNode, cs);
   31.43 +    public ContinueNode(final Source source, final long token, final int finish, final IdentNode label) {
   31.44 +        super(source, token, finish);
   31.45 +        this.label = label;
   31.46      }
   31.47  
   31.48      @Override
   31.49 -    protected Node copy(final CopyState cs) {
   31.50 -        return new ContinueNode(this, cs);
   31.51 +    public boolean hasGoto() {
   31.52 +        return true;
   31.53      }
   31.54  
   31.55      @Override
   31.56      public Node accept(final NodeVisitor visitor) {
   31.57 -        if (visitor.enterContinueNode(this) != null) {
   31.58 +        if (visitor.enterContinueNode(this)) {
   31.59              return visitor.leaveContinueNode(this);
   31.60          }
   31.61  
   31.62 @@ -69,21 +65,20 @@
   31.63      }
   31.64  
   31.65      /**
   31.66 -     * Return the target label of this continue node.
   31.67 -     * @return the target label.
   31.68 +     * Get the label for this break node
   31.69 +     * @return label, or null if none
   31.70       */
   31.71 -    public Label getTargetLabel() {
   31.72 -        assert targetNode instanceof WhileNode : "continue target must be a while node";
   31.73 -        return ((WhileNode)targetNode).getContinueLabel();
   31.74 +    public IdentNode getLabel() {
   31.75 +        return label;
   31.76      }
   31.77  
   31.78      @Override
   31.79      public void toString(final StringBuilder sb) {
   31.80          sb.append("continue");
   31.81  
   31.82 -        if (labelNode != null) {
   31.83 +        if (label != null) {
   31.84              sb.append(' ');
   31.85 -            labelNode.getLabel().toString(sb);
   31.86 +            label.toString(sb);
   31.87          }
   31.88      }
   31.89  }
    32.1 --- a/src/jdk/nashorn/internal/ir/DoWhileNode.java	Fri Apr 19 18:23:00 2013 +0530
    32.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    32.3 @@ -1,82 +0,0 @@
    32.4 -/*
    32.5 - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    32.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    32.7 - *
    32.8 - * This code is free software; you can redistribute it and/or modify it
    32.9 - * under the terms of the GNU General Public License version 2 only, as
   32.10 - * published by the Free Software Foundation.  Oracle designates this
   32.11 - * particular file as subject to the "Classpath" exception as provided
   32.12 - * by Oracle in the LICENSE file that accompanied this code.
   32.13 - *
   32.14 - * This code is distributed in the hope that it will be useful, but WITHOUT
   32.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   32.16 - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   32.17 - * version 2 for more details (a copy is included in the LICENSE file that
   32.18 - * accompanied this code).
   32.19 - *
   32.20 - * You should have received a copy of the GNU General Public License version
   32.21 - * 2 along with this work; if not, write to the Free Software Foundation,
   32.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   32.23 - *
   32.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   32.25 - * or visit www.oracle.com if you need additional information or have any
   32.26 - * questions.
   32.27 - */
   32.28 -
   32.29 -package jdk.nashorn.internal.ir;
   32.30 -
   32.31 -import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   32.32 -import jdk.nashorn.internal.runtime.Source;
   32.33 -
   32.34 -/**
   32.35 - * Loop representing do while loops. This is mostly split from WhileNode
   32.36 - * because of the different order of the Phi Traversals
   32.37 - *
   32.38 - */
   32.39 -public class DoWhileNode extends WhileNode {
   32.40 -
   32.41 -    /**
   32.42 -     * Constructor
   32.43 -     *
   32.44 -     * @param source     the source
   32.45 -     * @param token      token
   32.46 -     * @param finish     finish
   32.47 -     */
   32.48 -    public DoWhileNode(final Source source, final long token, final int finish) {
   32.49 -        super(source, token, finish);
   32.50 -    }
   32.51 -
   32.52 -    /**
   32.53 -     * Copy constructor
   32.54 -     *
   32.55 -     * @param doWhileNode source node
   32.56 -     * @param cs          copy state
   32.57 -     */
   32.58 -    protected DoWhileNode(final DoWhileNode doWhileNode, final CopyState cs) {
   32.59 -        super(doWhileNode, cs);
   32.60 -    }
   32.61 -
   32.62 -    @Override
   32.63 -    protected Node copy(final CopyState cs) {
   32.64 -        return new DoWhileNode(this, cs);
   32.65 -    }
   32.66 -
   32.67 -    @Override
   32.68 -    public Node accept(final NodeVisitor visitor) {
   32.69 -        if (visitor.enterDoWhileNode(this) != null) {
   32.70 -            body = (Block)body.accept(visitor);
   32.71 -            test = test.accept(visitor);
   32.72 -
   32.73 -            return visitor.leaveDoWhileNode(this);
   32.74 -        }
   32.75 -
   32.76 -        return this;
   32.77 -    }
   32.78 -
   32.79 -    @Override
   32.80 -    public void toString(final StringBuilder sb) {
   32.81 -        sb.append("while (");
   32.82 -        test.toString(sb);
   32.83 -        sb.append(')');
   32.84 -    }
   32.85 -}
    33.1 --- a/src/jdk/nashorn/internal/ir/EmptyNode.java	Fri Apr 19 18:23:00 2013 +0530
    33.2 +++ b/src/jdk/nashorn/internal/ir/EmptyNode.java	Fri Apr 19 16:11:16 2013 +0200
    33.3 @@ -25,14 +25,15 @@
    33.4  
    33.5  package jdk.nashorn.internal.ir;
    33.6  
    33.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    33.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    33.9  import jdk.nashorn.internal.runtime.Source;
   33.10  
   33.11  /**
   33.12   * IR representation for an empty statement.
   33.13 - *
   33.14   */
   33.15 -public class EmptyNode extends Node {
   33.16 +@Immutable
   33.17 +public final class EmptyNode extends Node {
   33.18  
   33.19      /**
   33.20       * Constructor
   33.21 @@ -57,7 +58,7 @@
   33.22  
   33.23      @Override
   33.24      public Node accept(final NodeVisitor visitor) {
   33.25 -        if (visitor.enterEmptyNode(this) != null) {
   33.26 +        if (visitor.enterEmptyNode(this)) {
   33.27              return visitor.leaveEmptyNode(this);
   33.28          }
   33.29          return this;
    34.1 --- a/src/jdk/nashorn/internal/ir/ExecuteNode.java	Fri Apr 19 18:23:00 2013 +0530
    34.2 +++ b/src/jdk/nashorn/internal/ir/ExecuteNode.java	Fri Apr 19 16:11:16 2013 +0200
    34.3 @@ -25,6 +25,7 @@
    34.4  
    34.5  package jdk.nashorn.internal.ir;
    34.6  
    34.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    34.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    34.9  import jdk.nashorn.internal.runtime.Source;
   34.10  
   34.11 @@ -33,9 +34,10 @@
   34.12   * node means "this code will be executed" and evaluating it results in
   34.13   * statements being added to the IR
   34.14   */
   34.15 -public class ExecuteNode extends Node {
   34.16 +@Immutable
   34.17 +public final class ExecuteNode extends Node {
   34.18      /** Expression to execute. */
   34.19 -    private Node expression;
   34.20 +    private final Node expression;
   34.21  
   34.22      /**
   34.23       * Constructor
   34.24 @@ -50,6 +52,11 @@
   34.25          this.expression = expression;
   34.26      }
   34.27  
   34.28 +    private ExecuteNode(final ExecuteNode executeNode, final Node expression) {
   34.29 +        super(executeNode);
   34.30 +        this.expression = expression;
   34.31 +    }
   34.32 +
   34.33      /**
   34.34       * Constructor
   34.35       *
   34.36 @@ -60,34 +67,15 @@
   34.37          this.expression = expression;
   34.38      }
   34.39  
   34.40 -    private ExecuteNode(final ExecuteNode executeNode, final CopyState cs) {
   34.41 -        super(executeNode);
   34.42 -        this.expression = cs.existingOrCopy(executeNode.expression);
   34.43 -    }
   34.44 -
   34.45      @Override
   34.46 -    protected Node copy(final CopyState cs) {
   34.47 -        return new ExecuteNode(this, cs);
   34.48 -    }
   34.49 -
   34.50 -    @Override
   34.51 -    public boolean equals(final Object other) {
   34.52 -        if (!super.equals(other)) {
   34.53 -            return false;
   34.54 -        }
   34.55 -        return expression.equals(((ExecuteNode)other).getExpression());
   34.56 -    }
   34.57 -
   34.58 -    @Override
   34.59 -    public int hashCode() {
   34.60 -        return super.hashCode() ^ expression.hashCode();
   34.61 +    public boolean isTerminal() {
   34.62 +        return expression.isTerminal();
   34.63      }
   34.64  
   34.65      @Override
   34.66      public Node accept(final NodeVisitor visitor) {
   34.67 -        if (visitor.enterExecuteNode(this) != null) {
   34.68 -            setExpression(expression.accept(visitor));
   34.69 -            return visitor.leaveExecuteNode(this);
   34.70 +        if (visitor.enterExecuteNode(this)) {
   34.71 +            return visitor.leaveExecuteNode(setExpression(expression.accept(visitor)));
   34.72          }
   34.73  
   34.74          return this;
   34.75 @@ -109,8 +97,12 @@
   34.76      /**
   34.77       * Reset the expression to be executed
   34.78       * @param expression the expression
   34.79 +     * @return new or same execute node
   34.80       */
   34.81 -    public void setExpression(final Node expression) {
   34.82 -        this.expression = expression;
   34.83 +    public ExecuteNode setExpression(final Node expression) {
   34.84 +        if (this.expression == expression) {
   34.85 +            return this;
   34.86 +        }
   34.87 +        return new ExecuteNode(this, expression);
   34.88      }
   34.89  }
    35.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    35.2 +++ b/src/jdk/nashorn/internal/ir/Flags.java	Fri Apr 19 16:11:16 2013 +0200
    35.3 @@ -0,0 +1,69 @@
    35.4 +/*
    35.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    35.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    35.7 + *
    35.8 + * This code is free software; you can redistribute it and/or modify it
    35.9 + * under the terms of the GNU General Public License version 2 only, as
   35.10 + * published by the Free Software Foundation.  Oracle designates this
   35.11 + * particular file as subject to the "Classpath" exception as provided
   35.12 + * by Oracle in the LICENSE file that accompanied this code.
   35.13 + *
   35.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   35.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   35.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   35.17 + * version 2 for more details (a copy is included in the LICENSE file that
   35.18 + * accompanied this code).
   35.19 + *
   35.20 + * You should have received a copy of the GNU General Public License version
   35.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   35.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   35.23 + *
   35.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   35.25 + * or visit www.oracle.com if you need additional information or have any
   35.26 + * questions.
   35.27 + */
   35.28 +
   35.29 +package jdk.nashorn.internal.ir;
   35.30 +
   35.31 +/**
   35.32 + * Interface implemented by all nodes that have flags in
   35.33 + * a lexical context. This is needed as we sometimes have to save
   35.34 + * the setting of flags in the lexical context until a block
   35.35 + * is completely finished and its final version (after multiple
   35.36 + * copy on writes) is placed in the lexical context
   35.37 + *
   35.38 + * @param <T> lexical context node that can have flags set during code generation
   35.39 + */
   35.40 +public interface Flags<T extends LexicalContextNode> {
   35.41 +
   35.42 +    /**
   35.43 +     * Check if a flag is set in a lexical context node
   35.44 +     * @param flag flag to check
   35.45 +     * @return flags
   35.46 +     */
   35.47 +    public boolean getFlag(int flag);
   35.48 +
   35.49 +    /**
   35.50 +     * Clear a flag of a LexicalContextNode
   35.51 +     * @param lc lexical context
   35.52 +     * @param flag flag to clear
   35.53 +     * @return the new LexicalContext node if flags were changed, same otherwise
   35.54 +     */
   35.55 +    public T clearFlag(final LexicalContext lc, int flag);
   35.56 +
   35.57 +    /**
   35.58 +     * Set a flag of a LexicalContextNode
   35.59 +     * @param lc lexical context
   35.60 +     * @param flag flag to set
   35.61 +     * @return the new LexicalContext node if flags were changed, same otherwise
   35.62 +     */
   35.63 +    public T setFlag(final LexicalContext lc, int flag);
   35.64 +
   35.65 +    /**
   35.66 +     * Set all flags of a LexicalContextNode, overwriting previous flags
   35.67 +     * @param lc lexical context
   35.68 +     * @param flags new flags value
   35.69 +     * @return the new LexicalContext node if flags were changed, same otherwise
   35.70 +     */
   35.71 +    public T setFlags(final LexicalContext lc, int flags);
   35.72 +}
    36.1 --- a/src/jdk/nashorn/internal/ir/ForNode.java	Fri Apr 19 18:23:00 2013 +0530
    36.2 +++ b/src/jdk/nashorn/internal/ir/ForNode.java	Fri Apr 19 16:11:16 2013 +0200
    36.3 @@ -25,73 +25,75 @@
    36.4  
    36.5  package jdk.nashorn.internal.ir;
    36.6  
    36.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    36.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    36.9  import jdk.nashorn.internal.runtime.Source;
   36.10  
   36.11  /**
   36.12   * IR representing a FOR statement.
   36.13 - *
   36.14   */
   36.15 -public class ForNode extends WhileNode {
   36.16 +@Immutable
   36.17 +public final class ForNode extends LoopNode {
   36.18      /** Initialize expression. */
   36.19 -    private Node init;
   36.20 +    private final Node init;
   36.21  
   36.22      /** Test expression. */
   36.23 -    private Node modify;
   36.24 +    private final Node modify;
   36.25  
   36.26      /** Iterator symbol. */
   36.27      private Symbol iterator;
   36.28  
   36.29 -    /** is for in */
   36.30 -    private boolean isForIn;
   36.31 +    /** Is this a normal for loop? */
   36.32 +    public static final int IS_FOR      = 1 << 0;
   36.33  
   36.34 -    /** is for each */
   36.35 -    private boolean isForEach;
   36.36 +    /** Is this a normal for in loop? */
   36.37 +    public static final int IS_FOR_IN   = 1 << 1;
   36.38 +
   36.39 +    /** Is this a normal for each in loop? */
   36.40 +    public static final int IS_FOR_EACH = 1 << 2;
   36.41 +
   36.42 +    private final int flags;
   36.43  
   36.44      /**
   36.45       * Constructor
   36.46       *
   36.47 -     * @param source     the source
   36.48 -     * @param token      token
   36.49 -     * @param finish     finish
   36.50 +     * @param source the source
   36.51 +     * @param token  token
   36.52 +     * @param finish finish
   36.53 +     * @param init   init
   36.54 +     * @param test   test
   36.55 +     * @param body   body
   36.56 +     * @param modify modify
   36.57 +     * @param flags  flags
   36.58       */
   36.59 -    public ForNode(final Source source, final long token, final int finish) {
   36.60 -        super(source, token, finish);
   36.61 +    public ForNode(final Source source, final long token, final int finish, final Node init, final Node test, final Block body, final Node modify, final int flags) {
   36.62 +        super(source, token, finish, test, body, false);
   36.63 +        this.init   = init;
   36.64 +        this.modify = modify;
   36.65 +        this.flags  = flags;
   36.66      }
   36.67  
   36.68 -    private ForNode(final ForNode forNode, final CopyState cs) {
   36.69 -        super(forNode, cs);
   36.70 -
   36.71 -        this.init      = cs.existingOrCopy(forNode.init);
   36.72 -        this.modify    = cs.existingOrCopy(forNode.modify);
   36.73 -        this.iterator  = forNode.iterator;
   36.74 -        this.isForIn   = forNode.isForIn;
   36.75 -        this.isForEach = forNode.isForEach;
   36.76 +    private ForNode(final ForNode forNode, final Node init, final Node test, final Block body, final Node modify, final int flags, final boolean controlFlowEscapes) {
   36.77 +        super(forNode, test, body, controlFlowEscapes);
   36.78 +        this.init   = init;
   36.79 +        this.modify = modify;
   36.80 +        this.flags  = flags;
   36.81 +        this.iterator = forNode.iterator; //TODO is this acceptable? symbols are never cloned, just copied as references
   36.82      }
   36.83  
   36.84      @Override
   36.85 -    protected Node copy(final CopyState cs) {
   36.86 -        return new ForNode(this, cs);
   36.87 +    public Node ensureUniqueLabels(LexicalContext lc) {
   36.88 +        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
   36.89      }
   36.90  
   36.91      @Override
   36.92 -    public Node accept(final NodeVisitor visitor) {
   36.93 -        if (visitor.enterForNode(this) != null) {
   36.94 -            if (init != null) {
   36.95 -                init = init.accept(visitor);
   36.96 -            }
   36.97 -
   36.98 -            if (test != null) {
   36.99 -                test = test.accept(visitor);
  36.100 -            }
  36.101 -
  36.102 -            if (modify != null) {
  36.103 -                modify = modify.accept(visitor);
  36.104 -            }
  36.105 -
  36.106 -            body = (Block)body.accept(visitor);
  36.107 -
  36.108 -            return visitor.leaveForNode(this);
  36.109 +    protected Node accept(final LexicalContext lc, final NodeVisitor visitor) {
  36.110 +        if (visitor.enterForNode(this)) {
  36.111 +            return visitor.leaveForNode(
  36.112 +                setInit(lc, init == null ? null : init.accept(visitor)).
  36.113 +                setTest(lc, test == null ? null : test.accept(visitor)).
  36.114 +                setModify(lc, modify == null ? null : modify.accept(visitor)).
  36.115 +                setBody(lc, (Block)body.accept(visitor)));
  36.116          }
  36.117  
  36.118          return this;
  36.119 @@ -122,6 +124,19 @@
  36.120          sb.append(')');
  36.121      }
  36.122  
  36.123 +    @Override
  36.124 +    public boolean hasGoto() {
  36.125 +        return !isForIn() && test == null;
  36.126 +    }
  36.127 +
  36.128 +    @Override
  36.129 +    public boolean mustEnter() {
  36.130 +        if (isForIn()) {
  36.131 +            return false; //may be an empty set to iterate over, then we skip the loop
  36.132 +        }
  36.133 +        return test == null;
  36.134 +    }
  36.135 +
  36.136      /**
  36.137       * Get the initialization expression for this for loop
  36.138       * @return the initialization expression
  36.139 @@ -132,10 +147,15 @@
  36.140  
  36.141      /**
  36.142       * Reset the initialization expression for this for loop
  36.143 +     * @param lc lexical context
  36.144       * @param init new initialization expression
  36.145 +     * @return new for node if changed or existing if not
  36.146       */
  36.147 -    public void setInit(final Node init) {
  36.148 -        this.init = init;
  36.149 +    public ForNode setInit(final LexicalContext lc, final Node init) {
  36.150 +        if (this.init == init) {
  36.151 +            return this;
  36.152 +        }
  36.153 +        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
  36.154      }
  36.155  
  36.156      /**
  36.157 @@ -143,14 +163,16 @@
  36.158       * @return true if this is a for in constructor
  36.159       */
  36.160      public boolean isForIn() {
  36.161 -        return isForIn;
  36.162 +        return (flags & IS_FOR_IN) != 0;
  36.163      }
  36.164  
  36.165      /**
  36.166       * Flag this to be a for in construct
  36.167 +     * @param lc lexical context
  36.168 +     * @return new for node if changed or existing if not
  36.169       */
  36.170 -    public void setIsForIn() {
  36.171 -        this.isForIn = true;
  36.172 +    public ForNode setIsForIn(final LexicalContext lc) {
  36.173 +        return setFlags(lc, flags | IS_FOR_IN);
  36.174      }
  36.175  
  36.176      /**
  36.177 @@ -159,14 +181,16 @@
  36.178       * @return true if this is a for each construct
  36.179       */
  36.180      public boolean isForEach() {
  36.181 -        return isForEach;
  36.182 +        return (flags & IS_FOR_EACH) != 0;
  36.183      }
  36.184  
  36.185      /**
  36.186       * Flag this to be a for each construct
  36.187 +     * @param lc lexical context
  36.188 +     * @return new for node if changed or existing if not
  36.189       */
  36.190 -    public void setIsForEach() {
  36.191 -        this.isForEach = true;
  36.192 +    public ForNode setIsForEach(final LexicalContext lc) {
  36.193 +        return setFlags(lc, flags | IS_FOR_EACH);
  36.194      }
  36.195  
  36.196      /**
  36.197 @@ -195,10 +219,15 @@
  36.198  
  36.199      /**
  36.200       * Reset the modification expression for this ForNode
  36.201 +     * @param lc lexical context
  36.202       * @param modify new modification expression
  36.203 +     * @return new for node if changed or existing if not
  36.204       */
  36.205 -    public void setModify(final Node modify) {
  36.206 -        this.modify = modify;
  36.207 +    public ForNode setModify(final LexicalContext lc, final Node modify) {
  36.208 +        if (this.modify == modify) {
  36.209 +            return this;
  36.210 +        }
  36.211 +        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
  36.212      }
  36.213  
  36.214      @Override
  36.215 @@ -207,7 +236,39 @@
  36.216      }
  36.217  
  36.218      @Override
  36.219 -    public void setTest(final Node test) {
  36.220 -        this.test = test;
  36.221 +    public ForNode setTest(final LexicalContext lc, final Node test) {
  36.222 +        if (this.test == test) {
  36.223 +            return this;
  36.224 +        }
  36.225 +        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
  36.226      }
  36.227 +
  36.228 +    @Override
  36.229 +    public Block getBody() {
  36.230 +        return body;
  36.231 +    }
  36.232 +
  36.233 +    @Override
  36.234 +    public ForNode setBody(final LexicalContext lc, final Block body) {
  36.235 +        if (this.body == body) {
  36.236 +            return this;
  36.237 +        }
  36.238 +        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
  36.239 +    }
  36.240 +
  36.241 +    @Override
  36.242 +    public ForNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) {
  36.243 +        if (this.controlFlowEscapes == controlFlowEscapes) {
  36.244 +            return this;
  36.245 +        }
  36.246 +        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
  36.247 +    }
  36.248 +
  36.249 +    private ForNode setFlags(final LexicalContext lc, final int flags) {
  36.250 +        if (this.flags == flags) {
  36.251 +            return this;
  36.252 +        }
  36.253 +        return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
  36.254 +    }
  36.255 +
  36.256  }
    37.1 --- a/src/jdk/nashorn/internal/ir/FunctionNode.java	Fri Apr 19 18:23:00 2013 +0530
    37.2 +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java	Fri Apr 19 16:11:16 2013 +0200
    37.3 @@ -30,23 +30,19 @@
    37.4  import static jdk.nashorn.internal.ir.Symbol.IS_CONSTANT;
    37.5  import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
    37.6  
    37.7 -import java.util.ArrayList;
    37.8  import java.util.Collections;
    37.9  import java.util.EnumSet;
   37.10 -import java.util.HashMap;
   37.11 -import java.util.Iterator;
   37.12 +import java.util.HashSet;
   37.13  import java.util.List;
   37.14 -import java.util.Map;
   37.15 -import java.util.Stack;
   37.16 +import java.util.Set;
   37.17  import jdk.nashorn.internal.codegen.CompileUnit;
   37.18  import jdk.nashorn.internal.codegen.Compiler;
   37.19 -import jdk.nashorn.internal.codegen.Frame;
   37.20 -import jdk.nashorn.internal.codegen.MethodEmitter;
   37.21 +import jdk.nashorn.internal.codegen.CompilerConstants;
   37.22  import jdk.nashorn.internal.codegen.Namespace;
   37.23  import jdk.nashorn.internal.codegen.types.Type;
   37.24  import jdk.nashorn.internal.ir.annotations.Ignore;
   37.25 +import jdk.nashorn.internal.ir.annotations.Immutable;
   37.26  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   37.27 -import jdk.nashorn.internal.parser.Parser;
   37.28  import jdk.nashorn.internal.runtime.ScriptFunction;
   37.29  import jdk.nashorn.internal.runtime.Source;
   37.30  import jdk.nashorn.internal.runtime.UserAccessorProperty;
   37.31 @@ -55,9 +51,11 @@
   37.32  /**
   37.33   * IR representation for function (or script.)
   37.34   */
   37.35 -public class FunctionNode extends Block {
   37.36 +@Immutable
   37.37 +public final class FunctionNode extends LexicalContextNode implements Flags<FunctionNode> {
   37.38  
   37.39 -    private static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class);
   37.40 +    /** Type used for all FunctionNodes */
   37.41 +    public static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class);
   37.42  
   37.43      /** Function kinds */
   37.44      public enum Kind {
   37.45 @@ -90,131 +88,105 @@
   37.46          /** method has had its types finalized */
   37.47          FINALIZED,
   37.48          /** method has been emitted to bytecode */
   37.49 -        EMITTED,
   37.50 -        /** code installed in a class loader */
   37.51 -        INSTALLED
   37.52 +        EMITTED
   37.53      }
   37.54  
   37.55      /** External function identifier. */
   37.56      @Ignore
   37.57 -    private IdentNode ident;
   37.58 +    private final IdentNode ident;
   37.59 +
   37.60 +    /** The body of the function node */
   37.61 +    private final Block body;
   37.62  
   37.63      /** Internal function name. */
   37.64 -    private String name;
   37.65 +    private final String name;
   37.66  
   37.67      /** Compilation unit. */
   37.68 -    private CompileUnit compileUnit;
   37.69 -
   37.70 -    /** Method emitter for current method. */
   37.71 -    private MethodEmitter method;
   37.72 +    private final CompileUnit compileUnit;
   37.73  
   37.74      /** Function kind. */
   37.75 -    private Kind kind;
   37.76 +    private final Kind kind;
   37.77  
   37.78      /** List of parameters. */
   37.79 -    private List<IdentNode> parameters;
   37.80 +    private final List<IdentNode> parameters;
   37.81  
   37.82      /** First token of function. **/
   37.83 -    private long firstToken;
   37.84 +    private final long firstToken;
   37.85  
   37.86      /** Last token of function. **/
   37.87 -    private long lastToken;
   37.88 +    private final long lastToken;
   37.89  
   37.90 -    /** Variable frames. */
   37.91 -    private Frame frames;
   37.92 +    /** Declared symbols in this function node */
   37.93 +    @Ignore
   37.94 +    private final Set<Symbol> declaredSymbols;
   37.95  
   37.96      /** Method's namespace. */
   37.97      private final Namespace namespace;
   37.98  
   37.99 -    /** Node representing current this. */
  37.100 -    @Ignore
  37.101 -    private IdentNode thisNode;
  37.102 -
  37.103 -    /** Node representing current scope. */
  37.104 -    @Ignore
  37.105 -    private IdentNode scopeNode;
  37.106 -
  37.107 -    /** Node representing return value. */
  37.108 -    @Ignore
  37.109 -    private IdentNode resultNode;
  37.110 -
  37.111 -    /** Node representing current arguments. */
  37.112 -    @Ignore
  37.113 -    private IdentNode argumentsNode;
  37.114 -
  37.115 -    /** Node representing callee */
  37.116 -    @Ignore
  37.117 -    private IdentNode calleeNode;
  37.118 -
  37.119 -    /** Node representing varargs */
  37.120 -    @Ignore
  37.121 -    private IdentNode varArgsNode;
  37.122 -
  37.123 -    /** Pending label list. */
  37.124 -    private final Stack<LabelNode> labelStack;
  37.125 -
  37.126 -    /** Pending control list. */
  37.127 -    private final Stack<Node> controlStack;
  37.128 -
  37.129 -    /** VarNode for this function statement */
  37.130 -    @Ignore //this is explicit code anyway and should not be traversed after lower
  37.131 -    private VarNode funcVarNode;
  37.132 -
  37.133 -    /** Line number for function declaration */
  37.134 -    @Ignore
  37.135 -    private LineNumberNode funcVarLineNumberNode;
  37.136 -
  37.137 -    /** Initializer var func = __callee__, where applicable */
  37.138 -    @Ignore
  37.139 -    private Node selfSymbolInit;
  37.140 -
  37.141      /** Current compilation state */
  37.142      @Ignore
  37.143      private final EnumSet<CompilationState> compilationState;
  37.144  
  37.145 -    /** Type hints, e.g based on parameters at call site */
  37.146 -    private final Map<IdentNode, Type> specializedTypes;
  37.147 -
  37.148      /** Function flags. */
  37.149 -    private int flags;
  37.150 +    private final int flags;
  37.151  
  37.152      /** Is anonymous function flag. */
  37.153 -    private static final int IS_ANONYMOUS                = 1 << 0;
  37.154 +    public static final int IS_ANONYMOUS                = 1 << 0;
  37.155 +
  37.156      /** Is the function created in a function declaration (as opposed to a function expression) */
  37.157 -    private static final int IS_DECLARED                 = 1 << 1;
  37.158 +    public static final int IS_DECLARED                 = 1 << 1;
  37.159 +
  37.160      /** is this a strict mode function? */
  37.161 -    private static final int IS_STRICT_MODE              = 1 << 2;
  37.162 +    public static final int IS_STRICT                   = 1 << 2;
  37.163 +
  37.164      /** Does the function use the "arguments" identifier ? */
  37.165 -    private static final int USES_ARGUMENTS              = 1 << 3;
  37.166 -    /** Are we lowered ? */
  37.167 -    private static final int IS_LOWERED                  = 1 << 4;
  37.168 +    public static final int USES_ARGUMENTS              = 1 << 3;
  37.169 +
  37.170      /** Has this node been split because it was too large? */
  37.171 -    private static final int IS_SPLIT                    = 1 << 5;
  37.172 +    public static final int IS_SPLIT                    = 1 << 4;
  37.173 +
  37.174      /** Does the function call eval? */
  37.175 -    private static final int HAS_EVAL                    = 1 << 6;
  37.176 +    public static final int HAS_EVAL                    = 1 << 5;
  37.177 +
  37.178      /** Does the function contain a with block ? */
  37.179 -    private static final int HAS_WITH                    = 1 << 7;
  37.180 +    public static final int HAS_WITH                    = 1 << 6;
  37.181 +
  37.182      /** Does a descendant function contain a with or eval? */
  37.183 -    private static final int HAS_DESCENDANT_WITH_OR_EVAL = 1 << 8;
  37.184 -    /** Does the function define "arguments" identifier as a parameter of nested function name? */
  37.185 -    private static final int DEFINES_ARGUMENTS           = 1 << 9;
  37.186 -    /** Does the function need a self symbol? */
  37.187 -    private static final int NEEDS_SELF_SYMBOL           = 1 << 10;
  37.188 +    public static final int HAS_DESCENDANT_WITH_OR_EVAL = 1 << 7;
  37.189 +
  37.190 +    /**
  37.191 +     * Flag this function as one that defines the identifier "arguments" as a function parameter or nested function
  37.192 +     * name. This precludes it from needing to have an Arguments object defined as "arguments" local variable. Note that
  37.193 +     * defining a local variable named "arguments" still requires construction of the Arguments object (see
  37.194 +     * ECMAScript 5.1 Chapter 10.5).
  37.195 +     * @see #needsArguments()
  37.196 +     */
  37.197 +    public static final int DEFINES_ARGUMENTS           = 1 << 8;
  37.198 +
  37.199      /** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */
  37.200 -    private static final int USES_ANCESTOR_SCOPE         = 1 << 11;
  37.201 +    public static final int USES_ANCESTOR_SCOPE         = 1 << 9;
  37.202 +
  37.203      /** Is this function lazily compiled? */
  37.204 -    private static final int IS_LAZY                     = 1 << 12;
  37.205 +    public static final int IS_LAZY                     = 1 << 10;
  37.206 +
  37.207      /** Does this function have lazy, yet uncompiled children */
  37.208 -    private static final int HAS_LAZY_CHILDREN           = 1 << 13;
  37.209 +    public static final int HAS_LAZY_CHILDREN           = 1 << 11;
  37.210 +
  37.211      /** Does this function have lazy, yet uncompiled children */
  37.212 -    private static final int IS_PROGRAM                   = 1 << 14;
  37.213 +    public static final int IS_PROGRAM                  = 1 << 12;
  37.214 +
  37.215 +    /** Does this function have nested declarations? */
  37.216 +    public static final int HAS_FUNCTION_DECLARATIONS   = 1 << 13;
  37.217  
  37.218      /** Does this function or any nested functions contain a with or an eval? */
  37.219      private static final int HAS_DEEP_WITH_OR_EVAL = HAS_EVAL | HAS_WITH | HAS_DESCENDANT_WITH_OR_EVAL;
  37.220 +
  37.221      /** Does this function need to store all its variables in scope? */
  37.222      private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_WITH_OR_EVAL | IS_SPLIT | HAS_LAZY_CHILDREN;
  37.223 +
  37.224      /** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */
  37.225      private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL;
  37.226 +
  37.227      /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep with or eval.
  37.228       *  We also pessimistically need a parent scope if we have lazy children that have not yet been compiled */
  37.229      private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_WITH_OR_EVAL | HAS_LAZY_CHILDREN;
  37.230 @@ -225,107 +197,77 @@
  37.231      /**
  37.232       * Constructor
  37.233       *
  37.234 -     * @param source    the source
  37.235 -     * @param token     token
  37.236 -     * @param finish    finish
  37.237 -     * @param namespace the namespace
  37.238 -     * @param ident     the identifier
  37.239 -     * @param name      the name of the function
  37.240 +     * @param source     the source
  37.241 +     * @param token      token
  37.242 +     * @param finish     finish
  37.243 +     * @param firstToken first token of the funtion node (including the function declaration)
  37.244 +     * @param namespace  the namespace
  37.245 +     * @param ident      the identifier
  37.246 +     * @param name       the name of the function
  37.247 +     * @param parameters parameter list
  37.248 +     * @param kind       kind of function as in {@link FunctionNode.Kind}
  37.249 +     * @param flags      initial flags
  37.250       */
  37.251 -    public FunctionNode(final Source source, final long token, final int finish, final Namespace namespace, final IdentNode ident, final String name) {
  37.252 +    public FunctionNode(
  37.253 +        final Source source,
  37.254 +        final long token,
  37.255 +        final int finish,
  37.256 +        final long firstToken,
  37.257 +        final Namespace namespace,
  37.258 +        final IdentNode ident,
  37.259 +        final String name,
  37.260 +        final List<IdentNode> parameters,
  37.261 +        final FunctionNode.Kind kind,
  37.262 +        final int flags) {
  37.263          super(source, token, finish);
  37.264  
  37.265          this.ident             = ident;
  37.266          this.name              = name;
  37.267 -        this.kind              = Kind.NORMAL;
  37.268 -        this.parameters        = new ArrayList<>();
  37.269 -        this.firstToken        = token;
  37.270 +        this.kind              = kind;
  37.271 +        this.parameters        = parameters;
  37.272 +        this.firstToken        = firstToken;
  37.273          this.lastToken         = token;
  37.274          this.namespace         = namespace;
  37.275 -        this.labelStack        = new Stack<>();
  37.276 -        this.controlStack      = new Stack<>();
  37.277          this.compilationState  = EnumSet.of(CompilationState.INITIALIZED);
  37.278 -        this.specializedTypes  = new HashMap<>();
  37.279 +        this.declaredSymbols   = new HashSet<>();
  37.280 +        this.flags             = flags;
  37.281 +        this.compileUnit       = null;
  37.282 +        this.body              = null;
  37.283      }
  37.284  
  37.285 -    private FunctionNode(final FunctionNode functionNode, final CopyState cs) {
  37.286 -        super(functionNode, cs);
  37.287 +    private FunctionNode(final FunctionNode functionNode, final long lastToken, final int flags, final Type returnType, final CompileUnit compileUnit, final EnumSet<CompilationState> compilationState, final Block body) {
  37.288 +        super(functionNode);
  37.289 +        this.flags = flags;
  37.290 +        this.returnType = returnType;
  37.291 +        this.compileUnit = compileUnit;
  37.292 +        this.lastToken = lastToken;
  37.293 +        this.compilationState = compilationState;
  37.294 +        this.body  = body;
  37.295  
  37.296 -        this.ident = (IdentNode)cs.existingOrCopy(functionNode.ident);
  37.297 -        this.name  = functionNode.name;
  37.298 +        // the fields below never change - they are final and assigned in constructor
  37.299 +        this.name = functionNode.name;
  37.300 +        this.ident = functionNode.ident;
  37.301 +        this.namespace = functionNode.namespace;
  37.302 +        this.declaredSymbols = functionNode.declaredSymbols;
  37.303          this.kind  = functionNode.kind;
  37.304 -
  37.305 -        this.parameters = new ArrayList<>();
  37.306 -        for (final IdentNode param : functionNode.getParameters()) {
  37.307 -            this.parameters.add((IdentNode)cs.existingOrCopy(param));
  37.308 -        }
  37.309 -
  37.310 -        this.firstToken        = functionNode.firstToken;
  37.311 -        this.lastToken         = functionNode.lastToken;
  37.312 -        this.namespace         = functionNode.getNamespace();
  37.313 -        this.thisNode          = (IdentNode)cs.existingOrCopy(functionNode.thisNode);
  37.314 -        this.scopeNode         = (IdentNode)cs.existingOrCopy(functionNode.scopeNode);
  37.315 -        this.resultNode        = (IdentNode)cs.existingOrCopy(functionNode.resultNode);
  37.316 -        this.argumentsNode     = (IdentNode)cs.existingOrCopy(functionNode.argumentsNode);
  37.317 -        this.varArgsNode       = (IdentNode)cs.existingOrCopy(functionNode.varArgsNode);
  37.318 -        this.calleeNode        = (IdentNode)cs.existingOrCopy(functionNode.calleeNode);
  37.319 -        this.labelStack        = new Stack<>();
  37.320 -        this.controlStack      = new Stack<>();
  37.321 -
  37.322 -        this.flags = functionNode.flags;
  37.323 -
  37.324 -        this.funcVarNode = (VarNode)cs.existingOrCopy(functionNode.funcVarNode);
  37.325 -        /** VarNode for this function statement */
  37.326 -
  37.327 -        this.compilationState = EnumSet.copyOf(functionNode.compilationState);
  37.328 -        this.specializedTypes = new HashMap<>();
  37.329 +        this.parameters = functionNode.parameters;
  37.330 +        this.firstToken = functionNode.firstToken;
  37.331      }
  37.332  
  37.333      @Override
  37.334 -    protected Node copy(final CopyState cs) {
  37.335 -        // deep clone all parent blocks
  37.336 -        return new FunctionNode(this, cs);
  37.337 -    }
  37.338 -
  37.339 -    @Override
  37.340 -    public Node accept(final NodeVisitor visitor) {
  37.341 -        final FunctionNode  saveFunctionNode  = visitor.getCurrentFunctionNode();
  37.342 -        final Block         saveBlock         = visitor.getCurrentBlock();
  37.343 -        final MethodEmitter saveMethodEmitter = visitor.getCurrentMethodEmitter();
  37.344 -        final CompileUnit   saveCompileUnit   = visitor.getCurrentCompileUnit();
  37.345 -
  37.346 -        visitor.setCurrentFunctionNode(this);
  37.347 -        visitor.setCurrentBlock(this);
  37.348 -
  37.349 -        try {
  37.350 -            if (visitor.enterFunctionNode(this) != null) {
  37.351 -                if (ident != null) {
  37.352 -                    ident = (IdentNode)ident.accept(visitor);
  37.353 -                }
  37.354 -
  37.355 -                for (int i = 0, count = parameters.size(); i < count; i++) {
  37.356 -                    parameters.set(i, (IdentNode)parameters.get(i).accept(visitor));
  37.357 -                }
  37.358 -
  37.359 -                for (int i = 0, count = statements.size(); i < count; i++) {
  37.360 -                    statements.set(i, statements.get(i).accept(visitor));
  37.361 -                }
  37.362 -
  37.363 -                return visitor.leaveFunctionNode(this);
  37.364 -            }
  37.365 -        } finally {
  37.366 -            visitor.setCurrentBlock(saveBlock);
  37.367 -            visitor.setCurrentFunctionNode(saveFunctionNode);
  37.368 -            visitor.setCurrentCompileUnit(saveCompileUnit);
  37.369 -            visitor.setCurrentMethodEmitter(saveMethodEmitter);
  37.370 +    public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
  37.371 +        if (visitor.enterFunctionNode(this)) {
  37.372 +            return visitor.leaveFunctionNode(setBody(lc, (Block)body.accept(visitor)));
  37.373          }
  37.374 -
  37.375          return this;
  37.376      }
  37.377  
  37.378 -    @Override
  37.379 -    public boolean needsScope() {
  37.380 -        return super.needsScope() || isProgram();
  37.381 +    /**
  37.382 +     * Get the compilation state of this function
  37.383 +     * @return the compilation state
  37.384 +     */
  37.385 +    public EnumSet<CompilationState> getState() {
  37.386 +        return compilationState;
  37.387      }
  37.388  
  37.389      /**
  37.390 @@ -357,62 +299,17 @@
  37.391       * FunctionNode has been lowered, the compiler will add
  37.392       * {@code CompilationState#LOWERED} to the state vector
  37.393       *
  37.394 +     * @param lc lexical context
  37.395       * @param state {@link CompilationState} to add
  37.396 +     * @return function node or a new one if state was changed
  37.397       */
  37.398 -    public void setState(final CompilationState state) {
  37.399 -        compilationState.add(state);
  37.400 -    }
  37.401 -
  37.402 -    /*
  37.403 -     * Frame management.
  37.404 -     */
  37.405 -
  37.406 -    /**
  37.407 -     * Push a new block frame.
  37.408 -     *
  37.409 -     * @return the new frame
  37.410 -     */
  37.411 -    public final Frame pushFrame() {
  37.412 -        frames = new Frame(frames);
  37.413 -        return frames;
  37.414 -    }
  37.415 -
  37.416 -    /**
  37.417 -     * Pop a block frame.
  37.418 -     */
  37.419 -    public final void popFrame() {
  37.420 -        frames = frames.getPrevious();
  37.421 -    }
  37.422 -
  37.423 -    /**
  37.424 -     * Create a temporary variable to the current frame.
  37.425 -     *
  37.426 -     * @param currentFrame Frame to add to - defaults to current function frame
  37.427 -     * @param type  Strong type of symbol.
  37.428 -     * @param node  Primary node to use symbol.
  37.429 -     *
  37.430 -     * @return Symbol used.
  37.431 -     */
  37.432 -    public Symbol newTemporary(final Frame currentFrame, final Type type, final Node node) {
  37.433 -        assert currentFrame != null;
  37.434 -        Symbol symbol = node.getSymbol();
  37.435 -
  37.436 -        // If no symbol already present.
  37.437 -        if (symbol == null) {
  37.438 -            final String uname = uniqueName(TEMP_PREFIX.tag());
  37.439 -            symbol = new Symbol(uname, IS_TEMP, type);
  37.440 -            symbol.setNode(node);
  37.441 +    public FunctionNode setState(final LexicalContext lc, final CompilationState state) {
  37.442 +        if (this.compilationState.equals(state)) {
  37.443 +            return this;
  37.444          }
  37.445 -
  37.446 -        // Assign a slot if it doesn't have one.
  37.447 -        if (!symbol.hasSlot()) {
  37.448 -            currentFrame.addSymbol(symbol);
  37.449 -        }
  37.450 -
  37.451 -        // Set symbol to node.
  37.452 -        node.setSymbol(symbol);
  37.453 -
  37.454 -        return symbol;
  37.455 +        final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
  37.456 +        newState.add(state);
  37.457 +        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, newState, body));
  37.458      }
  37.459  
  37.460      /**
  37.461 @@ -425,18 +322,6 @@
  37.462      }
  37.463  
  37.464      /**
  37.465 -     * Add a new temporary variable to the current frame
  37.466 -     *
  37.467 -     * @param type Strong type of symbol
  37.468 -     * @param node Primary node to use symbol
  37.469 -     *
  37.470 -     * @return symbol used
  37.471 -     */
  37.472 -    public Symbol newTemporary(final Type type, final Node node) {
  37.473 -        return newTemporary(frames, type, node);
  37.474 -    }
  37.475 -
  37.476 -    /**
  37.477       * Create a virtual symbol for a literal.
  37.478       *
  37.479       * @param literalNode Primary node to use symbol.
  37.480 @@ -444,9 +329,8 @@
  37.481       * @return Symbol used.
  37.482       */
  37.483      public Symbol newLiteral(final LiteralNode<?> literalNode) {
  37.484 -        final String uname = uniqueName(LITERAL_PREFIX.tag());
  37.485 +        final String uname = uniqueName(LITERAL_PREFIX.symbolName());
  37.486          final Symbol symbol = new Symbol(uname, IS_CONSTANT, literalNode.getType());
  37.487 -        symbol.setNode(literalNode);
  37.488          literalNode.setSymbol(symbol);
  37.489  
  37.490          return symbol;
  37.491 @@ -482,29 +366,35 @@
  37.492          sb.append(')');
  37.493      }
  37.494  
  37.495 +    @Override
  37.496 +    public boolean getFlag(final int flag) {
  37.497 +        return (flags & flag) != 0;
  37.498 +    }
  37.499 +
  37.500 +    @Override
  37.501 +    public FunctionNode setFlags(final LexicalContext lc, int flags) {
  37.502 +        if (this.flags == flags) {
  37.503 +            return this;
  37.504 +        }
  37.505 +        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
  37.506 +    }
  37.507 +
  37.508 +    @Override
  37.509 +    public FunctionNode clearFlag(final LexicalContext lc, final int flag) {
  37.510 +        return setFlags(lc, flags & ~flag);
  37.511 +    }
  37.512 +
  37.513 +    @Override
  37.514 +    public FunctionNode setFlag(final LexicalContext lc, final int flag) {
  37.515 +        return setFlags(lc, flags | flag);
  37.516 +    }
  37.517 +
  37.518      /**
  37.519       * Returns true if the function is the top-level program.
  37.520       * @return True if this function node represents the top-level program.
  37.521       */
  37.522      public boolean isProgram() {
  37.523 -        return (flags & IS_PROGRAM) != 0;
  37.524 -    }
  37.525 -
  37.526 -    /**
  37.527 -     * Marks the function as representing the top-level program.
  37.528 -     */
  37.529 -    public void setProgram() {
  37.530 -        flags |= IS_PROGRAM;
  37.531 -    }
  37.532 -
  37.533 -    /**
  37.534 -     * Get the control stack. Used when parsing to establish nesting depths of
  37.535 -     * different control structures
  37.536 -     *
  37.537 -     * @return the control stack
  37.538 -     */
  37.539 -    public Stack<Node> getControlStack() {
  37.540 -        return controlStack;
  37.541 +        return getFlag(IS_PROGRAM);
  37.542      }
  37.543  
  37.544      /**
  37.545 @@ -512,15 +402,7 @@
  37.546       * @return true if lazy
  37.547       */
  37.548      public boolean isLazy() {
  37.549 -        return (flags & IS_LAZY) != 0;
  37.550 -    }
  37.551 -
  37.552 -    /**
  37.553 -     * Set if this function should be lazily generated
  37.554 -     * @param isLazy is lazy
  37.555 -     */
  37.556 -    public void setIsLazy(final boolean isLazy) {
  37.557 -        this.flags = isLazy ? flags | IS_LAZY : flags & ~IS_LAZY;
  37.558 +        return getFlag(IS_LAZY);
  37.559      }
  37.560  
  37.561      /**
  37.562 @@ -529,37 +411,7 @@
  37.563       * @return true if {@code with} is used
  37.564       */
  37.565      public boolean hasWith() {
  37.566 -        return (flags & HAS_WITH) != 0;
  37.567 -    }
  37.568 -
  37.569 -    /**
  37.570 -     * Flag this function as using the {@code with} keyword
  37.571 -     * @param ancestors the iterator over functions in this functions's containing lexical context
  37.572 -     */
  37.573 -    public void setHasWith(final Iterator<FunctionNode> ancestors) {
  37.574 -        if(!hasWith()) {
  37.575 -            this.flags |= HAS_WITH;
  37.576 -            // with requires scope in parents.
  37.577 -            // TODO: refine this. with should not force all variables in parents to be in scope, only those that are
  37.578 -            // actually referenced as identifiers by name
  37.579 -            markParentForWithOrEval(ancestors);
  37.580 -        }
  37.581 -    }
  37.582 -
  37.583 -    private void markParentForWithOrEval(final Iterator<FunctionNode> ancestors) {
  37.584 -        // If this is invoked, then either us or a descendant uses with or eval, meaning we must have our own scope.
  37.585 -        setNeedsScope();
  37.586 -
  37.587 -        if(ancestors.hasNext()) {
  37.588 -            ancestors.next().setDescendantHasWithOrEval(ancestors);
  37.589 -        }
  37.590 -    }
  37.591 -
  37.592 -    private void setDescendantHasWithOrEval(final Iterator<FunctionNode> ancestors) {
  37.593 -        if((flags & HAS_DESCENDANT_WITH_OR_EVAL) == 0) {
  37.594 -            flags |= HAS_DESCENDANT_WITH_OR_EVAL;
  37.595 -            markParentForWithOrEval(ancestors);
  37.596 -        }
  37.597 +        return getFlag(HAS_WITH);
  37.598      }
  37.599  
  37.600      /**
  37.601 @@ -568,18 +420,7 @@
  37.602       * @return true if {@code eval} is used
  37.603       */
  37.604      public boolean hasEval() {
  37.605 -        return (flags & HAS_EVAL) != 0;
  37.606 -    }
  37.607 -
  37.608 -    /**
  37.609 -     * Flag this function as calling the {@code eval} function
  37.610 -     * @param ancestors the iterator over functions in this functions's containing lexical context
  37.611 -     */
  37.612 -    public void setHasEval(final Iterator<FunctionNode> ancestors) {
  37.613 -        if(!hasEval()) {
  37.614 -            this.flags |= HAS_EVAL;
  37.615 -            markParentForWithOrEval(ancestors);
  37.616 -        }
  37.617 +        return getFlag(HAS_EVAL);
  37.618      }
  37.619  
  37.620      /**
  37.621 @@ -591,7 +432,7 @@
  37.622       * @return true if this or a nested function contains with or eval
  37.623       */
  37.624      public boolean hasDeepWithOrEval() {
  37.625 -        return (flags & HAS_DEEP_WITH_OR_EVAL) != 0;
  37.626 +        return getFlag(HAS_DEEP_WITH_OR_EVAL);
  37.627      }
  37.628  
  37.629      /**
  37.630 @@ -603,90 +444,11 @@
  37.631      }
  37.632  
  37.633      /**
  37.634 -     * Set the first token for this function
  37.635 -     * @param firstToken the first token
  37.636 +     * Check whether this function has nested function declarations
  37.637 +     * @return true if nested function declarations exist
  37.638       */
  37.639 -    public void setFirstToken(final long firstToken) {
  37.640 -        this.firstToken = firstToken;
  37.641 -    }
  37.642 -
  37.643 -    /**
  37.644 -     * Returns a list of functions declared by this function. Only includes declared functions, and does not include any
  37.645 -     * function expressions that might occur in its body.
  37.646 -     * @return a list of functions declared by this function.
  37.647 -     */
  37.648 -    public List<FunctionNode> getDeclaredFunctions() {
  37.649 -        // Note that the function does not have a dedicated list of declared functions, but rather relies on the
  37.650 -        // invariant that all function declarations are at the beginning of the statement list as VarNode with a
  37.651 -        // FunctionNode marked as statement with its variable initializer. Every VarNode is also preceded by a
  37.652 -        // LineNumberNode. This invariant is established by the parser and has to be preserved in visitors.
  37.653 -        final List<FunctionNode> fns = new ArrayList<>();
  37.654 -        for (final Node stmt : statements) {
  37.655 -            if(stmt instanceof LineNumberNode) {
  37.656 -                continue;
  37.657 -            } else if(stmt instanceof VarNode) {
  37.658 -                final Node init = ((VarNode)stmt).getInit();
  37.659 -                if(init instanceof FunctionNode) {
  37.660 -                    final FunctionNode fn = (FunctionNode)init;
  37.661 -                    if(fn.isDeclared()) {
  37.662 -                        fns.add(fn);
  37.663 -                        continue;
  37.664 -                    }
  37.665 -                }
  37.666 -            }
  37.667 -            // Node is neither a LineNumberNode, nor a function declaration VarNode. Since all function declarations are
  37.668 -            // at the start of the function, we've reached the end of function declarations.
  37.669 -            break;
  37.670 -        }
  37.671 -        return fns;
  37.672 -    }
  37.673 -
  37.674 -    /**
  37.675 -     * Get the label stack. This is used by the parser to establish
  37.676 -     * label nesting depth
  37.677 -     *
  37.678 -     * @return the label stack
  37.679 -     */
  37.680 -    public Stack<LabelNode> getLabelStack() {
  37.681 -        return labelStack;
  37.682 -    }
  37.683 -
  37.684 -    /**
  37.685 -     * If this function needs to use var args, return the identifier to the node used
  37.686 -     * for the var args structure
  37.687 -     *
  37.688 -     * @return IdentNode representing the var args structure
  37.689 -     */
  37.690 -    public IdentNode getVarArgsNode() {
  37.691 -        return varArgsNode;
  37.692 -    }
  37.693 -
  37.694 -    /**
  37.695 -     * Set the identifier to the node used for the var args structure
  37.696 -     *
  37.697 -     * @param varArgsNode IdentNode representing the var args
  37.698 -     */
  37.699 -    public void setVarArgsNode(final IdentNode varArgsNode) {
  37.700 -        this.varArgsNode = varArgsNode;
  37.701 -    }
  37.702 -
  37.703 -    /**
  37.704 -     * If this function uses the {@code callee} variable, return the node used
  37.705 -     * as this variable
  37.706 -     *
  37.707 -     * @return an IdentNode representing the {@code callee} variable
  37.708 -     */
  37.709 -    public IdentNode getCalleeNode() {
  37.710 -        return calleeNode;
  37.711 -    }
  37.712 -
  37.713 -    /**
  37.714 -     * If this function uses the {@code callee} variable, set the node representing the
  37.715 -     * callee
  37.716 -     * @param calleeNode an IdentNode representing the callee
  37.717 -     */
  37.718 -    public void setCalleeNode(final IdentNode calleeNode) {
  37.719 -        this.calleeNode = calleeNode;
  37.720 +    public boolean hasDeclaredFunctions() {
  37.721 +        return getFlag(HAS_FUNCTION_DECLARATIONS);
  37.722      }
  37.723  
  37.724      /**
  37.725 @@ -697,26 +459,7 @@
  37.726       * @return true if the function's generated Java method needs a {@code callee} parameter.
  37.727       */
  37.728      public boolean needsCallee() {
  37.729 -        return needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrictMode());
  37.730 -    }
  37.731 -
  37.732 -    /**
  37.733 -     * If this is a function where {@code arguments} is used, return the node used as the {@code arguments}
  37.734 -     * variable
  37.735 -     * @return an IdentNode representing {@code arguments}
  37.736 -     */
  37.737 -    public IdentNode getArgumentsNode() {
  37.738 -        return argumentsNode;
  37.739 -    }
  37.740 -
  37.741 -    /**
  37.742 -     * If this is a Function where {@code arguments} is used, an identifier to the node representing
  37.743 -     * the {@code arguments} value has to be supplied by the compiler
  37.744 -     *
  37.745 -     * @param argumentsNode IdentNode that represents {@code arguments}
  37.746 -     */
  37.747 -    public void setArgumentsNode(final IdentNode argumentsNode) {
  37.748 -        this.argumentsNode = argumentsNode;
  37.749 +        return needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrict());
  37.750      }
  37.751  
  37.752      /**
  37.753 @@ -728,11 +471,42 @@
  37.754      }
  37.755  
  37.756      /**
  37.757 -     * Reset the identifier for this function
  37.758 -     * @param ident IdentNode for new identifier
  37.759 +     * Return a set of symbols declared in this function node. This
  37.760 +     * is only relevant after Attr, otherwise it will be an empty
  37.761 +     * set as no symbols have been introduced
  37.762 +     * @return set of declared symbols in function
  37.763       */
  37.764 -    public void setIdent(final IdentNode ident) {
  37.765 -        this.ident = ident;
  37.766 +    public Set<Symbol> getDeclaredSymbols() {
  37.767 +        return Collections.unmodifiableSet(declaredSymbols);
  37.768 +    }
  37.769 +
  37.770 +    /**
  37.771 +     * Add a declared symbol to this function node
  37.772 +     * @param symbol symbol that is declared
  37.773 +     */
  37.774 +    public void addDeclaredSymbol(final Symbol symbol) {
  37.775 +        declaredSymbols.add(symbol);
  37.776 +    }
  37.777 +
  37.778 +    /**
  37.779 +     * Get the function body
  37.780 +     * @return the function body
  37.781 +     */
  37.782 +    public Block getBody() {
  37.783 +        return body;
  37.784 +    }
  37.785 +
  37.786 +    /**
  37.787 +     * Reset the function body
  37.788 +     * @param lc lexical context
  37.789 +     * @param body new body
  37.790 +     * @return new function node if body changed, same if not
  37.791 +     */
  37.792 +    public FunctionNode setBody(final LexicalContext lc, final Block body) {
  37.793 +        if(this.body == body) {
  37.794 +            return this;
  37.795 +        }
  37.796 +        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
  37.797      }
  37.798  
  37.799      /**
  37.800 @@ -748,17 +522,6 @@
  37.801      }
  37.802  
  37.803      /**
  37.804 -     * Flag this function as one that defines the identifier "arguments" as a function parameter or nested function
  37.805 -     * name. This precludes it from needing to have an Arguments object defined as "arguments" local variable. Note that
  37.806 -     * defining a local variable named "arguments" still requires construction of the Arguments object (see
  37.807 -     * ECMAScript 5.1 Chapter 10.5).
  37.808 -     * @see #needsArguments()
  37.809 -     */
  37.810 -    public void setDefinesArguments() {
  37.811 -        this.flags |= DEFINES_ARGUMENTS;
  37.812 -    }
  37.813 -
  37.814 -    /**
  37.815       * Returns true if this function needs to have an Arguments object defined as a local variable named "arguments".
  37.816       * Functions that use "arguments" as identifier and don't define it as a name of a parameter or a nested function
  37.817       * (see ECMAScript 5.1 Chapter 10.5), as well as any function that uses eval or with, or has a nested function that
  37.818 @@ -770,15 +533,7 @@
  37.819      public boolean needsArguments() {
  37.820          // uses "arguments" or calls eval, but it does not redefine "arguments", and finally, it's not a script, since
  37.821          // for top-level script, "arguments" is picked up from Context by Global.init() instead.
  37.822 -        return (flags & MAYBE_NEEDS_ARGUMENTS) != 0 && (flags & DEFINES_ARGUMENTS) == 0 && !isProgram();
  37.823 -    }
  37.824 -
  37.825 -    /**
  37.826 -     * Flags this function as one that uses the "arguments" identifier.
  37.827 -     * @see #needsArguments()
  37.828 -     */
  37.829 -    public void setUsesArguments() {
  37.830 -        flags |= USES_ARGUMENTS;
  37.831 +        return getFlag(MAYBE_NEEDS_ARGUMENTS) && !getFlag(DEFINES_ARGUMENTS) && !isProgram();
  37.832      }
  37.833  
  37.834      /**
  37.835 @@ -789,7 +544,7 @@
  37.836       * @return true if the function needs parent scope.
  37.837       */
  37.838      public boolean needsParentScope() {
  37.839 -        return (flags & NEEDS_PARENT_SCOPE) != 0 || isProgram();
  37.840 +        return getFlag(NEEDS_PARENT_SCOPE) || isProgram();
  37.841      }
  37.842  
  37.843      /**
  37.844 @@ -802,15 +557,6 @@
  37.845      }
  37.846  
  37.847      /**
  37.848 -     * Set the kind of this function
  37.849 -     * @see FunctionNode.Kind
  37.850 -     * @param kind the kind
  37.851 -     */
  37.852 -    public void setKind(final Kind kind) {
  37.853 -        this.kind = kind;
  37.854 -    }
  37.855 -
  37.856 -    /**
  37.857       * Return the last token for this function's code
  37.858       * @return last token
  37.859       */
  37.860 @@ -820,10 +566,15 @@
  37.861  
  37.862      /**
  37.863       * Set the last token for this function's code
  37.864 +     * @param lc lexical context
  37.865       * @param lastToken the last token
  37.866 +     * @return function node or a new one if state was changed
  37.867       */
  37.868 -    public void setLastToken(final long lastToken) {
  37.869 -        this.lastToken = lastToken;
  37.870 +    public FunctionNode setLastToken(final LexicalContext lc, final long lastToken) {
  37.871 +        if (this.lastToken == lastToken) {
  37.872 +            return this;
  37.873 +        }
  37.874 +        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
  37.875      }
  37.876  
  37.877      /**
  37.878 @@ -835,21 +586,13 @@
  37.879      }
  37.880  
  37.881      /**
  37.882 -     * Set the name of this function
  37.883 -     * @param name the name
  37.884 -     */
  37.885 -    public void setName(final String name) {
  37.886 -        this.name = name;
  37.887 -    }
  37.888 -
  37.889 -    /**
  37.890       * Check if this function should have all its variables in its own scope. Scripts, split sub-functions, and
  37.891       * functions having with and/or eval blocks are such.
  37.892       *
  37.893       * @return true if all variables should be in scope
  37.894       */
  37.895      public boolean allVarsInScope() {
  37.896 -        return isProgram() || (flags & HAS_ALL_VARS_IN_SCOPE) != 0;
  37.897 +        return isProgram() || getFlag(HAS_ALL_VARS_IN_SCOPE);
  37.898      }
  37.899  
  37.900      /**
  37.901 @@ -858,15 +601,7 @@
  37.902       * @return true if this function is split from a larger one
  37.903       */
  37.904      public boolean isSplit() {
  37.905 -        return (flags & IS_SPLIT) != 0;
  37.906 -    }
  37.907 -
  37.908 -    /**
  37.909 -     * Flag this function node as being a sub-function generated by the splitter
  37.910 -     */
  37.911 -    public void setIsSplit() {
  37.912 -        this.flags |= IS_SPLIT;
  37.913 -        setNeedsScope();
  37.914 +        return getFlag(IS_SPLIT);
  37.915      }
  37.916  
  37.917      /**
  37.918 @@ -875,15 +610,7 @@
  37.919       * @return true if there are lazy child functions
  37.920       */
  37.921      public boolean hasLazyChildren() {
  37.922 -        return (flags & HAS_LAZY_CHILDREN) != 0;
  37.923 -    }
  37.924 -
  37.925 -    /**
  37.926 -     * Flag this function node as having yet-to-be-generated child functions
  37.927 -     */
  37.928 -    public void setHasLazyChildren() {
  37.929 -        this.flags |= HAS_LAZY_CHILDREN;
  37.930 -        setNeedsScope();
  37.931 +        return getFlag(HAS_LAZY_CHILDREN);
  37.932      }
  37.933  
  37.934      /**
  37.935 @@ -895,66 +622,13 @@
  37.936      }
  37.937  
  37.938      /**
  37.939 -     * Set the paremeters to this function
  37.940 -     * @param parameters a list of IdentNodes representing parameters in left to right order
  37.941 -     */
  37.942 -    public void setParameters(final List<IdentNode> parameters) {
  37.943 -        this.parameters = parameters;
  37.944 -    }
  37.945 -
  37.946 -    /**
  37.947       * Get a specialized type for an identity, if one exists
  37.948       * @param node node to check specialized type for
  37.949       * @return null if no specialization exists, otherwise type
  37.950       */
  37.951 +    @SuppressWarnings("static-method")
  37.952      public Type getSpecializedType(final IdentNode node) {
  37.953 -        return specializedTypes.get(node);
  37.954 -    }
  37.955 -
  37.956 -    /**
  37.957 -     * Set parameter type hints for specialization.
  37.958 -     * @param types types array of length equal to parameter list size
  37.959 -     */
  37.960 -    public void setParameterTypes(final Class<?>[] types) {
  37.961 -        assert types.length == parameters.size() : "Type vector length doesn't correspond to parameter types";
  37.962 -        //diff - skip the callee and this etc, they are not explicit params in the parse tree
  37.963 -        for (int i = 0; i < types.length ; i++) {
  37.964 -            specializedTypes.put(parameters.get(i), Type.typeFor(types[i]));
  37.965 -        }
  37.966 -    }
  37.967 -
  37.968 -    /**
  37.969 -     * Get the identifier for the variable in which the function return value
  37.970 -     * should be stored
  37.971 -     * @return an IdentNode representing the return value
  37.972 -     */
  37.973 -    public IdentNode getResultNode() {
  37.974 -        return resultNode;
  37.975 -    }
  37.976 -
  37.977 -    /**
  37.978 -     * Set the identifier representing the variable in which the function return
  37.979 -     * value should be stored
  37.980 -     * @param resultNode an IdentNode representing the return value
  37.981 -     */
  37.982 -    public void setResultNode(final IdentNode resultNode) {
  37.983 -        this.resultNode = resultNode;
  37.984 -    }
  37.985 -
  37.986 -    /**
  37.987 -     * Get the identifier representing this function's scope
  37.988 -     * @return an IdentNode representing this function's scope
  37.989 -     */
  37.990 -    public IdentNode getScopeNode() {
  37.991 -        return scopeNode;
  37.992 -    }
  37.993 -
  37.994 -    /**
  37.995 -     * Set the identifier representing this function's scope
  37.996 -     * @param scopeNode an IdentNode representing this function's scope
  37.997 -     */
  37.998 -    public void setScopeNode(final IdentNode scopeNode) {
  37.999 -        this.scopeNode = scopeNode;
 37.1000 +        return null; //TODO implement specialized types later
 37.1001      }
 37.1002  
 37.1003      /**
 37.1004 @@ -962,15 +636,7 @@
 37.1005       * @return true if function is declared.
 37.1006       */
 37.1007      public boolean isDeclared() {
 37.1008 -        return (flags & IS_DECLARED) != 0;
 37.1009 -    }
 37.1010 -
 37.1011 -    /**
 37.1012 -     * Flag this function as being created as a function declaration (as opposed to a function expression).
 37.1013 -     * @see Parser
 37.1014 -     */
 37.1015 -    public void setIsDeclared() {
 37.1016 -        this.flags |= IS_DECLARED;
 37.1017 +        return getFlag(IS_DECLARED);
 37.1018      }
 37.1019  
 37.1020      /**
 37.1021 @@ -978,15 +644,7 @@
 37.1022       * @return true if function is anonymous
 37.1023       */
 37.1024      public boolean isAnonymous() {
 37.1025 -        return (flags & IS_ANONYMOUS) != 0;
 37.1026 -    }
 37.1027 -
 37.1028 -    /**
 37.1029 -     * Flag this function as an anonymous function.
 37.1030 -     * @see Parser
 37.1031 -     */
 37.1032 -    public void setIsAnonymous() {
 37.1033 -        this.flags |= IS_ANONYMOUS;
 37.1034 +        return getFlag(IS_ANONYMOUS);
 37.1035      }
 37.1036  
 37.1037      /**
 37.1038 @@ -995,109 +653,7 @@
 37.1039       * @return true if function needs a symbol for self
 37.1040       */
 37.1041      public boolean needsSelfSymbol() {
 37.1042 -        return (flags & NEEDS_SELF_SYMBOL) != 0;
 37.1043 -    }
 37.1044 -
 37.1045 -    /**
 37.1046 -     * Get the initializer statement for the __callee__ variable, where applicable
 37.1047 -     * for self references
 37.1048 -     * @return initialization
 37.1049 -     */
 37.1050 -    public Node getSelfSymbolInit() {
 37.1051 -        return this.selfSymbolInit;
 37.1052 -    }
 37.1053 -
 37.1054 -    /**
 37.1055 -     * Flag the function as needing a self symbol. This is needed only for
 37.1056 -     * self referring functions
 37.1057 -     * @param selfSymbolInit initialization expression for self symbol
 37.1058 -     */
 37.1059 -    public void setNeedsSelfSymbol(final Node selfSymbolInit) {
 37.1060 -        this.flags |= NEEDS_SELF_SYMBOL;
 37.1061 -        this.selfSymbolInit = selfSymbolInit;
 37.1062 -    }
 37.1063 -
 37.1064 -    /**
 37.1065 -     * Marks this function as using any of its ancestors' scopes.
 37.1066 -     */
 37.1067 -    public void setUsesAncestorScope() {
 37.1068 -        this.flags |= USES_ANCESTOR_SCOPE;
 37.1069 -    }
 37.1070 -
 37.1071 -    @Override
 37.1072 -    void setUsesParentScopeSymbol(Symbol symbol, Iterator<Block> ancestors) {
 37.1073 -        setUsesAncestorScope();
 37.1074 -        super.setUsesParentScopeSymbol(symbol, ancestors);
 37.1075 -    }
 37.1076 -
 37.1077 -    /**
 37.1078 -     * Return the node representing {@code this} in this function
 37.1079 -     * @return IdentNode representing {@code this}
 37.1080 -     */
 37.1081 -    public IdentNode getThisNode() {
 37.1082 -        return thisNode;
 37.1083 -    }
 37.1084 -
 37.1085 -    /**
 37.1086 -     * Set the node representing {@code this} in this function
 37.1087 -     * @param thisNode identifier representing {@code this}
 37.1088 -     */
 37.1089 -    public void setThisNode(final IdentNode thisNode) {
 37.1090 -        this.thisNode = thisNode;
 37.1091 -    }
 37.1092 -
 37.1093 -    /**
 37.1094 -     * Every function declared as {@code function x()} is internally hoisted
 37.1095 -     * and represented as {@code var x = function()  ... }. This getter returns
 37.1096 -     * the VarNode representing this virtual assignment
 37.1097 -     *
 37.1098 -     * @return the var node emitted for setting this function symbol
 37.1099 -     */
 37.1100 -    public VarNode getFunctionVarNode() {
 37.1101 -        return funcVarNode;
 37.1102 -    }
 37.1103 -
 37.1104 -    /**
 37.1105 -     * Set the virtual VarNode assignment for this function.
 37.1106 -     * @see FunctionNode#getFunctionVarNode()
 37.1107 -     *
 37.1108 -     * @param varNode the virtual var node assignment
 37.1109 -     */
 37.1110 -    public void setFunctionVarNode(final VarNode varNode) {
 37.1111 -        funcVarNode = varNode;
 37.1112 -    }
 37.1113 -
 37.1114 -    /**
 37.1115 -     * The line number information where the function was declared must be propagated
 37.1116 -     * to the virtual {@code var x = function() ... } assignment described in
 37.1117 -     * {@link FunctionNode#getFunctionVarNode()}
 37.1118 -     * This maintains the line number of the declaration
 37.1119 -     *
 37.1120 -     * @return a line number node representing the line this function was declared
 37.1121 -     */
 37.1122 -    public LineNumberNode getFunctionVarLineNumberNode() {
 37.1123 -        return funcVarLineNumberNode;
 37.1124 -    }
 37.1125 -
 37.1126 -    /**
 37.1127 -     * Set the virtual VarNode assignment for this function, along with
 37.1128 -     * a line number node for tracking the original start line of the function
 37.1129 -     * declaration
 37.1130 -     *
 37.1131 -     * @param varNode    the virtual var node assignment
 37.1132 -     * @param lineNumber the line number node for the function declaration
 37.1133 -     */
 37.1134 -    public void setFunctionVarNode(final VarNode varNode, final LineNumberNode lineNumber) {
 37.1135 -        funcVarNode           = varNode;
 37.1136 -        funcVarLineNumberNode = lineNumber;
 37.1137 -    }
 37.1138 -
 37.1139 -    /**
 37.1140 -     * Get the namespace this function uses for its symbols
 37.1141 -     * @return the namespace
 37.1142 -     */
 37.1143 -    public Namespace getNamespace() {
 37.1144 -        return namespace;
 37.1145 +        return body.getFlag(Block.NEEDS_SELF_SYMBOL);
 37.1146      }
 37.1147  
 37.1148      @Override
 37.1149 @@ -1118,47 +674,38 @@
 37.1150  
 37.1151      /**
 37.1152       * Set the function return type
 37.1153 -     *
 37.1154 +     * @param lc lexical context
 37.1155       * @param returnType new return type
 37.1156 +     * @return function node or a new one if state was changed
 37.1157       */
 37.1158 -    public void setReturnType(final Type returnType) {
 37.1159 +    public FunctionNode setReturnType(final LexicalContext lc, final Type returnType) {
 37.1160          //we never bother with object types narrower than objects, that will lead to byte code verification errors
 37.1161          //as for instance even if we know we are returning a string from a method, the code generator will always
 37.1162          //treat it as an object, at least for now
 37.1163 -        this.returnType = Type.widest(this.returnType,  returnType.isObject() ? Type.OBJECT : returnType);
 37.1164 -    }
 37.1165 -
 37.1166 -    /**
 37.1167 -     * Set strict mode on or off for this function
 37.1168 -     *
 37.1169 -     * @param isStrictMode true if strict mode should be enabled
 37.1170 -     */
 37.1171 -    public void setStrictMode(final boolean isStrictMode) {
 37.1172 -        flags = isStrictMode ? flags | IS_STRICT_MODE : flags & ~IS_STRICT_MODE;
 37.1173 +        if (this.returnType == returnType) {
 37.1174 +            return this;
 37.1175 +        }
 37.1176 +        return Node.replaceInLexicalContext(
 37.1177 +            lc,
 37.1178 +            this,
 37.1179 +            new FunctionNode(
 37.1180 +                this,
 37.1181 +                lastToken,
 37.1182 +                flags,
 37.1183 +                Type.widest(this.returnType, returnType.isObject() ?
 37.1184 +                    Type.OBJECT :
 37.1185 +                    returnType),
 37.1186 +                compileUnit,
 37.1187 +                compilationState,
 37.1188 +                body));
 37.1189      }
 37.1190  
 37.1191      /**
 37.1192       * Check if the function is generated in strict mode
 37.1193       * @return true if strict mode enabled for function
 37.1194       */
 37.1195 -    public boolean isStrictMode() {
 37.1196 -        return (flags & IS_STRICT_MODE) != 0;
 37.1197 -    }
 37.1198 -
 37.1199 -    /**
 37.1200 -     * Set the lowered state
 37.1201 -     */
 37.1202 -    public void setIsLowered() {
 37.1203 -        flags |= IS_LOWERED;
 37.1204 -    }
 37.1205 -
 37.1206 -    /**
 37.1207 -     * Get the lowered state
 37.1208 -     *
 37.1209 -     * @return true if function is lowered
 37.1210 -     */
 37.1211 -    public boolean isLowered() {
 37.1212 -        return (flags & IS_LOWERED) != 0;
 37.1213 +    public boolean isStrict() {
 37.1214 +        return getFlag(IS_STRICT);
 37.1215      }
 37.1216  
 37.1217      /**
 37.1218 @@ -1173,25 +720,47 @@
 37.1219      /**
 37.1220       * Reset the compile unit used to compile this function
 37.1221       * @see Compiler
 37.1222 +     * @param lc lexical context
 37.1223       * @param compileUnit the compile unit
 37.1224 +     * @return function node or a new one if state was changed
 37.1225       */
 37.1226 -    public void setCompileUnit(final CompileUnit compileUnit) {
 37.1227 -        this.compileUnit = compileUnit;
 37.1228 +    public FunctionNode setCompileUnit(final LexicalContext lc, final CompileUnit compileUnit) {
 37.1229 +        if (this.compileUnit == compileUnit) {
 37.1230 +            return this;
 37.1231 +        }
 37.1232 +        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
 37.1233      }
 37.1234  
 37.1235      /**
 37.1236 -     * Return the method emitter used to write bytecode for this function
 37.1237 -     * @return the method emitter
 37.1238 +     * Create a temporary variable to the current frame.
 37.1239 +     *
 37.1240 +     * @param block that needs the temporary
 37.1241 +     * @param type  Strong type of symbol.
 37.1242 +     * @param node  Primary node to use symbol.
 37.1243 +     *
 37.1244 +     * @return Symbol used.
 37.1245       */
 37.1246 -    public MethodEmitter getMethodEmitter() {
 37.1247 -        return method;
 37.1248 +    public Symbol ensureSymbol(final Block block, final Type type, final Node node) {
 37.1249 +        Symbol symbol = node.getSymbol();
 37.1250 +
 37.1251 +        // If no symbol already present.
 37.1252 +        if (symbol == null) {
 37.1253 +            final String uname = uniqueName(TEMP_PREFIX.symbolName());
 37.1254 +            symbol = new Symbol(uname, IS_TEMP, type);
 37.1255 +            block.putSymbol(uname, symbol);
 37.1256 +            node.setSymbol(symbol);
 37.1257 +        }
 37.1258 +
 37.1259 +        return symbol;
 37.1260      }
 37.1261  
 37.1262      /**
 37.1263 -     * Set the method emitter that is to be used to write bytecode for this function
 37.1264 -     * @param method a method emitter
 37.1265 +     * Get the symbol for a compiler constant, or null if not available (yet)
 37.1266 +     * @param cc compiler constant
 37.1267 +     * @return symbol for compiler constant, or null if not defined yet (for example in Lower)
 37.1268       */
 37.1269 -    public void setMethodEmitter(final MethodEmitter method) {
 37.1270 -        this.method = method;
 37.1271 +    public Symbol compilerConstant(final CompilerConstants cc) {
 37.1272 +        return body.getExistingSymbol(cc.symbolName());
 37.1273      }
 37.1274 +
 37.1275  }
    38.1 --- a/src/jdk/nashorn/internal/ir/IdentNode.java	Fri Apr 19 18:23:00 2013 +0530
    38.2 +++ b/src/jdk/nashorn/internal/ir/IdentNode.java	Fri Apr 19 16:11:16 2013 +0200
    38.3 @@ -32,13 +32,15 @@
    38.4  
    38.5  import jdk.nashorn.internal.codegen.ObjectClassGenerator;
    38.6  import jdk.nashorn.internal.codegen.types.Type;
    38.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    38.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    38.9  import jdk.nashorn.internal.runtime.Source;
   38.10  
   38.11  /**
   38.12   * IR representation for an identifier.
   38.13   */
   38.14 -public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNode>, FunctionCall {
   38.15 +@Immutable
   38.16 +public final class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNode>, FunctionCall {
   38.17      private static final int PROPERTY_NAME    = 1 << 0;
   38.18      private static final int INITIALIZED_HERE = 1 << 1;
   38.19      private static final int FUNCTION         = 1 << 2;
   38.20 @@ -47,9 +49,9 @@
   38.21      private final String name;
   38.22  
   38.23      /** Type for a callsite, e.g. X in a get()X or a set(X)V */
   38.24 -    private Type callSiteType;
   38.25 +    private final Type callSiteType;
   38.26  
   38.27 -    private byte flags;
   38.28 +    private final int flags;
   38.29  
   38.30      /**
   38.31       * Constructor
   38.32 @@ -62,6 +64,15 @@
   38.33      public IdentNode(final Source source, final long token, final int finish, final String name) {
   38.34          super(source, token, finish);
   38.35          this.name = name;
   38.36 +        this.callSiteType = null;
   38.37 +        this.flags = 0;
   38.38 +    }
   38.39 +
   38.40 +    private IdentNode(final IdentNode identNode, final String name, final Type callSiteType, final int flags) {
   38.41 +        super(identNode);
   38.42 +        this.name = name;
   38.43 +        this.callSiteType = callSiteType;
   38.44 +        this.flags = flags;
   38.45      }
   38.46  
   38.47      /**
   38.48 @@ -71,8 +82,9 @@
   38.49       */
   38.50      public IdentNode(final IdentNode identNode) {
   38.51          super(identNode);
   38.52 -        this.name  = identNode.getName();
   38.53 -        this.flags = identNode.flags;
   38.54 +        this.name         = identNode.getName();
   38.55 +        this.callSiteType = null;
   38.56 +        this.flags        = identNode.flags;
   38.57      }
   38.58  
   38.59      @Override
   38.60 @@ -92,40 +104,15 @@
   38.61  
   38.62      @Override
   38.63      public IdentNode setType(final Type type) {
   38.64 -        if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
   38.65 -            ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
   38.66 -        }
   38.67          // do NOT, repeat NOT touch the symbol here. it might be a local variable or whatever. This is the override if it isn't
   38.68 -        if(this.callSiteType == type) {
   38.69 +        if (this.callSiteType == type) {
   38.70              return this;
   38.71          }
   38.72 -        final IdentNode n = (IdentNode)clone();
   38.73 -        n.callSiteType = type;
   38.74 -        return n;
   38.75 -    }
   38.76 +        if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
   38.77 +            ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType());
   38.78 +        }
   38.79  
   38.80 -    @Override
   38.81 -    protected Node copy(final CopyState cs) {
   38.82 -        return new IdentNode(this);
   38.83 -    }
   38.84 -
   38.85 -    /**
   38.86 -     * Test to see if two IdentNode are the same.
   38.87 -     *
   38.88 -     * @param other Other ident.
   38.89 -     * @return true if the idents are the same.
   38.90 -     */
   38.91 -    @Override
   38.92 -    public boolean equals(final Object other) {
   38.93 -        if (other instanceof IdentNode) {
   38.94 -            return name.equals(((IdentNode)other).name);
   38.95 -        }
   38.96 -        return false;
   38.97 -    }
   38.98 -
   38.99 -    @Override
  38.100 -    public int hashCode() {
  38.101 -        return name.hashCode();
  38.102 +        return new IdentNode(this, name, type, flags);
  38.103      }
  38.104  
  38.105      /**
  38.106 @@ -135,7 +122,7 @@
  38.107       */
  38.108      @Override
  38.109      public Node accept(final NodeVisitor visitor) {
  38.110 -        if (visitor.enterIdentNode(this) != null) {
  38.111 +        if (visitor.enterIdentNode(this)) {
  38.112              return visitor.leaveIdentNode(this);
  38.113          }
  38.114  
  38.115 @@ -147,7 +134,7 @@
  38.116          if (hasCallSiteType()) {
  38.117              sb.append('{');
  38.118              final String desc = getType().getDescriptor();
  38.119 -            sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
  38.120 +            sb.append(desc.charAt(desc.length() - 1) == ';' ? 'O' : getType().getDescriptor());
  38.121              sb.append('}');
  38.122          }
  38.123  
  38.124 @@ -191,10 +178,10 @@
  38.125       * @return a node equivalent to this one except for the requested change.
  38.126       */
  38.127      public IdentNode setIsPropertyName() {
  38.128 -        if(isPropertyName()) return this;
  38.129 -        final IdentNode n = (IdentNode)clone();
  38.130 -        n.flags |= PROPERTY_NAME;
  38.131 -        return n;
  38.132 +        if (isPropertyName()) {
  38.133 +            return this;
  38.134 +        }
  38.135 +        return new IdentNode(this, name, callSiteType, flags | PROPERTY_NAME);
  38.136      }
  38.137  
  38.138      /**
  38.139 @@ -210,10 +197,10 @@
  38.140       * @return a node equivalent to this one except for the requested change.
  38.141       */
  38.142      public IdentNode setIsInitializedHere() {
  38.143 -        if(isInitializedHere()) return this;
  38.144 -        final IdentNode n = (IdentNode)clone();
  38.145 -        n.flags |= INITIALIZED_HERE;
  38.146 -        return n;
  38.147 +        if (isInitializedHere()) {
  38.148 +            return this;
  38.149 +        }
  38.150 +        return new IdentNode(this, name, callSiteType, flags | INITIALIZED_HERE);
  38.151      }
  38.152  
  38.153      /**
  38.154 @@ -223,7 +210,7 @@
  38.155       * @return true if this IdentNode is special
  38.156       */
  38.157      public boolean isSpecialIdentity() {
  38.158 -        return name.equals(__DIR__.tag()) || name.equals(__FILE__.tag()) || name.equals(__LINE__.tag());
  38.159 +        return name.equals(__DIR__.symbolName()) || name.equals(__FILE__.symbolName()) || name.equals(__LINE__.symbolName());
  38.160      }
  38.161  
  38.162      @Override
  38.163 @@ -236,9 +223,9 @@
  38.164       * @return an ident node identical to this one in all aspects except with its function flag set.
  38.165       */
  38.166      public IdentNode setIsFunction() {
  38.167 -        if(isFunction()) return this;
  38.168 -        final IdentNode n = (IdentNode)clone();
  38.169 -        n.flags |= FUNCTION;
  38.170 -        return n;
  38.171 +        if (isFunction()) {
  38.172 +            return this;
  38.173 +        }
  38.174 +        return new IdentNode(this, name, callSiteType, flags | FUNCTION);
  38.175      }
  38.176  }
    39.1 --- a/src/jdk/nashorn/internal/ir/IfNode.java	Fri Apr 19 18:23:00 2013 +0530
    39.2 +++ b/src/jdk/nashorn/internal/ir/IfNode.java	Fri Apr 19 16:11:16 2013 +0200
    39.3 @@ -25,22 +25,23 @@
    39.4  
    39.5  package jdk.nashorn.internal.ir;
    39.6  
    39.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    39.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    39.9  import jdk.nashorn.internal.runtime.Source;
   39.10  
   39.11  /**
   39.12   * IR representation for an IF statement.
   39.13 - *
   39.14   */
   39.15 -public class IfNode extends Node {
   39.16 +@Immutable
   39.17 +public final class IfNode extends Node {
   39.18      /** Test expression. */
   39.19 -    private Node test;
   39.20 +    private final Node test;
   39.21  
   39.22      /** Pass statements. */
   39.23 -    private Block pass;
   39.24 +    private final Block pass;
   39.25  
   39.26      /** Fail statements. */
   39.27 -    private Block fail;
   39.28 +    private final Block fail;
   39.29  
   39.30      /**
   39.31       * Constructor
   39.32 @@ -54,37 +55,30 @@
   39.33       */
   39.34      public IfNode(final Source source, final long token, final int finish, final Node test, final Block pass, final Block fail) {
   39.35          super(source, token, finish);
   39.36 -
   39.37          this.test = test;
   39.38          this.pass = pass;
   39.39          this.fail = fail;
   39.40      }
   39.41  
   39.42 -    private IfNode(final IfNode ifNode, final CopyState cs) {
   39.43 +    private IfNode(final IfNode ifNode, final Node test, final Block pass, final Block fail) {
   39.44          super(ifNode);
   39.45 -
   39.46 -        this.test = cs.existingOrCopy(ifNode.test);
   39.47 -        this.pass = (Block)cs.existingOrCopy(ifNode.pass);
   39.48 -        this.fail = (Block)cs.existingOrCopy(ifNode.fail);
   39.49 +        this.test = test;
   39.50 +        this.pass = pass;
   39.51 +        this.fail = fail;
   39.52      }
   39.53  
   39.54      @Override
   39.55 -    protected Node copy(final CopyState cs) {
   39.56 -        return new IfNode(this, cs);
   39.57 +    public boolean isTerminal() {
   39.58 +        return pass.isTerminal() && fail != null && fail.isTerminal();
   39.59      }
   39.60  
   39.61      @Override
   39.62      public Node accept(final NodeVisitor visitor) {
   39.63 -        if (visitor.enterIfNode(this) != null) {
   39.64 -            test = test.accept(visitor);
   39.65 -
   39.66 -            pass = (Block)pass.accept(visitor);
   39.67 -
   39.68 -            if (fail != null) {
   39.69 -                fail = (Block)fail.accept(visitor);
   39.70 -            }
   39.71 -
   39.72 -            return visitor.leaveIfNode(this);
   39.73 +        if (visitor.enterIfNode(this)) {
   39.74 +            return visitor.leaveIfNode(
   39.75 +                setTest(test.accept(visitor)).
   39.76 +                setPass((Block)pass.accept(visitor)).
   39.77 +                setFail(fail == null ? null : (Block)fail.accept(visitor)));
   39.78          }
   39.79  
   39.80          return this;
   39.81 @@ -105,6 +99,13 @@
   39.82          return fail;
   39.83      }
   39.84  
   39.85 +    private IfNode setFail(final Block fail) {
   39.86 +        if (this.fail == fail) {
   39.87 +            return this;
   39.88 +        }
   39.89 +        return new IfNode(this, test, pass, fail);
   39.90 +    }
   39.91 +
   39.92      /**
   39.93       * Get the then block for this IfNode
   39.94       * @return the then block
   39.95 @@ -113,6 +114,13 @@
   39.96          return pass;
   39.97      }
   39.98  
   39.99 +    private IfNode setPass(final Block pass) {
  39.100 +        if (this.pass == pass) {
  39.101 +            return this;
  39.102 +        }
  39.103 +        return new IfNode(this, test, pass, fail);
  39.104 +    }
  39.105 +
  39.106      /**
  39.107       * Get the test expression for this IfNode
  39.108       * @return the test expression
  39.109 @@ -124,8 +132,12 @@
  39.110      /**
  39.111       * Reset the test expression for this IfNode
  39.112       * @param test a new test expression
  39.113 +     * @return new or same IfNode
  39.114       */
  39.115 -    public void setTest(final Node test) {
  39.116 -        this.test = test;
  39.117 +    public IfNode setTest(final Node test) {
  39.118 +        if (this.test == test) {
  39.119 +            return this;
  39.120 +        }
  39.121 +        return new IfNode(this, test, pass, fail);
  39.122      }
  39.123  }
    40.1 --- a/src/jdk/nashorn/internal/ir/IndexNode.java	Fri Apr 19 18:23:00 2013 +0530
    40.2 +++ b/src/jdk/nashorn/internal/ir/IndexNode.java	Fri Apr 19 16:11:16 2013 +0200
    40.3 @@ -25,22 +25,18 @@
    40.4  
    40.5  package jdk.nashorn.internal.ir;
    40.6  
    40.7 -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
    40.8 -
    40.9 -import jdk.nashorn.internal.codegen.ObjectClassGenerator;
   40.10  import jdk.nashorn.internal.codegen.types.Type;
   40.11 +import jdk.nashorn.internal.ir.annotations.Immutable;
   40.12  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   40.13  import jdk.nashorn.internal.runtime.Source;
   40.14  
   40.15  /**
   40.16   * IR representation of an indexed access (brackets operator.)
   40.17 - *
   40.18   */
   40.19 -public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
   40.20 -    /** Property ident. */
   40.21 -    private Node index;
   40.22 -
   40.23 -    private boolean hasCallSiteType;
   40.24 +@Immutable
   40.25 +public final class IndexNode extends BaseNode {
   40.26 +    /** Property index. */
   40.27 +    private final Node index;
   40.28  
   40.29      /**
   40.30       * Constructors
   40.31 @@ -52,50 +48,27 @@
   40.32       * @param index   index for access
   40.33       */
   40.34      public IndexNode(final Source source, final long token, final int finish, final Node base, final Node index) {
   40.35 -        super(source, token, finish, base);
   40.36 -
   40.37 +        super(source, token, finish, base, false, false);
   40.38          this.index = index;
   40.39      }
   40.40  
   40.41 -    /**
   40.42 -     * Copy constructor
   40.43 -     *
   40.44 -     * @param indexNode source node
   40.45 -     */
   40.46 -    public IndexNode(final IndexNode indexNode) {
   40.47 -        this(indexNode, new CopyState());
   40.48 -    }
   40.49 -
   40.50 -    private IndexNode(final IndexNode indexNode, final CopyState cs) {
   40.51 -        super(indexNode, cs);
   40.52 -
   40.53 -        index = cs.existingOrCopy(indexNode.index);
   40.54 -    }
   40.55 -
   40.56 -    @Override
   40.57 -    protected Node copy(final CopyState cs) {
   40.58 -        return new IndexNode(this, cs);
   40.59 -    }
   40.60 -
   40.61 -    @Override
   40.62 -    public boolean equals(final Object other) {
   40.63 -        if (!super.equals(other)) {
   40.64 -            return false;
   40.65 -        }
   40.66 -        return index.equals(((IndexNode)other).getIndex());
   40.67 -    }
   40.68 -
   40.69 -    @Override
   40.70 -    public int hashCode() {
   40.71 -        return super.hashCode() ^ getIndex().hashCode();
   40.72 +    private IndexNode(final IndexNode indexNode, final Node base, final Node index, final boolean isFunction, final boolean hasCallSiteType) {
   40.73 +        super(indexNode, base, isFunction, hasCallSiteType);
   40.74 +        this.index = index;
   40.75      }
   40.76  
   40.77      @Override
   40.78      public Node accept(final NodeVisitor visitor) {
   40.79 -        if (visitor.enterIndexNode(this) != null) {
   40.80 -            base = base.accept(visitor);
   40.81 -            index = index.accept(visitor);
   40.82 -            return visitor.leaveIndexNode(this);
   40.83 +        if (visitor.enterIndexNode(this)) {
   40.84 +            final Node      newBase  = base.accept(visitor);
   40.85 +            final Node      newIndex = index.accept(visitor);
   40.86 +            final IndexNode newNode;
   40.87 +            if (newBase != base || newIndex != index) {
   40.88 +                newNode = new IndexNode(this, newBase, newIndex, isFunction(), hasCallSiteType());
   40.89 +            } else {
   40.90 +                newNode = this;
   40.91 +            }
   40.92 +            return visitor.leaveIndexNode(newNode);
   40.93          }
   40.94  
   40.95          return this;
   40.96 @@ -105,7 +78,7 @@
   40.97      public void toString(final StringBuilder sb) {
   40.98          final boolean needsParen = tokenType().needsParens(base.tokenType(), true);
   40.99  
  40.100 -        if (hasCallSiteType) {
  40.101 +        if (hasCallSiteType()) {
  40.102              sb.append('{');
  40.103              final String desc = getType().getDescriptor();
  40.104              sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
  40.105 @@ -135,27 +108,19 @@
  40.106          return index;
  40.107      }
  40.108  
  40.109 -    /**
  40.110 -     * Reset the index expression for this IndexNode
  40.111 -     * @param index a new index expression
  40.112 -     */
  40.113 -    public void setIndex(final Node index) {
  40.114 -        this.index = index;
  40.115 +    @Override
  40.116 +    public BaseNode setIsFunction() {
  40.117 +        if (isFunction()) {
  40.118 +            return this;
  40.119 +        }
  40.120 +        return new IndexNode(this, base, index, true, hasCallSiteType());
  40.121      }
  40.122  
  40.123      @Override
  40.124      public IndexNode setType(final Type type) {
  40.125 -        if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
  40.126 -            ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
  40.127 -        }
  40.128 -        hasCallSiteType = true;
  40.129 -        getSymbol().setTypeOverride(type);
  40.130 -        return this;
  40.131 -    }
  40.132 -
  40.133 -    @Override
  40.134 -    public boolean canHaveCallSiteType() {
  40.135 -        return true; //carried by the symbol and always the same nodetype==symboltype
  40.136 +        logTypeChange(type);
  40.137 +        getSymbol().setTypeOverride(type); //always a temp so this is fine.
  40.138 +        return new IndexNode(this, base, index, isFunction(), true);
  40.139      }
  40.140  
  40.141  }
    41.1 --- a/src/jdk/nashorn/internal/ir/LabelNode.java	Fri Apr 19 18:23:00 2013 +0530
    41.2 +++ b/src/jdk/nashorn/internal/ir/LabelNode.java	Fri Apr 19 16:11:16 2013 +0200
    41.3 @@ -25,29 +25,20 @@
    41.4  
    41.5  package jdk.nashorn.internal.ir;
    41.6  
    41.7 -import jdk.nashorn.internal.ir.annotations.Ignore;
    41.8 +import jdk.nashorn.internal.ir.annotations.Immutable;
    41.9  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   41.10  import jdk.nashorn.internal.runtime.Source;
   41.11  
   41.12  /**
   41.13   * IR representation for a labeled statement.
   41.14 - *
   41.15   */
   41.16 -
   41.17 -public class LabelNode extends Node {
   41.18 +@Immutable
   41.19 +public final class LabelNode extends LexicalContextNode {
   41.20      /** Label ident. */
   41.21 -    private IdentNode label;
   41.22 +    private final IdentNode label;
   41.23  
   41.24      /** Statements. */
   41.25 -    private Block body;
   41.26 -
   41.27 -    /** Node to break from. */
   41.28 -    @Ignore
   41.29 -    private Node breakNode;
   41.30 -
   41.31 -    /** Node to continue. */
   41.32 -    @Ignore
   41.33 -    private Node continueNode;
   41.34 +    private final Block body;
   41.35  
   41.36      /**
   41.37       * Constructor
   41.38 @@ -65,26 +56,23 @@
   41.39          this.body  = body;
   41.40      }
   41.41  
   41.42 -    private LabelNode(final LabelNode labelNode, final CopyState cs) {
   41.43 +    private LabelNode(final LabelNode labelNode, final IdentNode label, final Block body) {
   41.44          super(labelNode);
   41.45 -
   41.46 -        this.label        = (IdentNode)cs.existingOrCopy(labelNode.label);
   41.47 -        this.body         = (Block)cs.existingOrCopy(labelNode.body);
   41.48 -        this.breakNode    = cs.existingOrSame(labelNode.breakNode);
   41.49 -        this.continueNode = cs.existingOrSame(labelNode.continueNode);
   41.50 +        this.label = label;
   41.51 +        this.body  = body;
   41.52      }
   41.53  
   41.54      @Override
   41.55 -    protected Node copy(final CopyState cs) {
   41.56 -        return new LabelNode(this, cs);
   41.57 +    public boolean isTerminal() {
   41.58 +        return body.isTerminal();
   41.59      }
   41.60  
   41.61      @Override
   41.62 -    public Node accept(final NodeVisitor visitor) {
   41.63 -        if (visitor.enterLabelNode(this) != null) {
   41.64 -            label = (IdentNode)label.accept(visitor);
   41.65 -            body  = (Block)body.accept(visitor);
   41.66 -            return visitor.leaveLabelNode(this);
   41.67 +    public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
   41.68 +        if (visitor.enterLabelNode(this)) {
   41.69 +            return visitor.leaveLabelNode(
   41.70 +                setLabel(visitor.getLexicalContext(), (IdentNode)label.accept(visitor)).
   41.71 +                setBody(visitor.getLexicalContext(), (Block)body.accept(visitor)));
   41.72          }
   41.73  
   41.74          return this;
   41.75 @@ -106,44 +94,15 @@
   41.76  
   41.77      /**
   41.78       * Reset the body of the node
   41.79 +     * @param lc lexical context
   41.80       * @param body new body
   41.81 +     * @return new for node if changed or existing if not
   41.82       */
   41.83 -    public void setBody(final Block body) {
   41.84 -        this.body = body;
   41.85 -    }
   41.86 -
   41.87 -    /**
   41.88 -     * Get the break node for this node
   41.89 -     * @return the break node
   41.90 -     */
   41.91 -    public Node getBreakNode() {
   41.92 -        return breakNode;
   41.93 -    }
   41.94 -
   41.95 -    /**
   41.96 -     * Reset the break node for this node
   41.97 -     * @param breakNode the break node
   41.98 -     */
   41.99 -    public void setBreakNode(final Node breakNode) {
  41.100 -        assert breakNode instanceof BreakableNode || breakNode instanceof Block : "Invalid break node: " + breakNode;
  41.101 -        this.breakNode = breakNode;
  41.102 -    }
  41.103 -
  41.104 -    /**
  41.105 -     * Get the continue node for this node
  41.106 -     * @return the continue node
  41.107 -     */
  41.108 -    public Node getContinueNode() {
  41.109 -        return continueNode;
  41.110 -    }
  41.111 -
  41.112 -    /**
  41.113 -     * Reset the continue node for this node
  41.114 -     * @param continueNode the continue node
  41.115 -     */
  41.116 -    public void setContinueNode(final Node continueNode) {
  41.117 -        assert continueNode instanceof WhileNode : "invalid continue node: " + continueNode;
  41.118 -        this.continueNode = continueNode;
  41.119 +    public LabelNode setBody(final LexicalContext lc, final Block body) {
  41.120 +        if (this.body == body) {
  41.121 +            return this;
  41.122 +        }
  41.123 +        return Node.replaceInLexicalContext(lc, this, new LabelNode(this, label, body));
  41.124      }
  41.125  
  41.126      /**
  41.127 @@ -154,4 +113,11 @@
  41.128          return label;
  41.129      }
  41.130  
  41.131 +    private LabelNode setLabel(final LexicalContext lc, final IdentNode label) {
  41.132 +        if (this.label == label) {
  41.133 +            return this;
  41.134 +        }
  41.135 +        return Node.replaceInLexicalContext(lc, this, new LabelNode(this, label, body));
  41.136 +    }
  41.137 +
  41.138  }
    42.1 --- a/src/jdk/nashorn/internal/ir/LabeledNode.java	Fri Apr 19 18:23:00 2013 +0530
    42.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    42.3 @@ -1,123 +0,0 @@
    42.4 -/*
    42.5 - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    42.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    42.7 - *
    42.8 - * This code is free software; you can redistribute it and/or modify it
    42.9 - * under the terms of the GNU General Public License version 2 only, as
   42.10 - * published by the Free Software Foundation.  Oracle designates this
   42.11 - * particular file as subject to the "Classpath" exception as provided
   42.12 - * by Oracle in the LICENSE file that accompanied this code.
   42.13 - *
   42.14 - * This code is distributed in the hope that it will be useful, but WITHOUT
   42.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   42.16 - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   42.17 - * version 2 for more details (a copy is included in the LICENSE file that
   42.18 - * accompanied this code).
   42.19 - *
   42.20 - * You should have received a copy of the GNU General Public License version
   42.21 - * 2 along with this work; if not, write to the Free Software Foundation,
   42.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   42.23 - *
   42.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   42.25 - * or visit www.oracle.com if you need additional information or have any
   42.26 - * questions.
   42.27 - */
   42.28 -
   42.29 -package jdk.nashorn.internal.ir;
   42.30 -
   42.31 -import jdk.nashorn.internal.ir.annotations.Ignore;
   42.32 -import jdk.nashorn.internal.runtime.Source;
   42.33 -
   42.34 -/**
   42.35 - * IR base class for break and continue.
   42.36 - *
   42.37 - */
   42.38 -public abstract class LabeledNode extends Node {
   42.39 -    /** Optional label. */
   42.40 -    @Ignore
   42.41 -    protected final LabelNode labelNode;
   42.42 -
   42.43 -    /** Target control node. */
   42.44 -    @Ignore
   42.45 -    protected final Node targetNode;
   42.46 -
   42.47 -    /** Try chain. */
   42.48 -    @Ignore
   42.49 -    protected final TryNode tryChain;
   42.50 -
   42.51 -    /** scope nesting level */
   42.52 -    protected int scopeNestingLevel;
   42.53 -
   42.54 -    /**
   42.55 -     * Constructor
   42.56 -     *
   42.57 -     * @param source     the source
   42.58 -     * @param token      token
   42.59 -     * @param finish     finish
   42.60 -     * @param labelNode  the label node
   42.61 -     * @param targetNode the place to break to
   42.62 -     * @param tryChain   the try chain
   42.63 -     */
   42.64 -    public LabeledNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) {
   42.65 -        super(source, token, finish);
   42.66 -
   42.67 -        this.labelNode  = labelNode;
   42.68 -        this.targetNode = targetNode;
   42.69 -        this.tryChain   = tryChain;
   42.70 -    }
   42.71 -
   42.72 -    /**
   42.73 -     * Copy constructor
   42.74 -     *
   42.75 -     * @param labeledNode source node
   42.76 -     * @param cs          copy state
   42.77 -     */
   42.78 -    protected LabeledNode(final LabeledNode labeledNode, final CopyState cs) {
   42.79 -        super(labeledNode);
   42.80 -
   42.81 -        this.labelNode         = (LabelNode)cs.existingOrCopy(labeledNode.labelNode);
   42.82 -        this.targetNode        = cs.existingOrSame(labeledNode.targetNode);
   42.83 -        this.tryChain          = (TryNode)cs.existingOrSame(labeledNode.tryChain);
   42.84 -        this.scopeNestingLevel = labeledNode.scopeNestingLevel;
   42.85 -    }
   42.86 -
   42.87 -    /**
   42.88 -     * Get the label
   42.89 -     * @return the label
   42.90 -     */
   42.91 -    public LabelNode getLabel() {
   42.92 -        return labelNode;
   42.93 -    }
   42.94 -
   42.95 -    /**
   42.96 -     * Get the target node
   42.97 -     * @return the target node
   42.98 -     */
   42.99 -    public Node getTargetNode() {
  42.100 -        return targetNode;
  42.101 -    }
  42.102 -
  42.103 -    /**
  42.104 -     * Get the surrounding try chain
  42.105 -     * @return the try chain
  42.106 -     */
  42.107 -    public TryNode getTryChain() {
  42.108 -        return tryChain;
  42.109 -    }
  42.110 -
  42.111 -    /**
  42.112 -     * Get the scope nesting level
  42.113 -     * @return nesting level
  42.114 -     */
  42.115 -    public int getScopeNestingLevel() {
  42.116 -        return scopeNestingLevel;
  42.117 -    }
  42.118 -
  42.119 -    /**
  42.120 -     * Set scope nesting level
  42.121 -     * @param level the new level
  42.122 -     */
  42.123 -    public void setScopeNestingLevel(final int level) {
  42.124 -        scopeNestingLevel = level;
  42.125 -    }
  42.126 -}
    43.1 --- a/src/jdk/nashorn/internal/ir/LexicalContext.java	Fri Apr 19 18:23:00 2013 +0530
    43.2 +++ b/src/jdk/nashorn/internal/ir/LexicalContext.java	Fri Apr 19 16:11:16 2013 +0200
    43.3 @@ -1,40 +1,224 @@
    43.4 +/*
    43.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    43.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    43.7 + *
    43.8 + * This code is free software; you can redistribute it and/or modify it
    43.9 + * under the terms of the GNU General Public License version 2 only, as
   43.10 + * published by the Free Software Foundation.  Oracle designates this
   43.11 + * particular file as subject to the "Classpath" exception as provided
   43.12 + * by Oracle in the LICENSE file that accompanied this code.
   43.13 + *
   43.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   43.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   43.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   43.17 + * version 2 for more details (a copy is included in the LICENSE file that
   43.18 + * accompanied this code).
   43.19 + *
   43.20 + * You should have received a copy of the GNU General Public License version
   43.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   43.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   43.23 + *
   43.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   43.25 + * or visit www.oracle.com if you need additional information or have any
   43.26 + * questions.
   43.27 + */
   43.28  package jdk.nashorn.internal.ir;
   43.29  
   43.30 -import java.util.ArrayDeque;
   43.31 -import java.util.Deque;
   43.32 +import java.io.File;
   43.33  import java.util.Iterator;
   43.34  import java.util.NoSuchElementException;
   43.35  
   43.36 +import jdk.nashorn.internal.codegen.Label;
   43.37 +import jdk.nashorn.internal.runtime.Debug;
   43.38 +import jdk.nashorn.internal.runtime.Source;
   43.39 +
   43.40  /**
   43.41   * A class that tracks the current lexical context of node visitation as a stack of {@link Block} nodes. Has special
   43.42   * methods to retrieve useful subsets of the context.
   43.43 + *
   43.44 + * This is implemented with a primitive array and a stack pointer, because it really makes a difference
   43.45 + * performance wise. None of the collection classes were optimal
   43.46   */
   43.47 -public class LexicalContext implements Cloneable {
   43.48 -    private final Deque<Block> lexicalContext;
   43.49 +public class LexicalContext {
   43.50 +    private LexicalContextNode[] stack;
   43.51 +
   43.52 +    private int[] flags;
   43.53 +    private int sp;
   43.54  
   43.55      /**
   43.56       * Creates a new empty lexical context.
   43.57       */
   43.58      public LexicalContext() {
   43.59 -        lexicalContext = new ArrayDeque<>();
   43.60 +        stack = new LexicalContextNode[16];
   43.61 +        flags = new int[16];
   43.62      }
   43.63  
   43.64      /**
   43.65 -     * Pushes a new block on top of the context, making it the innermost open block.
   43.66 -     * @param block the new block
   43.67 +     * Set the flags for a lexical context node on the stack. Does not
   43.68 +     * replace the flags, but rather adds to them
   43.69 +     *
   43.70 +     * @param node  node
   43.71 +     * @param flag  new flag to set
   43.72       */
   43.73 -    public void push(Block block) {
   43.74 -        //new Exception(block.toString()).printStackTrace();
   43.75 -        lexicalContext.push(block);
   43.76 +    public void setFlag(final LexicalContextNode node, final int flag) {
   43.77 +        if (flag != 0) {
   43.78 +            for (int i = sp - 1; i >= 0; i--) {
   43.79 +                if (stack[i] == node) {
   43.80 +                    flags[i] |= flag;
   43.81 +                    //System.err.println("Setting flag " + node + " " + flag);
   43.82 +                    return;
   43.83 +                }
   43.84 +            }
   43.85 +        }
   43.86 +        assert false;
   43.87      }
   43.88  
   43.89      /**
   43.90 -     * Pops the innermost block off the context.
   43.91 -     * @param the block expected to be popped, used to detect unbalanced pushes/pops
   43.92 +     * Get the flags for a lexical context node on the stack
   43.93 +     * @param node node
   43.94 +     * @return the flags for the node
   43.95       */
   43.96 -    public void pop(Block block) {
   43.97 -        final Block popped = lexicalContext.pop();
   43.98 -        assert popped == block;
   43.99 +    public int getFlags(final LexicalContextNode node) {
  43.100 +        for (int i = sp - 1; i >= 0; i--) {
  43.101 +            if (stack[i] == node) {
  43.102 +                return flags[i];
  43.103 +            }
  43.104 +        }
  43.105 +        throw new AssertionError("flag node not on context stack");
  43.106 +    }
  43.107 +
  43.108 +    /**
  43.109 +     * Get the function body of a function node on the lexical context
  43.110 +     * stack. This will trigger an assertion if node isn't present
  43.111 +     * @param functionNode function node
  43.112 +     * @return body of function node
  43.113 +     */
  43.114 +    public Block getFunctionBody(final FunctionNode functionNode) {
  43.115 +        for (int i = sp - 1; i >= 0 ; i--) {
  43.116 +            if (stack[i] == functionNode) {
  43.117 +                return (Block)stack[i + 1];
  43.118 +            }
  43.119 +        }
  43.120 +        throw new AssertionError(functionNode.getName() + " not on context stack");
  43.121 +    }
  43.122 +
  43.123 +    /**
  43.124 +     * Return all nodes in the LexicalContext
  43.125 +     * @return all nodes
  43.126 +     */
  43.127 +    public Iterator<LexicalContextNode> getAllNodes() {
  43.128 +        return new NodeIterator<>(LexicalContextNode.class);
  43.129 +    }
  43.130 +
  43.131 +    /**
  43.132 +     * Returns the outermost function in this context. It is either the program, or a lazily compiled function.
  43.133 +     * @return the outermost function in this context.
  43.134 +     */
  43.135 +    public FunctionNode getOutermostFunction() {
  43.136 +        return (FunctionNode)stack[0];
  43.137 +    }
  43.138 +
  43.139 +
  43.140 +
  43.141 +    /**
  43.142 +     * Pushes a new block on top of the context, making it the innermost open block.
  43.143 +     * @param node the new node
  43.144 +     * @return the node that was pushed
  43.145 +     */
  43.146 +    public <T extends LexicalContextNode> T push(final T node) {
  43.147 +        if (sp == stack.length) {
  43.148 +            final LexicalContextNode[] newStack = new LexicalContextNode[sp * 2];
  43.149 +            System.arraycopy(stack, 0, newStack, 0, sp);
  43.150 +            stack = newStack;
  43.151 +
  43.152 +            final int[] newFlags = new int[sp * 2];
  43.153 +            System.arraycopy(flags, 0, newFlags, 0, sp);
  43.154 +            flags = newFlags;
  43.155 +
  43.156 +        }
  43.157 +        stack[sp] = node;
  43.158 +        flags[sp] = 0;
  43.159 +
  43.160 +        sp++;
  43.161 +
  43.162 +        return node;
  43.163 +    }
  43.164 +
  43.165 +    /**
  43.166 +     * Is the context empty?
  43.167 +     * @return true if empty
  43.168 +     */
  43.169 +    public boolean isEmpty() {
  43.170 +        return sp == 0;
  43.171 +    }
  43.172 +
  43.173 +    /**
  43.174 +     * The depth of the lexical context
  43.175 +     * @return depth
  43.176 +     */
  43.177 +    public int size() {
  43.178 +        return sp;
  43.179 +    }
  43.180 +
  43.181 +    /**
  43.182 +     * Pops the innermost block off the context and all nodes that has been contributed
  43.183 +     * since it was put there
  43.184 +     *
  43.185 +     * @param node the node expected to be popped, used to detect unbalanced pushes/pops
  43.186 +     * @return the node that was popped
  43.187 +     */
  43.188 +    @SuppressWarnings("unchecked")
  43.189 +    public <T extends LexicalContextNode> T pop(final T node) {
  43.190 +        --sp;
  43.191 +        final LexicalContextNode popped = stack[sp];
  43.192 +        if (popped instanceof Flags) {
  43.193 +            return (T)((Flags<?>)popped).setFlag(this, flags[sp]);
  43.194 +        }
  43.195 +
  43.196 +        return (T)popped;
  43.197 +    }
  43.198 +
  43.199 +
  43.200 +    /**
  43.201 +     * Return the top element in the context
  43.202 +     * @return the node that was pushed last
  43.203 +     */
  43.204 +    public LexicalContextNode peek() {
  43.205 +        return stack[sp - 1];
  43.206 +    }
  43.207 +
  43.208 +    /**
  43.209 +     * Check if a node is in the lexical context
  43.210 +     * @param node node to check for
  43.211 +     * @return true if in the context
  43.212 +     */
  43.213 +    public boolean contains(final LexicalContextNode node) {
  43.214 +        for (int i = 0; i < sp; i++) {
  43.215 +            if (stack[i] == node) {
  43.216 +                return true;
  43.217 +            }
  43.218 +        }
  43.219 +        return false;
  43.220 +    }
  43.221 +
  43.222 +    /**
  43.223 +     * Replace a node on the lexical context with a new one. Normally
  43.224 +     * you should try to engineer IR traversals so this isn't needed
  43.225 +     *
  43.226 +     * @param oldNode old node
  43.227 +     * @param newNode new node
  43.228 +     * @return the new node
  43.229 +     */
  43.230 +    public LexicalContextNode replace(final LexicalContextNode oldNode, final LexicalContextNode newNode) {
  43.231 +       //System.err.println("REPLACE old=" + Debug.id(oldNode) + " new=" + Debug.id(newNode));
  43.232 +        for (int i = sp - 1; i >= 0; i--) {
  43.233 +            if (stack[i] == oldNode) {
  43.234 +                assert i == (sp - 1) : "violation of contract - we always expect to find the replacement node on top of the lexical context stack: " + newNode + " has " + stack[i + 1].getClass() + " above it";
  43.235 +                stack[i] = newNode;
  43.236 +                break;
  43.237 +            }
  43.238 +         }
  43.239 +        return newNode;
  43.240      }
  43.241  
  43.242      /**
  43.243 @@ -42,7 +226,7 @@
  43.244       * @return an iterator over all blocks in the context.
  43.245       */
  43.246      public Iterator<Block> getBlocks() {
  43.247 -        return lexicalContext.iterator();
  43.248 +        return new NodeIterator<>(Block.class);
  43.249      }
  43.250  
  43.251      /**
  43.252 @@ -50,47 +234,17 @@
  43.253       * @return an iterator over all functions in the context.
  43.254       */
  43.255      public Iterator<FunctionNode> getFunctions() {
  43.256 -        return new FunctionIterator(getBlocks());
  43.257 +        return new NodeIterator<>(FunctionNode.class);
  43.258      }
  43.259  
  43.260 -    private static final class FunctionIterator implements Iterator<FunctionNode> {
  43.261 -        private final Iterator<Block> it;
  43.262 -        private FunctionNode next;
  43.263 -
  43.264 -        FunctionIterator(Iterator<Block> it) {
  43.265 -            this.it = it;
  43.266 -            next = findNext();
  43.267 -        }
  43.268 -
  43.269 -        @Override
  43.270 -        public boolean hasNext() {
  43.271 -            return next != null;
  43.272 -        }
  43.273 -
  43.274 -        @Override
  43.275 -        public FunctionNode next() {
  43.276 -            if(next == null) {
  43.277 -                throw new NoSuchElementException();
  43.278 -            }
  43.279 -            FunctionNode lnext = next;
  43.280 -            next = findNext();
  43.281 -            return lnext;
  43.282 -        }
  43.283 -
  43.284 -        private FunctionNode findNext() {
  43.285 -            while(it.hasNext()) {
  43.286 -                final Block block = it.next();
  43.287 -                if(block instanceof FunctionNode) {
  43.288 -                    return ((FunctionNode)block);
  43.289 -                }
  43.290 -            }
  43.291 -            return null;
  43.292 -        }
  43.293 -
  43.294 -        @Override
  43.295 -        public void remove() {
  43.296 -            throw new UnsupportedOperationException();
  43.297 -        }
  43.298 +    /**
  43.299 +     * Get the parent block for the current lexical context block
  43.300 +     * @return parent block
  43.301 +     */
  43.302 +    public Block getParentBlock() {
  43.303 +        final Iterator<Block> iter = new NodeIterator<>(Block.class, getCurrentFunction());
  43.304 +        iter.next();
  43.305 +        return iter.hasNext() ? iter.next() : null;
  43.306      }
  43.307  
  43.308      /**
  43.309 @@ -98,12 +252,12 @@
  43.310       * @param block the block whose ancestors are returned
  43.311       * @return an iterator over all ancestors block of the given block.
  43.312       */
  43.313 -    public Iterator<Block> getAncestorBlocks(Block block) {
  43.314 -        final Iterator<Block> it = getBlocks();
  43.315 -        while(it.hasNext()) {
  43.316 -            final Block b = it.next();
  43.317 -            if(block == b) {
  43.318 -                return it;
  43.319 +    public Iterator<Block> getAncestorBlocks(final Block block) {
  43.320 +        final Iterator<Block> iter = getBlocks();
  43.321 +        while (iter.hasNext()) {
  43.322 +            final Block b = iter.next();
  43.323 +            if (block == b) {
  43.324 +                return iter;
  43.325              }
  43.326          }
  43.327          throw new AssertionError("Block is not on the current lexical context stack");
  43.328 @@ -115,17 +269,17 @@
  43.329       * @return an iterator over a block and all its ancestors.
  43.330       */
  43.331      public Iterator<Block> getBlocks(final Block block) {
  43.332 -        final Iterator<Block> it = getAncestorBlocks(block);
  43.333 +        final Iterator<Block> iter = getAncestorBlocks(block);
  43.334          return new Iterator<Block>() {
  43.335              boolean blockReturned = false;
  43.336              @Override
  43.337              public boolean hasNext() {
  43.338 -                return it.hasNext() || !blockReturned;
  43.339 +                return iter.hasNext() || !blockReturned;
  43.340              }
  43.341              @Override
  43.342              public Block next() {
  43.343 -                if(blockReturned) {
  43.344 -                    return it.next();
  43.345 +                if (blockReturned) {
  43.346 +                    return iter.next();
  43.347                  }
  43.348                  blockReturned = true;
  43.349                  return block;
  43.350 @@ -138,45 +292,25 @@
  43.351      }
  43.352  
  43.353      /**
  43.354 -     * Returns the closest function node to the block. If the block is itself a function, it is returned.
  43.355 -     * @param block the block
  43.356 -     * @return the function closest to the block.
  43.357 -     * @see #getParentFunction(Block)
  43.358 +     * Get the function for this block. If the block is itself a function
  43.359 +     * this returns identity
  43.360 +     * @param block block for which to get function
  43.361 +     * @return function for block
  43.362       */
  43.363 -    public FunctionNode getFunction(Block block) {
  43.364 -        if(block instanceof FunctionNode) {
  43.365 -            return (FunctionNode)block;
  43.366 -        }
  43.367 -        return getParentFunction(block);
  43.368 -    }
  43.369 -
  43.370 -    /**
  43.371 -     * Returns the closest function node to the block and all its ancestor functions. If the block is itself a function,
  43.372 -     * it is returned too.
  43.373 -     * @param block the block
  43.374 -     * @return the closest function node to the block and all its ancestor functions.
  43.375 -     */
  43.376 -    public Iterator<FunctionNode> getFunctions(final Block block) {
  43.377 -        return new FunctionIterator(getBlocks(block));
  43.378 -    }
  43.379 -
  43.380 -    /**
  43.381 -     * Returns the containing function of the block. If the block is itself a function, its parent function is returned.
  43.382 -     * @param block the block
  43.383 -     * @return the containing function of the block.
  43.384 -     * @see #getFunction(Block)
  43.385 -     */
  43.386 -    public FunctionNode getParentFunction(Block block) {
  43.387 -        return getFirstFunction(getAncestorBlocks(block));
  43.388 -    }
  43.389 -
  43.390 -    private static FunctionNode getFirstFunction(Iterator<Block> it) {
  43.391 -        while(it.hasNext()) {
  43.392 -            final Block ancestor = it.next();
  43.393 -            if(ancestor instanceof FunctionNode) {
  43.394 -                return (FunctionNode)ancestor;
  43.395 +    public FunctionNode getFunction(final Block block) {
  43.396 +        final Iterator<LexicalContextNode> iter = new NodeIterator<>(LexicalContextNode.class);
  43.397 +        while (iter.hasNext()) {
  43.398 +            final LexicalContextNode next = iter.next();
  43.399 +            if (next == block) {
  43.400 +                while (iter.hasNext()) {
  43.401 +                    final LexicalContextNode next2 = iter.next();
  43.402 +                    if (next2 instanceof FunctionNode) {
  43.403 +                        return (FunctionNode)next2;
  43.404 +                    }
  43.405 +                }
  43.406              }
  43.407          }
  43.408 +        assert false;
  43.409          return null;
  43.410      }
  43.411  
  43.412 @@ -185,7 +319,7 @@
  43.413       * @return the innermost block in the context.
  43.414       */
  43.415      public Block getCurrentBlock() {
  43.416 -        return lexicalContext.element();
  43.417 +        return getBlocks().next();
  43.418      }
  43.419  
  43.420      /**
  43.421 @@ -193,6 +327,292 @@
  43.422       * @return the innermost function in the context.
  43.423       */
  43.424      public FunctionNode getCurrentFunction() {
  43.425 -        return getFirstFunction(getBlocks());
  43.426 +        if (isEmpty()) {
  43.427 +            return null;
  43.428 +        }
  43.429 +        return new NodeIterator<>(FunctionNode.class).next();
  43.430 +    }
  43.431 +
  43.432 +    /**
  43.433 +     * Get the block in which a symbol is defined
  43.434 +     * @param symbol symbol
  43.435 +     * @return block in which the symbol is defined, assert if no such block in context
  43.436 +     */
  43.437 +    public Block getDefiningBlock(final Symbol symbol) {
  43.438 +        if (symbol.isTemp()) {
  43.439 +            return null;
  43.440 +        }
  43.441 +        final String name = symbol.getName();
  43.442 +        for (final Iterator<Block> it = getBlocks(); it.hasNext();) {
  43.443 +            final Block next = it.next();
  43.444 +            if (next.getExistingSymbol(name) == symbol) {
  43.445 +                return next;
  43.446 +            }
  43.447 +        }
  43.448 +        throw new AssertionError("Couldn't find symbol " + name + " in the context");
  43.449 +    }
  43.450 +
  43.451 +    /**
  43.452 +     * Get the function in which a symbol is defined
  43.453 +     * @param symbol symbol
  43.454 +     * @return function node in which this symbol is defined, assert if no such symbol exists in context
  43.455 +     */
  43.456 +    public FunctionNode getDefiningFunction(Symbol symbol) {
  43.457 +        if (symbol.isTemp()) {
  43.458 +            return null;
  43.459 +        }
  43.460 +        final String name = symbol.getName();
  43.461 +        for (final Iterator<LexicalContextNode> iter = new NodeIterator<>(LexicalContextNode.class); iter.hasNext();) {
  43.462 +            final LexicalContextNode next = iter.next();
  43.463 +            if (next instanceof Block && ((Block)next).getExistingSymbol(name) == symbol) {
  43.464 +                while (iter.hasNext()) {
  43.465 +                    final LexicalContextNode next2 = iter.next();
  43.466 +                    if (next2 instanceof FunctionNode) {
  43.467 +                        return ((FunctionNode)next2);
  43.468 +                    }
  43.469 +                }
  43.470 +                throw new AssertionError("Defining block for symbol " + name + " has no function in the context");
  43.471 +            }
  43.472 +        }
  43.473 +        throw new AssertionError("Couldn't find symbol " + name + " in the context");
  43.474 +    }
  43.475 +
  43.476 +    /**
  43.477 +     * Is the topmost lexical context element a function body?
  43.478 +     * @return true if function body
  43.479 +     */
  43.480 +    public boolean isFunctionBody() {
  43.481 +        return getParentBlock() == null;
  43.482 +    }
  43.483 +
  43.484 +    /**
  43.485 +     * Returns true if the expression defining the function is a callee of a CallNode that should be the second
  43.486 +     * element on the stack, e.g. <code>(function(){})()</code>. That is, if the stack ends with
  43.487 +     * {@code [..., CallNode, FunctionNode]} then {@code callNode.getFunction()} should be equal to
  43.488 +     * {@code functionNode}, and the top of the stack should itself be a variant of {@code functionNode}.
  43.489 +     * @param functionNode the function node being tested
  43.490 +     * @return true if the expression defining the current function is a callee of a call expression.
  43.491 +     */
  43.492 +    public boolean isFunctionDefinedInCurrentCall(FunctionNode functionNode) {
  43.493 +        final LexicalContextNode parent = stack[sp - 2];
  43.494 +        if(parent instanceof CallNode && ((CallNode)parent).getFunction() == functionNode) {
  43.495 +            assert functionNode.getSource() == peek().getSource();
  43.496 +            return true;
  43.497 +        }
  43.498 +        return false;
  43.499 +    }
  43.500 +
  43.501 +    /**
  43.502 +     * Get the parent function for a function in the lexical context
  43.503 +     * @param functionNode function for which to get parent
  43.504 +     * @return parent function of functionNode or null if none (e.g. if functionNode is the program)
  43.505 +     */
  43.506 +    public FunctionNode getParentFunction(final FunctionNode functionNode) {
  43.507 +        final Iterator<FunctionNode> iter = new NodeIterator<>(FunctionNode.class);
  43.508 +        while (iter.hasNext()) {
  43.509 +            final FunctionNode next = iter.next();
  43.510 +            if (next == functionNode) {
  43.511 +                return iter.hasNext() ? iter.next() : null;
  43.512 +            }
  43.513 +        }
  43.514 +        assert false;
  43.515 +        return null;
  43.516 +    }
  43.517 +
  43.518 +    /**
  43.519 +     * Check if lexical context is currently inside a with block
  43.520 +     * @return true if in a with block
  43.521 +     */
  43.522 +    public boolean inWith() {
  43.523 +        return getScopeNestingLevelTo(null) > 0;
  43.524 +    }
  43.525 +
  43.526 +    /**
  43.527 +     * Count the number of with scopes until a given node
  43.528 +     * @param until node to stop counting at, or null if all nodes should be counted
  43.529 +     * @return number of with scopes encountered in the context
  43.530 +     */
  43.531 +    public int getScopeNestingLevelTo(final LexicalContextNode until) {
  43.532 +        //count the number of with nodes until "until" is hit
  43.533 +        int n = 0;
  43.534 +        for (final Iterator<WithNode> iter = new NodeIterator<>(WithNode.class, until); iter.hasNext(); iter.next()) {
  43.535 +            n++;
  43.536 +        }
  43.537 +        return n;
  43.538 +    }
  43.539 +
  43.540 +    private BreakableNode getBreakable() {
  43.541 +        for (final NodeIterator<BreakableNode> iter = new NodeIterator<>(BreakableNode.class, getCurrentFunction()); iter.hasNext(); ) {
  43.542 +            final BreakableNode next = iter.next();
  43.543 +            if (next.isBreakableWithoutLabel()) {
  43.544 +                return next;
  43.545 +            }
  43.546 +        }
  43.547 +        return null;
  43.548 +    }
  43.549 +
  43.550 +    /**
  43.551 +     * Find the breakable node corresponding to this label.
  43.552 +     * @param label label to search for, if null the closest breakable node will be returned unconditionally, e.g. a while loop with no label
  43.553 +     * @return closest breakable node
  43.554 +     */
  43.555 +    public BreakableNode getBreakable(final IdentNode label) {
  43.556 +        if (label != null) {
  43.557 +            final LabelNode foundLabel = findLabel(label.getName());
  43.558 +            if (foundLabel != null) {
  43.559 +                // iterate to the nearest breakable to the foundLabel
  43.560 +                BreakableNode breakable = null;
  43.561 +                for (final NodeIterator<BreakableNode> iter = new NodeIterator<>(BreakableNode.class, foundLabel); iter.hasNext(); ) {
  43.562 +                    breakable = iter.next();
  43.563 +                }
  43.564 +                return breakable;
  43.565 +            }
  43.566 +            return null;
  43.567 +        }
  43.568 +        return getBreakable();
  43.569 +    }
  43.570 +
  43.571 +    private LoopNode getContinueTo() {
  43.572 +        final Iterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, getCurrentFunction());
  43.573 +        return iter.hasNext() ? iter.next() : null;
  43.574 +    }
  43.575 +
  43.576 +    /**
  43.577 +     * Find the continue target node corresponding to this label.
  43.578 +     * @param label label to search for, if null the closest loop node will be returned unconditionally, e.g. a while loop with no label
  43.579 +     * @return closest continue target node
  43.580 +     */
  43.581 +    public LoopNode getContinueTo(final IdentNode label) {
  43.582 +        if (label != null) {
  43.583 +            final LabelNode foundLabel = findLabel(label.getName());
  43.584 +            if (foundLabel != null) {
  43.585 +                // iterate to the nearest loop to the foundLabel
  43.586 +                LoopNode loop = null;
  43.587 +                for (final NodeIterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, foundLabel); iter.hasNext(); ) {
  43.588 +                    loop = iter.next();
  43.589 +                }
  43.590 +                return loop;
  43.591 +            }
  43.592 +            return null;
  43.593 +        }
  43.594 +        return getContinueTo();
  43.595 +    }
  43.596 +
  43.597 +    /**
  43.598 +     * Check the lexical context for a given label node by name
  43.599 +     * @param name name of the label
  43.600 +     * @return LabelNode if found, null otherwise
  43.601 +     */
  43.602 +    public LabelNode findLabel(final String name) {
  43.603 +        for (final Iterator<LabelNode> iter = new NodeIterator<>(LabelNode.class, getCurrentFunction()); iter.hasNext(); ) {
  43.604 +            final LabelNode next = iter.next();
  43.605 +            if (next.getLabel().getName().equals(name)) {
  43.606 +                return next;
  43.607 +            }
  43.608 +        }
  43.609 +        return null;
  43.610 +    }
  43.611 +
  43.612 +    /**
  43.613 +     * Checks whether a given label is a jump destination that lies outside a given
  43.614 +     * split node
  43.615 +     * @param splitNode the split node
  43.616 +     * @param label     the label
  43.617 +     * @return true if label resides outside the split node
  43.618 +     */
  43.619 +    public boolean isExternalTarget(final SplitNode splitNode, final Label label) {
  43.620 +        boolean targetFound = false;
  43.621 +        for (int i = sp - 1; i >= 0; i--) {
  43.622 +            final LexicalContextNode next = stack[i];
  43.623 +            if (next == splitNode) {
  43.624 +                return !targetFound;
  43.625 +            }
  43.626 +
  43.627 +            if (next instanceof BreakableNode) {
  43.628 +                for (final Label l : ((BreakableNode)next).getLabels()) {
  43.629 +                    if (l == label) {
  43.630 +                        targetFound = true;
  43.631 +                        break;
  43.632 +                    }
  43.633 +                }
  43.634 +            }
  43.635 +        }
  43.636 +        assert false : label + " was expected in lexical context " + LexicalContext.this + " but wasn't";
  43.637 +        return false;
  43.638 +    }
  43.639 +
  43.640 +    @Override
  43.641 +    public String toString() {
  43.642 +        final StringBuffer sb = new StringBuffer();
  43.643 +        sb.append("[ ");
  43.644 +        for (int i = 0; i < sp; i++) {
  43.645 +            final Node node = stack[i];
  43.646 +            sb.append(node.getClass().getSimpleName());
  43.647 +            sb.append('@');
  43.648 +            sb.append(Debug.id(node));
  43.649 +            sb.append(':');
  43.650 +            final Source source = node.getSource();
  43.651 +            String src = source.toString();
  43.652 +            if (src.indexOf(File.pathSeparator) != -1) {
  43.653 +                src = src.substring(src.lastIndexOf(File.pathSeparator));
  43.654 +            }
  43.655 +            src += ' ';
  43.656 +            src += source.getLine(node.getStart());
  43.657 +            sb.append(' ');
  43.658 +        }
  43.659 +        sb.append(" ==> ]");
  43.660 +        return sb.toString();
  43.661 +    }
  43.662 +
  43.663 +    private class NodeIterator <T extends LexicalContextNode> implements Iterator<T> {
  43.664 +        private int index;
  43.665 +        private T next;
  43.666 +        private final Class<T> clazz;
  43.667 +        private LexicalContextNode until;
  43.668 +
  43.669 +        NodeIterator(final Class<T> clazz) {
  43.670 +            this(clazz, null);
  43.671 +        }
  43.672 +
  43.673 +        NodeIterator(final Class<T> clazz, final LexicalContextNode until) {
  43.674 +            this.index = sp - 1;
  43.675 +            this.clazz = clazz;
  43.676 +            this.until = until;
  43.677 +            this.next  = findNext();
  43.678 +        }
  43.679 +
  43.680 +        @Override
  43.681 +        public boolean hasNext() {
  43.682 +            return next != null;
  43.683 +        }
  43.684 +
  43.685 +        @Override
  43.686 +        public T next() {
  43.687 +            if (next == null) {
  43.688 +                throw new NoSuchElementException();
  43.689 +            }
  43.690 +            T lnext = next;
  43.691 +            next = findNext();
  43.692 +            return lnext;
  43.693 +        }
  43.694 +
  43.695 +        private T findNext() {
  43.696 +            for (int i = index; i >= 0; i--) {
  43.697 +                final Node node = stack[i];
  43.698 +                if (node == until) {
  43.699 +                    return null;
  43.700 +                }
  43.701 +                if (clazz.isAssignableFrom(node.getClass())) {
  43.702 +                    index = i - 1;
  43.703 +                    return clazz.cast(node);
  43.704 +                }
  43.705 +            }
  43.706 +            return null;
  43.707 +        }
  43.708 +
  43.709 +        @Override
  43.710 +        public void remove() {
  43.711 +            throw new UnsupportedOperationException();
  43.712 +        }
  43.713      }
  43.714  }
    44.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    44.2 +++ b/src/jdk/nashorn/internal/ir/LexicalContextNode.java	Fri Apr 19 16:11:16 2013 +0200
    44.3 @@ -0,0 +1,73 @@
    44.4 +/*
    44.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    44.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    44.7 + *
    44.8 + * This code is free software; you can redistribute it and/or modify it
    44.9 + * under the terms of the GNU General Public License version 2 only, as
   44.10 + * published by the Free Software Foundation.  Oracle designates this
   44.11 + * particular file as subject to the "Classpath" exception as provided
   44.12 + * by Oracle in the LICENSE file that accompanied this code.
   44.13 + *
   44.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   44.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   44.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   44.17 + * version 2 for more details (a copy is included in the LICENSE file that
   44.18 + * accompanied this code).
   44.19 + *
   44.20 + * You should have received a copy of the GNU General Public License version
   44.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   44.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   44.23 + *
   44.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   44.25 + * or visit www.oracle.com if you need additional information or have any
   44.26 + * questions.
   44.27 + */
   44.28 +package jdk.nashorn.internal.ir;
   44.29 +
   44.30 +import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   44.31 +import jdk.nashorn.internal.runtime.Source;
   44.32 +
   44.33 +/**
   44.34 + * Superclass for nodes that can be part of the lexical context
   44.35 + * @see LexicalContext
   44.36 + */
   44.37 +public abstract class LexicalContextNode extends Node {
   44.38 +    /**
   44.39 +     * Constructor
   44.40 +     *
   44.41 +     * @param source source
   44.42 +     * @param token  token
   44.43 +     * @param finish finish
   44.44 +     */
   44.45 +    protected LexicalContextNode(final Source source, final long token, final int finish) {
   44.46 +        super(source, token, finish);
   44.47 +    }
   44.48 +
   44.49 +    /**
   44.50 +     * Copy constructor
   44.51 +     *
   44.52 +     * @param node source node
   44.53 +     */
   44.54 +    protected LexicalContextNode(final LexicalContextNode node) {
   44.55 +        super(node);
   44.56 +    }
   44.57 +
   44.58 +    /**
   44.59 +     * Accept function for the node given a lexical context. It must be prepared
   44.60 +     * to replace itself if present in the lexical context
   44.61 +     *
   44.62 +     * @param lc      lexical context
   44.63 +     * @param visitor node visitor
   44.64 +     *
   44.65 +     * @return new node or same node depending on state change
   44.66 +     */
   44.67 +    protected abstract Node accept(final LexicalContext lc, final NodeVisitor visitor);
   44.68 +
   44.69 +    @Override
   44.70 +    public Node accept(final NodeVisitor visitor) {
   44.71 +        final LexicalContext lc = visitor.getLexicalContext();
   44.72 +        lc.push(this);
   44.73 +        final LexicalContextNode newNode = (LexicalContextNode)accept(lc, visitor);
   44.74 +        return lc.pop(newNode);
   44.75 +    }
   44.76 +}
    45.1 --- a/src/jdk/nashorn/internal/ir/LineNumberNode.java	Fri Apr 19 18:23:00 2013 +0530
    45.2 +++ b/src/jdk/nashorn/internal/ir/LineNumberNode.java	Fri Apr 19 16:11:16 2013 +0200
    45.3 @@ -25,6 +25,7 @@
    45.4  
    45.5  package jdk.nashorn.internal.ir;
    45.6  
    45.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    45.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    45.9  import jdk.nashorn.internal.parser.Token;
   45.10  import jdk.nashorn.internal.runtime.Source;
   45.11 @@ -32,8 +33,8 @@
   45.12  /**
   45.13   * IR Node representing a line number
   45.14   */
   45.15 -
   45.16 -public class LineNumberNode extends Node {
   45.17 +@Immutable
   45.18 +public final class LineNumberNode extends Node {
   45.19      /** Line number */
   45.20      private final int lineNumber;
   45.21  
   45.22 @@ -46,24 +47,17 @@
   45.23       */
   45.24      public LineNumberNode(final Source source, final long token, final int lineNumber) {
   45.25          super(source, token, Token.descPosition(token));
   45.26 -
   45.27          this.lineNumber = lineNumber;
   45.28      }
   45.29  
   45.30 -   private LineNumberNode(final LineNumberNode lineNumberNode) {
   45.31 +    private LineNumberNode(final LineNumberNode lineNumberNode) {
   45.32          super(lineNumberNode);
   45.33 -
   45.34          this.lineNumber = lineNumberNode.getLineNumber();
   45.35      }
   45.36  
   45.37      @Override
   45.38 -    protected Node copy(final CopyState cs) {
   45.39 -        return new LineNumberNode(this);
   45.40 -    }
   45.41 -
   45.42 -    @Override
   45.43      public Node accept(final NodeVisitor visitor) {
   45.44 -        if (visitor.enterLineNumberNode(this) != null) {
   45.45 +        if (visitor.enterLineNumberNode(this)) {
   45.46              return visitor.leaveLineNumberNode(this);
   45.47          }
   45.48  
    46.1 --- a/src/jdk/nashorn/internal/ir/LiteralNode.java	Fri Apr 19 18:23:00 2013 +0530
    46.2 +++ b/src/jdk/nashorn/internal/ir/LiteralNode.java	Fri Apr 19 16:11:16 2013 +0200
    46.3 @@ -30,6 +30,7 @@
    46.4  import java.util.List;
    46.5  import jdk.nashorn.internal.codegen.CompileUnit;
    46.6  import jdk.nashorn.internal.codegen.types.Type;
    46.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    46.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    46.9  import jdk.nashorn.internal.parser.Lexer.LexerToken;
   46.10  import jdk.nashorn.internal.parser.Token;
   46.11 @@ -44,6 +45,7 @@
   46.12   *
   46.13   * @param <T> the literal type
   46.14   */
   46.15 +@Immutable
   46.16  public abstract class LiteralNode<T> extends Node implements PropertyKey {
   46.17      /** Literal value */
   46.18      protected final T value;
   46.19 @@ -93,23 +95,6 @@
   46.20          return value == null;
   46.21      }
   46.22  
   46.23 -    @Override
   46.24 -    public int hashCode() {
   46.25 -        return value == null ? 0 : value.hashCode();
   46.26 -    }
   46.27 -
   46.28 -    @Override
   46.29 -    public boolean equals(final Object other) {
   46.30 -        if (!(other instanceof LiteralNode<?>)) {
   46.31 -            return false;
   46.32 -        }
   46.33 -        final LiteralNode<?> otherNode = (LiteralNode<?>)other;
   46.34 -        if (otherNode.isNull()) {
   46.35 -            return isNull();
   46.36 -        }
   46.37 -        return ((LiteralNode<?>)other).getValue().equals(value);
   46.38 -    }
   46.39 -
   46.40      /**
   46.41       * Check if the literal value is boolean true
   46.42       * @return true if literal value is boolean true
   46.43 @@ -226,7 +211,7 @@
   46.44       */
   46.45      @Override
   46.46      public Node accept(final NodeVisitor visitor) {
   46.47 -        if (visitor.enterLiteralNode(this) != null) {
   46.48 +        if (visitor.enterLiteralNode(this)) {
   46.49              return visitor.leaveLiteralNode(this);
   46.50          }
   46.51  
   46.52 @@ -274,7 +259,8 @@
   46.53          return new NodeLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
   46.54      }
   46.55  
   46.56 -    private static class BooleanLiteralNode extends LiteralNode<Boolean> {
   46.57 +    @Immutable
   46.58 +    private static final class BooleanLiteralNode extends LiteralNode<Boolean> {
   46.59  
   46.60          private BooleanLiteralNode(final Source source, final long token, final int finish, final boolean value) {
   46.61              super(source, Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value);
   46.62 @@ -285,11 +271,6 @@
   46.63          }
   46.64  
   46.65          @Override
   46.66 -        protected Node copy(final CopyState cs) {
   46.67 -            return new BooleanLiteralNode(this);
   46.68 -        }
   46.69 -
   46.70 -        @Override
   46.71          public boolean isTrue() {
   46.72              return value;
   46.73          }
   46.74 @@ -331,7 +312,8 @@
   46.75          return new BooleanLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
   46.76      }
   46.77  
   46.78 -    private static class NumberLiteralNode extends LiteralNode<Number> {
   46.79 +    @Immutable
   46.80 +    private static final class NumberLiteralNode extends LiteralNode<Number> {
   46.81  
   46.82          private final Type type = numberGetType(value);
   46.83  
   46.84 @@ -358,11 +340,6 @@
   46.85          }
   46.86  
   46.87          @Override
   46.88 -        protected Node copy(final CopyState cs) {
   46.89 -            return new NumberLiteralNode(this);
   46.90 -        }
   46.91 -
   46.92 -        @Override
   46.93          public Type getType() {
   46.94              return type;
   46.95          }
   46.96 @@ -407,11 +384,6 @@
   46.97          private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) {
   46.98              super(literalNode);
   46.99          }
  46.100 -
  46.101 -        @Override
  46.102 -        protected Node copy(final CopyState cs) {
  46.103 -            return new UndefinedLiteralNode(this);
  46.104 -        }
  46.105      }
  46.106  
  46.107      /**
  46.108 @@ -440,6 +412,7 @@
  46.109          return new UndefinedLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
  46.110      }
  46.111  
  46.112 +    @Immutable
  46.113      private static class StringLiteralNode extends LiteralNode<String> {
  46.114          private StringLiteralNode(final Source source, final long token, final int finish, final String value) {
  46.115              super(source, Token.recast(token, TokenType.STRING), finish, value);
  46.116 @@ -450,11 +423,6 @@
  46.117          }
  46.118  
  46.119          @Override
  46.120 -        protected Node copy(final CopyState cs) {
  46.121 -            return new StringLiteralNode(this);
  46.122 -        }
  46.123 -
  46.124 -        @Override
  46.125          public void toString(final StringBuilder sb) {
  46.126              sb.append('\"');
  46.127              sb.append(value);
  46.128 @@ -488,6 +456,7 @@
  46.129          return new StringLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
  46.130      }
  46.131  
  46.132 +    @Immutable
  46.133      private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> {
  46.134          private LexerTokenLiteralNode(final Source source, final long token, final int finish, final LexerToken value) {
  46.135              super(source, Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here?
  46.136 @@ -498,11 +467,6 @@
  46.137          }
  46.138  
  46.139          @Override
  46.140 -        protected Node copy(final CopyState cs) {
  46.141 -            return new LexerTokenLiteralNode(this);
  46.142 -        }
  46.143 -
  46.144 -        @Override
  46.145          public Type getType() {
  46.146              return Type.OBJECT;
  46.147          }
  46.148 @@ -539,7 +503,7 @@
  46.149          return new LexerTokenLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
  46.150      }
  46.151  
  46.152 -    private static class NodeLiteralNode extends LiteralNode<Node> {
  46.153 +    private static final class NodeLiteralNode extends LiteralNode<Node> {
  46.154  
  46.155          private NodeLiteralNode(final Source source, final long token, final int finish) {
  46.156              this(source, token, finish, null);
  46.157 @@ -558,13 +522,8 @@
  46.158          }
  46.159  
  46.160          @Override
  46.161 -        protected Node copy(final CopyState cs) {
  46.162 -            return new NodeLiteralNode(this);
  46.163 -        }
  46.164 -
  46.165 -        @Override
  46.166          public Node accept(final NodeVisitor visitor) {
  46.167 -            if (visitor.enterLiteralNode(this) != null) {
  46.168 +            if (visitor.enterLiteralNode(this)) {
  46.169                  if (value != null) {
  46.170                      final Node newValue = value.accept(visitor);
  46.171                      if(value != newValue) {
  46.172 @@ -617,7 +576,7 @@
  46.173      /**
  46.174       * Array literal node class.
  46.175       */
  46.176 -    public static class ArrayLiteralNode extends LiteralNode<Node[]> {
  46.177 +    public static final class ArrayLiteralNode extends LiteralNode<Node[]> {
  46.178          private static class PostsetMarker {
  46.179              //empty
  46.180          }
  46.181 @@ -705,11 +664,6 @@
  46.182              this.elementType = node.elementType;
  46.183          }
  46.184  
  46.185 -        @Override
  46.186 -        protected Node copy(final CopyState cs) {
  46.187 -            return new ArrayLiteralNode(this);
  46.188 -        }
  46.189 -
  46.190          /**
  46.191           * Compute things like widest element type needed. Internal use from compiler only
  46.192           */
  46.193 @@ -894,7 +848,7 @@
  46.194  
  46.195          @Override
  46.196          public Node accept(final NodeVisitor visitor) {
  46.197 -            if (visitor.enterLiteralNode(this) != null) {
  46.198 +            if (visitor.enterLiteralNode(this)) {
  46.199                  for (int i = 0; i < value.length; i++) {
  46.200                      final Node element = value[i];
  46.201                      if (element != null) {
    47.1 --- a/src/jdk/nashorn/internal/ir/Location.java	Fri Apr 19 18:23:00 2013 +0530
    47.2 +++ b/src/jdk/nashorn/internal/ir/Location.java	Fri Apr 19 16:11:16 2013 +0200
    47.3 @@ -25,16 +25,13 @@
    47.4  
    47.5  package jdk.nashorn.internal.ir;
    47.6  
    47.7 -import java.util.Objects;
    47.8  import jdk.nashorn.internal.parser.Token;
    47.9  import jdk.nashorn.internal.parser.TokenType;
   47.10  import jdk.nashorn.internal.runtime.Source;
   47.11  
   47.12  /**
   47.13   * Used to locate an entity back to it's source file.
   47.14 - *
   47.15   */
   47.16 -
   47.17  public class Location implements Cloneable {
   47.18      /** Source of entity. */
   47.19      private final Source source;
   47.20 @@ -73,22 +70,13 @@
   47.21      }
   47.22  
   47.23      @Override
   47.24 -    public boolean equals(final Object other) {
   47.25 -        if (other == null) {
   47.26 -            return false;
   47.27 -        }
   47.28 -
   47.29 -        if (other.getClass() != this.getClass()) {
   47.30 -            return false;
   47.31 -        }
   47.32 -
   47.33 -        final Location loc = (Location)other;
   47.34 -        return token == loc.token && Objects.equals(source, loc.source);
   47.35 +    public final boolean equals(final Object other) {
   47.36 +        return super.equals(other);
   47.37      }
   47.38  
   47.39      @Override
   47.40 -    public int hashCode() {
   47.41 -        return Token.hashCode(token) ^ Objects.hashCode(source);
   47.42 +    public final int hashCode() {
   47.43 +        return super.hashCode();
   47.44      }
   47.45  
   47.46      /**
    48.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    48.2 +++ b/src/jdk/nashorn/internal/ir/LoopNode.java	Fri Apr 19 16:11:16 2013 +0200
    48.3 @@ -0,0 +1,176 @@
    48.4 +/*
    48.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    48.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    48.7 + *
    48.8 + * This code is free software; you can redistribute it and/or modify it
    48.9 + * under the terms of the GNU General Public License version 2 only, as
   48.10 + * published by the Free Software Foundation.  Oracle designates this
   48.11 + * particular file as subject to the "Classpath" exception as provided
   48.12 + * by Oracle in the LICENSE file that accompanied this code.
   48.13 + *
   48.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   48.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   48.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   48.17 + * version 2 for more details (a copy is included in the LICENSE file that
   48.18 + * accompanied this code).
   48.19 + *
   48.20 + * You should have received a copy of the GNU General Public License version
   48.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   48.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   48.23 + *
   48.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   48.25 + * or visit www.oracle.com if you need additional information or have any
   48.26 + * questions.
   48.27 + */
   48.28 +
   48.29 +package jdk.nashorn.internal.ir;
   48.30 +
   48.31 +import java.util.Arrays;
   48.32 +import java.util.List;
   48.33 +
   48.34 +import jdk.nashorn.internal.codegen.Label;
   48.35 +import jdk.nashorn.internal.runtime.Source;
   48.36 +
   48.37 +/**
   48.38 + * A loop node, for example a while node, do while node or for node
   48.39 + */
   48.40 +public abstract class LoopNode extends BreakableNode {
   48.41 +    /** loop continue label. */
   48.42 +    protected final Label continueLabel;
   48.43 +
   48.44 +    /** Loop test node, null if infinite */
   48.45 +    protected final Node test;
   48.46 +
   48.47 +    /** Loop body */
   48.48 +    protected final Block body;
   48.49 +
   48.50 +    /** Can control flow escape from loop, e.g. through breaks or continues to outer loops? */
   48.51 +    protected final boolean controlFlowEscapes;
   48.52 +
   48.53 +    /**
   48.54 +     * Constructor
   48.55 +     *
   48.56 +     * @param source  source
   48.57 +     * @param token   token
   48.58 +     * @param finish  finish
   48.59 +     * @param test    test, or null if infinite loop
   48.60 +     * @param body    loop body
   48.61 +     * @param controlFlowEscapes controlFlowEscapes
   48.62 +     */
   48.63 +    protected LoopNode(final Source source, final long token, final int finish, final Node test, final Block body, final boolean controlFlowEscapes) {
   48.64 +        super(source, token, finish, new Label("while_break"));
   48.65 +        this.continueLabel = new Label("while_continue");
   48.66 +        this.test = test;
   48.67 +        this.body = body;
   48.68 +        this.controlFlowEscapes = controlFlowEscapes;
   48.69 +    }
   48.70 +
   48.71 +    /**
   48.72 +     * Constructor
   48.73 +     *
   48.74 +     * @param loopNode loop node
   48.75 +     * @param test     new test
   48.76 +     * @param body     new body
   48.77 +     * @param controlFlowEscapes controlFlowEscapes
   48.78 +     */
   48.79 +    protected LoopNode(final LoopNode loopNode, final Node test, final Block body, final boolean controlFlowEscapes) {
   48.80 +        super(loopNode);
   48.81 +        this.continueLabel = new Label(loopNode.continueLabel);
   48.82 +        this.test = test;
   48.83 +        this.body = body;
   48.84 +        this.controlFlowEscapes = controlFlowEscapes;
   48.85 +    }
   48.86 +
   48.87 +    @Override
   48.88 +    public abstract Node ensureUniqueLabels(final LexicalContext lc);
   48.89 +
   48.90 +    /**
   48.91 +     * Does the control flow escape from this loop, i.e. through breaks or
   48.92 +     * continues to outer loops?
   48.93 +     * @return true if control flow escapes
   48.94 +     */
   48.95 +    public boolean controlFlowEscapes() {
   48.96 +        return controlFlowEscapes;
   48.97 +    }
   48.98 +
   48.99 +
  48.100 +    @Override
  48.101 +    public boolean isTerminal() {
  48.102 +        if (!mustEnter()) {
  48.103 +            return false;
  48.104 +        }
  48.105 +        //must enter but control flow may escape - then not terminal
  48.106 +        if (controlFlowEscapes) {
  48.107 +            return false;
  48.108 +        }
  48.109 +        //must enter, but body ends with return - then terminal
  48.110 +        if (body.isTerminal()) {
  48.111 +            return true;
  48.112 +        }
  48.113 +        //no breaks or returns, it is still terminal if we can never exit
  48.114 +        return test == null;
  48.115 +    }
  48.116 +
  48.117 +    /**
  48.118 +     * Conservative check: does this loop have to be entered?
  48.119 +     * @return true if body will execute at least once
  48.120 +     */
  48.121 +    public abstract boolean mustEnter();
  48.122 +
  48.123 +    /**
  48.124 +     * Get the continue label for this while node, i.e. location to go to on continue
  48.125 +     * @return continue label
  48.126 +     */
  48.127 +    public Label getContinueLabel() {
  48.128 +        return continueLabel;
  48.129 +    }
  48.130 +
  48.131 +    @Override
  48.132 +    public List<Label> getLabels() {
  48.133 +        return Arrays.asList(breakLabel, continueLabel);
  48.134 +    }
  48.135 +
  48.136 +    @Override
  48.137 +    public boolean isLoop() {
  48.138 +        return true;
  48.139 +    }
  48.140 +
  48.141 +    /**
  48.142 +     * Get the body for this for node
  48.143 +     * @return the body
  48.144 +     */
  48.145 +    public abstract Block getBody();
  48.146 +
  48.147 +    /**
  48.148 +     * @param lc   lexical context
  48.149 +     * @param body new body
  48.150 +     * @return new for node if changed or existing if not
  48.151 +     */
  48.152 +    public abstract LoopNode setBody(final LexicalContext lc, final Block body);
  48.153 +
  48.154 +    /**
  48.155 +     * Get the test for this for node
  48.156 +     * @return the test
  48.157 +     */
  48.158 +    public abstract Node getTest();
  48.159 +
  48.160 +    /**
  48.161 +     * Set the test for this for node
  48.162 +     *
  48.163 +     * @param lc lexical context
  48.164 +     * @param test new test
  48.165 +     * @return same or new node depending on if test was changed
  48.166 +     */
  48.167 +    public abstract LoopNode setTest(final LexicalContext lc, final Node test);
  48.168 +
  48.169 +    /**
  48.170 +     * Set the control flow escapes flag for this node.
  48.171 +     * TODO  - integrate this with Lowering in a better way
  48.172 +     *
  48.173 +     * @param lc lexical context
  48.174 +     * @param controlFlowEscapes control flow escapes value
  48.175 +     * @return new loop node if changed otherwise the same
  48.176 +     */
  48.177 +    public abstract LoopNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes);
  48.178 +
  48.179 +}
    49.1 --- a/src/jdk/nashorn/internal/ir/Node.java	Fri Apr 19 18:23:00 2013 +0530
    49.2 +++ b/src/jdk/nashorn/internal/ir/Node.java	Fri Apr 19 16:11:16 2013 +0200
    49.3 @@ -25,7 +25,9 @@
    49.4  
    49.5  package jdk.nashorn.internal.ir;
    49.6  
    49.7 -import java.util.IdentityHashMap;
    49.8 +import java.util.ArrayList;
    49.9 +import java.util.List;
   49.10 +
   49.11  import jdk.nashorn.internal.codegen.types.Type;
   49.12  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   49.13  import jdk.nashorn.internal.parser.Token;
   49.14 @@ -33,30 +35,17 @@
   49.15  
   49.16  /**
   49.17   * Nodes are used to compose Abstract Syntax Trees.
   49.18 - *
   49.19   */
   49.20  public abstract class Node extends Location {
   49.21      /** Node symbol. */
   49.22      private Symbol symbol;
   49.23  
   49.24      /** Start of source range. */
   49.25 -    protected int start;
   49.26 +    protected final int start;
   49.27  
   49.28      /** End of source range. */
   49.29      protected int finish;
   49.30  
   49.31 -    /** Has this node been resolved - i.e. emitted code already */
   49.32 -    private boolean isResolved;
   49.33 -
   49.34 -    /** Is this node terminal */
   49.35 -    private boolean isTerminal;
   49.36 -
   49.37 -    /** Is this a goto node */
   49.38 -    private boolean hasGoto;
   49.39 -
   49.40 -    /** Is this a discard */
   49.41 -    private boolean shouldDiscard;
   49.42 -
   49.43      /**
   49.44       * Constructor
   49.45       *
   49.46 @@ -72,6 +61,21 @@
   49.47      }
   49.48  
   49.49      /**
   49.50 +     * Constructor
   49.51 +     *
   49.52 +     * @param source  source
   49.53 +     * @param token   token
   49.54 +     * @param start   start
   49.55 +     * @param finish  finish
   49.56 +     */
   49.57 +    protected Node(final Source source, final long token, final int start, final int finish) {
   49.58 +        super(source, token);
   49.59 +
   49.60 +        this.start = start;
   49.61 +        this.finish = finish;
   49.62 +    }
   49.63 +
   49.64 +    /**
   49.65       * Copy constructor
   49.66       *
   49.67       * @param node source node
   49.68 @@ -79,13 +83,9 @@
   49.69      protected Node(final Node node) {
   49.70          super(node);
   49.71  
   49.72 -        this.symbol        = node.symbol;
   49.73 -        this.isResolved    = node.isResolved;
   49.74 -        this.isTerminal    = node.isTerminal;
   49.75 -        this.hasGoto       = node.hasGoto;
   49.76 -        this.shouldDiscard = node.shouldDiscard;
   49.77 -        this.start         = node.start;
   49.78 -        this.finish        = node.finish;
   49.79 +        this.symbol = node.symbol;
   49.80 +        this.start  = node.start;
   49.81 +        this.finish = node.finish;
   49.82      }
   49.83  
   49.84      /**
   49.85 @@ -156,28 +156,6 @@
   49.86      }
   49.87  
   49.88      /**
   49.89 -     * Test to see if code been generated for this node. Set isResolved if not.
   49.90 -     *
   49.91 -     * @return True if node has already been resolved.
   49.92 -     */
   49.93 -    public boolean testResolved() {
   49.94 -        if (isResolved()) {
   49.95 -            return true;
   49.96 -        }
   49.97 -
   49.98 -        setIsResolved(true);
   49.99 -
  49.100 -        return false;
  49.101 -    }
  49.102 -
  49.103 -    /**
  49.104 -     * Reset the resolved flag.
  49.105 -     */
  49.106 -    public void resetResolved() {
  49.107 -        setIsResolved(false);
  49.108 -    }
  49.109 -
  49.110 -    /**
  49.111       * Is this a debug info node like LineNumberNode etc?
  49.112       *
  49.113       * @return true if this is a debug node
  49.114 @@ -187,72 +165,13 @@
  49.115      }
  49.116  
  49.117      /**
  49.118 -     * Helper class used for node cloning
  49.119 +     * For reference copies - ensure that labels in the copy node are unique
  49.120 +     * using an appropriate copy constructor
  49.121 +     * @param lc lexical context
  49.122 +     * @return new node or same of no labels
  49.123       */
  49.124 -    public static final class CopyState {
  49.125 -        private final IdentityHashMap<Node, Node> cloneMap = new IdentityHashMap<>();
  49.126 -
  49.127 -        /**
  49.128 -         * Find existing or create new copy of the node.
  49.129 -         *
  49.130 -         * @param node Node to copy.
  49.131 -         *
  49.132 -         * @return New object.
  49.133 -         */
  49.134 -        public Node existingOrCopy(final Node node) {
  49.135 -            if (node != null) {
  49.136 -                Node copy = cloneMap.get(node);
  49.137 -
  49.138 -                if (copy == null) {
  49.139 -                    copy = node.copy(this);
  49.140 -                    cloneMap.put(node, copy);
  49.141 -                }
  49.142 -
  49.143 -                return copy;
  49.144 -            }
  49.145 -
  49.146 -            return node;
  49.147 -        }
  49.148 -
  49.149 -        /**
  49.150 -         * Find existing or use old copy of the node.
  49.151 -         *
  49.152 -         * @param node Node to copy.
  49.153 -         *
  49.154 -         * @return new object.
  49.155 -         */
  49.156 -        public Node existingOrSame(final Node node) {
  49.157 -            if (node != null) {
  49.158 -                Node copy = cloneMap.get(node);
  49.159 -
  49.160 -                if (copy == null) {
  49.161 -                    copy = node;
  49.162 -                }
  49.163 -
  49.164 -                return copy;
  49.165 -            }
  49.166 -
  49.167 -            return node;
  49.168 -        }
  49.169 -    }
  49.170 -
  49.171 -    /**
  49.172 -     * Deep copy the node.
  49.173 -     *
  49.174 -     * @return Deep copy of the  Node.
  49.175 -     */
  49.176 -    public final Node copy() {
  49.177 -        return copy(new CopyState());
  49.178 -    }
  49.179 -
  49.180 -    /**
  49.181 -     * Deep copy the node.
  49.182 -     *
  49.183 -     * @param cs CopyState passed around to re-use certain nodes.
  49.184 -     * @return Deep copy of the  Node.
  49.185 -     */
  49.186 -    protected Node copy(final CopyState cs) {
  49.187 -        return cs.existingOrCopy(this);
  49.188 +    public Node ensureUniqueLabels(final LexicalContext lc) {
  49.189 +        return this;
  49.190      }
  49.191  
  49.192      /**
  49.193 @@ -283,35 +202,7 @@
  49.194       * @return true if terminal
  49.195       */
  49.196      public boolean hasTerminalFlags() {
  49.197 -        return isTerminal || hasGoto;
  49.198 -    }
  49.199 -
  49.200 -    /**
  49.201 -     * Copy the terminal flags state of a node to another node
  49.202 -     *
  49.203 -     * @param other source node
  49.204 -     */
  49.205 -    public void copyTerminalFlags(final Node other) {
  49.206 -        isTerminal = other.isTerminal;
  49.207 -        hasGoto    = other.hasGoto;
  49.208 -    }
  49.209 -
  49.210 -    /**
  49.211 -     * Check if the return value of this expression should be discarded
  49.212 -     * @return true if return value is discarded
  49.213 -     */
  49.214 -    public boolean shouldDiscard() {
  49.215 -        return shouldDiscard;
  49.216 -    }
  49.217 -
  49.218 -    /**
  49.219 -     * Setter that determines whether this node's return value should be discarded
  49.220 -     * or not
  49.221 -     *
  49.222 -     * @param shouldDiscard true if return value is discarded, false otherwise
  49.223 -     */
  49.224 -    public void setDiscard(final boolean shouldDiscard) {
  49.225 -        this.shouldDiscard = shouldDiscard;
  49.226 +        return isTerminal() || hasGoto();
  49.227      }
  49.228  
  49.229      /**
  49.230 @@ -336,29 +227,7 @@
  49.231       * @return true if node has goto semantics
  49.232       */
  49.233      public boolean hasGoto() {
  49.234 -        return hasGoto;
  49.235 -    }
  49.236 -
  49.237 -    /**
  49.238 -     * Flag this node as having goto semantics as described in {@link Node#hasGoto()}
  49.239 -     */
  49.240 -    public void setHasGoto() {
  49.241 -        this.hasGoto = true;
  49.242 -    }
  49.243 -
  49.244 -    /**
  49.245 -     * Check whether this node is resolved, i.e. code has been generated for it
  49.246 -     * @return true if node is resolved
  49.247 -     */
  49.248 -    public boolean isResolved() {
  49.249 -        return isResolved;
  49.250 -    }
  49.251 -
  49.252 -    /**
  49.253 -     * Flag this node as resolved or not, i.e. code has been generated for it
  49.254 -     */
  49.255 -    private void setIsResolved(boolean isResolved) {
  49.256 -        this.isResolved = isResolved;
  49.257 +        return false;
  49.258      }
  49.259  
  49.260      /**
  49.261 @@ -370,14 +239,6 @@
  49.262      }
  49.263  
  49.264      /**
  49.265 -     * Set start position for node
  49.266 -     * @param start start position
  49.267 -     */
  49.268 -    public void setStart(final int start) {
  49.269 -        this.start = start;
  49.270 -    }
  49.271 -
  49.272 -    /**
  49.273       * Return the Symbol the compiler has assigned to this Node. The symbol
  49.274       * is the place where it's expression value is stored after evaluation
  49.275       *
  49.276 @@ -404,17 +265,29 @@
  49.277       * @return true if this node is terminal
  49.278       */
  49.279      public boolean isTerminal() {
  49.280 -        return isTerminal;
  49.281 +        return false;
  49.282      }
  49.283  
  49.284 -    /**
  49.285 -     * Set this to be a terminal node, i.e. it terminates control flow as described
  49.286 -     * in {@link Node#isTerminal()}
  49.287 -     *
  49.288 -     * @param isTerminal true if this is a terminal node, false otherwise
  49.289 -     */
  49.290 -    public void setIsTerminal(final boolean isTerminal) {
  49.291 -        this.isTerminal = isTerminal;
  49.292 +    //on change, we have to replace the entire list, that's we can't simple do ListIterator.set
  49.293 +    static <T extends Node> List<T> accept(final NodeVisitor visitor, final Class<T> clazz, final List<T> list) {
  49.294 +        boolean changed = false;
  49.295 +        final List<T> newList = new ArrayList<>();
  49.296 +
  49.297 +        for (final Node node : list) {
  49.298 +            final T newNode = clazz.cast(node.accept(visitor));
  49.299 +            if (newNode != node) {
  49.300 +                changed = true;
  49.301 +            }
  49.302 +            newList.add(newNode);
  49.303 +        }
  49.304 +
  49.305 +        return changed ? newList : list;
  49.306      }
  49.307  
  49.308 +    static <T extends LexicalContextNode> T replaceInLexicalContext(final LexicalContext lc, final T oldNode, final T newNode) {
  49.309 +        if (lc != null) {
  49.310 +            lc.replace(oldNode, newNode);
  49.311 +        }
  49.312 +        return newNode;
  49.313 +    }
  49.314  }
    50.1 --- a/src/jdk/nashorn/internal/ir/ObjectNode.java	Fri Apr 19 18:23:00 2013 +0530
    50.2 +++ b/src/jdk/nashorn/internal/ir/ObjectNode.java	Fri Apr 19 16:11:16 2013 +0200
    50.3 @@ -25,16 +25,18 @@
    50.4  
    50.5  package jdk.nashorn.internal.ir;
    50.6  
    50.7 -import java.util.ArrayList;
    50.8  import java.util.Collections;
    50.9  import java.util.List;
   50.10 +
   50.11 +import jdk.nashorn.internal.ir.annotations.Immutable;
   50.12  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   50.13  import jdk.nashorn.internal.runtime.Source;
   50.14  
   50.15  /**
   50.16   * IR representation of an object literal.
   50.17   */
   50.18 -public class ObjectNode extends Node {
   50.19 +@Immutable
   50.20 +public final class ObjectNode extends Node {
   50.21  
   50.22      /** Literal elements. */
   50.23      private final List<Node> elements;
   50.24 @@ -49,35 +51,18 @@
   50.25       */
   50.26      public ObjectNode(final Source source, final long token, final int finish, final List<Node> elements) {
   50.27          super(source, token, finish);
   50.28 -
   50.29          this.elements = elements;
   50.30      }
   50.31  
   50.32 -    private ObjectNode(final ObjectNode objectNode, final CopyState cs) {
   50.33 +    private ObjectNode(final ObjectNode objectNode, final List<Node> elements) {
   50.34          super(objectNode);
   50.35 -
   50.36 -        final List<Node> newElements = new ArrayList<>();
   50.37 -
   50.38 -        for (final Node element : objectNode.elements) {
   50.39 -            newElements.add(cs.existingOrCopy(element));
   50.40 -        }
   50.41 -
   50.42 -        this.elements = newElements;
   50.43 -    }
   50.44 -
   50.45 -    @Override
   50.46 -    protected Node copy(final CopyState cs) {
   50.47 -        return new ObjectNode(this, cs);
   50.48 +        this.elements = elements;
   50.49      }
   50.50  
   50.51      @Override
   50.52      public Node accept(final NodeVisitor visitor) {
   50.53 -        if (visitor.enterObjectNode(this) != null) {
   50.54 -            for (int i = 0, count = elements.size(); i < count; i++) {
   50.55 -                elements.set(i, elements.get(i).accept(visitor));
   50.56 -            }
   50.57 -
   50.58 -            return visitor.leaveObjectNode(this);
   50.59 +        if (visitor.enterObjectNode(this)) {
   50.60 +            return visitor.leaveObjectNode(setElements(Node.accept(visitor, Node.class, elements)));
   50.61          }
   50.62  
   50.63          return this;
   50.64 @@ -112,4 +97,11 @@
   50.65      public List<Node> getElements() {
   50.66          return Collections.unmodifiableList(elements);
   50.67      }
   50.68 +
   50.69 +    private ObjectNode setElements(final List<Node> elements) {
   50.70 +        if (this.elements == elements) {
   50.71 +            return this;
   50.72 +        }
   50.73 +        return new ObjectNode(this, elements);
   50.74 +    }
   50.75  }
    51.1 --- a/src/jdk/nashorn/internal/ir/PropertyNode.java	Fri Apr 19 18:23:00 2013 +0530
    51.2 +++ b/src/jdk/nashorn/internal/ir/PropertyNode.java	Fri Apr 19 16:11:16 2013 +0200
    51.3 @@ -25,28 +25,27 @@
    51.4  
    51.5  package jdk.nashorn.internal.ir;
    51.6  
    51.7 -import jdk.nashorn.internal.ir.annotations.Reference;
    51.8 +import jdk.nashorn.internal.ir.annotations.Immutable;
    51.9  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   51.10  import jdk.nashorn.internal.runtime.Source;
   51.11  
   51.12  /**
   51.13   * IR representation of an object literal property.
   51.14   */
   51.15 -public class PropertyNode extends Node {
   51.16 +@Immutable
   51.17 +public final class PropertyNode extends Node {
   51.18  
   51.19      /** Property key. */
   51.20 -    private PropertyKey key;
   51.21 +    private final PropertyKey key;
   51.22  
   51.23      /** Property value. */
   51.24 -    private Node value;
   51.25 +    private final Node value;
   51.26  
   51.27      /** Property getter. */
   51.28 -    @Reference
   51.29 -    private Node getter;
   51.30 +    private final FunctionNode getter;
   51.31  
   51.32      /** Property getter. */
   51.33 -    @Reference
   51.34 -    private Node setter;
   51.35 +    private final FunctionNode setter;
   51.36  
   51.37      /**
   51.38       * Constructor
   51.39 @@ -56,26 +55,23 @@
   51.40       * @param finish  finish
   51.41       * @param key     the key of this property
   51.42       * @param value   the value of this property
   51.43 +     * @param getter  getter function body
   51.44 +     * @param setter  setter function body
   51.45       */
   51.46 -    public PropertyNode(final Source source, final long token, final int finish, final PropertyKey key, final Node value) {
   51.47 +    public PropertyNode(final Source source, final long token, final int finish, final PropertyKey key, final Node value, final FunctionNode getter, final FunctionNode setter) {
   51.48          super(source, token, finish);
   51.49 -
   51.50          this.key    = key;
   51.51          this.value  = value;
   51.52 +        this.getter = getter;
   51.53 +        this.setter = setter;
   51.54      }
   51.55  
   51.56 -    private PropertyNode(final PropertyNode propertyNode, final CopyState cs) {
   51.57 +    private PropertyNode(final PropertyNode propertyNode, final PropertyKey key, final Node value, final FunctionNode getter, final FunctionNode setter) {
   51.58          super(propertyNode);
   51.59 -
   51.60 -        this.key    = (PropertyKey)cs.existingOrCopy((Node)propertyNode.key);
   51.61 -        this.value  = cs.existingOrCopy(propertyNode.value);
   51.62 -        this.getter = cs.existingOrSame(propertyNode.getter);
   51.63 -        this.setter = cs.existingOrSame(propertyNode.setter);
   51.64 -    }
   51.65 -
   51.66 -    @Override
   51.67 -    protected Node copy(final CopyState cs) {
   51.68 -        return new PropertyNode(this, cs);
   51.69 +        this.key    = key;
   51.70 +        this.value  = value;
   51.71 +        this.getter = getter;
   51.72 +        this.setter = setter;
   51.73      }
   51.74  
   51.75      /**
   51.76 @@ -88,22 +84,12 @@
   51.77  
   51.78      @Override
   51.79      public Node accept(final NodeVisitor visitor) {
   51.80 -        if (visitor.enterPropertyNode(this) != null) {
   51.81 -            key = (PropertyKey)((Node)key).accept(visitor);
   51.82 -
   51.83 -            if (value != null) {
   51.84 -                value = value.accept(visitor);
   51.85 -            }
   51.86 -
   51.87 -            if (getter != null) {
   51.88 -                getter = getter.accept(visitor);
   51.89 -            }
   51.90 -
   51.91 -            if (setter != null) {
   51.92 -                setter = setter.accept(visitor);
   51.93 -            }
   51.94 -
   51.95 -            return visitor.leavePropertyNode(this);
   51.96 +        if (visitor.enterPropertyNode(this)) {
   51.97 +            return visitor.leavePropertyNode(
   51.98 +                setKey((PropertyKey)((Node)key).accept(visitor)).
   51.99 +                setValue(value == null ? null : value.accept(visitor)).
  51.100 +                setGetter(getter == null ? null : (FunctionNode)getter.accept(visitor)).
  51.101 +                setSetter(setter == null ? null : (FunctionNode)setter.accept(visitor)));
  51.102          }
  51.103  
  51.104          return this;
  51.105 @@ -136,16 +122,20 @@
  51.106       * Get the getter for this property
  51.107       * @return getter or null if none exists
  51.108       */
  51.109 -    public Node getGetter() {
  51.110 +    public FunctionNode getGetter() {
  51.111          return getter;
  51.112      }
  51.113  
  51.114      /**
  51.115       * Set the getter of this property, null if none
  51.116       * @param getter getter
  51.117 +     * @return same node or new node if state changed
  51.118       */
  51.119 -    public void setGetter(final Node getter) {
  51.120 -        this.getter = getter;
  51.121 +    public PropertyNode setGetter(final FunctionNode getter) {
  51.122 +        if (this.getter == getter) {
  51.123 +            return this;
  51.124 +        }
  51.125 +        return new PropertyNode(this, key, value, getter, setter);
  51.126      }
  51.127  
  51.128      /**
  51.129 @@ -156,20 +146,31 @@
  51.130          return (Node)key;
  51.131      }
  51.132  
  51.133 +    private PropertyNode setKey(final PropertyKey key) {
  51.134 +        if (this.key == key) {
  51.135 +            return this;
  51.136 +        }
  51.137 +        return new PropertyNode(this, key, value, getter, setter);
  51.138 +    }
  51.139 +
  51.140      /**
  51.141       * Get the setter for this property
  51.142       * @return setter or null if none exists
  51.143       */
  51.144 -    public Node getSetter() {
  51.145 +    public FunctionNode getSetter() {
  51.146          return setter;
  51.147      }
  51.148  
  51.149      /**
  51.150       * Set the setter for this property, null if none
  51.151       * @param setter setter
  51.152 +     * @return same node or new node if state changed
  51.153       */
  51.154 -    public void setSetter(final Node setter) {
  51.155 -        this.setter = setter;
  51.156 +    public PropertyNode setSetter(final FunctionNode setter) {
  51.157 +        if (this.setter == setter) {
  51.158 +            return this;
  51.159 +        }
  51.160 +        return new PropertyNode(this, key, value, getter, setter);
  51.161      }
  51.162  
  51.163      /**
  51.164 @@ -183,8 +184,12 @@
  51.165      /**
  51.166       * Set the value of this property
  51.167       * @param value new value
  51.168 +     * @return same node or new node if state changed
  51.169       */
  51.170 -    public void setValue(final Node value) {
  51.171 -        this.value = value;
  51.172 -    }
  51.173 +    public PropertyNode setValue(final Node value) {
  51.174 +        if (this.value == value) {
  51.175 +            return this;
  51.176 +        }
  51.177 +        return new PropertyNode(this, key, value, getter, setter);
  51.178 +   }
  51.179  }
    52.1 --- a/src/jdk/nashorn/internal/ir/ReturnNode.java	Fri Apr 19 18:23:00 2013 +0530
    52.2 +++ b/src/jdk/nashorn/internal/ir/ReturnNode.java	Fri Apr 19 16:11:16 2013 +0200
    52.3 @@ -27,22 +27,17 @@
    52.4  
    52.5  import static jdk.nashorn.internal.parser.TokenType.RETURN;
    52.6  import static jdk.nashorn.internal.parser.TokenType.YIELD;
    52.7 -
    52.8 -import jdk.nashorn.internal.ir.annotations.Ignore;
    52.9 +import jdk.nashorn.internal.ir.annotations.Immutable;
   52.10  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   52.11  import jdk.nashorn.internal.runtime.Source;
   52.12  
   52.13  /**
   52.14   * IR representation for RETURN or YIELD statements.
   52.15 - *
   52.16   */
   52.17 +@Immutable
   52.18  public class ReturnNode extends Node {
   52.19      /** Optional expression. */
   52.20 -    private Node expression;
   52.21 -
   52.22 -    /** Try chain. */
   52.23 -    @Ignore
   52.24 -    private final TryNode tryChain;
   52.25 +    private final Node expression;
   52.26  
   52.27      /**
   52.28       * Constructor
   52.29 @@ -51,27 +46,20 @@
   52.30       * @param token      token
   52.31       * @param finish     finish
   52.32       * @param expression expression to return
   52.33 -     * @param tryChain   surrounding try chain.
   52.34       */
   52.35 -    public ReturnNode(final Source source, final long token, final int finish, final Node expression, final TryNode tryChain) {
   52.36 +    public ReturnNode(final Source source, final long token, final int finish, final Node expression) {
   52.37          super(source, token, finish);
   52.38 -
   52.39          this.expression = expression;
   52.40 -        this.tryChain   = tryChain;
   52.41 -
   52.42 -        setIsTerminal(true);
   52.43      }
   52.44  
   52.45 -    private ReturnNode(final ReturnNode returnNode, final CopyState cs) {
   52.46 +    private ReturnNode(final ReturnNode returnNode, final Node expression) {
   52.47          super(returnNode);
   52.48 -
   52.49 -        this.expression = cs.existingOrCopy(returnNode.expression);
   52.50 -        this.tryChain   = (TryNode)cs.existingOrSame(returnNode.tryChain);
   52.51 +        this.expression = expression;
   52.52      }
   52.53  
   52.54      @Override
   52.55 -    protected Node copy(final CopyState cs) {
   52.56 -        return new ReturnNode(this, cs);
   52.57 +    public boolean isTerminal() {
   52.58 +        return true;
   52.59      }
   52.60  
   52.61      /**
   52.62 @@ -100,11 +88,10 @@
   52.63  
   52.64      @Override
   52.65      public Node accept(final NodeVisitor visitor) {
   52.66 -        if (visitor.enterReturnNode(this) != null) {
   52.67 +        if (visitor.enterReturnNode(this)) {
   52.68              if (expression != null) {
   52.69 -                expression = expression.accept(visitor);
   52.70 +                return visitor.leaveReturnNode(setExpression(expression.accept(visitor)));
   52.71              }
   52.72 -
   52.73              return visitor.leaveReturnNode(this);
   52.74          }
   52.75  
   52.76 @@ -121,25 +108,6 @@
   52.77          }
   52.78      }
   52.79  
   52.80 -    @Override
   52.81 -    public boolean equals(final Object other) {
   52.82 -        if (other instanceof ReturnNode) {
   52.83 -            final ReturnNode otherReturn = (ReturnNode)other;
   52.84 -            if (hasExpression() != otherReturn.hasExpression()) {
   52.85 -                return false;
   52.86 -            } else if (hasExpression()) {
   52.87 -                return otherReturn.getExpression().equals(getExpression());
   52.88 -            }
   52.89 -            return true;
   52.90 -        }
   52.91 -        return false;
   52.92 -    }
   52.93 -
   52.94 -    @Override
   52.95 -    public int hashCode() {
   52.96 -        return 0x4711_17 ^ (expression == null ? 0 : expression.hashCode());
   52.97 -    }
   52.98 -
   52.99      /**
  52.100       * Get the expression this node returns
  52.101       * @return return expression, or null if void return
  52.102 @@ -151,16 +119,13 @@
  52.103      /**
  52.104       * Reset the expression this node returns
  52.105       * @param expression new expression, or null if void return
  52.106 +     * @return new or same return node
  52.107       */
  52.108 -    public void setExpression(final Node expression) {
  52.109 -        this.expression = expression;
  52.110 +    public ReturnNode setExpression(final Node expression) {
  52.111 +        if (this.expression == expression) {
  52.112 +            return this;
  52.113 +        }
  52.114 +        return new ReturnNode(this, expression);
  52.115      }
  52.116  
  52.117 -    /**
  52.118 -     * Get the surrounding try chain for this return node
  52.119 -     * @return try chain
  52.120 -     */
  52.121 -    public TryNode getTryChain() {
  52.122 -        return tryChain;
  52.123 -    }
  52.124  }
    53.1 --- a/src/jdk/nashorn/internal/ir/RuntimeNode.java	Fri Apr 19 18:23:00 2013 +0530
    53.2 +++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java	Fri Apr 19 16:11:16 2013 +0200
    53.3 @@ -30,14 +30,15 @@
    53.4  import java.util.Collections;
    53.5  import java.util.List;
    53.6  import jdk.nashorn.internal.codegen.types.Type;
    53.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    53.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    53.9  import jdk.nashorn.internal.parser.TokenType;
   53.10  import jdk.nashorn.internal.runtime.Source;
   53.11  
   53.12  /**
   53.13   * IR representation for a runtime call.
   53.14 - *
   53.15   */
   53.16 +@Immutable
   53.17  public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
   53.18  
   53.19      /**
   53.20 @@ -271,10 +272,10 @@
   53.21      private final List<Node> args;
   53.22  
   53.23      /** Call site override - e.g. we know that a ScriptRuntime.ADD will return an int */
   53.24 -    private Type callSiteType;
   53.25 +    private final Type callSiteType;
   53.26  
   53.27      /** is final - i.e. may not be removed again, lower in the code pipeline */
   53.28 -    private boolean isFinal;
   53.29 +    private final boolean isFinal;
   53.30  
   53.31      /**
   53.32       * Constructor
   53.33 @@ -290,6 +291,17 @@
   53.34  
   53.35          this.request      = request;
   53.36          this.args         = args;
   53.37 +        this.callSiteType = null;
   53.38 +        this.isFinal      = false;
   53.39 +    }
   53.40 +
   53.41 +    private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final Type callSiteType, final boolean isFinal, final List<Node> args) {
   53.42 +        super(runtimeNode);
   53.43 +
   53.44 +        this.request      = request;
   53.45 +        this.args         = args;
   53.46 +        this.callSiteType = callSiteType;
   53.47 +        this.isFinal      = isFinal;
   53.48      }
   53.49  
   53.50      /**
   53.51 @@ -326,8 +338,10 @@
   53.52      public RuntimeNode(final Node parent, final Request request, final List<Node> args) {
   53.53          super(parent);
   53.54  
   53.55 -        this.request = request;
   53.56 -        this.args    = args;
   53.57 +        this.request      = request;
   53.58 +        this.args         = args;
   53.59 +        this.callSiteType = null;
   53.60 +        this.isFinal      = false;
   53.61      }
   53.62  
   53.63      /**
   53.64 @@ -350,20 +364,6 @@
   53.65          this(parent, request, parent.lhs(), parent.rhs());
   53.66      }
   53.67  
   53.68 -    private RuntimeNode(final RuntimeNode runtimeNode, final CopyState cs) {
   53.69 -        super(runtimeNode);
   53.70 -
   53.71 -        final List<Node> newArgs = new ArrayList<>();
   53.72 -
   53.73 -        for (final Node arg : runtimeNode.args) {
   53.74 -            newArgs.add(cs.existingOrCopy(arg));
   53.75 -        }
   53.76 -
   53.77 -        this.request      = runtimeNode.request;
   53.78 -        this.args         = newArgs;
   53.79 -        this.callSiteType = runtimeNode.callSiteType;
   53.80 -    }
   53.81 -
   53.82      /**
   53.83       * Is this node final - i.e. it can never be replaced with other nodes again
   53.84       * @return true if final
   53.85 @@ -374,14 +374,14 @@
   53.86  
   53.87      /**
   53.88       * Flag this node as final - i.e it may never be replaced with other nodes again
   53.89 +     * @param isFinal is the node final, i.e. can not be removed and replaced by a less generic one later in codegen
   53.90 +     * @return same runtime node if already final, otherwise a new one
   53.91       */
   53.92 -    public void setIsFinal() {
   53.93 -        this.isFinal = true;
   53.94 -    }
   53.95 -
   53.96 -    @Override
   53.97 -    protected Node copy(final CopyState cs) {
   53.98 -        return new RuntimeNode(this, cs);
   53.99 +    public RuntimeNode setIsFinal(final boolean isFinal) {
  53.100 +        if (this.isFinal == isFinal) {
  53.101 +            return this;
  53.102 +        }
  53.103 +        return new RuntimeNode(this, request, callSiteType, isFinal, args);
  53.104      }
  53.105  
  53.106      /**
  53.107 @@ -394,8 +394,10 @@
  53.108  
  53.109      @Override
  53.110      public RuntimeNode setType(final Type type) {
  53.111 -        this.callSiteType = type;
  53.112 -        return this;
  53.113 +        if (this.callSiteType == type) {
  53.114 +            return this;
  53.115 +        }
  53.116 +        return new RuntimeNode(this, request, type, isFinal, args);
  53.117      }
  53.118  
  53.119      @Override
  53.120 @@ -409,12 +411,12 @@
  53.121  
  53.122      @Override
  53.123      public Node accept(final NodeVisitor visitor) {
  53.124 -        if (visitor.enterRuntimeNode(this) != null) {
  53.125 -            for (int i = 0, count = args.size(); i < count; i++) {
  53.126 -                args.set(i, args.get(i).accept(visitor));
  53.127 +        if (visitor.enterRuntimeNode(this)) {
  53.128 +            final List<Node> newArgs = new ArrayList<>();
  53.129 +            for (final Node arg : args) {
  53.130 +                newArgs.add(arg.accept(visitor));
  53.131              }
  53.132 -
  53.133 -            return visitor.leaveRuntimeNode(this);
  53.134 +            return visitor.leaveRuntimeNode(setArgs(newArgs));
  53.135          }
  53.136  
  53.137          return this;
  53.138 @@ -449,6 +451,13 @@
  53.139          return Collections.unmodifiableList(args);
  53.140      }
  53.141  
  53.142 +    private RuntimeNode setArgs(final List<Node> args) {
  53.143 +        if (this.args == args) {
  53.144 +            return this;
  53.145 +        }
  53.146 +        return new RuntimeNode(this, request, callSiteType, isFinal, args);
  53.147 +    }
  53.148 +
  53.149      /**
  53.150       * Get the request that this runtime node implements
  53.151       * @return the request
    54.1 --- a/src/jdk/nashorn/internal/ir/SplitNode.java	Fri Apr 19 18:23:00 2013 +0530
    54.2 +++ b/src/jdk/nashorn/internal/ir/SplitNode.java	Fri Apr 19 16:11:16 2013 +0200
    54.3 @@ -25,99 +25,65 @@
    54.4  
    54.5  package jdk.nashorn.internal.ir;
    54.6  
    54.7 -import java.util.ArrayList;
    54.8 -import java.util.Collections;
    54.9 -import java.util.List;
   54.10  import jdk.nashorn.internal.codegen.CompileUnit;
   54.11 -import jdk.nashorn.internal.codegen.Label;
   54.12 -import jdk.nashorn.internal.codegen.MethodEmitter;
   54.13 -import jdk.nashorn.internal.ir.annotations.Ignore;
   54.14 -import jdk.nashorn.internal.ir.annotations.Reference;
   54.15 +import jdk.nashorn.internal.ir.annotations.Immutable;
   54.16  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   54.17  
   54.18 -
   54.19  /**
   54.20   * Node indicating code is split across classes.
   54.21   */
   54.22 -public class SplitNode extends Node {
   54.23 +@Immutable
   54.24 +public class SplitNode extends LexicalContextNode {
   54.25      /** Split node method name. */
   54.26      private final String name;
   54.27  
   54.28      /** Compilation unit. */
   54.29 -    private CompileUnit compileUnit;
   54.30 -
   54.31 -    /** Method emitter for current method. */
   54.32 -    private MethodEmitter method;
   54.33 -
   54.34 -    /** Method emitter for caller method. */
   54.35 -    private MethodEmitter caller;
   54.36 -
   54.37 -    /** Containing function. */
   54.38 -    @Reference
   54.39 -    private final FunctionNode functionNode;
   54.40 -
   54.41 -    /** A list of target labels in parent methods this split node may encounter. */
   54.42 -    @Ignore
   54.43 -    private final List<Label> externalTargets;
   54.44 -
   54.45 -    /** True if this split node or any of its children contain a return statement. */
   54.46 -    private boolean hasReturn;
   54.47 +    private final CompileUnit compileUnit;
   54.48  
   54.49      /** Body of split code. */
   54.50 -    @Ignore
   54.51 -    private Node body;
   54.52 +    private final Node body;
   54.53  
   54.54      /**
   54.55       * Constructor
   54.56       *
   54.57 -     * @param name          name of split node
   54.58 -     * @param functionNode  function node to split in
   54.59 -     * @param body          body of split code
   54.60 +     * @param name        name of split node
   54.61 +     * @param body        body of split code
   54.62 +     * @param compileUnit compile unit to use for the body
   54.63       */
   54.64 -    public SplitNode(final String name, final FunctionNode functionNode, final Node body) {
   54.65 +    public SplitNode(final String name, final Node body, final CompileUnit compileUnit) {
   54.66          super(body.getSource(), body.getToken(), body.getFinish());
   54.67 -
   54.68 -        this.name         = name;
   54.69 -        this.functionNode = functionNode;
   54.70 -        this.body         = body;
   54.71 -        this.externalTargets = new ArrayList<>();
   54.72 +        this.name        = name;
   54.73 +        this.body        = body;
   54.74 +        this.compileUnit = compileUnit;
   54.75      }
   54.76  
   54.77 -    private SplitNode(final SplitNode splitNode, final CopyState cs) {
   54.78 +    private SplitNode(final SplitNode splitNode, final Node body) {
   54.79          super(splitNode);
   54.80 +        this.name        = splitNode.name;
   54.81 +        this.body        = body;
   54.82 +        this.compileUnit = splitNode.compileUnit;
   54.83 +    }
   54.84  
   54.85 -        this.name         = splitNode.name;
   54.86 -        this.functionNode = (FunctionNode)cs.existingOrSame(splitNode.functionNode);
   54.87 -        this.body         = cs.existingOrCopy(splitNode.body);
   54.88 -        this.externalTargets = new ArrayList<>();
   54.89 +    /**
   54.90 +     * Get the body for this split node - i.e. the actual code it encloses
   54.91 +     * @return body for split node
   54.92 +     */
   54.93 +    public Node getBody() {
   54.94 +        return body;
   54.95 +    }
   54.96 +
   54.97 +    private SplitNode setBody(final LexicalContext lc, final Node body) {
   54.98 +        if (this.body == body) {
   54.99 +            return this;
  54.100 +        }
  54.101 +        return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body));
  54.102      }
  54.103  
  54.104      @Override
  54.105 -    protected Node copy(final CopyState cs) {
  54.106 -        return new SplitNode(this, cs);
  54.107 -    }
  54.108 -
  54.109 -    @Override
  54.110 -    public Node accept(final NodeVisitor visitor) {
  54.111 -        final CompileUnit   saveCompileUnit = visitor.getCurrentCompileUnit();
  54.112 -        final MethodEmitter saveMethod      = visitor.getCurrentMethodEmitter();
  54.113 -
  54.114 -        setCaller(saveMethod);
  54.115 -
  54.116 -        visitor.setCurrentCompileUnit(getCompileUnit());
  54.117 -        visitor.setCurrentMethodEmitter(getMethodEmitter());
  54.118 -
  54.119 -        try {
  54.120 -            if (visitor.enterSplitNode(this) != null) {
  54.121 -                body = body.accept(visitor);
  54.122 -
  54.123 -                return visitor.leaveSplitNode(this);
  54.124 -            }
  54.125 -        } finally {
  54.126 -            visitor.setCurrentCompileUnit(saveCompileUnit);
  54.127 -            visitor.setCurrentMethodEmitter(saveMethod);
  54.128 +    public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
  54.129 +        if (visitor.enterSplitNode(this)) {
  54.130 +            return visitor.leaveSplitNode(setBody(lc, body.accept(visitor)));
  54.131          }
  54.132 -
  54.133          return this;
  54.134      }
  54.135  
  54.136 @@ -130,22 +96,6 @@
  54.137      }
  54.138  
  54.139      /**
  54.140 -     * Get the method emitter of the caller for this split node
  54.141 -     * @return caller method emitter
  54.142 -     */
  54.143 -    public MethodEmitter getCaller() {
  54.144 -        return caller;
  54.145 -    }
  54.146 -
  54.147 -    /**
  54.148 -     * Set the caller method emitter for this split node
  54.149 -     * @param caller method emitter
  54.150 -     */
  54.151 -    public void setCaller(final MethodEmitter caller) {
  54.152 -        this.caller = caller;
  54.153 -    }
  54.154 -
  54.155 -    /**
  54.156       * Get the name for this split node
  54.157       * @return name
  54.158       */
  54.159 @@ -161,67 +111,4 @@
  54.160          return compileUnit;
  54.161      }
  54.162  
  54.163 -    /**
  54.164 -     * Set the compile unit for this split node
  54.165 -     * @param compileUnit compile unit
  54.166 -     */
  54.167 -    public void setCompileUnit(final CompileUnit compileUnit) {
  54.168 -        this.compileUnit = compileUnit;
  54.169 -    }
  54.170 -
  54.171 -    /**
  54.172 -     * Get the method emitter for this split node
  54.173 -     * @return method emitter
  54.174 -     */
  54.175 -    public MethodEmitter getMethodEmitter() {
  54.176 -        return method;
  54.177 -    }
  54.178 -
  54.179 -    /**
  54.180 -     * Set the method emitter for this split node
  54.181 -     * @param method method emitter
  54.182 -     */
  54.183 -    public void setMethodEmitter(final MethodEmitter method) {
  54.184 -        this.method = method;
  54.185 -    }
  54.186 -
  54.187 -    /**
  54.188 -     * Get the function node this SplitNode splits
  54.189 -     * @return function node reference
  54.190 -     */
  54.191 -    public FunctionNode getFunctionNode() {
  54.192 -        return functionNode;
  54.193 -    }
  54.194 -
  54.195 -    /**
  54.196 -     * Get the external targets for this SplitNode
  54.197 -     * @return list of external targets
  54.198 -     */
  54.199 -    public List<Label> getExternalTargets() {
  54.200 -        return Collections.unmodifiableList(externalTargets);
  54.201 -    }
  54.202 -
  54.203 -    /**
  54.204 -     * Add an external target for this SplitNode
  54.205 -     * @param targetLabel target label
  54.206 -     */
  54.207 -    public void addExternalTarget(final Label targetLabel) {
  54.208 -        externalTargets.add(targetLabel);
  54.209 -    }
  54.210 -
  54.211 -    /**
  54.212 -     * Check whether this SplitNode returns a value
  54.213 -     * @return true if return
  54.214 -     */
  54.215 -    public boolean hasReturn() {
  54.216 -        return hasReturn;
  54.217 -    }
  54.218 -
  54.219 -    /**
  54.220 -     * Set whether this SplitNode returns a value or not
  54.221 -     * @param hasReturn true if return exists, false otherwise
  54.222 -     */
  54.223 -    public void setHasReturn(final boolean hasReturn) {
  54.224 -        this.hasReturn = hasReturn;
  54.225 -    }
  54.226  }
    55.1 --- a/src/jdk/nashorn/internal/ir/SwitchNode.java	Fri Apr 19 18:23:00 2013 +0530
    55.2 +++ b/src/jdk/nashorn/internal/ir/SwitchNode.java	Fri Apr 19 16:11:16 2013 +0200
    55.3 @@ -28,73 +28,84 @@
    55.4  import java.util.ArrayList;
    55.5  import java.util.Collections;
    55.6  import java.util.List;
    55.7 +
    55.8  import jdk.nashorn.internal.codegen.Label;
    55.9 -import jdk.nashorn.internal.ir.annotations.Ignore;
   55.10 +import jdk.nashorn.internal.ir.annotations.Immutable;
   55.11  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   55.12  import jdk.nashorn.internal.runtime.Source;
   55.13  
   55.14  /**
   55.15   * IR representation of a SWITCH statement.
   55.16   */
   55.17 -public class SwitchNode extends BreakableNode {
   55.18 +@Immutable
   55.19 +public final class SwitchNode extends BreakableNode {
   55.20      /** Switch expression. */
   55.21 -    private Node expression;
   55.22 +    private final Node expression;
   55.23 +
   55.24 +    /** Switch cases. */
   55.25 +    private final List<CaseNode> cases;
   55.26 +
   55.27 +    /** Switch default index. */
   55.28 +    private final int defaultCaseIndex;
   55.29  
   55.30      /** Tag symbol. */
   55.31      private Symbol tag;
   55.32  
   55.33 -    /** Switch cases. */
   55.34 -    private List<CaseNode> cases;
   55.35 -
   55.36 -    /** Switch default. */
   55.37 -    @Ignore //points to one of the members in the list above, don't traverse twice
   55.38 -    private CaseNode defaultCase;
   55.39 -
   55.40      /**
   55.41       * Constructor
   55.42       *
   55.43 -     * @param source  the source
   55.44 -     * @param token   token
   55.45 -     * @param finish  finish
   55.46 +     * @param source      the source
   55.47 +     * @param token       token
   55.48 +     * @param finish      finish
   55.49 +     * @param expression  switch expression
   55.50 +     * @param cases       cases
   55.51 +     * @param defaultCase the default case node - null if none, otherwise has to be present in cases list
   55.52       */
   55.53 -    public SwitchNode(final Source source, final long token, final int finish) {
   55.54 -        super(source, token, finish);
   55.55 -        this.breakLabel  = new Label("switch_break");
   55.56 +    public SwitchNode(final Source source, final long token, final int finish, final Node expression, final List<CaseNode> cases, final CaseNode defaultCase) {
   55.57 +        super(source, token, finish, new Label("switch_break"));
   55.58 +        this.expression       = expression;
   55.59 +        this.cases            = cases;
   55.60 +        this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase);
   55.61      }
   55.62  
   55.63 -    private SwitchNode(final SwitchNode switchNode, final CopyState cs) {
   55.64 +    private SwitchNode(final SwitchNode switchNode, final Node expression, final List<CaseNode> cases, final int defaultCase) {
   55.65          super(switchNode);
   55.66 -
   55.67 -        final List<CaseNode> newCases = new ArrayList<>();
   55.68 -
   55.69 -        for (final CaseNode caseNode : switchNode.getCases()) {
   55.70 -           newCases.add((CaseNode)cs.existingOrCopy(caseNode));
   55.71 -        }
   55.72 -
   55.73 -        this.expression  = cs.existingOrCopy(switchNode.getExpression());
   55.74 -        this.tag         = switchNode.getTag();
   55.75 -        this.cases       = newCases;
   55.76 -        this.defaultCase = (CaseNode)cs.existingOrCopy(switchNode.getDefaultCase());
   55.77 -        this.breakLabel  = new Label(switchNode.getBreakLabel());
   55.78 +        this.expression       = expression;
   55.79 +        this.cases            = cases;
   55.80 +        this.defaultCaseIndex = defaultCase;
   55.81 +        this.tag              = switchNode.getTag(); //TODO are symbols inhereted as references?
   55.82      }
   55.83  
   55.84      @Override
   55.85 -    protected Node copy(final CopyState cs) {
   55.86 -        return new SwitchNode(this, cs);
   55.87 +    public Node ensureUniqueLabels(final LexicalContext lc) {
   55.88 +        final List<CaseNode> newCases = new ArrayList<>();
   55.89 +        for (final CaseNode caseNode : cases) {
   55.90 +            newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody()));
   55.91 +        }
   55.92 +        return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex));
   55.93      }
   55.94  
   55.95      @Override
   55.96 -    public Node accept(final NodeVisitor visitor) {
   55.97 -        if (visitor.enterSwitchNode(this) != null) {
   55.98 -            expression = expression.accept(visitor);
   55.99 +    public boolean isTerminal() {
  55.100 +        //there must be a default case, and that including all other cases must terminate
  55.101 +        if (!cases.isEmpty() && defaultCaseIndex != -1) {
  55.102 +            for (final CaseNode caseNode : cases) {
  55.103 +                if (!caseNode.isTerminal()) {
  55.104 +                    return false;
  55.105 +                }
  55.106 +            }
  55.107 +            return true;
  55.108 +        }
  55.109 +        return false;
  55.110  
  55.111 -            for (int i = 0, count = cases.size(); i < count; i++) {
  55.112 -                cases.set(i, (CaseNode)cases.get(i).accept(visitor));
  55.113 -            }
  55.114 +    }
  55.115  
  55.116 -            //the default case is in the cases list and should not be explicitly traversed!
  55.117 -
  55.118 -            return visitor.leaveSwitchNode(this);
  55.119 +    @Override
  55.120 +    public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
  55.121 +        if (visitor.enterSwitchNode(this)) {
  55.122 +            return visitor.leaveSwitchNode(
  55.123 +                setExpression(visitor.getLexicalContext(), expression.accept(visitor)).
  55.124 +                setCases(visitor.getLexicalContext(), Node.accept(visitor, CaseNode.class, cases), defaultCaseIndex));
  55.125          }
  55.126  
  55.127          return this;
  55.128 @@ -108,6 +119,14 @@
  55.129      }
  55.130  
  55.131      /**
  55.132 +     * Return the case node that is default case
  55.133 +     * @return default case or null if none
  55.134 +     */
  55.135 +    public CaseNode getDefaultCase() {
  55.136 +        return defaultCaseIndex == -1 ? null : cases.get(defaultCaseIndex);
  55.137 +    }
  55.138 +
  55.139 +    /**
  55.140       * Get the cases in this switch
  55.141       * @return a list of case nodes
  55.142       */
  55.143 @@ -116,27 +135,33 @@
  55.144      }
  55.145  
  55.146      /**
  55.147 -     * Set or reset the list of cases in this switch
  55.148 -     * @param cases a list of cases, case nodes
  55.149 +     * Replace case nodes with new list. the cases have to be the same
  55.150 +     * and the default case index the same. This is typically used
  55.151 +     * by NodeVisitors who perform operations on every case node
  55.152 +     * @param lc    lexical context
  55.153 +     * @param cases list of cases
  55.154 +     * @return new switcy node or same if no state was changed
  55.155       */
  55.156 -    public void setCases(final List<CaseNode> cases) {
  55.157 -        this.cases = cases;
  55.158 +    public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases) {
  55.159 +        return setCases(lc, cases, defaultCaseIndex);
  55.160 +    }
  55.161 +
  55.162 +    private SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final int defaultCaseIndex) {
  55.163 +        if (this.cases == cases) {
  55.164 +            return this;
  55.165 +        }
  55.166 +        return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex));
  55.167      }
  55.168  
  55.169      /**
  55.170 -     * Get the default case for this switch
  55.171 -     * @return default case node
  55.172 +     * Set or reset the list of cases in this switch
  55.173 +     * @param lc lexical context
  55.174 +     * @param cases a list of cases, case nodes
  55.175 +     * @param defaultCase a case in the list that is the default - must be in the list or class will assert
  55.176 +     * @return new switch node or same if no state was changed
  55.177       */
  55.178 -    public CaseNode getDefaultCase() {
  55.179 -        return defaultCase;
  55.180 -    }
  55.181 -
  55.182 -    /**
  55.183 -     * Set the default case for this switch
  55.184 -     * @param defaultCase default case node
  55.185 -     */
  55.186 -    public void setDefaultCase(final CaseNode defaultCase) {
  55.187 -        this.defaultCase = defaultCase;
  55.188 +    public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final CaseNode defaultCase) {
  55.189 +        return setCases(lc, cases, defaultCase == null ? -1 : cases.indexOf(defaultCase));
  55.190      }
  55.191  
  55.192      /**
  55.193 @@ -149,10 +174,15 @@
  55.194  
  55.195      /**
  55.196       * Set or reset the expression to switch on
  55.197 +     * @param lc lexical context
  55.198       * @param expression switch expression
  55.199 +     * @return new switch node or same if no state was changed
  55.200       */
  55.201 -    public void setExpression(final Node expression) {
  55.202 -        this.expression = expression;
  55.203 +    public SwitchNode setExpression(final LexicalContext lc, final Node expression) {
  55.204 +        if (this.expression == expression) {
  55.205 +            return this;
  55.206 +        }
  55.207 +        return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex));
  55.208      }
  55.209  
  55.210      /**
    56.1 --- a/src/jdk/nashorn/internal/ir/Symbol.java	Fri Apr 19 18:23:00 2013 +0530
    56.2 +++ b/src/jdk/nashorn/internal/ir/Symbol.java	Fri Apr 19 16:11:16 2013 +0200
    56.3 @@ -29,8 +29,10 @@
    56.4  import java.util.HashSet;
    56.5  import java.util.Set;
    56.6  import java.util.StringTokenizer;
    56.7 +
    56.8  import jdk.nashorn.internal.codegen.types.Type;
    56.9  import jdk.nashorn.internal.runtime.Context;
   56.10 +import jdk.nashorn.internal.runtime.Debug;
   56.11  import jdk.nashorn.internal.runtime.options.Options;
   56.12  
   56.13  /**
   56.14 @@ -63,6 +65,8 @@
   56.15      public static final int IS_LET           = 1 << 8;
   56.16      /** Is this an internal symbol, never represented explicitly in source code */
   56.17      public static final int IS_INTERNAL      = 1 << 9;
   56.18 +    /** Is this a function self-reference symbol */
   56.19 +    public static final int IS_FUNCTION_SELF = 1 << 10;
   56.20  
   56.21      /** Null or name identifying symbol. */
   56.22      private final String name;
   56.23 @@ -70,12 +74,6 @@
   56.24      /** Symbol flags. */
   56.25      private int flags;
   56.26  
   56.27 -    /** Defining node. */
   56.28 -    private Node node;
   56.29 -
   56.30 -    /** Definition block. */
   56.31 -    private final Block block;
   56.32 -
   56.33      /** Type of symbol. */
   56.34      private Type type;
   56.35  
   56.36 @@ -121,16 +119,12 @@
   56.37       *
   56.38       * @param name  name of symbol
   56.39       * @param flags symbol flags
   56.40 -     * @param node  node this symbol is in
   56.41 -     * @param block block this symbol is in
   56.42       * @param type  type of this symbol
   56.43       * @param slot  bytecode slot for this symbol
   56.44       */
   56.45 -    protected Symbol(final String name, final int flags, final Node node, final Block block, final Type type, final int slot) {
   56.46 +    protected Symbol(final String name, final int flags, final Type type, final int slot) {
   56.47          this.name       = name;
   56.48          this.flags      = flags;
   56.49 -        this.node       = node;
   56.50 -        this.block      = block;
   56.51          this.type       = type;
   56.52          this.slot       = slot;
   56.53          this.fieldIndex = -1;
   56.54 @@ -142,11 +136,9 @@
   56.55       *
   56.56       * @param name  name of symbol
   56.57       * @param flags symbol flags
   56.58 -     * @param node  node this symbol is in
   56.59 -     * @param block block this symbol is in
   56.60       */
   56.61 -    public Symbol(final String name, final int flags, final Node node, final Block block) {
   56.62 -        this(name, flags, node, block, Type.UNKNOWN, -1);
   56.63 +    public Symbol(final String name, final int flags) {
   56.64 +        this(name, flags, Type.UNKNOWN, -1);
   56.65      }
   56.66  
   56.67      /**
   56.68 @@ -157,7 +149,7 @@
   56.69       * @param type  type of this symbol
   56.70       */
   56.71      public Symbol(final String name, final int flags, final Type type) {
   56.72 -        this(name, flags, null, null, type, -1);
   56.73 +        this(name, flags, type, -1);
   56.74      }
   56.75  
   56.76      private static String align(final String string, final int max) {
   56.77 @@ -269,20 +261,6 @@
   56.78          return type.isCategory2() ? 2 : 1;
   56.79      }
   56.80  
   56.81 -    @Override
   56.82 -    public boolean equals(final Object other) {
   56.83 -        if (!(other instanceof Symbol)) {
   56.84 -            return false;
   56.85 -        }
   56.86 -        final Symbol symbol = (Symbol) other;
   56.87 -        return name.equals(symbol.name) && block.equals(symbol.block);
   56.88 -    }
   56.89 -
   56.90 -    @Override
   56.91 -    public int hashCode() {
   56.92 -        return name.hashCode() ^ block.hashCode();
   56.93 -    }
   56.94 -
   56.95      private static String type(final String desc) {
   56.96          switch (desc.charAt(desc.length() - 1)) {
   56.97          case ';':
   56.98 @@ -371,14 +349,14 @@
   56.99      /**
  56.100       * Flag this symbol as scope as described in {@link Symbol#isScope()}
  56.101       */
  56.102 -    public void setIsScope() {
  56.103 +    /**
  56.104 +     * Flag this symbol as scope as described in {@link Symbol#isScope()}
  56.105 +     */
  56.106 +     public void setIsScope() {
  56.107          if (!isScope()) {
  56.108              trace("SET IS SCOPE");
  56.109          }
  56.110          flags |= IS_SCOPE;
  56.111 -        if(!isGlobal()) {
  56.112 -            getBlock().setNeedsScope();
  56.113 -        }
  56.114      }
  56.115  
  56.116      /**
  56.117 @@ -478,11 +456,11 @@
  56.118      }
  56.119  
  56.120      /**
  56.121 -     * Get the block in which the symbol is defined
  56.122 -     * @return a block
  56.123 +     * Flag this symbol as a function's self-referencing symbol.
  56.124 +     * @return true if this symbol as a function's self-referencing symbol.
  56.125       */
  56.126 -    public Block getBlock() {
  56.127 -        return block;
  56.128 +    public boolean isFunctionSelf() {
  56.129 +        return (flags & IS_FUNCTION_SELF) == IS_FUNCTION_SELF;
  56.130      }
  56.131  
  56.132      /**
  56.133 @@ -492,7 +470,7 @@
  56.134       * @return field index
  56.135       */
  56.136      public int getFieldIndex() {
  56.137 -        assert fieldIndex != -1 : "fieldIndex must be initialized";
  56.138 +        assert fieldIndex != -1 : "fieldIndex must be initialized " + fieldIndex;
  56.139          return fieldIndex;
  56.140      }
  56.141  
  56.142 @@ -503,7 +481,6 @@
  56.143       * @param fieldIndex field index - a positive integer
  56.144       */
  56.145      public void setFieldIndex(final int fieldIndex) {
  56.146 -        assert this.fieldIndex == -1 : "fieldIndex must be initialized only once";
  56.147          this.fieldIndex = fieldIndex;
  56.148      }
  56.149  
  56.150 @@ -524,22 +501,6 @@
  56.151      }
  56.152  
  56.153      /**
  56.154 -     * Get the node this symbol stores the result for
  56.155 -     * @return node
  56.156 -     */
  56.157 -    public Node getNode() {
  56.158 -        return node;
  56.159 -    }
  56.160 -
  56.161 -    /**
  56.162 -     * Set the node this symbol stores the result for
  56.163 -     * @param node node
  56.164 -     */
  56.165 -    public void setNode(final Node node) {
  56.166 -        this.node = node;
  56.167 -    }
  56.168 -
  56.169 -    /**
  56.170       * Get the name of this symbol
  56.171       * @return symbol name
  56.172       */
  56.173 @@ -616,18 +577,25 @@
  56.174      }
  56.175  
  56.176      /**
  56.177 -     * Check if this symbol is in the global scope, i.e. it is on the outermost level
  56.178 -     * in the script
  56.179 -     * @return true if this this is a global scope symbol
  56.180 +     * From a lexical context, set this symbol as needing scope, which
  56.181 +     * will set flags for the defining block that will be written when
  56.182 +     * block is popped from the lexical context stack, used by codegen
  56.183 +     * when flags need to be tagged, but block is in the
  56.184 +     * middle of evaluation and cannot be modified.
  56.185 +     *
  56.186 +     * @param lc     lexical context
  56.187 +     * @param symbol symbol
  56.188       */
  56.189 -    public boolean isTopLevel() {
  56.190 -        return block instanceof FunctionNode && ((FunctionNode) block).isProgram();
  56.191 +    public static void setSymbolIsScope(final LexicalContext lc, final Symbol symbol) {
  56.192 +        symbol.setIsScope();
  56.193 +        if (!symbol.isGlobal()) {
  56.194 +            lc.setFlag(lc.getDefiningBlock(symbol), Block.NEEDS_SCOPE);
  56.195 +        }
  56.196      }
  56.197  
  56.198 -
  56.199      private void trace(final String desc) {
  56.200          if (TRACE_SYMBOLS != null && (TRACE_SYMBOLS.isEmpty() || TRACE_SYMBOLS.contains(name))) {
  56.201 -            Context.err("SYMBOL: '" + name + "' " + desc);
  56.202 +            Context.err(Debug.id(this) + " SYMBOL: '" + name + "' " + desc);
  56.203              if (TRACE_SYMBOLS_STACKTRACE != null && (TRACE_SYMBOLS_STACKTRACE.isEmpty() || TRACE_SYMBOLS_STACKTRACE.contains(name))) {
  56.204                  new Throwable().printStackTrace(Context.getCurrentErr());
  56.205              }
    57.1 --- a/src/jdk/nashorn/internal/ir/TernaryNode.java	Fri Apr 19 18:23:00 2013 +0530
    57.2 +++ b/src/jdk/nashorn/internal/ir/TernaryNode.java	Fri Apr 19 16:11:16 2013 +0200
    57.3 @@ -25,15 +25,21 @@
    57.4  
    57.5  package jdk.nashorn.internal.ir;
    57.6  
    57.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    57.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    57.9  import jdk.nashorn.internal.runtime.Source;
   57.10  
   57.11  /**
   57.12   * TernaryNode nodes represent three operand operations (?:).
   57.13   */
   57.14 -public class TernaryNode extends BinaryNode {
   57.15 +@Immutable
   57.16 +public final class TernaryNode extends Node {
   57.17 +    private final Node lhs;
   57.18 +
   57.19 +    private final Node rhs;
   57.20 +
   57.21      /** Third argument. */
   57.22 -    private Node third;
   57.23 +    private final Node third;
   57.24  
   57.25      /**
   57.26       * Constructor
   57.27 @@ -45,43 +51,26 @@
   57.28       * @param third  third node
   57.29       */
   57.30      public TernaryNode(final Source source, final long token, final Node lhs, final Node rhs, final Node third) {
   57.31 -        super(source, token, lhs, rhs);
   57.32 -
   57.33 -        this.finish = third.getFinish();
   57.34 +        super(source, token, third.getFinish());
   57.35 +        this.lhs = lhs;
   57.36 +        this.rhs = rhs;
   57.37          this.third = third;
   57.38      }
   57.39  
   57.40 -    private TernaryNode(final TernaryNode ternaryNode, final CopyState cs) {
   57.41 -        super(ternaryNode, cs);
   57.42 -
   57.43 -        this.third = cs.existingOrCopy(ternaryNode.third);
   57.44 -    }
   57.45 -
   57.46 -    @Override
   57.47 -    protected Node copy(final CopyState cs) {
   57.48 -        return new TernaryNode(this, cs);
   57.49 -    }
   57.50 -
   57.51 -    @Override
   57.52 -    public boolean equals(final Object other) {
   57.53 -        if (!super.equals(other)) {
   57.54 -            return false;
   57.55 -        }
   57.56 -        return third.equals(((TernaryNode)other).third());
   57.57 -    }
   57.58 -
   57.59 -    @Override
   57.60 -    public int hashCode() {
   57.61 -        return super.hashCode() ^ third().hashCode();
   57.62 +    private TernaryNode(final TernaryNode ternaryNode, final Node lhs, final Node rhs, final Node third) {
   57.63 +        super(ternaryNode);
   57.64 +        this.lhs = lhs;
   57.65 +        this.rhs = rhs;
   57.66 +        this.third = third;
   57.67      }
   57.68  
   57.69      @Override
   57.70      public Node accept(final NodeVisitor visitor) {
   57.71 -        if (visitor.enterTernaryNode(this) != null) {
   57.72 +        if (visitor.enterTernaryNode(this)) {
   57.73              final Node newLhs = lhs().accept(visitor);
   57.74              final Node newRhs = rhs().accept(visitor);
   57.75              final Node newThird = third.accept(visitor);
   57.76 -            return visitor.leaveTernaryNode((TernaryNode)setThird(newThird).setLHS(newLhs).setRHS(newRhs));
   57.77 +            return visitor.leaveTernaryNode(setThird(newThird).setLHS(newLhs).setRHS(newRhs));
   57.78          }
   57.79  
   57.80          return this;
   57.81 @@ -123,6 +112,22 @@
   57.82      }
   57.83  
   57.84      /**
   57.85 +     * Get the lhs node for this ternary expression, i.e. "x" in x ? y : z
   57.86 +     * @return a node
   57.87 +     */
   57.88 +    public Node lhs() {
   57.89 +        return lhs;
   57.90 +    }
   57.91 +
   57.92 +    /**
   57.93 +     * Get the rhs node for this ternary expression, i.e. "y" in x ? y : z
   57.94 +     * @return a node
   57.95 +     */
   57.96 +    public Node rhs() {
   57.97 +        return rhs;
   57.98 +    }
   57.99 +
  57.100 +    /**
  57.101       * Get the "third" node for this ternary expression, i.e. "z" in x ? y : z
  57.102       * @return a node
  57.103       */
  57.104 @@ -131,14 +136,38 @@
  57.105      }
  57.106  
  57.107      /**
  57.108 +     * Set the left hand side expression for this node
  57.109 +     * @param lhs new left hand side expression
  57.110 +     * @return a node equivalent to this one except for the requested change.
  57.111 +     */
  57.112 +    public TernaryNode setLHS(final Node lhs) {
  57.113 +        if (this.lhs == lhs) {
  57.114 +            return this;
  57.115 +        }
  57.116 +        return new TernaryNode(this, lhs, rhs, third);
  57.117 +    }
  57.118 +
  57.119 +    /**
  57.120 +     * Set the right hand side expression for this node
  57.121 +     * @param rhs new left hand side expression
  57.122 +     * @return a node equivalent to this one except for the requested change.
  57.123 +     */
  57.124 +    public TernaryNode setRHS(final Node rhs) {
  57.125 +        if (this.rhs == rhs) {
  57.126 +            return this;
  57.127 +        }
  57.128 +        return new TernaryNode(this, lhs, rhs, third);
  57.129 +    }
  57.130 +
  57.131 +    /**
  57.132       * Reset the "third" node for this ternary expression, i.e. "z" in x ? y : z
  57.133       * @param third a node
  57.134       * @return a node equivalent to this one except for the requested change.
  57.135       */
  57.136      public TernaryNode setThird(final Node third) {
  57.137 -        if(this.third == third) return this;
  57.138 -        final TernaryNode n = (TernaryNode)clone();
  57.139 -        n.third = third;
  57.140 -        return n;
  57.141 +        if (this.third == third) {
  57.142 +            return this;
  57.143 +        }
  57.144 +        return new TernaryNode(this, lhs, rhs, third);
  57.145      }
  57.146  }
    58.1 --- a/src/jdk/nashorn/internal/ir/ThrowNode.java	Fri Apr 19 18:23:00 2013 +0530
    58.2 +++ b/src/jdk/nashorn/internal/ir/ThrowNode.java	Fri Apr 19 16:11:16 2013 +0200
    58.3 @@ -25,20 +25,17 @@
    58.4  
    58.5  package jdk.nashorn.internal.ir;
    58.6  
    58.7 -import jdk.nashorn.internal.ir.annotations.Ignore;
    58.8 +import jdk.nashorn.internal.ir.annotations.Immutable;
    58.9  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   58.10  import jdk.nashorn.internal.runtime.Source;
   58.11  
   58.12  /**
   58.13   * IR representation for THROW statements.
   58.14   */
   58.15 -public class ThrowNode extends Node {
   58.16 +@Immutable
   58.17 +public final class ThrowNode extends Node {
   58.18      /** Exception expression. */
   58.19 -    private Node expression;
   58.20 -
   58.21 -    /** Try chain. */
   58.22 -    @Ignore
   58.23 -    private final TryNode tryChain;
   58.24 +    private final Node expression;
   58.25  
   58.26      /**
   58.27       * Constructor
   58.28 @@ -47,26 +44,21 @@
   58.29       * @param token      token
   58.30       * @param finish     finish
   58.31       * @param expression expression to throw
   58.32 -     * @param tryChain   surrounding try chain
   58.33       */
   58.34 -    public ThrowNode(final Source source, final long token, final int finish, final Node expression, final TryNode tryChain) {
   58.35 +    public ThrowNode(final Source source, final long token, final int finish, final Node expression) {
   58.36          super(source, token, finish);
   58.37  
   58.38          this.expression = expression;
   58.39 -        this.tryChain = tryChain;
   58.40 -        setIsTerminal(true);
   58.41      }
   58.42  
   58.43 -    private ThrowNode(final ThrowNode throwNode, final CopyState cs) {
   58.44 -        super(throwNode);
   58.45 -
   58.46 -        this.expression = cs.existingOrCopy(throwNode.expression);
   58.47 -        this.tryChain = (TryNode)cs.existingOrSame(throwNode.tryChain);
   58.48 +    private ThrowNode(final Node node, final Node expression) {
   58.49 +        super(node);
   58.50 +        this.expression = expression;
   58.51      }
   58.52  
   58.53      @Override
   58.54 -    protected Node copy(final CopyState cs) {
   58.55 -        return new ThrowNode(this, cs);
   58.56 +    public boolean isTerminal() {
   58.57 +        return true;
   58.58      }
   58.59  
   58.60      /**
   58.61 @@ -75,9 +67,8 @@
   58.62       */
   58.63      @Override
   58.64      public Node accept(final NodeVisitor visitor) {
   58.65 -        if (visitor.enterThrowNode(this) != null) {
   58.66 -            setExpression(expression.accept(visitor));
   58.67 -            return visitor.leaveThrowNode(this);
   58.68 +        if (visitor.enterThrowNode(this)) {
   58.69 +            return visitor.leaveThrowNode(setExpression(expression.accept(visitor)));
   58.70          }
   58.71  
   58.72          return this;
   58.73 @@ -103,16 +94,13 @@
   58.74      /**
   58.75       * Reset the expression being thrown by this node
   58.76       * @param expression new expression
   58.77 +     * @return new or same thrownode
   58.78       */
   58.79 -    public void setExpression(final Node expression) {
   58.80 -        this.expression = expression;
   58.81 +    public ThrowNode setExpression(final Node expression) {
   58.82 +        if (this.expression == expression) {
   58.83 +            return this;
   58.84 +        }
   58.85 +        return new ThrowNode(this, expression);
   58.86      }
   58.87  
   58.88 -    /**
   58.89 -     * Get surrounding tryChain for this node
   58.90 -     * @return try chain
   58.91 -     */
   58.92 -    public TryNode getTryChain() {
   58.93 -        return tryChain;
   58.94 -    }
   58.95  }
    59.1 --- a/src/jdk/nashorn/internal/ir/TryNode.java	Fri Apr 19 18:23:00 2013 +0530
    59.2 +++ b/src/jdk/nashorn/internal/ir/TryNode.java	Fri Apr 19 16:11:16 2013 +0200
    59.3 @@ -28,30 +28,28 @@
    59.4  import java.util.ArrayList;
    59.5  import java.util.Collections;
    59.6  import java.util.List;
    59.7 +
    59.8  import jdk.nashorn.internal.codegen.Label;
    59.9 -import jdk.nashorn.internal.ir.annotations.Ignore;
   59.10 +import jdk.nashorn.internal.ir.annotations.Immutable;
   59.11  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   59.12  import jdk.nashorn.internal.runtime.Source;
   59.13  
   59.14  /**
   59.15   * IR representation of a TRY statement.
   59.16   */
   59.17 -public class TryNode extends Node {
   59.18 -    /** Try chain. */
   59.19 -    @Ignore //don't print, will be apparent from the AST
   59.20 -    private TryNode next;
   59.21 -
   59.22 +@Immutable
   59.23 +public final class TryNode extends Node {
   59.24      /** Try statements. */
   59.25 -    private Block body;
   59.26 +    private final Block body;
   59.27  
   59.28      /** List of catch clauses. */
   59.29 -    private List<Block> catchBlocks;
   59.30 +    private final List<Block> catchBlocks;
   59.31  
   59.32      /** Finally clause. */
   59.33 -    private Block finallyBody;
   59.34 +    private final Block finallyBody;
   59.35  
   59.36      /** Exit label. */
   59.37 -    private Label exit;
   59.38 +    private final Label exit;
   59.39  
   59.40      /** Exception symbol. */
   59.41      private Symbol exception;
   59.42 @@ -62,37 +60,46 @@
   59.43      /**
   59.44       * Constructor
   59.45       *
   59.46 -     * @param source  the source
   59.47 -     * @param token   token
   59.48 -     * @param finish  finish
   59.49 -     * @param next    next try node in chain
   59.50 +     * @param source      the source
   59.51 +     * @param token       token
   59.52 +     * @param finish      finish
   59.53 +     * @param body        try node body
   59.54 +     * @param catchBlocks list of catch blocks in order
   59.55 +     * @param finallyBody body of finally block or null if none
   59.56       */
   59.57 -    public TryNode(final Source source, final long token, final int finish, final TryNode next) {
   59.58 +    public TryNode(final Source source, final long token, final int finish, final Block body, final List<Block> catchBlocks, final Block finallyBody) {
   59.59          super(source, token, finish);
   59.60 -
   59.61 -        this.next = next;
   59.62 +        this.body = body;
   59.63 +        this.catchBlocks = catchBlocks;
   59.64 +        this.finallyBody = finallyBody;
   59.65          this.exit = new Label("exit");
   59.66      }
   59.67  
   59.68 -    private TryNode(final TryNode tryNode, final CopyState cs) {
   59.69 +    private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody) {
   59.70          super(tryNode);
   59.71 -
   59.72 -        final List<Block> newCatchBlocks = new ArrayList<>();
   59.73 -
   59.74 -        for (final Block block : tryNode.catchBlocks) {
   59.75 -            newCatchBlocks.add((Block)cs.existingOrCopy(block));
   59.76 -        }
   59.77 -
   59.78 -        this.next        = (TryNode)cs.existingOrSame(tryNode.getNext());
   59.79 -        this.body        = (Block)cs.existingOrCopy(tryNode.getBody());
   59.80 -        this.catchBlocks = newCatchBlocks;
   59.81 -        this.finallyBody = (Block)cs.existingOrCopy(tryNode.getFinallyBody());
   59.82 -        this.exit        = new Label(tryNode.getExit());
   59.83 +        this.body = body;
   59.84 +        this.catchBlocks = catchBlocks;
   59.85 +        this.finallyBody = finallyBody;
   59.86 +        this.exit = new Label(tryNode.exit);
   59.87      }
   59.88  
   59.89      @Override
   59.90 -    protected Node copy(final CopyState cs) {
   59.91 -        return new TryNode(this, cs);
   59.92 +    public Node ensureUniqueLabels(final LexicalContext lc) {
   59.93 +        //try nodes are never in lex context
   59.94 +        return new TryNode(this, body, catchBlocks, finallyBody);
   59.95 +    }
   59.96 +
   59.97 +    @Override
   59.98 +    public boolean isTerminal() {
   59.99 +        if (body.isTerminal()) {
  59.100 +            for (final Block catchBlock : getCatchBlocks()) {
  59.101 +                if (!catchBlock.isTerminal()) {
  59.102 +                    return false;
  59.103 +                }
  59.104 +            }
  59.105 +            return true;
  59.106 +        }
  59.107 +        return false;
  59.108      }
  59.109  
  59.110      /**
  59.111 @@ -101,21 +108,16 @@
  59.112       */
  59.113      @Override
  59.114      public Node accept(final NodeVisitor visitor) {
  59.115 -        if (visitor.enterTryNode(this) != null) {
  59.116 -            // Need to do first for termination analysis.
  59.117 -            if (finallyBody != null) {
  59.118 -                finallyBody = (Block)finallyBody.accept(visitor);
  59.119 -            }
  59.120 -
  59.121 -            body = (Block)body.accept(visitor);
  59.122 -
  59.123 -            final List<Block> newCatchBlocks = new ArrayList<>(catchBlocks.size());
  59.124 -            for (final Block catchBlock : catchBlocks) {
  59.125 -                newCatchBlocks.add((Block)catchBlock.accept(visitor));
  59.126 -            }
  59.127 -            this.catchBlocks = newCatchBlocks;
  59.128 -
  59.129 -            return visitor.leaveTryNode(this);
  59.130 +        if (visitor.enterTryNode(this)) {
  59.131 +            // Need to do finallybody first for termination analysis. TODO still necessary?
  59.132 +            final Block newFinallyBody = finallyBody == null ? null : (Block)finallyBody.accept(visitor);
  59.133 +            final Block newBody        = (Block)body.accept(visitor);
  59.134 +            return visitor.leaveTryNode(
  59.135 +                setBody(newBody).
  59.136 +                setFinallyBody(newFinallyBody).
  59.137 +                setCatchBlocks(Node.accept(visitor, Block.class, catchBlocks)).
  59.138 +                setException(exception).
  59.139 +                setFinallyCatchAll(finallyCatchAll));
  59.140          }
  59.141  
  59.142          return this;
  59.143 @@ -123,7 +125,7 @@
  59.144  
  59.145      @Override
  59.146      public void toString(final StringBuilder sb) {
  59.147 -        sb.append("try");
  59.148 +        sb.append("try ");
  59.149      }
  59.150  
  59.151      /**
  59.152 @@ -137,9 +139,13 @@
  59.153      /**
  59.154       * Reset the body of this try block
  59.155       * @param body new body
  59.156 +     * @return new TryNode or same if unchanged
  59.157       */
  59.158 -    public void setBody(final Block body) {
  59.159 -        this.body = body;
  59.160 +    public TryNode setBody(final Block body) {
  59.161 +        if (this.body == body) {
  59.162 +            return this;
  59.163 +        }
  59.164 +        return new TryNode(this,  body, catchBlocks, finallyBody);
  59.165      }
  59.166  
  59.167      /**
  59.168 @@ -151,16 +157,7 @@
  59.169          for (final Block catchBlock : catchBlocks) {
  59.170              catches.add((CatchNode)catchBlock.getStatements().get(0));
  59.171          }
  59.172 -        return catches;
  59.173 -    }
  59.174 -
  59.175 -    /**
  59.176 -     * Returns true if the specified block is the body of this try block, or any of its catch blocks.
  59.177 -     * @param block the block
  59.178 -     * @return true if the specified block is the body of this try block, or any of its catch blocks.
  59.179 -     */
  59.180 -    public boolean isChildBlock(Block block) {
  59.181 -        return body == block || catchBlocks.contains(block);
  59.182 +        return Collections.unmodifiableList(catches);
  59.183      }
  59.184  
  59.185      /**
  59.186 @@ -174,9 +171,13 @@
  59.187      /**
  59.188       * Set the catch blocks of this try
  59.189       * @param catchBlocks list of catch blocks
  59.190 +     * @return new TryNode or same if unchanged
  59.191       */
  59.192 -    public void setCatchBlocks(final List<Block> catchBlocks) {
  59.193 -        this.catchBlocks = catchBlocks;
  59.194 +    public TryNode setCatchBlocks(final List<Block> catchBlocks) {
  59.195 +        if (this.catchBlocks == catchBlocks) {
  59.196 +            return this;
  59.197 +        }
  59.198 +        return new TryNode(this, body, catchBlocks, finallyBody);
  59.199      }
  59.200  
  59.201      /**
  59.202 @@ -190,9 +191,11 @@
  59.203      /**
  59.204       * Set the exception symbol for this try block
  59.205       * @param exception a symbol for the compiler to store the exception in
  59.206 +     * @return new TryNode or same if unchanged
  59.207       */
  59.208 -    public void setException(final Symbol exception) {
  59.209 +    public TryNode setException(final Symbol exception) {
  59.210          this.exception = exception;
  59.211 +        return this;
  59.212      }
  59.213  
  59.214      /**
  59.215 @@ -207,9 +210,13 @@
  59.216       * If a finally block exists, the synthetic catchall needs another symbol to
  59.217       * store its throwable
  59.218       * @param finallyCatchAll a symbol for the finally catch all exception
  59.219 +     * @return new TryNode or same if unchanged
  59.220 +     *
  59.221 +     * TODO can this still be stateful?
  59.222       */
  59.223 -    public void setFinallyCatchAll(final Symbol finallyCatchAll) {
  59.224 +    public TryNode setFinallyCatchAll(final Symbol finallyCatchAll) {
  59.225          this.finallyCatchAll = finallyCatchAll;
  59.226 +        return this;
  59.227      }
  59.228  
  59.229      /**
  59.230 @@ -221,14 +228,6 @@
  59.231      }
  59.232  
  59.233      /**
  59.234 -     * Set the exit label for this try block
  59.235 -     * @param exit label
  59.236 -     */
  59.237 -    public void setExit(final Label exit) {
  59.238 -        this.exit = exit;
  59.239 -    }
  59.240 -
  59.241 -    /**
  59.242       * Get the body of the finally clause for this try
  59.243       * @return finally body, or null if no finally
  59.244       */
  59.245 @@ -239,24 +238,12 @@
  59.246      /**
  59.247       * Set the finally body of this try
  59.248       * @param finallyBody new finally body
  59.249 +     * @return new TryNode or same if unchanged
  59.250       */
  59.251 -    public void setFinallyBody(final Block finallyBody) {
  59.252 -        this.finallyBody = finallyBody;
  59.253 -    }
  59.254 -
  59.255 -    /**
  59.256 -     * Get next try node in try chain
  59.257 -     * @return next try node
  59.258 -     */
  59.259 -    public TryNode getNext() {
  59.260 -        return next;
  59.261 -    }
  59.262 -
  59.263 -    /**
  59.264 -     * Set next try node in try chain
  59.265 -     * @param next next try node
  59.266 -     */
  59.267 -    public void setNext(final TryNode next) {
  59.268 -        this.next = next;
  59.269 +    public TryNode setFinallyBody(final Block finallyBody) {
  59.270 +        if (this.finallyBody == finallyBody) {
  59.271 +            return this;
  59.272 +        }
  59.273 +        return new TryNode(this, body, catchBlocks, finallyBody);
  59.274      }
  59.275  }
    60.1 --- a/src/jdk/nashorn/internal/ir/UnaryNode.java	Fri Apr 19 18:23:00 2013 +0530
    60.2 +++ b/src/jdk/nashorn/internal/ir/UnaryNode.java	Fri Apr 19 16:11:16 2013 +0200
    60.3 @@ -31,6 +31,7 @@
    60.4  import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
    60.5  
    60.6  import jdk.nashorn.internal.codegen.types.Type;
    60.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    60.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    60.9  import jdk.nashorn.internal.parser.Token;
   60.10  import jdk.nashorn.internal.parser.TokenType;
   60.11 @@ -39,9 +40,10 @@
   60.12  /**
   60.13   * UnaryNode nodes represent single operand operations.
   60.14   */
   60.15 -public class UnaryNode extends Node implements Assignment<Node> {
   60.16 +@Immutable
   60.17 +public final class UnaryNode extends Node implements Assignment<Node> {
   60.18      /** Right hand side argument. */
   60.19 -    private Node rhs;
   60.20 +    private final Node rhs;
   60.21  
   60.22      /**
   60.23       * Constructor
   60.24 @@ -51,23 +53,26 @@
   60.25       * @param rhs    expression
   60.26       */
   60.27      public UnaryNode(final Source source, final long token, final Node rhs) {
   60.28 -        super(source, token, Token.descPosition(token));
   60.29 -
   60.30 -        this.start  = Math.min(rhs.getStart(), Token.descPosition(token));
   60.31 -        this.finish = Math.max(Token.descPosition(token) + Token.descLength(token), rhs.getFinish());
   60.32 -        this.rhs    = rhs;
   60.33 +        this(source, token, Math.min(rhs.getStart(), Token.descPosition(token)), Math.max(Token.descPosition(token) + Token.descLength(token), rhs.getFinish()), rhs);
   60.34      }
   60.35  
   60.36      /**
   60.37 -     * Copy constructor
   60.38 -     *
   60.39 -     * @param unaryNode source node
   60.40 -     * @param cs        copy state
   60.41 +     * Constructor
   60.42 +     * @param source the source
   60.43 +     * @param token  token
   60.44 +     * @param start  start
   60.45 +     * @param finish finish
   60.46 +     * @param rhs    expression
   60.47       */
   60.48 -    protected UnaryNode(final UnaryNode unaryNode, final CopyState cs) {
   60.49 +    public UnaryNode(final Source source, final long token, final int start, final int finish, final Node rhs) {
   60.50 +        super(source, token, start, finish);
   60.51 +        this.rhs = rhs;
   60.52 +    }
   60.53 +
   60.54 +
   60.55 +    private UnaryNode(final UnaryNode unaryNode, final Node rhs) {
   60.56          super(unaryNode);
   60.57 -
   60.58 -        this.rhs = cs.existingOrCopy(unaryNode.rhs);
   60.59 +        this.rhs = rhs;
   60.60      }
   60.61  
   60.62      /**
   60.63 @@ -113,31 +118,13 @@
   60.64          return getAssignmentDest();
   60.65      }
   60.66  
   60.67 -    @Override
   60.68 -    public boolean equals(final Object other) {
   60.69 -        if (!super.equals(other)) {
   60.70 -            return false;
   60.71 -        }
   60.72 -        return rhs.equals(((UnaryNode)other).rhs());
   60.73 -    }
   60.74 -
   60.75 -    @Override
   60.76 -    public int hashCode() {
   60.77 -        return super.hashCode() ^ rhs().hashCode();
   60.78 -    }
   60.79 -
   60.80 -    @Override
   60.81 -    protected Node copy(final CopyState cs) {
   60.82 -        return new UnaryNode(this, cs);
   60.83 -    }
   60.84 -
   60.85      /**
   60.86       * Assist in IR navigation.
   60.87       * @param visitor IR navigating visitor.
   60.88       */
   60.89      @Override
   60.90      public Node accept(final NodeVisitor visitor) {
   60.91 -        if (visitor.enterUnaryNode(this) != null) {
   60.92 +        if (visitor.enterUnaryNode(this)) {
   60.93              return visitor.leaveUnaryNode(setRHS(rhs.accept(visitor)));
   60.94          }
   60.95  
   60.96 @@ -219,9 +206,9 @@
   60.97       * @return a node equivalent to this one except for the requested change.
   60.98       */
   60.99      public UnaryNode setRHS(final Node rhs) {
  60.100 -        if(this.rhs == rhs) return this;
  60.101 -        final UnaryNode n = (UnaryNode)clone();
  60.102 -        n.rhs = rhs;
  60.103 -        return n;
  60.104 +        if (this.rhs == rhs) {
  60.105 +            return this;
  60.106 +        }
  60.107 +        return new UnaryNode(this, rhs);
  60.108      }
  60.109  }
    61.1 --- a/src/jdk/nashorn/internal/ir/VarNode.java	Fri Apr 19 18:23:00 2013 +0530
    61.2 +++ b/src/jdk/nashorn/internal/ir/VarNode.java	Fri Apr 19 16:11:16 2013 +0200
    61.3 @@ -25,21 +25,31 @@
    61.4  
    61.5  package jdk.nashorn.internal.ir;
    61.6  
    61.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    61.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    61.9  import jdk.nashorn.internal.runtime.Source;
   61.10  
   61.11  /**
   61.12   * Node represents a var/let declaration.
   61.13   */
   61.14 -public class VarNode extends Node implements Assignment<IdentNode> {
   61.15 +@Immutable
   61.16 +public final class VarNode extends Node implements Assignment<IdentNode> {
   61.17      /** Var name. */
   61.18 -    private IdentNode name;
   61.19 +    private final IdentNode name;
   61.20  
   61.21      /** Initialization expression. */
   61.22 -    private Node init;
   61.23 +    private final Node init;
   61.24  
   61.25      /** Is this a var statement (as opposed to a "var" in a for loop statement) */
   61.26 -    private final boolean isStatement;
   61.27 +    private final int flags;
   61.28 +
   61.29 +    /** Flag that determines if this function node is a statement */
   61.30 +    public static final int IS_STATEMENT = 1 << 0;
   61.31 +
   61.32 +    /** Flag that determines if this is the last function declaration in a function
   61.33 +     *  This is used to micro optimize the placement of return value assignments for
   61.34 +     *  a program node */
   61.35 +    public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 1;
   61.36  
   61.37      /**
   61.38       * Constructor
   61.39 @@ -51,7 +61,14 @@
   61.40       * @param init   init node or null if just a declaration
   61.41       */
   61.42      public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init) {
   61.43 -        this(source, token, finish, name, init, true);
   61.44 +        this(source, token, finish, name, init, IS_STATEMENT);
   61.45 +    }
   61.46 +
   61.47 +    private VarNode(final VarNode varNode, final IdentNode name, final Node init, final int flags) {
   61.48 +        super(varNode);
   61.49 +        this.name = init == null ? name : name.setIsInitializedHere();
   61.50 +        this.init = init;
   61.51 +        this.flags = flags;
   61.52      }
   61.53  
   61.54      /**
   61.55 @@ -62,28 +79,14 @@
   61.56       * @param finish finish
   61.57       * @param name   name of variable
   61.58       * @param init   init node or null if just a declaration
   61.59 -     * @param isStatement if this is a var statement (true), or a for-loop initializer (false)
   61.60 +     * @param flags  flags
   61.61       */
   61.62 -    public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init, boolean isStatement) {
   61.63 +    public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init, final int flags) {
   61.64          super(source, token, finish);
   61.65  
   61.66          this.name  = init == null ? name : name.setIsInitializedHere();
   61.67          this.init  = init;
   61.68 -        this.isStatement = isStatement;
   61.69 -    }
   61.70 -
   61.71 -
   61.72 -    private VarNode(final VarNode varNode, final CopyState cs) {
   61.73 -        super(varNode);
   61.74 -
   61.75 -        this.name = (IdentNode)cs.existingOrCopy(varNode.name);
   61.76 -        this.init = cs.existingOrCopy(varNode.init);
   61.77 -        this.isStatement = varNode.isStatement;
   61.78 -    }
   61.79 -
   61.80 -    @Override
   61.81 -    protected Node copy(final CopyState cs) {
   61.82 -        return new VarNode(this, cs);
   61.83 +        this.flags = flags;
   61.84      }
   61.85  
   61.86      @Override
   61.87 @@ -115,45 +118,17 @@
   61.88      }
   61.89  
   61.90      /**
   61.91 -     * Test to see if two VarNodes are the same.
   61.92 -     * @param other Other VarNode.
   61.93 -     * @return True if the VarNodes are the same.
   61.94 -     */
   61.95 -    @Override
   61.96 -    public boolean equals(final Object other) {
   61.97 -        if (other instanceof VarNode) {
   61.98 -            final VarNode otherNode    = (VarNode)other;
   61.99 -            final boolean nameMatches  = name.equals(otherNode.name);
  61.100 -            if (hasInit() != otherNode.hasInit()) {
  61.101 -                return false;
  61.102 -            } else if (init == null) {
  61.103 -                return nameMatches;
  61.104 -            } else {
  61.105 -                return nameMatches && init.equals(otherNode.init);
  61.106 -            }
  61.107 -        }
  61.108 -        return false;
  61.109 -    }
  61.110 -
  61.111 -    @Override
  61.112 -    public int hashCode() {
  61.113 -        return name.hashCode() ^ (init == null ? 0 : init.hashCode());
  61.114 -    }
  61.115 -
  61.116 -    /**
  61.117       * Assist in IR navigation.
  61.118       * @param visitor IR navigating visitor.
  61.119       */
  61.120      @Override
  61.121      public Node accept(final NodeVisitor visitor) {
  61.122 -        if (visitor.enterVarNode(this) != null) {
  61.123 +        if (visitor.enterVarNode(this)) {
  61.124              final IdentNode newName = (IdentNode)name.accept(visitor);
  61.125 -            final Node newInit = init == null ? null : init.accept(visitor);
  61.126 -            final VarNode newThis;
  61.127 -            if(name != newName || init != newInit) {
  61.128 -                newThis = (VarNode)clone();
  61.129 -                newThis.init = newInit;
  61.130 -                newThis.name = newInit == null ? newName : newName.setIsInitializedHere();
  61.131 +            final Node      newInit = init == null ? null : init.accept(visitor);
  61.132 +            final VarNode   newThis;
  61.133 +            if (name != newName || init != newInit) {
  61.134 +                newThis = new VarNode(this, newName, newInit, flags);
  61.135              } else {
  61.136                  newThis = this;
  61.137              }
  61.138 @@ -187,10 +162,10 @@
  61.139       * @return a node equivalent to this one except for the requested change.
  61.140       */
  61.141      public VarNode setInit(final Node init) {
  61.142 -        if(this.init == init) return this;
  61.143 -        final VarNode n = (VarNode)clone();
  61.144 -        n.init = init;
  61.145 -        return n;
  61.146 +        if (this.init == init) {
  61.147 +            return this;
  61.148 +        }
  61.149 +        return new VarNode(this, name, init, flags);
  61.150      }
  61.151  
  61.152      /**
  61.153 @@ -204,12 +179,38 @@
  61.154      /**
  61.155       * Reset the identifier for this VarNode
  61.156       * @param name new IdentNode representing the variable being set or declared
  61.157 +     * @return a node equivalent to this one except for the requested change.
  61.158       */
  61.159 -    private VarNode setName(final IdentNode name) {
  61.160 -        if(this.name == name) return this;
  61.161 -        final VarNode n = (VarNode)clone();
  61.162 -        n.name = name;
  61.163 -        return n;
  61.164 +    public VarNode setName(final IdentNode name) {
  61.165 +        if (this.name == name) {
  61.166 +            return this;
  61.167 +        }
  61.168 +        return new VarNode(this, name, init, flags);
  61.169 +    }
  61.170 +
  61.171 +    private VarNode setFlags(final int flags) {
  61.172 +        if (this.flags == flags) {
  61.173 +            return this;
  61.174 +        }
  61.175 +        return new VarNode(this, name, init, flags);
  61.176 +    }
  61.177 +
  61.178 +    /**
  61.179 +     * Check if a flag is set for this var node
  61.180 +     * @param flag flag
  61.181 +     * @return true if flag is set
  61.182 +     */
  61.183 +    public boolean getFlag(final int flag) {
  61.184 +        return (flags & flag) == flag;
  61.185 +    }
  61.186 +
  61.187 +    /**
  61.188 +     * Set a flag for this var node
  61.189 +     * @param flag flag
  61.190 +     * @return new node if flags changed, same otherwise
  61.191 +     */
  61.192 +    public VarNode setFlag(final int flag) {
  61.193 +        return setFlags(flags | flag);
  61.194      }
  61.195  
  61.196      /**
  61.197 @@ -217,7 +218,7 @@
  61.198       * @return true if this is a var statement (as opposed to a var initializer in a for loop).
  61.199       */
  61.200      public boolean isStatement() {
  61.201 -        return isStatement;
  61.202 +        return (flags & IS_STATEMENT) != 0;
  61.203      }
  61.204  
  61.205      /**
    62.1 --- a/src/jdk/nashorn/internal/ir/WhileNode.java	Fri Apr 19 18:23:00 2013 +0530
    62.2 +++ b/src/jdk/nashorn/internal/ir/WhileNode.java	Fri Apr 19 16:11:16 2013 +0200
    62.3 @@ -25,7 +25,7 @@
    62.4  
    62.5  package jdk.nashorn.internal.ir;
    62.6  
    62.7 -import jdk.nashorn.internal.codegen.Label;
    62.8 +import jdk.nashorn.internal.ir.annotations.Immutable;
    62.9  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
   62.10  import jdk.nashorn.internal.runtime.Source;
   62.11  
   62.12 @@ -33,130 +33,126 @@
   62.13   * IR representation for a WHILE statement. This is the superclass of all
   62.14   * loop nodes
   62.15   */
   62.16 -public class WhileNode extends BreakableNode {
   62.17 -    /** Test expression. */
   62.18 -    protected Node test;
   62.19 +@Immutable
   62.20 +public final class WhileNode extends LoopNode {
   62.21  
   62.22 -    /** For body. */
   62.23 -    protected Block body;
   62.24 -
   62.25 -    /** loop continue label. */
   62.26 -    protected Label continueLabel;
   62.27 +    /** is this a do while node ? */
   62.28 +    private final boolean isDoWhile;
   62.29  
   62.30      /**
   62.31       * Constructor
   62.32       *
   62.33 -     * @param source  the source
   62.34 -     * @param token   token
   62.35 -     * @param finish  finish
   62.36 +     * @param source    the source
   62.37 +     * @param token     token
   62.38 +     * @param finish    finish
   62.39 +     * @param isDoWhile is this a do while loop?
   62.40       */
   62.41 -    public WhileNode(final Source source, final long token, final int finish) {
   62.42 -        super(source, token, finish);
   62.43 -
   62.44 -        this.breakLabel    = new Label("while_break");
   62.45 -        this.continueLabel = new Label("while_continue");
   62.46 +    public WhileNode(final Source source, final long token, final int finish, final boolean isDoWhile) {
   62.47 +        super(source, token, finish, null, null, false);
   62.48 +        this.isDoWhile = isDoWhile;
   62.49      }
   62.50  
   62.51      /**
   62.52 -     * Copy constructor
   62.53 +     * Internal copy constructor
   62.54       *
   62.55 -     * @param whileNode source node
   62.56 -     * @param cs        copy state
   62.57 +     * @param whileNode while node
   62.58 +     * @param test      test
   62.59 +     * @param body      body
   62.60 +     * @param controlFlowEscapes control flow escapes?
   62.61       */
   62.62 -    protected WhileNode(final WhileNode whileNode, final CopyState cs) {
   62.63 -        super(whileNode);
   62.64 -
   62.65 -        this.test          = cs.existingOrCopy(whileNode.test);
   62.66 -        this.body          = (Block)cs.existingOrCopy(whileNode.body);
   62.67 -        this.breakLabel    = new Label(whileNode.breakLabel);
   62.68 -        this.continueLabel = new Label(whileNode.continueLabel);
   62.69 +    protected WhileNode(final WhileNode whileNode, final Node test, final Block body, final boolean controlFlowEscapes) {
   62.70 +        super(whileNode, test, body, controlFlowEscapes);
   62.71 +        this.isDoWhile = whileNode.isDoWhile;
   62.72      }
   62.73  
   62.74      @Override
   62.75 -    protected Node copy(final CopyState cs) {
   62.76 -        return new WhileNode(this, cs);
   62.77 +    public Node ensureUniqueLabels(final LexicalContext lc) {
   62.78 +        return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
   62.79      }
   62.80  
   62.81      @Override
   62.82 -    public boolean isLoop() {
   62.83 -        return true;
   62.84 +    public boolean hasGoto() {
   62.85 +        return test == null;
   62.86      }
   62.87  
   62.88 -    /**
   62.89 -     * Assist in IR navigation.
   62.90 -     * @param visitor IR navigating visitor.
   62.91 -     */
   62.92      @Override
   62.93 -    public Node accept(final NodeVisitor visitor) {
   62.94 -        if (visitor.enterWhileNode(this) != null) {
   62.95 -            test = test.accept(visitor);
   62.96 -            body = (Block)body.accept(visitor);
   62.97 +    protected Node accept(final LexicalContext lc, final NodeVisitor visitor) {
   62.98 +        if (visitor.enterWhileNode(this)) {
   62.99 +            if (isDoWhile()) {
  62.100 +                return visitor.leaveWhileNode(
  62.101 +                        setTest(lc, test.accept(visitor)).
  62.102 +                        setBody(lc, (Block)body.accept(visitor)));
  62.103 +            }
  62.104 +            return visitor.leaveWhileNode(
  62.105 +                    setBody(lc, (Block)body.accept(visitor)).
  62.106 +                    setTest(lc, test.accept(visitor)));
  62.107  
  62.108 -            return visitor.leaveWhileNode(this);
  62.109          }
  62.110          return this;
  62.111      }
  62.112  
  62.113      @Override
  62.114 -    public void toString(final StringBuilder sb) {
  62.115 -        sb.append("while (");
  62.116 -        test.toString(sb);
  62.117 -        sb.append(')');
  62.118 +    public Node getTest() {
  62.119 +        return test;
  62.120      }
  62.121  
  62.122 -    /**
  62.123 -     * Get the loop body
  62.124 -     * @return body
  62.125 -     */
  62.126 +    @Override
  62.127 +    public WhileNode setTest(final LexicalContext lc, final Node test) {
  62.128 +        if (this.test == test) {
  62.129 +            return this;
  62.130 +        }
  62.131 +        return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
  62.132 +    }
  62.133 +
  62.134 +    @Override
  62.135      public Block getBody() {
  62.136          return body;
  62.137      }
  62.138  
  62.139 -    /**
  62.140 -     * Reset the loop body
  62.141 -     * @param body new body
  62.142 -     */
  62.143 -    public void setBody(final Block body) {
  62.144 -        this.body = body;
  62.145 +    @Override
  62.146 +    public WhileNode setBody(final LexicalContext lc, final Block body) {
  62.147 +        if (this.body == body) {
  62.148 +            return this;
  62.149 +        }
  62.150 +        return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
  62.151 +    }
  62.152 +
  62.153 +    @Override
  62.154 +    public WhileNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) {
  62.155 +        if (this.controlFlowEscapes == controlFlowEscapes) {
  62.156 +            return this;
  62.157 +        }
  62.158 +        return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
  62.159      }
  62.160  
  62.161      /**
  62.162 -     * Set the break label (described in {@link WhileNode#getBreakLabel()} for this while node
  62.163 -     * @param breakLabel break label
  62.164 +     * Check if this is a do while loop or a normal while loop
  62.165 +     * @return true if do while
  62.166       */
  62.167 -    public void setBreakLabel(final Label breakLabel) {
  62.168 -        this.breakLabel = breakLabel;
  62.169 +    public boolean isDoWhile() {
  62.170 +        return isDoWhile;
  62.171      }
  62.172  
  62.173 -    /**
  62.174 -     * Get the continue label for this while node, i.e. location to go to on continue
  62.175 -     * @return continue label
  62.176 -     */
  62.177 -    public Label getContinueLabel() {
  62.178 -        return continueLabel;
  62.179 +    @Override
  62.180 +    public void toString(final StringBuilder sb) {
  62.181 +        if (isDoWhile()) {
  62.182 +            sb.append("do {");
  62.183 +            body.toString(sb);
  62.184 +            sb.append("} while (");
  62.185 +            test.toString(sb);
  62.186 +            sb.append(')');
  62.187 +        } else {
  62.188 +            sb.append("while (");
  62.189 +            test.toString(sb);
  62.190 +            sb.append(')');
  62.191 +        }
  62.192      }
  62.193  
  62.194 -    /**
  62.195 -     * Set the continue label (described in {@link WhileNode#getContinueLabel()} for this while node
  62.196 -     * @param continueLabel continue label
  62.197 -     */
  62.198 -    public void setContinueLabel(final Label continueLabel) {
  62.199 -        this.continueLabel = continueLabel;
  62.200 -    }
  62.201 -
  62.202 -    /**
  62.203 -     * Get the test expression for this loop, that upon evaluation to true does another iteration
  62.204 -     * @return test expression
  62.205 -     */
  62.206 -    public Node getTest() {
  62.207 -        return test;
  62.208 -    }
  62.209 -
  62.210 -    /**
  62.211 -     * Set the test expression for this loop
  62.212 -     * @param test test expression, null if infinite loop
  62.213 -     */
  62.214 -    public void setTest(final Node test) {
  62.215 -        this.test = test;
  62.216 +    @Override
  62.217 +    public boolean mustEnter() {
  62.218 +        if (isDoWhile()) {
  62.219 +            return true;
  62.220 +        }
  62.221 +        return test == null;
  62.222      }
  62.223  }
    63.1 --- a/src/jdk/nashorn/internal/ir/WithNode.java	Fri Apr 19 18:23:00 2013 +0530
    63.2 +++ b/src/jdk/nashorn/internal/ir/WithNode.java	Fri Apr 19 16:11:16 2013 +0200
    63.3 @@ -25,18 +25,20 @@
    63.4  
    63.5  package jdk.nashorn.internal.ir;
    63.6  
    63.7 +import jdk.nashorn.internal.ir.annotations.Immutable;
    63.8  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    63.9  import jdk.nashorn.internal.runtime.Source;
   63.10  
   63.11  /**
   63.12   * IR representation for {@code with} statements.
   63.13   */
   63.14 -public class WithNode extends Node {
   63.15 +@Immutable
   63.16 +public final class WithNode extends LexicalContextNode {
   63.17     /** This expression. */
   63.18 -    private Node expression;
   63.19 +    private final Node expression;
   63.20  
   63.21      /** Statements. */
   63.22 -    private Block body;
   63.23 +    private final Block body;
   63.24  
   63.25      /**
   63.26       * Constructor
   63.27 @@ -44,42 +46,39 @@
   63.28       * @param source     the source
   63.29       * @param token      token
   63.30       * @param finish     finish
   63.31 -     * @param expression expression in parenthesis
   63.32 -     * @param body       with node body
   63.33       */
   63.34 -    public WithNode(final Source source, final long token, final int finish, final Node expression, final Block body) {
   63.35 +    public WithNode(final Source source, final long token, final int finish) {
   63.36          super(source, token, finish);
   63.37  
   63.38 +        this.expression = null;
   63.39 +        this.body       = null;
   63.40 +    }
   63.41 +
   63.42 +    private WithNode(final WithNode node, final Node expression, final Block body) {
   63.43 +        super(node);
   63.44 +
   63.45          this.expression = expression;
   63.46          this.body       = body;
   63.47      }
   63.48  
   63.49 -    private WithNode(final WithNode withNode, final CopyState cs) {
   63.50 -        super(withNode);
   63.51 -
   63.52 -        this.expression = cs.existingOrCopy(withNode.expression);
   63.53 -        this.body       = (Block)cs.existingOrCopy(withNode.body);
   63.54 -    }
   63.55 -
   63.56 -    @Override
   63.57 -    protected Node copy(final CopyState cs) {
   63.58 -        return new WithNode(this, cs);
   63.59 -    }
   63.60 -
   63.61      /**
   63.62       * Assist in IR navigation.
   63.63       *
   63.64       * @param visitor IR navigating visitor.
   63.65       */
   63.66      @Override
   63.67 -    public Node accept(final NodeVisitor visitor) {
   63.68 -        if (visitor.enterWithNode(this) != null) {
   63.69 -            expression = expression.accept(visitor);
   63.70 -            body = (Block)body.accept(visitor);
   63.71 -            return visitor.leaveWithNode(this);
   63.72 +    public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
   63.73 +        if (visitor.enterWithNode(this)) {
   63.74 +             return visitor.leaveWithNode(
   63.75 +                setExpression(lc, expression.accept(visitor)).
   63.76 +                setBody(lc, (Block)body.accept(visitor)));
   63.77          }
   63.78 +        return this;
   63.79 +    }
   63.80  
   63.81 -        return this;
   63.82 +    @Override
   63.83 +    public boolean isTerminal() {
   63.84 +        return body.isTerminal();
   63.85      }
   63.86  
   63.87      @Override
   63.88 @@ -99,10 +98,15 @@
   63.89  
   63.90      /**
   63.91       * Reset the body of this with node
   63.92 +     * @param lc lexical context
   63.93       * @param body new body
   63.94 +     * @return new or same withnode
   63.95       */
   63.96 -    public void setBody(final Block body) {
   63.97 -        this.body = body;
   63.98 +    public WithNode setBody(final LexicalContext lc, final Block body) {
   63.99 +        if (this.body == body) {
  63.100 +            return this;
  63.101 +        }
  63.102 +        return Node.replaceInLexicalContext(lc, this, new WithNode(this, expression, body));
  63.103      }
  63.104  
  63.105      /**
  63.106 @@ -115,10 +119,15 @@
  63.107  
  63.108      /**
  63.109       * Reset the expression of this with node
  63.110 +     * @param lc lexical context
  63.111       * @param expression new expression
  63.112 +     * @return new or same withnode
  63.113       */
  63.114 -    public void setExpression(final Node expression) {
  63.115 -        this.expression = expression;
  63.116 +    public WithNode setExpression(final LexicalContext lc, final Node expression) {
  63.117 +        if (this.expression == expression) {
  63.118 +            return this;
  63.119 +        }
  63.120 +        return Node.replaceInLexicalContext(lc, this, new WithNode(this, expression, body));
  63.121      }
  63.122  }
  63.123  
    64.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    64.2 +++ b/src/jdk/nashorn/internal/ir/annotations/Immutable.java	Fri Apr 19 16:11:16 2013 +0200
    64.3 @@ -0,0 +1,34 @@
    64.4 +/*
    64.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    64.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    64.7 + *
    64.8 + * This code is free software; you can redistribute it and/or modify it
    64.9 + * under the terms of the GNU General Public License version 2 only, as
   64.10 + * published by the Free Software Foundation.  Oracle designates this
   64.11 + * particular file as subject to the "Classpath" exception as provided
   64.12 + * by Oracle in the LICENSE file that accompanied this code.
   64.13 + *
   64.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   64.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   64.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   64.17 + * version 2 for more details (a copy is included in the LICENSE file that
   64.18 + * accompanied this code).
   64.19 + *
   64.20 + * You should have received a copy of the GNU General Public License version
   64.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   64.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   64.23 + *
   64.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   64.25 + * or visit www.oracle.com if you need additional information or have any
   64.26 + * questions.
   64.27 + */
   64.28 +
   64.29 +package jdk.nashorn.internal.ir.annotations;
   64.30 +
   64.31 +/**
   64.32 + * Tag for nodes that are immutable. To be immutable all fields must be
   64.33 + * final and copy on write semantics must be in place
   64.34 + */
   64.35 +public @interface Immutable {
   64.36 +    //empty
   64.37 +}
    65.1 --- a/src/jdk/nashorn/internal/ir/debug/ASTWriter.java	Fri Apr 19 18:23:00 2013 +0530
    65.2 +++ b/src/jdk/nashorn/internal/ir/debug/ASTWriter.java	Fri Apr 19 16:11:16 2013 +0200
    65.3 @@ -33,7 +33,9 @@
    65.4  import java.util.Iterator;
    65.5  import java.util.LinkedList;
    65.6  import java.util.List;
    65.7 +
    65.8  import jdk.nashorn.internal.ir.BinaryNode;
    65.9 +import jdk.nashorn.internal.ir.Block;
   65.10  import jdk.nashorn.internal.ir.Node;
   65.11  import jdk.nashorn.internal.ir.TernaryNode;
   65.12  import jdk.nashorn.internal.ir.annotations.Ignore;
   65.13 @@ -113,6 +115,10 @@
   65.14              type += "#" + node.getSymbol();
   65.15          }
   65.16  
   65.17 +        if (node instanceof Block && ((Block)node).needsScope()) {
   65.18 +            type += " <scope>";
   65.19 +        }
   65.20 +
   65.21          final List<Field> children = new LinkedList<>();
   65.22  
   65.23          if (!isReference) {
   65.24 @@ -121,10 +127,6 @@
   65.25  
   65.26          String status = "";
   65.27  
   65.28 -        if (node.shouldDiscard()) {
   65.29 -            status += " Discard";
   65.30 -        }
   65.31 -
   65.32          if (node.isTerminal()) {
   65.33              status += " Terminal";
   65.34          }
    66.1 --- a/src/jdk/nashorn/internal/ir/debug/JSONWriter.java	Fri Apr 19 18:23:00 2013 +0530
    66.2 +++ b/src/jdk/nashorn/internal/ir/debug/JSONWriter.java	Fri Apr 19 16:11:16 2013 +0200
    66.3 @@ -36,7 +36,6 @@
    66.4  import jdk.nashorn.internal.ir.CaseNode;
    66.5  import jdk.nashorn.internal.ir.CatchNode;
    66.6  import jdk.nashorn.internal.ir.ContinueNode;
    66.7 -import jdk.nashorn.internal.ir.DoWhileNode;
    66.8  import jdk.nashorn.internal.ir.EmptyNode;
    66.9  import jdk.nashorn.internal.ir.ExecuteNode;
   66.10  import jdk.nashorn.internal.ir.ForNode;
   66.11 @@ -88,7 +87,7 @@
   66.12          final Parser       parser     = new Parser(env, new Source(name, code), new Context.ThrowErrorManager(), env._strict);
   66.13          final JSONWriter   jsonWriter = new JSONWriter(includeLoc);
   66.14          try {
   66.15 -            final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.tag());
   66.16 +            final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.symbolName());
   66.17              functionNode.accept(jsonWriter);
   66.18              return jsonWriter.getString();
   66.19          } catch (final ParserException e) {
   66.20 @@ -98,11 +97,16 @@
   66.21      }
   66.22  
   66.23      @Override
   66.24 -    protected Node enterDefault(final Node node) {
   66.25 +    protected boolean enterDefault(final Node node) {
   66.26          objectStart();
   66.27          location(node);
   66.28  
   66.29 -        return node;
   66.30 +        return true;
   66.31 +    }
   66.32 +
   66.33 +    private boolean leave() {
   66.34 +        objectEnd();
   66.35 +        return false;
   66.36      }
   66.37  
   66.38      @Override
   66.39 @@ -112,7 +116,7 @@
   66.40      }
   66.41  
   66.42      @Override
   66.43 -    public Node enterAccessNode(final AccessNode accessNode) {
   66.44 +    public boolean enterAccessNode(final AccessNode accessNode) {
   66.45          enterDefault(accessNode);
   66.46  
   66.47          type("MemberExpression");
   66.48 @@ -128,11 +132,11 @@
   66.49  
   66.50          property("computed", false);
   66.51  
   66.52 -        return leaveDefault(accessNode);
   66.53 +        return leave();
   66.54      }
   66.55  
   66.56      @Override
   66.57 -    public Node enterBlock(final Block block) {
   66.58 +    public boolean enterBlock(final Block block) {
   66.59          enterDefault(block);
   66.60  
   66.61          type("BlockStatement");
   66.62 @@ -140,21 +144,21 @@
   66.63  
   66.64          array("body", block.getStatements());
   66.65  
   66.66 -        return leaveDefault(block);
   66.67 +        return leave();
   66.68      }
   66.69  
   66.70      private static boolean isLogical(final TokenType tt) {
   66.71          switch (tt) {
   66.72 -            case AND:
   66.73 -            case OR:
   66.74 -                return true;
   66.75 -            default:
   66.76 -                return false;
   66.77 +        case AND:
   66.78 +        case OR:
   66.79 +            return true;
   66.80 +        default:
   66.81 +            return false;
   66.82          }
   66.83      }
   66.84  
   66.85      @Override
   66.86 -    public Node enterBinaryNode(final BinaryNode binaryNode) {
   66.87 +    public boolean enterBinaryNode(final BinaryNode binaryNode) {
   66.88          enterDefault(binaryNode);
   66.89  
   66.90          final String name;
   66.91 @@ -179,29 +183,29 @@
   66.92          property("right");
   66.93          binaryNode.rhs().accept(this);
   66.94  
   66.95 -        return leaveDefault(binaryNode);
   66.96 +        return leave();
   66.97      }
   66.98  
   66.99      @Override
  66.100 -    public Node enterBreakNode(final BreakNode breakNode) {
  66.101 +    public boolean enterBreakNode(final BreakNode breakNode) {
  66.102          enterDefault(breakNode);
  66.103  
  66.104          type("BreakStatement");
  66.105          comma();
  66.106  
  66.107 -        final LabelNode label = breakNode.getLabel();
  66.108 +        final IdentNode label = breakNode.getLabel();
  66.109          if (label != null) {
  66.110 -            property("label", label.getLabel().getName());
  66.111 +            property("label", label.getName());
  66.112          } else {
  66.113              property("label");
  66.114              nullValue();
  66.115          }
  66.116  
  66.117 -        return leaveDefault(breakNode);
  66.118 +        return leave();
  66.119      }
  66.120  
  66.121      @Override
  66.122 -    public Node enterCallNode(final CallNode callNode) {
  66.123 +    public boolean enterCallNode(final CallNode callNode) {
  66.124          enterDefault(callNode);
  66.125  
  66.126          type("CallExpression");
  66.127 @@ -213,11 +217,11 @@
  66.128  
  66.129          array("arguments", callNode.getArgs());
  66.130  
  66.131 -        return leaveDefault(callNode);
  66.132 +        return leave();
  66.133      }
  66.134  
  66.135      @Override
  66.136 -    public Node enterCaseNode(final CaseNode caseNode) {
  66.137 +    public boolean enterCaseNode(final CaseNode caseNode) {
  66.138          enterDefault(caseNode);
  66.139  
  66.140          type("SwitchCase");
  66.141 @@ -234,11 +238,11 @@
  66.142  
  66.143          array("consequent", caseNode.getBody().getStatements());
  66.144  
  66.145 -        return leaveDefault(caseNode);
  66.146 +        return leave();
  66.147      }
  66.148  
  66.149      @Override
  66.150 -    public Node enterCatchNode(final CatchNode catchNode) {
  66.151 +    public boolean enterCatchNode(final CatchNode catchNode) {
  66.152          enterDefault(catchNode);
  66.153  
  66.154          type("CatchClause");
  66.155 @@ -260,55 +264,38 @@
  66.156          property("body");
  66.157          catchNode.getBody().accept(this);
  66.158  
  66.159 -        return leaveDefault(catchNode);
  66.160 +        return leave();
  66.161      }
  66.162  
  66.163      @Override
  66.164 -    public Node enterContinueNode(final ContinueNode continueNode) {
  66.165 +    public boolean enterContinueNode(final ContinueNode continueNode) {
  66.166          enterDefault(continueNode);
  66.167  
  66.168          type("ContinueStatement");
  66.169          comma();
  66.170  
  66.171 -        final LabelNode label = continueNode.getLabel();
  66.172 +        final IdentNode label = continueNode.getLabel();
  66.173          if (label != null) {
  66.174 -            property("label", label.getLabel().getName());
  66.175 +            property("label", label.getName());
  66.176          } else {
  66.177              property("label");
  66.178              nullValue();
  66.179          }
  66.180  
  66.181 -        return leaveDefault(continueNode);
  66.182 +        return leave();
  66.183      }
  66.184  
  66.185      @Override
  66.186 -    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
  66.187 -        enterDefault(doWhileNode);
  66.188 -
  66.189 -        type("DoWhileStatement");
  66.190 -        comma();
  66.191 -
  66.192 -        property("body");
  66.193 -        doWhileNode.getBody().accept(this);
  66.194 -        comma();
  66.195 -
  66.196 -        property("test");
  66.197 -        doWhileNode.getTest().accept(this);
  66.198 -
  66.199 -        return leaveDefault(doWhileNode);
  66.200 -    }
  66.201 -
  66.202 -    @Override
  66.203 -    public Node enterEmptyNode(final EmptyNode emptyNode) {
  66.204 +    public boolean enterEmptyNode(final EmptyNode emptyNode) {
  66.205          enterDefault(emptyNode);
  66.206  
  66.207          type("EmptyStatement");
  66.208  
  66.209 -        return leaveDefault(emptyNode);
  66.210 +        return leave();
  66.211      }
  66.212  
  66.213      @Override
  66.214 -    public Node enterExecuteNode(final ExecuteNode executeNode) {
  66.215 +    public boolean enterExecuteNode(final ExecuteNode executeNode) {
  66.216          enterDefault(executeNode);
  66.217  
  66.218          type("ExpressionStatement");
  66.219 @@ -317,11 +304,11 @@
  66.220          property("expression");
  66.221          executeNode.getExpression().accept(this);
  66.222  
  66.223 -        return leaveDefault(executeNode);
  66.224 +        return leave();
  66.225      }
  66.226  
  66.227      @Override
  66.228 -    public Node enterForNode(final ForNode forNode) {
  66.229 +    public boolean enterForNode(final ForNode forNode) {
  66.230          enterDefault(forNode);
  66.231  
  66.232          if (forNode.isForIn() || (forNode.isForEach() && forNode.getInit() != null)) {
  66.233 @@ -380,11 +367,11 @@
  66.234              forNode.getBody().accept(this);
  66.235          }
  66.236  
  66.237 -        return leaveDefault(forNode);
  66.238 +        return leave();
  66.239      }
  66.240  
  66.241      @Override
  66.242 -    public Node enterFunctionNode(final FunctionNode functionNode) {
  66.243 +    public boolean enterFunctionNode(final FunctionNode functionNode) {
  66.244          enterDefault(functionNode);
  66.245  
  66.246          final boolean program = functionNode.isProgram();
  66.247 @@ -419,7 +406,7 @@
  66.248          }
  66.249  
  66.250          // body consists of nested functions and statements
  66.251 -        final List<Node> stats = functionNode.getStatements();
  66.252 +        final List<Node> stats = functionNode.getBody().getStatements();
  66.253          final int size = stats.size();
  66.254          int idx = 0;
  66.255          arrayStart("body");
  66.256 @@ -435,11 +422,11 @@
  66.257          }
  66.258          arrayEnd();
  66.259  
  66.260 -        return leaveDefault(functionNode);
  66.261 +        return leave();
  66.262      }
  66.263  
  66.264      @Override
  66.265 -    public Node enterIdentNode(final IdentNode identNode) {
  66.266 +    public boolean enterIdentNode(final IdentNode identNode) {
  66.267          enterDefault(identNode);
  66.268  
  66.269          final String name = identNode.getName();
  66.270 @@ -451,11 +438,11 @@
  66.271              property("name", identNode.getName());
  66.272          }
  66.273  
  66.274 -        return leaveDefault(identNode);
  66.275 +        return leave();
  66.276      }
  66.277  
  66.278      @Override
  66.279 -    public Node enterIfNode(final IfNode ifNode) {
  66.280 +    public boolean enterIfNode(final IfNode ifNode) {
  66.281          enterDefault(ifNode);
  66.282  
  66.283          type("IfStatement");
  66.284 @@ -477,11 +464,11 @@
  66.285              nullValue();
  66.286          }
  66.287  
  66.288 -        return leaveDefault(ifNode);
  66.289 +        return leave();
  66.290      }
  66.291  
  66.292      @Override
  66.293 -    public Node enterIndexNode(final IndexNode indexNode) {
  66.294 +    public boolean enterIndexNode(final IndexNode indexNode) {
  66.295          enterDefault(indexNode);
  66.296  
  66.297          type("MemberExpression");
  66.298 @@ -497,11 +484,11 @@
  66.299  
  66.300          property("computed", true);
  66.301  
  66.302 -        return leaveDefault(indexNode);
  66.303 +        return leave();
  66.304      }
  66.305  
  66.306      @Override
  66.307 -    public Node enterLabelNode(final LabelNode labelNode) {
  66.308 +    public boolean enterLabelNode(final LabelNode labelNode) {
  66.309          enterDefault(labelNode);
  66.310  
  66.311          type("LabeledStatement");
  66.312 @@ -514,17 +501,17 @@
  66.313          property("body");
  66.314          labelNode.getBody().accept(this);
  66.315  
  66.316 -        return leaveDefault(labelNode);
  66.317 +        return leave();
  66.318      }
  66.319  
  66.320      @Override
  66.321 -    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
  66.322 -        return null;
  66.323 +    public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
  66.324 +        return false;
  66.325      }
  66.326  
  66.327      @SuppressWarnings("rawtypes")
  66.328      @Override
  66.329 -    public Node enterLiteralNode(final LiteralNode literalNode) {
  66.330 +    public boolean enterLiteralNode(final LiteralNode literalNode) {
  66.331          enterDefault(literalNode);
  66.332  
  66.333          if (literalNode instanceof LiteralNode.ArrayLiteralNode) {
  66.334 @@ -556,11 +543,11 @@
  66.335              }
  66.336          }
  66.337  
  66.338 -        return leaveDefault(literalNode);
  66.339 +        return leave();
  66.340      }
  66.341  
  66.342      @Override
  66.343 -    public Node enterObjectNode(final ObjectNode objectNode) {
  66.344 +    public boolean enterObjectNode(final ObjectNode objectNode) {
  66.345          enterDefault(objectNode);
  66.346  
  66.347          type("ObjectExpression");
  66.348 @@ -568,11 +555,11 @@
  66.349  
  66.350          array("properties", objectNode.getElements());
  66.351  
  66.352 -        return leaveDefault(objectNode);
  66.353 +        return leave();
  66.354      }
  66.355  
  66.356      @Override
  66.357 -    public Node enterPropertyNode(final PropertyNode propertyNode) {
  66.358 +    public boolean enterPropertyNode(final PropertyNode propertyNode) {
  66.359          final Node key = propertyNode.getKey();
  66.360  
  66.361          final Node value = propertyNode.getValue();
  66.362 @@ -634,11 +621,11 @@
  66.363              }
  66.364          }
  66.365  
  66.366 -        return null;
  66.367 +        return false;
  66.368      }
  66.369  
  66.370      @Override
  66.371 -    public Node enterReturnNode(final ReturnNode returnNode) {
  66.372 +    public boolean enterReturnNode(final ReturnNode returnNode) {
  66.373          enterDefault(returnNode);
  66.374  
  66.375          type("ReturnStatement");
  66.376 @@ -652,31 +639,29 @@
  66.377              nullValue();
  66.378          }
  66.379  
  66.380 -        return leaveDefault(returnNode);
  66.381 +        return leave();
  66.382      }
  66.383  
  66.384      @Override
  66.385 -    public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
  66.386 +    public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
  66.387          final RuntimeNode.Request req = runtimeNode.getRequest();
  66.388  
  66.389          if (req == RuntimeNode.Request.DEBUGGER) {
  66.390              enterDefault(runtimeNode);
  66.391 -
  66.392              type("DebuggerStatement");
  66.393 -
  66.394 -            return leaveDefault(runtimeNode);
  66.395 +            return leave();
  66.396          }
  66.397  
  66.398 -        return null;
  66.399 +        return false;
  66.400      }
  66.401  
  66.402      @Override
  66.403 -    public Node enterSplitNode(final SplitNode splitNode) {
  66.404 -        return null;
  66.405 +    public boolean enterSplitNode(final SplitNode splitNode) {
  66.406 +        return false;
  66.407      }
  66.408  
  66.409      @Override
  66.410 -    public Node enterSwitchNode(final SwitchNode switchNode) {
  66.411 +    public boolean enterSwitchNode(final SwitchNode switchNode) {
  66.412          enterDefault(switchNode);
  66.413  
  66.414          type("SwitchStatement");
  66.415 @@ -688,11 +673,11 @@
  66.416  
  66.417          array("cases", switchNode.getCases());
  66.418  
  66.419 -        return leaveDefault(switchNode);
  66.420 +        return leave();
  66.421      }
  66.422  
  66.423      @Override
  66.424 -    public Node enterTernaryNode(final TernaryNode ternaryNode) {
  66.425 +    public boolean enterTernaryNode(final TernaryNode ternaryNode) {
  66.426          enterDefault(ternaryNode);
  66.427  
  66.428          type("ConditionalExpression");
  66.429 @@ -709,11 +694,11 @@
  66.430          property("alternate");
  66.431          ternaryNode.third().accept(this);
  66.432  
  66.433 -        return leaveDefault(ternaryNode);
  66.434 +        return leave();
  66.435      }
  66.436  
  66.437      @Override
  66.438 -    public Node enterThrowNode(final ThrowNode throwNode) {
  66.439 +    public boolean enterThrowNode(final ThrowNode throwNode) {
  66.440          enterDefault(throwNode);
  66.441  
  66.442          type("ThrowStatement");
  66.443 @@ -722,11 +707,11 @@
  66.444          property("argument");
  66.445          throwNode.getExpression().accept(this);
  66.446  
  66.447 -        return leaveDefault(throwNode);
  66.448 +        return leave();
  66.449      }
  66.450  
  66.451      @Override
  66.452 -    public Node enterTryNode(final TryNode tryNode) {
  66.453 +    public boolean enterTryNode(final TryNode tryNode) {
  66.454          enterDefault(tryNode);
  66.455  
  66.456          type("TryStatement");
  66.457 @@ -747,11 +732,11 @@
  66.458              nullValue();
  66.459          }
  66.460  
  66.461 -        return leaveDefault(tryNode);
  66.462 +        return leave();
  66.463      }
  66.464  
  66.465      @Override
  66.466 -    public Node enterUnaryNode(final UnaryNode unaryNode) {
  66.467 +    public boolean enterUnaryNode(final UnaryNode unaryNode) {
  66.468          enterDefault(unaryNode);
  66.469  
  66.470          final TokenType tokenType = unaryNode.tokenType();
  66.471 @@ -769,25 +754,25 @@
  66.472              final boolean prefix;
  66.473              final String operator;
  66.474              switch (tokenType) {
  66.475 -                case INCPOSTFIX:
  66.476 -                    prefix = false;
  66.477 -                    operator = "++";
  66.478 -                    break;
  66.479 -                case DECPOSTFIX:
  66.480 -                    prefix = false;
  66.481 -                    operator = "--";
  66.482 -                    break;
  66.483 -                case INCPREFIX:
  66.484 -                    operator = "++";
  66.485 -                    prefix = true;
  66.486 -                    break;
  66.487 -                case DECPREFIX:
  66.488 -                    operator = "--";
  66.489 -                    prefix = true;
  66.490 -                    break;
  66.491 -                default:
  66.492 -                    prefix = false;
  66.493 -                    operator = tokenType.getName();
  66.494 +            case INCPOSTFIX:
  66.495 +                prefix = false;
  66.496 +                operator = "++";
  66.497 +                break;
  66.498 +            case DECPOSTFIX:
  66.499 +                prefix = false;
  66.500 +                operator = "--";
  66.501 +                break;
  66.502 +            case INCPREFIX:
  66.503 +                operator = "++";
  66.504 +                prefix = true;
  66.505 +                break;
  66.506 +            case DECPREFIX:
  66.507 +                operator = "--";
  66.508 +                prefix = true;
  66.509 +                break;
  66.510 +            default:
  66.511 +                prefix = false;
  66.512 +                operator = tokenType.getName();
  66.513              }
  66.514  
  66.515              type(unaryNode.isAssignment()? "UpdateExpression" : "UnaryExpression");
  66.516 @@ -803,11 +788,11 @@
  66.517              unaryNode.rhs().accept(this);
  66.518          }
  66.519  
  66.520 -        return leaveDefault(unaryNode);
  66.521 +        return leave();
  66.522      }
  66.523  
  66.524      @Override
  66.525 -    public Node enterVarNode(final VarNode varNode) {
  66.526 +    public boolean enterVarNode(final VarNode varNode) {
  66.527          enterDefault(varNode);
  66.528  
  66.529          type("VariableDeclaration");
  66.530 @@ -839,28 +824,37 @@
  66.531          // declarations
  66.532          arrayEnd();
  66.533  
  66.534 -        return leaveDefault(varNode);
  66.535 +        return leave();
  66.536      }
  66.537  
  66.538      @Override
  66.539 -    public Node enterWhileNode(final WhileNode whileNode) {
  66.540 +    public boolean enterWhileNode(final WhileNode whileNode) {
  66.541          enterDefault(whileNode);
  66.542  
  66.543 -        type("WhileStatement");
  66.544 +        type(whileNode.isDoWhile() ? "DoWhileStatement" : "WhileStatement");
  66.545          comma();
  66.546  
  66.547 -        property("test");
  66.548 -        whileNode.getTest().accept(this);
  66.549 -        comma();
  66.550 +        if (whileNode.isDoWhile()) {
  66.551 +            property("body");
  66.552 +            whileNode.getBody().accept(this);
  66.553 +            comma();
  66.554  
  66.555 -        property("block");
  66.556 -        whileNode.getBody().accept(this);
  66.557 +            property("test");
  66.558 +            whileNode.getTest().accept(this);
  66.559 +        } else {
  66.560 +            property("test");
  66.561 +            whileNode.getTest().accept(this);
  66.562 +            comma();
  66.563  
  66.564 -        return leaveDefault(whileNode);
  66.565 +            property("block");
  66.566 +            whileNode.getBody().accept(this);
  66.567 +        }
  66.568 +
  66.569 +        return leave();
  66.570      }
  66.571  
  66.572      @Override
  66.573 -    public Node enterWithNode(final WithNode withNode) {
  66.574 +    public boolean enterWithNode(final WithNode withNode) {
  66.575          enterDefault(withNode);
  66.576  
  66.577          type("WithStatement");
  66.578 @@ -873,8 +867,8 @@
  66.579          property("body");
  66.580          withNode.getBody().accept(this);
  66.581  
  66.582 -        return leaveDefault(withNode);
  66.583 -    }
  66.584 +        return leave();
  66.585 +   }
  66.586  
  66.587      // Internals below
  66.588  
    67.1 --- a/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java	Fri Apr 19 18:23:00 2013 +0530
    67.2 +++ b/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java	Fri Apr 19 16:11:16 2013 +0200
    67.3 @@ -26,30 +26,22 @@
    67.4  package jdk.nashorn.internal.ir.debug;
    67.5  
    67.6  import java.util.List;
    67.7 -import jdk.nashorn.internal.ir.AccessNode;
    67.8 +
    67.9 +import jdk.nashorn.internal.ir.BinaryNode;
   67.10  import jdk.nashorn.internal.ir.Block;
   67.11 -import jdk.nashorn.internal.ir.BreakNode;
   67.12 -import jdk.nashorn.internal.ir.CallNode;
   67.13  import jdk.nashorn.internal.ir.CaseNode;
   67.14  import jdk.nashorn.internal.ir.CatchNode;
   67.15 -import jdk.nashorn.internal.ir.ContinueNode;
   67.16 -import jdk.nashorn.internal.ir.DoWhileNode;
   67.17  import jdk.nashorn.internal.ir.ExecuteNode;
   67.18  import jdk.nashorn.internal.ir.ForNode;
   67.19  import jdk.nashorn.internal.ir.FunctionNode;
   67.20  import jdk.nashorn.internal.ir.IfNode;
   67.21 -import jdk.nashorn.internal.ir.IndexNode;
   67.22  import jdk.nashorn.internal.ir.LabelNode;
   67.23  import jdk.nashorn.internal.ir.LineNumberNode;
   67.24  import jdk.nashorn.internal.ir.Node;
   67.25 -import jdk.nashorn.internal.ir.ReturnNode;
   67.26 -import jdk.nashorn.internal.ir.RuntimeNode;
   67.27  import jdk.nashorn.internal.ir.SplitNode;
   67.28  import jdk.nashorn.internal.ir.SwitchNode;
   67.29  import jdk.nashorn.internal.ir.Symbol;
   67.30 -import jdk.nashorn.internal.ir.ThrowNode;
   67.31  import jdk.nashorn.internal.ir.TryNode;
   67.32 -import jdk.nashorn.internal.ir.UnaryNode;
   67.33  import jdk.nashorn.internal.ir.VarNode;
   67.34  import jdk.nashorn.internal.ir.WhileNode;
   67.35  import jdk.nashorn.internal.ir.WithNode;
   67.36 @@ -136,21 +128,20 @@
   67.37      /*
   67.38       * Visits.
   67.39       */
   67.40 +
   67.41      @Override
   67.42 -    public Node enterAccessNode(final AccessNode accessNode) {
   67.43 -        accessNode.toString(sb);
   67.44 -        return null;
   67.45 +    public boolean enterDefault(final Node node) {
   67.46 +        node.toString(sb);
   67.47 +        return false;
   67.48      }
   67.49  
   67.50      @Override
   67.51 -    public Node enterBlock(final Block block) {
   67.52 +    public boolean enterBlock(final Block block) {
   67.53          sb.append(' ');
   67.54          sb.append('{');
   67.55  
   67.56          indent += TABWIDTH;
   67.57  
   67.58 -        final boolean isFunction = block instanceof FunctionNode;
   67.59 -
   67.60          final List<Node> statements = block.getStatements();
   67.61  
   67.62          boolean lastLineNumber = false;
   67.63 @@ -161,14 +152,14 @@
   67.64                  indent();
   67.65              }
   67.66  
   67.67 -            if (statement instanceof UnaryNode) {
   67.68 -                statement.toString(sb);
   67.69 -            } else {
   67.70 -                statement.accept(this);
   67.71 -            }
   67.72 +            statement.accept(this);
   67.73  
   67.74              lastLineNumber = statement instanceof LineNumberNode;
   67.75  
   67.76 +            if (statement instanceof FunctionNode) {
   67.77 +                continue;
   67.78 +            }
   67.79 +
   67.80              final Symbol symbol = statement.getSymbol();
   67.81  
   67.82              if (symbol != null) {
   67.83 @@ -200,72 +191,42 @@
   67.84          indent();
   67.85          sb.append("}");
   67.86  
   67.87 -        if (isFunction) {
   67.88 -            sb.append(EOLN);
   67.89 -        }
   67.90 -
   67.91 -        return null;
   67.92 +        return false;
   67.93      }
   67.94  
   67.95      @Override
   67.96 -    public Node enterBreakNode(final BreakNode breakNode) {
   67.97 -        breakNode.toString(sb);
   67.98 -        return null;
   67.99 +    public boolean enterBinaryNode(final BinaryNode binaryNode) {
  67.100 +        binaryNode.lhs().accept(this);
  67.101 +        sb.append(' ');
  67.102 +        sb.append(binaryNode.tokenType());
  67.103 +        sb.append(' ');
  67.104 +        binaryNode.rhs().accept(this);
  67.105 +        return false;
  67.106      }
  67.107  
  67.108      @Override
  67.109 -    public Node enterCallNode(final CallNode callNode) {
  67.110 -        callNode.toString(sb);
  67.111 -        return null;
  67.112 +    public boolean enterExecuteNode(final ExecuteNode executeNode) {
  67.113 +        executeNode.getExpression().accept(this);
  67.114 +        return false;
  67.115      }
  67.116  
  67.117      @Override
  67.118 -    public Node enterContinueNode(final ContinueNode continueNode) {
  67.119 -        continueNode.toString(sb);
  67.120 -        return null;
  67.121 +    public boolean enterForNode(final ForNode forNode) {
  67.122 +        forNode.toString(sb);
  67.123 +        forNode.getBody().accept(this);
  67.124 +        return false;
  67.125      }
  67.126  
  67.127      @Override
  67.128 -    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
  67.129 -        sb.append("do");
  67.130 -        doWhileNode.getBody().accept(this);
  67.131 -        sb.append(' ');
  67.132 -        doWhileNode.toString(sb);
  67.133 -
  67.134 -        return null;
  67.135 +    public boolean enterFunctionNode(final FunctionNode functionNode) {
  67.136 +        functionNode.toString(sb);
  67.137 +        enterBlock(functionNode.getBody());
  67.138 +        sb.append(EOLN);
  67.139 +        return false;
  67.140      }
  67.141  
  67.142      @Override
  67.143 -    public Node enterExecuteNode(final ExecuteNode executeNode) {
  67.144 -        final Node expression = executeNode.getExpression();
  67.145 -
  67.146 -        if (expression instanceof UnaryNode) {
  67.147 -            expression.toString(sb);
  67.148 -        } else {
  67.149 -            expression.accept(this);
  67.150 -        }
  67.151 -
  67.152 -        return null;
  67.153 -    }
  67.154 -
  67.155 -    @Override
  67.156 -    public Node enterForNode(final ForNode forNode) {
  67.157 -        forNode.toString(sb);
  67.158 -        forNode.getBody().accept(this);
  67.159 -
  67.160 -        return null;
  67.161 -    }
  67.162 -
  67.163 -    @Override
  67.164 -    public Node enterFunctionNode(final FunctionNode functionNode) {
  67.165 -        functionNode.toString(sb);
  67.166 -        enterBlock(functionNode);
  67.167 -
  67.168 -        return null;
  67.169 -    }
  67.170 -
  67.171 -    @Override
  67.172 -    public Node enterIfNode(final IfNode ifNode) {
  67.173 +    public boolean enterIfNode(final IfNode ifNode) {
  67.174          ifNode.toString(sb);
  67.175          ifNode.getPass().accept(this);
  67.176  
  67.177 @@ -276,55 +237,36 @@
  67.178              fail.accept(this);
  67.179          }
  67.180  
  67.181 -        return null;
  67.182 +        return false;
  67.183      }
  67.184  
  67.185      @Override
  67.186 -    public Node enterIndexNode(final IndexNode indexNode) {
  67.187 -        indexNode.toString(sb);
  67.188 -        return null;
  67.189 -    }
  67.190 -
  67.191 -    @Override
  67.192 -    public Node enterLabelNode(final LabelNode labeledNode) {
  67.193 +    public boolean enterLabelNode(final LabelNode labeledNode) {
  67.194          indent -= TABWIDTH;
  67.195          indent();
  67.196          indent += TABWIDTH;
  67.197          labeledNode.toString(sb);
  67.198          labeledNode.getBody().accept(this);
  67.199  
  67.200 -        return null;
  67.201 +        return false;
  67.202      }
  67.203  
  67.204      @Override
  67.205 -    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
  67.206 +    public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
  67.207          if (printLineNumbers) {
  67.208              lineNumberNode.toString(sb);
  67.209          }
  67.210  
  67.211 -        return null;
  67.212 -    }
  67.213 -
  67.214 -
  67.215 -    @Override
  67.216 -    public Node enterReturnNode(final ReturnNode returnNode) {
  67.217 -        returnNode.toString(sb);
  67.218 -        return null;
  67.219 +        return false;
  67.220      }
  67.221  
  67.222      @Override
  67.223 -    public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
  67.224 -        runtimeNode.toString(sb);
  67.225 -        return null;
  67.226 -    }
  67.227 -
  67.228 -    @Override
  67.229 -    public Node enterSplitNode(final SplitNode splitNode) {
  67.230 +    public boolean enterSplitNode(final SplitNode splitNode) {
  67.231          splitNode.toString(sb);
  67.232          sb.append(EOLN);
  67.233          indent += TABWIDTH;
  67.234          indent();
  67.235 -        return splitNode;
  67.236 +        return true;
  67.237      }
  67.238  
  67.239      @Override
  67.240 @@ -337,7 +279,7 @@
  67.241      }
  67.242  
  67.243      @Override
  67.244 -    public Node enterSwitchNode(final SwitchNode switchNode) {
  67.245 +    public boolean enterSwitchNode(final SwitchNode switchNode) {
  67.246          switchNode.toString(sb);
  67.247          sb.append(" {");
  67.248  
  67.249 @@ -357,24 +299,18 @@
  67.250          indent();
  67.251          sb.append("}");
  67.252  
  67.253 -        return null;
  67.254 -   }
  67.255 -
  67.256 -    @Override
  67.257 -    public Node enterThrowNode(final ThrowNode throwNode) {
  67.258 -        throwNode.toString(sb);
  67.259 -        return null;
  67.260 +        return false;
  67.261      }
  67.262  
  67.263      @Override
  67.264 -    public Node enterTryNode(final TryNode tryNode) {
  67.265 +    public boolean enterTryNode(final TryNode tryNode) {
  67.266          tryNode.toString(sb);
  67.267          tryNode.getBody().accept(this);
  67.268  
  67.269          final List<Block> catchBlocks = tryNode.getCatchBlocks();
  67.270  
  67.271          for (final Block catchBlock : catchBlocks) {
  67.272 -            final CatchNode catchNode = (CatchNode) catchBlock.getStatements().get(0);
  67.273 +            final CatchNode catchNode = (CatchNode)catchBlock.getStatements().get(0);
  67.274              catchNode.toString(sb);
  67.275              catchNode.getBody().accept(this);
  67.276          }
  67.277 @@ -386,35 +322,42 @@
  67.278              finallyBody.accept(this);
  67.279          }
  67.280  
  67.281 -        return null;
  67.282 +        return false;
  67.283      }
  67.284  
  67.285      @Override
  67.286 -    public Node enterVarNode(final VarNode varNode) {
  67.287 +    public boolean enterVarNode(final VarNode varNode) {
  67.288          sb.append("var ");
  67.289          varNode.getName().toString(sb);
  67.290          final Node init = varNode.getInit();
  67.291 -        if(init != null) {
  67.292 +        if (init != null) {
  67.293              sb.append(" = ");
  67.294              init.accept(this);
  67.295          }
  67.296 -        return null;
  67.297 +        return false;
  67.298      }
  67.299  
  67.300      @Override
  67.301 -    public Node enterWhileNode(final WhileNode whileNode) {
  67.302 -        whileNode.toString(sb);
  67.303 -        whileNode.getBody().accept(this);
  67.304 +    public boolean enterWhileNode(final WhileNode whileNode) {
  67.305 +        if (whileNode.isDoWhile()) {
  67.306 +            sb.append("do");
  67.307 +            whileNode.getBody().accept(this);
  67.308 +            sb.append(' ');
  67.309 +            whileNode.toString(sb);
  67.310 +        } else {
  67.311 +            whileNode.toString(sb);
  67.312 +            whileNode.getBody().accept(this);
  67.313 +        }
  67.314  
  67.315 -        return null;
  67.316 +        return false;
  67.317      }
  67.318  
  67.319      @Override
  67.320 -    public Node enterWithNode(final WithNode withNode) {
  67.321 +    public boolean enterWithNode(final WithNode withNode) {
  67.322          withNode.toString(sb);
  67.323          withNode.getBody().accept(this);
  67.324  
  67.325 -        return null;
  67.326 +        return false;
  67.327      }
  67.328  
  67.329  }
    68.1 --- a/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java	Fri Apr 19 18:23:00 2013 +0530
    68.2 +++ b/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java	Fri Apr 19 16:11:16 2013 +0200
    68.3 @@ -25,9 +25,8 @@
    68.4  
    68.5  package jdk.nashorn.internal.ir.visitor;
    68.6  
    68.7 -import jdk.nashorn.internal.codegen.CompileUnit;
    68.8 -import jdk.nashorn.internal.codegen.MethodEmitter;
    68.9  import jdk.nashorn.internal.ir.BinaryNode;
   68.10 +import jdk.nashorn.internal.ir.LexicalContext;
   68.11  import jdk.nashorn.internal.ir.Node;
   68.12  import jdk.nashorn.internal.ir.UnaryNode;
   68.13  
   68.14 @@ -45,15 +44,14 @@
   68.15      /**
   68.16       * Constructor
   68.17       *
   68.18 -     * @param compileUnit compile unit
   68.19 -     * @param method      method emitter
   68.20 +     * @param lc a custom lexical context
   68.21       */
   68.22 -    public NodeOperatorVisitor(final CompileUnit compileUnit, final MethodEmitter method) {
   68.23 -        super(compileUnit, method);
   68.24 +    public NodeOperatorVisitor(final LexicalContext lc) {
   68.25 +        super(lc);
   68.26      }
   68.27  
   68.28      @Override
   68.29 -    public final Node enterUnaryNode(final UnaryNode unaryNode) {
   68.30 +    public final boolean enterUnaryNode(final UnaryNode unaryNode) {
   68.31          switch (unaryNode.tokenType()) {
   68.32          case ADD:
   68.33              return enterADD(unaryNode);
   68.34 @@ -119,7 +117,7 @@
   68.35      }
   68.36  
   68.37      @Override
   68.38 -    public final Node enterBinaryNode(final BinaryNode binaryNode) {
   68.39 +    public final boolean enterBinaryNode(final BinaryNode binaryNode) {
   68.40          switch (binaryNode.tokenType()) {
   68.41          case ADD:
   68.42              return enterADD(binaryNode);
   68.43 @@ -287,17 +285,6 @@
   68.44      }
   68.45  
   68.46      /*
   68.47 -    @Override
   68.48 -    public Node enter(final TernaryNode ternaryNode) {
   68.49 -        return enterDefault(ternaryNode);
   68.50 -    }
   68.51 -
   68.52 -    @Override
   68.53 -    public Node leave(final TernaryNode ternaryNode) {
   68.54 -        return leaveDefault(ternaryNode);
   68.55 -    }*/
   68.56 -
   68.57 -    /*
   68.58       * Unary entries and exists.
   68.59       */
   68.60  
   68.61 @@ -305,9 +292,9 @@
   68.62       * Unary enter - callback for entering a unary +
   68.63       *
   68.64       * @param  unaryNode the node
   68.65 -     * @return processed node
   68.66 +     * @return true if traversal should continue and node children be traversed, false otherwise
   68.67       */
   68.68 -    public Node enterADD(final UnaryNode unaryNode) {
   68.69 +    public boolean enterADD(final UnaryNode unaryNode) {
   68.70          return enterDefault(unaryNode);
   68.71      }
   68.72  
   68.73 @@ -325,9 +312,9 @@
   68.74       * Unary enter - callback for entering a ~ operator
   68.75       *
   68.76       * @param  unaryNode the node
   68.77 -     * @return processed node
   68.78 +     * @return true if traversal should continue and node children be traversed, false otherwise
   68.79       */
   68.80 -    public Node enterBIT_NOT(final UnaryNode unaryNode) {
   68.81 +    public boolean enterBIT_NOT(final UnaryNode unaryNode) {
   68.82          return enterDefault(unaryNode);
   68.83      }
   68.84  
   68.85 @@ -345,9 +332,9 @@
   68.86       * Unary enter - callback for entering a conversion
   68.87       *
   68.88       * @param  unaryNode the node
   68.89 -     * @return processed node
   68.90 +     * @return true if traversal should continue and node children be traversed, false otherwise
   68.91       */
   68.92 -    public Node enterCONVERT(final UnaryNode unaryNode) {
   68.93 +    public boolean enterCONVERT(final UnaryNode unaryNode) {
   68.94          return enterDefault(unaryNode);
   68.95      }
   68.96  
   68.97 @@ -365,9 +352,9 @@
   68.98       * Unary enter - callback for entering a ++ or -- operator
   68.99       *
  68.100       * @param  unaryNode the node
  68.101 -     * @return processed node
  68.102 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.103       */
  68.104 -    public Node enterDECINC(final UnaryNode unaryNode) {
  68.105 +    public boolean enterDECINC(final UnaryNode unaryNode) {
  68.106          return enterDefault(unaryNode);
  68.107      }
  68.108  
  68.109 @@ -387,7 +374,7 @@
  68.110       * @param  unaryNode the node
  68.111       * @return processed node
  68.112       */
  68.113 -    public Node enterDELETE(final UnaryNode unaryNode) {
  68.114 +    public boolean enterDELETE(final UnaryNode unaryNode) {
  68.115          return enterDefault(unaryNode);
  68.116      }
  68.117  
  68.118 @@ -405,9 +392,9 @@
  68.119       * Unary enter - callback for entering a discard operator
  68.120       *
  68.121       * @param  unaryNode the node
  68.122 -     * @return processed node
  68.123 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.124       */
  68.125 -    public Node enterDISCARD(final UnaryNode unaryNode) {
  68.126 +    public boolean enterDISCARD(final UnaryNode unaryNode) {
  68.127          return enterDefault(unaryNode);
  68.128      }
  68.129  
  68.130 @@ -425,9 +412,9 @@
  68.131       * Unary enter - callback for entering a new operator
  68.132       *
  68.133       * @param  unaryNode the node
  68.134 -     * @return processed node
  68.135 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.136       */
  68.137 -    public Node enterNEW(final UnaryNode unaryNode) {
  68.138 +    public boolean enterNEW(final UnaryNode unaryNode) {
  68.139          return enterDefault(unaryNode);
  68.140      }
  68.141  
  68.142 @@ -445,9 +432,9 @@
  68.143       * Unary enter - callback for entering a ! operator
  68.144       *
  68.145       * @param  unaryNode the node
  68.146 -     * @return processed node
  68.147 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.148       */
  68.149 -    public Node enterNOT(final UnaryNode unaryNode) {
  68.150 +    public boolean enterNOT(final UnaryNode unaryNode) {
  68.151          return enterDefault(unaryNode);
  68.152      }
  68.153  
  68.154 @@ -465,9 +452,9 @@
  68.155       * Unary enter - callback for entering a unary -
  68.156       *
  68.157       * @param  unaryNode the node
  68.158 -     * @return processed node
  68.159 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.160       */
  68.161 -    public Node enterSUB(final UnaryNode unaryNode) {
  68.162 +    public boolean enterSUB(final UnaryNode unaryNode) {
  68.163          return enterDefault(unaryNode);
  68.164      }
  68.165  
  68.166 @@ -485,9 +472,9 @@
  68.167       * Unary enter - callback for entering a typeof
  68.168       *
  68.169       * @param  unaryNode the node
  68.170 -     * @return processed node
  68.171 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.172       */
  68.173 -    public Node enterTYPEOF(final UnaryNode unaryNode) {
  68.174 +    public boolean enterTYPEOF(final UnaryNode unaryNode) {
  68.175          return enterDefault(unaryNode);
  68.176      }
  68.177  
  68.178 @@ -505,9 +492,9 @@
  68.179       * Unary enter - callback for entering a void
  68.180       *
  68.181       * @param  unaryNode the node
  68.182 -     * @return processed node
  68.183 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.184       */
  68.185 -    public Node enterVOID(final UnaryNode unaryNode) {
  68.186 +    public boolean enterVOID(final UnaryNode unaryNode) {
  68.187          return enterDefault(unaryNode);
  68.188      }
  68.189  
  68.190 @@ -525,9 +512,9 @@
  68.191       * Binary enter - callback for entering + operator
  68.192       *
  68.193       * @param  binaryNode the node
  68.194 -     * @return processed node
  68.195 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.196       */
  68.197 -    public Node enterADD(final BinaryNode binaryNode) {
  68.198 +    public boolean enterADD(final BinaryNode binaryNode) {
  68.199          return enterDefault(binaryNode);
  68.200      }
  68.201  
  68.202 @@ -545,9 +532,9 @@
  68.203       * Binary enter - callback for entering {@literal &&} operator
  68.204       *
  68.205       * @param  binaryNode the node
  68.206 -     * @return processed node
  68.207 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.208       */
  68.209 -    public Node enterAND(final BinaryNode binaryNode) {
  68.210 +    public boolean enterAND(final BinaryNode binaryNode) {
  68.211          return enterDefault(binaryNode);
  68.212      }
  68.213  
  68.214 @@ -565,9 +552,9 @@
  68.215       * Binary enter - callback for entering an assignment
  68.216       *
  68.217       * @param  binaryNode the node
  68.218 -     * @return processed node
  68.219 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.220       */
  68.221 -    public Node enterASSIGN(final BinaryNode binaryNode) {
  68.222 +    public boolean enterASSIGN(final BinaryNode binaryNode) {
  68.223          return enterDefault(binaryNode);
  68.224      }
  68.225  
  68.226 @@ -585,9 +572,9 @@
  68.227       * Binary enter - callback for entering += operator
  68.228       *
  68.229       * @param  binaryNode the node
  68.230 -     * @return processed node
  68.231 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.232       */
  68.233 -    public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
  68.234 +    public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
  68.235          return enterDefault(binaryNode);
  68.236      }
  68.237  
  68.238 @@ -605,9 +592,9 @@
  68.239       * Binary enter - callback for entering {@literal &=} operator
  68.240       *
  68.241       * @param  binaryNode the node
  68.242 -     * @return processed node
  68.243 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.244       */
  68.245 -    public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
  68.246 +    public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
  68.247          return enterDefault(binaryNode);
  68.248      }
  68.249  
  68.250 @@ -625,9 +612,9 @@
  68.251       * Binary enter - callback for entering |= operator
  68.252       *
  68.253       * @param  binaryNode the node
  68.254 -     * @return processed node
  68.255 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.256       */
  68.257 -    public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
  68.258 +    public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
  68.259          return enterDefault(binaryNode);
  68.260      }
  68.261  
  68.262 @@ -645,9 +632,9 @@
  68.263       * Binary enter - callback for entering ^= operator
  68.264       *
  68.265       * @param  binaryNode the node
  68.266 -     * @return processed node
  68.267 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.268       */
  68.269 -    public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
  68.270 +    public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
  68.271          return enterDefault(binaryNode);
  68.272      }
  68.273  
  68.274 @@ -665,9 +652,9 @@
  68.275       * Binary enter - callback for entering /= operator
  68.276       *
  68.277       * @param  binaryNode the node
  68.278 -     * @return processed node
  68.279 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.280       */
  68.281 -    public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
  68.282 +    public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
  68.283          return enterDefault(binaryNode);
  68.284      }
  68.285  
  68.286 @@ -685,9 +672,9 @@
  68.287       * Binary enter - callback for entering %= operator
  68.288       *
  68.289       * @param  binaryNode the node
  68.290 -     * @return processed node
  68.291 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.292       */
  68.293 -    public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
  68.294 +    public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
  68.295          return enterDefault(binaryNode);
  68.296      }
  68.297  
  68.298 @@ -705,9 +692,9 @@
  68.299       * Binary enter - callback for entering *= operator
  68.300       *
  68.301       * @param  binaryNode the node
  68.302 -     * @return processed node
  68.303 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.304       */
  68.305 -    public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
  68.306 +    public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
  68.307          return enterDefault(binaryNode);
  68.308      }
  68.309  
  68.310 @@ -725,9 +712,9 @@
  68.311       * Binary enter - callback for entering {@literal >>=} operator
  68.312       *
  68.313       * @param  binaryNode the node
  68.314 -     * @return processed node
  68.315 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.316       */
  68.317 -    public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
  68.318 +    public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
  68.319          return enterDefault(binaryNode);
  68.320      }
  68.321  
  68.322 @@ -745,9 +732,9 @@
  68.323       * Binary enter - callback for entering a {@literal <<=} operator
  68.324       *
  68.325       * @param  binaryNode the node
  68.326 -     * @return processed node
  68.327 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.328       */
  68.329 -    public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
  68.330 +    public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
  68.331          return enterDefault(binaryNode);
  68.332      }
  68.333  
  68.334 @@ -765,9 +752,9 @@
  68.335       * Binary enter - callback for entering {@literal >>>=} operator
  68.336       *
  68.337       * @param  binaryNode the node
  68.338 -     * @return processed node
  68.339 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.340       */
  68.341 -    public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
  68.342 +    public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
  68.343          return enterDefault(binaryNode);
  68.344      }
  68.345  
  68.346 @@ -785,9 +772,9 @@
  68.347       * Binary enter - callback for entering -= operator
  68.348       *
  68.349       * @param  binaryNode the node
  68.350 -     * @return processed node
  68.351 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.352       */
  68.353 -    public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
  68.354 +    public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
  68.355          return enterDefault(binaryNode);
  68.356      }
  68.357  
  68.358 @@ -805,9 +792,9 @@
  68.359       * Binary enter - callback for entering a bind operator
  68.360       *
  68.361       * @param  binaryNode the node
  68.362 -     * @return processed node
  68.363 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.364       */
  68.365 -    public Node enterBIND(final BinaryNode binaryNode) {
  68.366 +    public boolean enterBIND(final BinaryNode binaryNode) {
  68.367          return enterDefault(binaryNode);
  68.368      }
  68.369  
  68.370 @@ -825,9 +812,9 @@
  68.371       * Binary enter - callback for entering {@literal &} operator
  68.372       *
  68.373       * @param  binaryNode the node
  68.374 -     * @return processed node
  68.375 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.376       */
  68.377 -    public Node enterBIT_AND(final BinaryNode binaryNode) {
  68.378 +    public boolean enterBIT_AND(final BinaryNode binaryNode) {
  68.379          return enterDefault(binaryNode);
  68.380      }
  68.381  
  68.382 @@ -845,9 +832,9 @@
  68.383       * Binary enter - callback for entering | operator
  68.384       *
  68.385       * @param  binaryNode the node
  68.386 -     * @return processed node
  68.387 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.388       */
  68.389 -    public Node enterBIT_OR(final BinaryNode binaryNode) {
  68.390 +    public boolean enterBIT_OR(final BinaryNode binaryNode) {
  68.391          return enterDefault(binaryNode);
  68.392      }
  68.393  
  68.394 @@ -865,9 +852,9 @@
  68.395       * Binary enter - callback for entering ^ operator
  68.396       *
  68.397       * @param  binaryNode the node
  68.398 -     * @return processed node
  68.399 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.400       */
  68.401 -    public Node enterBIT_XOR(final BinaryNode binaryNode) {
  68.402 +    public boolean enterBIT_XOR(final BinaryNode binaryNode) {
  68.403          return enterDefault(binaryNode);
  68.404      }
  68.405  
  68.406 @@ -886,9 +873,9 @@
  68.407       * (a, b) where the result is a
  68.408       *
  68.409       * @param  binaryNode the node
  68.410 -     * @return processed node
  68.411 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.412       */
  68.413 -    public Node enterCOMMALEFT(final BinaryNode binaryNode) {
  68.414 +    public boolean enterCOMMALEFT(final BinaryNode binaryNode) {
  68.415          return enterDefault(binaryNode);
  68.416      }
  68.417  
  68.418 @@ -908,9 +895,9 @@
  68.419       * (a, b) where the result is b
  68.420       *
  68.421       * @param  binaryNode the node
  68.422 -     * @return processed node
  68.423 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.424       */
  68.425 -    public Node enterCOMMARIGHT(final BinaryNode binaryNode) {
  68.426 +    public boolean enterCOMMARIGHT(final BinaryNode binaryNode) {
  68.427          return enterDefault(binaryNode);
  68.428      }
  68.429  
  68.430 @@ -929,9 +916,9 @@
  68.431       * Binary enter - callback for entering a division
  68.432       *
  68.433       * @param  binaryNode the node
  68.434 -     * @return processed node
  68.435 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.436       */
  68.437 -    public Node enterDIV(final BinaryNode binaryNode) {
  68.438 +    public boolean enterDIV(final BinaryNode binaryNode) {
  68.439          return enterDefault(binaryNode);
  68.440      }
  68.441  
  68.442 @@ -949,9 +936,9 @@
  68.443       * Binary enter - callback for entering == operator
  68.444       *
  68.445       * @param  binaryNode the node
  68.446 -     * @return processed node
  68.447 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.448       */
  68.449 -    public Node enterEQ(final BinaryNode binaryNode) {
  68.450 +    public boolean enterEQ(final BinaryNode binaryNode) {
  68.451          return enterDefault(binaryNode);
  68.452      }
  68.453  
  68.454 @@ -969,9 +956,9 @@
  68.455       * Binary enter - callback for entering === operator
  68.456       *
  68.457       * @param  binaryNode the node
  68.458 -     * @return processed node
  68.459 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.460       */
  68.461 -    public Node enterEQ_STRICT(final BinaryNode binaryNode) {
  68.462 +    public boolean enterEQ_STRICT(final BinaryNode binaryNode) {
  68.463          return enterDefault(binaryNode);
  68.464      }
  68.465  
  68.466 @@ -989,9 +976,9 @@
  68.467       * Binary enter - callback for entering {@literal >=} operator
  68.468       *
  68.469       * @param  binaryNode the node
  68.470 -     * @return processed node
  68.471 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.472       */
  68.473 -    public Node enterGE(final BinaryNode binaryNode) {
  68.474 +    public boolean enterGE(final BinaryNode binaryNode) {
  68.475          return enterDefault(binaryNode);
  68.476      }
  68.477  
  68.478 @@ -1009,9 +996,9 @@
  68.479       * Binary enter - callback for entering {@literal >} operator
  68.480       *
  68.481       * @param  binaryNode the node
  68.482 -     * @return processed node
  68.483 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.484       */
  68.485 -    public Node enterGT(final BinaryNode binaryNode) {
  68.486 +    public boolean enterGT(final BinaryNode binaryNode) {
  68.487          return enterDefault(binaryNode);
  68.488      }
  68.489  
  68.490 @@ -1029,9 +1016,9 @@
  68.491       * Binary enter - callback for entering in operator
  68.492       *
  68.493       * @param  binaryNode the node
  68.494 -     * @return processed node
  68.495 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.496       */
  68.497 -    public Node enterIN(final BinaryNode binaryNode) {
  68.498 +    public boolean enterIN(final BinaryNode binaryNode) {
  68.499          return enterDefault(binaryNode);
  68.500      }
  68.501  
  68.502 @@ -1049,9 +1036,9 @@
  68.503       * Binary enter - callback for entering instanceof operator
  68.504       *
  68.505       * @param  binaryNode the node
  68.506 -     * @return processed node
  68.507 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.508       */
  68.509 -    public Node enterINSTANCEOF(final BinaryNode binaryNode) {
  68.510 +    public boolean enterINSTANCEOF(final BinaryNode binaryNode) {
  68.511          return enterDefault(binaryNode);
  68.512      }
  68.513  
  68.514 @@ -1069,9 +1056,9 @@
  68.515       * Binary enter - callback for entering {@literal <=} operator
  68.516       *
  68.517       * @param  binaryNode the node
  68.518 -     * @return processed node
  68.519 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.520       */
  68.521 -    public Node enterLE(final BinaryNode binaryNode) {
  68.522 +    public boolean enterLE(final BinaryNode binaryNode) {
  68.523          return enterDefault(binaryNode);
  68.524      }
  68.525  
  68.526 @@ -1089,9 +1076,9 @@
  68.527       * Binary enter - callback for entering {@literal <} operator
  68.528       *
  68.529       * @param  binaryNode the node
  68.530 -     * @return processed node
  68.531 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.532       */
  68.533 -    public Node enterLT(final BinaryNode binaryNode) {
  68.534 +    public boolean enterLT(final BinaryNode binaryNode) {
  68.535          return enterDefault(binaryNode);
  68.536      }
  68.537  
  68.538 @@ -1108,9 +1095,9 @@
  68.539       * Binary enter - callback for entering % operator
  68.540       *
  68.541       * @param  binaryNode the node
  68.542 -     * @return processed node
  68.543 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.544       */
  68.545 -    public Node enterMOD(final BinaryNode binaryNode) {
  68.546 +    public boolean enterMOD(final BinaryNode binaryNode) {
  68.547          return enterDefault(binaryNode);
  68.548      }
  68.549  
  68.550 @@ -1128,9 +1115,9 @@
  68.551       * Binary enter - callback for entering * operator
  68.552       *
  68.553       * @param  binaryNode the node
  68.554 -     * @return processed node
  68.555 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.556       */
  68.557 -    public Node enterMUL(final BinaryNode binaryNode) {
  68.558 +    public boolean enterMUL(final BinaryNode binaryNode) {
  68.559          return enterDefault(binaryNode);
  68.560      }
  68.561  
  68.562 @@ -1148,9 +1135,9 @@
  68.563       * Binary enter - callback for entering != operator
  68.564       *
  68.565       * @param  binaryNode the node
  68.566 -     * @return processed node
  68.567 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.568       */
  68.569 -    public Node enterNE(final BinaryNode binaryNode) {
  68.570 +    public boolean enterNE(final BinaryNode binaryNode) {
  68.571          return enterDefault(binaryNode);
  68.572      }
  68.573  
  68.574 @@ -1168,9 +1155,9 @@
  68.575       * Binary enter - callback for entering a !== operator
  68.576       *
  68.577       * @param  binaryNode the node
  68.578 -     * @return processed node
  68.579 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.580       */
  68.581 -    public Node enterNE_STRICT(final BinaryNode binaryNode) {
  68.582 +    public boolean enterNE_STRICT(final BinaryNode binaryNode) {
  68.583          return enterDefault(binaryNode);
  68.584      }
  68.585  
  68.586 @@ -1188,9 +1175,9 @@
  68.587       * Binary enter - callback for entering || operator
  68.588       *
  68.589       * @param  binaryNode the node
  68.590 -     * @return processed node
  68.591 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.592       */
  68.593 -    public Node enterOR(final BinaryNode binaryNode) {
  68.594 +    public boolean enterOR(final BinaryNode binaryNode) {
  68.595          return enterDefault(binaryNode);
  68.596      }
  68.597  
  68.598 @@ -1208,9 +1195,9 @@
  68.599       * Binary enter - callback for entering {@literal >>} operator
  68.600       *
  68.601       * @param  binaryNode the node
  68.602 -     * @return processed node
  68.603 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.604       */
  68.605 -    public Node enterSAR(final BinaryNode binaryNode) {
  68.606 +    public boolean enterSAR(final BinaryNode binaryNode) {
  68.607          return enterDefault(binaryNode);
  68.608      }
  68.609  
  68.610 @@ -1228,9 +1215,9 @@
  68.611       * Binary enter - callback for entering {@literal <<} operator
  68.612       *
  68.613       * @param  binaryNode the node
  68.614 -     * @return processed node
  68.615 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.616       */
  68.617 -    public Node enterSHL(final BinaryNode binaryNode) {
  68.618 +    public boolean enterSHL(final BinaryNode binaryNode) {
  68.619          return enterDefault(binaryNode);
  68.620      }
  68.621  
  68.622 @@ -1247,9 +1234,9 @@
  68.623       * Binary enter - callback for entering {@literal >>>} operator
  68.624       *
  68.625       * @param  binaryNode the node
  68.626 -     * @return processed node
  68.627 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.628       */
  68.629 -    public Node enterSHR(final BinaryNode binaryNode) {
  68.630 +    public boolean enterSHR(final BinaryNode binaryNode) {
  68.631          return enterDefault(binaryNode);
  68.632      }
  68.633  
  68.634 @@ -1267,9 +1254,9 @@
  68.635       * Binary enter - callback for entering - operator
  68.636       *
  68.637       * @param  binaryNode the node
  68.638 -     * @return processed node
  68.639 +     * @return true if traversal should continue and node children be traversed, false otherwise
  68.640       */
  68.641 -    public Node enterSUB(final BinaryNode binaryNode) {
  68.642 +    public boolean enterSUB(final BinaryNode binaryNode) {
  68.643          return enterDefault(binaryNode);
  68.644      }
  68.645  
    69.1 --- a/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java	Fri Apr 19 18:23:00 2013 +0530
    69.2 +++ b/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java	Fri Apr 19 16:11:16 2013 +0200
    69.3 @@ -25,8 +25,6 @@
    69.4  
    69.5  package jdk.nashorn.internal.ir.visitor;
    69.6  
    69.7 -import jdk.nashorn.internal.codegen.CompileUnit;
    69.8 -import jdk.nashorn.internal.codegen.MethodEmitter;
    69.9  import jdk.nashorn.internal.ir.AccessNode;
   69.10  import jdk.nashorn.internal.ir.BinaryNode;
   69.11  import jdk.nashorn.internal.ir.Block;
   69.12 @@ -35,7 +33,6 @@
   69.13  import jdk.nashorn.internal.ir.CaseNode;
   69.14  import jdk.nashorn.internal.ir.CatchNode;
   69.15  import jdk.nashorn.internal.ir.ContinueNode;
   69.16 -import jdk.nashorn.internal.ir.DoWhileNode;
   69.17  import jdk.nashorn.internal.ir.EmptyNode;
   69.18  import jdk.nashorn.internal.ir.ExecuteNode;
   69.19  import jdk.nashorn.internal.ir.ForNode;
   69.20 @@ -44,6 +41,7 @@
   69.21  import jdk.nashorn.internal.ir.IfNode;
   69.22  import jdk.nashorn.internal.ir.IndexNode;
   69.23  import jdk.nashorn.internal.ir.LabelNode;
   69.24 +import jdk.nashorn.internal.ir.LexicalContext;
   69.25  import jdk.nashorn.internal.ir.LineNumberNode;
   69.26  import jdk.nashorn.internal.ir.LiteralNode;
   69.27  import jdk.nashorn.internal.ir.Node;
   69.28 @@ -65,41 +63,30 @@
   69.29   * Visitor used to navigate the IR.
   69.30   */
   69.31  public abstract class NodeVisitor {
   69.32 -    /** Current functionNode. */
   69.33 -    private FunctionNode currentFunctionNode;
   69.34 -
   69.35 -    /** Current compile unit used for class generation. */
   69.36 -    private CompileUnit compileUnit;
   69.37 +    private final LexicalContext lc;
   69.38  
   69.39      /**
   69.40 -     * Current method visitor used for method generation.
   69.41 -     * <p>
   69.42 -     * TODO: protected is just for convenience and readability, so that
   69.43 -     * subclasses can directly use 'method' - might want to change that
   69.44 -     */
   69.45 -    protected MethodEmitter method;
   69.46 -
   69.47 -    /** Current block. */
   69.48 -    private Block currentBlock;
   69.49 -
   69.50 -    /**
   69.51 -     * Constructor.
   69.52 +     * Constructor
   69.53       */
   69.54      public NodeVisitor() {
   69.55 -        this(null, null);
   69.56 +        this(new LexicalContext());
   69.57      }
   69.58  
   69.59      /**
   69.60       * Constructor
   69.61       *
   69.62 -     * @param compileUnit compile unit for this node visitor
   69.63 -     * @param method method emitter for this node visitor
   69.64 +     * @param lc a custom lexical context
   69.65       */
   69.66 -    public NodeVisitor(final CompileUnit compileUnit, final MethodEmitter method) {
   69.67 -        super();
   69.68 +    public NodeVisitor(final LexicalContext lc) {
   69.69 +        this.lc = lc;
   69.70 +    }
   69.71  
   69.72 -        this.compileUnit = compileUnit;
   69.73 -        this.method      = method;
   69.74 +    /**
   69.75 +     * Get the lexical context of this node visitor
   69.76 +     * @return lexical context
   69.77 +     */
   69.78 +    public LexicalContext getLexicalContext() {
   69.79 +        return lc;
   69.80      }
   69.81  
   69.82      /**
   69.83 @@ -118,10 +105,10 @@
   69.84       *
   69.85       * @see NodeVisitor#leaveDefault(Node)
   69.86       * @param node the node to visit
   69.87 -     * @return the node
   69.88 +     * @return true if traversal should continue and node children be traversed, false otherwise
   69.89       */
   69.90 -    protected Node enterDefault(final Node node) {
   69.91 -        return node;
   69.92 +    protected boolean enterDefault(final Node node) {
   69.93 +        return true;
   69.94      }
   69.95  
   69.96      /**
   69.97 @@ -150,9 +137,9 @@
   69.98       * Callback for entering an AccessNode
   69.99       *
  69.100       * @param  accessNode the node
  69.101 -     * @return processed node, null if traversal should end, null if traversal should end
  69.102 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.103       */
  69.104 -    public Node enterAccessNode(final AccessNode accessNode) {
  69.105 +    public boolean enterAccessNode(final AccessNode accessNode) {
  69.106          return enterDefault(accessNode);
  69.107      }
  69.108  
  69.109 @@ -170,9 +157,9 @@
  69.110       * Callback for entering a Block
  69.111       *
  69.112       * @param  block     the node
  69.113 -     * @return processed node, null if traversal should end
  69.114 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.115       */
  69.116 -    public Node enterBlock(final Block block) {
  69.117 +    public boolean enterBlock(final Block block) {
  69.118          return enterDefault(block);
  69.119      }
  69.120  
  69.121 @@ -192,7 +179,7 @@
  69.122       * @param  binaryNode  the node
  69.123       * @return processed   node
  69.124       */
  69.125 -    public Node enterBinaryNode(final BinaryNode binaryNode) {
  69.126 +    public boolean enterBinaryNode(final BinaryNode binaryNode) {
  69.127          return enterDefault(binaryNode);
  69.128      }
  69.129  
  69.130 @@ -210,9 +197,9 @@
  69.131       * Callback for entering a BreakNode
  69.132       *
  69.133       * @param  breakNode the node
  69.134 -     * @return processed node, null if traversal should end
  69.135 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.136       */
  69.137 -    public Node enterBreakNode(final BreakNode breakNode) {
  69.138 +    public boolean enterBreakNode(final BreakNode breakNode) {
  69.139          return enterDefault(breakNode);
  69.140      }
  69.141  
  69.142 @@ -230,9 +217,9 @@
  69.143       * Callback for entering a CallNode
  69.144       *
  69.145       * @param  callNode  the node
  69.146 -     * @return processed node, null if traversal should end
  69.147 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.148       */
  69.149 -    public Node enterCallNode(final CallNode callNode) {
  69.150 +    public boolean enterCallNode(final CallNode callNode) {
  69.151          return enterDefault(callNode);
  69.152      }
  69.153  
  69.154 @@ -250,9 +237,9 @@
  69.155       * Callback for entering a CaseNode
  69.156       *
  69.157       * @param  caseNode  the node
  69.158 -     * @return processed node, null if traversal should end
  69.159 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.160       */
  69.161 -    public Node enterCaseNode(final CaseNode caseNode) {
  69.162 +    public boolean enterCaseNode(final CaseNode caseNode) {
  69.163          return enterDefault(caseNode);
  69.164      }
  69.165  
  69.166 @@ -270,9 +257,9 @@
  69.167       * Callback for entering a CatchNode
  69.168       *
  69.169       * @param  catchNode the node
  69.170 -     * @return processed node, null if traversal should end
  69.171 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.172       */
  69.173 -    public Node enterCatchNode(final CatchNode catchNode) {
  69.174 +    public boolean enterCatchNode(final CatchNode catchNode) {
  69.175          return enterDefault(catchNode);
  69.176      }
  69.177  
  69.178 @@ -290,9 +277,9 @@
  69.179       * Callback for entering a ContinueNode
  69.180       *
  69.181       * @param  continueNode the node
  69.182 -     * @return processed node, null if traversal should end
  69.183 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.184       */
  69.185 -    public Node enterContinueNode(final ContinueNode continueNode) {
  69.186 +    public boolean enterContinueNode(final ContinueNode continueNode) {
  69.187          return enterDefault(continueNode);
  69.188      }
  69.189  
  69.190 @@ -307,32 +294,12 @@
  69.191      }
  69.192  
  69.193      /**
  69.194 -     * Callback for entering a DoWhileNode
  69.195 -     *
  69.196 -     * @param  doWhileNode the node
  69.197 -     * @return processed   node
  69.198 -     */
  69.199 -    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
  69.200 -        return enterDefault(doWhileNode);
  69.201 -    }
  69.202 -
  69.203 -    /**
  69.204 -     * Callback for leaving a DoWhileNode
  69.205 -     *
  69.206 -     * @param  doWhileNode the node
  69.207 -     * @return processed node, which will replace the original one, or the original node
  69.208 -     */
  69.209 -    public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
  69.210 -        return leaveDefault(doWhileNode);
  69.211 -    }
  69.212 -
  69.213 -    /**
  69.214       * Callback for entering an EmptyNode
  69.215       *
  69.216       * @param  emptyNode   the node
  69.217 -     * @return processed   node
  69.218 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.219       */
  69.220 -    public Node enterEmptyNode(final EmptyNode emptyNode) {
  69.221 +    public boolean enterEmptyNode(final EmptyNode emptyNode) {
  69.222          return enterDefault(emptyNode);
  69.223      }
  69.224  
  69.225 @@ -350,9 +317,9 @@
  69.226       * Callback for entering an ExecuteNode
  69.227       *
  69.228       * @param  executeNode the node
  69.229 -     * @return processed node, null if traversal should end
  69.230 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.231       */
  69.232 -    public Node enterExecuteNode(final ExecuteNode executeNode) {
  69.233 +    public boolean enterExecuteNode(final ExecuteNode executeNode) {
  69.234          return enterDefault(executeNode);
  69.235      }
  69.236  
  69.237 @@ -370,9 +337,9 @@
  69.238       * Callback for entering a ForNode
  69.239       *
  69.240       * @param  forNode   the node
  69.241 -     * @return processed node, null if traversal should end
  69.242 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.243       */
  69.244 -    public Node enterForNode(final ForNode forNode) {
  69.245 +    public boolean enterForNode(final ForNode forNode) {
  69.246          return enterDefault(forNode);
  69.247      }
  69.248  
  69.249 @@ -390,9 +357,9 @@
  69.250       * Callback for entering a FunctionNode
  69.251       *
  69.252       * @param  functionNode the node
  69.253 -     * @return processed    node
  69.254 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.255       */
  69.256 -    public Node enterFunctionNode(final FunctionNode functionNode) {
  69.257 +    public boolean enterFunctionNode(final FunctionNode functionNode) {
  69.258          return enterDefault(functionNode);
  69.259      }
  69.260  
  69.261 @@ -410,9 +377,9 @@
  69.262       * Callback for entering an IdentNode
  69.263       *
  69.264       * @param  identNode the node
  69.265 -     * @return processed node, null if traversal should end
  69.266 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.267       */
  69.268 -    public Node enterIdentNode(final IdentNode identNode) {
  69.269 +    public boolean enterIdentNode(final IdentNode identNode) {
  69.270          return enterDefault(identNode);
  69.271      }
  69.272  
  69.273 @@ -429,10 +396,10 @@
  69.274      /**
  69.275       * Callback for entering an IfNode
  69.276       *
  69.277 -     * @param  ifNode    the node
  69.278 -     * @return processed node, null if traversal should end
  69.279 +     * @param  ifNode the node
  69.280 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.281       */
  69.282 -    public Node enterIfNode(final IfNode ifNode) {
  69.283 +    public boolean enterIfNode(final IfNode ifNode) {
  69.284          return enterDefault(ifNode);
  69.285      }
  69.286  
  69.287 @@ -450,9 +417,9 @@
  69.288       * Callback for entering an IndexNode
  69.289       *
  69.290       * @param  indexNode  the node
  69.291 -     * @return processed node, null if traversal should end
  69.292 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.293       */
  69.294 -    public Node enterIndexNode(final IndexNode indexNode) {
  69.295 +    public boolean enterIndexNode(final IndexNode indexNode) {
  69.296          return enterDefault(indexNode);
  69.297      }
  69.298  
  69.299 @@ -470,9 +437,9 @@
  69.300       * Callback for entering a LabelNode
  69.301       *
  69.302       * @param  labelNode the node
  69.303 -     * @return processed node, null if traversal should end
  69.304 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.305       */
  69.306 -    public Node enterLabelNode(final LabelNode labelNode) {
  69.307 +    public boolean enterLabelNode(final LabelNode labelNode) {
  69.308          return enterDefault(labelNode);
  69.309      }
  69.310  
  69.311 @@ -490,9 +457,9 @@
  69.312       * Callback for entering a LineNumberNode
  69.313       *
  69.314       * @param  lineNumberNode the node
  69.315 -     * @return processed node, null if traversal should end
  69.316 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.317       */
  69.318 -    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
  69.319 +    public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
  69.320          return enterDefault(lineNumberNode);
  69.321      }
  69.322  
  69.323 @@ -510,9 +477,9 @@
  69.324       * Callback for entering a LiteralNode
  69.325       *
  69.326       * @param  literalNode the node
  69.327 -     * @return processed   node
  69.328 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.329       */
  69.330 -    public Node enterLiteralNode(final LiteralNode<?> literalNode) {
  69.331 +    public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
  69.332          return enterDefault(literalNode);
  69.333      }
  69.334  
  69.335 @@ -530,9 +497,9 @@
  69.336       * Callback for entering an ObjectNode
  69.337       *
  69.338       * @param  objectNode the node
  69.339 -     * @return processed  node
  69.340 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.341       */
  69.342 -    public Node enterObjectNode(final ObjectNode objectNode) {
  69.343 +    public boolean enterObjectNode(final ObjectNode objectNode) {
  69.344          return enterDefault(objectNode);
  69.345      }
  69.346  
  69.347 @@ -550,9 +517,9 @@
  69.348       * Callback for entering a PropertyNode
  69.349       *
  69.350       * @param  propertyNode the node
  69.351 -     * @return processed node, null if traversal should end
  69.352 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.353       */
  69.354 -    public Node enterPropertyNode(final PropertyNode propertyNode) {
  69.355 +    public boolean enterPropertyNode(final PropertyNode propertyNode) {
  69.356          return enterDefault(propertyNode);
  69.357      }
  69.358  
  69.359 @@ -570,9 +537,9 @@
  69.360       * Callback for entering a ReturnNode
  69.361       *
  69.362       * @param  returnNode the node
  69.363 -     * @return processed node, null if traversal should end
  69.364 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.365       */
  69.366 -    public Node enterReturnNode(final ReturnNode returnNode) {
  69.367 +    public boolean enterReturnNode(final ReturnNode returnNode) {
  69.368          return enterDefault(returnNode);
  69.369      }
  69.370  
  69.371 @@ -590,9 +557,9 @@
  69.372       * Callback for entering a RuntimeNode
  69.373       *
  69.374       * @param  runtimeNode the node
  69.375 -     * @return processed node, null if traversal should end
  69.376 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.377       */
  69.378 -    public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
  69.379 +    public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
  69.380          return enterDefault(runtimeNode);
  69.381      }
  69.382  
  69.383 @@ -610,9 +577,9 @@
  69.384       * Callback for entering a SplitNode
  69.385       *
  69.386       * @param  splitNode the node
  69.387 -     * @return processed node, null if traversal should end
  69.388 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.389       */
  69.390 -    public Node enterSplitNode(final SplitNode splitNode) {
  69.391 +    public boolean enterSplitNode(final SplitNode splitNode) {
  69.392          return enterDefault(splitNode);
  69.393      }
  69.394  
  69.395 @@ -630,9 +597,9 @@
  69.396       * Callback for entering a SwitchNode
  69.397       *
  69.398       * @param  switchNode the node
  69.399 -     * @return processed node, null if traversal should end
  69.400 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.401       */
  69.402 -    public Node enterSwitchNode(final SwitchNode switchNode) {
  69.403 +    public boolean enterSwitchNode(final SwitchNode switchNode) {
  69.404          return enterDefault(switchNode);
  69.405      }
  69.406  
  69.407 @@ -650,9 +617,9 @@
  69.408       * Callback for entering a TernaryNode
  69.409       *
  69.410       * @param  ternaryNode the node
  69.411 -     * @return processed node, null if traversal should end
  69.412 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.413       */
  69.414 -    public Node enterTernaryNode(final TernaryNode ternaryNode) {
  69.415 +    public boolean enterTernaryNode(final TernaryNode ternaryNode) {
  69.416          return enterDefault(ternaryNode);
  69.417      }
  69.418  
  69.419 @@ -670,9 +637,9 @@
  69.420       * Callback for entering a ThrowNode
  69.421       *
  69.422       * @param  throwNode the node
  69.423 -     * @return processed node, null if traversal should end
  69.424 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.425       */
  69.426 -    public Node enterThrowNode(final ThrowNode throwNode) {
  69.427 +    public boolean enterThrowNode(final ThrowNode throwNode) {
  69.428          return enterDefault(throwNode);
  69.429      }
  69.430  
  69.431 @@ -690,9 +657,9 @@
  69.432       * Callback for entering a TryNode
  69.433       *
  69.434       * @param  tryNode the node
  69.435 -     * @return processed node, null if traversal should end
  69.436 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.437       */
  69.438 -    public Node enterTryNode(final TryNode tryNode) {
  69.439 +    public boolean enterTryNode(final TryNode tryNode) {
  69.440          return enterDefault(tryNode);
  69.441      }
  69.442  
  69.443 @@ -710,9 +677,9 @@
  69.444       * Callback for entering a UnaryNode
  69.445       *
  69.446       * @param  unaryNode the node
  69.447 -     * @return processed node, null if traversal should end
  69.448 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.449       */
  69.450 -    public Node enterUnaryNode(final UnaryNode unaryNode) {
  69.451 +    public boolean enterUnaryNode(final UnaryNode unaryNode) {
  69.452          return enterDefault(unaryNode);
  69.453      }
  69.454  
  69.455 @@ -730,9 +697,9 @@
  69.456       * Callback for entering a VarNode
  69.457       *
  69.458       * @param  varNode   the node
  69.459 -     * @return processed node, null if traversal should end
  69.460 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.461       */
  69.462 -    public Node enterVarNode(final VarNode varNode) {
  69.463 +    public boolean enterVarNode(final VarNode varNode) {
  69.464          return enterDefault(varNode);
  69.465      }
  69.466  
  69.467 @@ -750,9 +717,9 @@
  69.468       * Callback for entering a WhileNode
  69.469       *
  69.470       * @param  whileNode the node
  69.471 -     * @return processed node, null if traversal should end
  69.472 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.473       */
  69.474 -    public Node enterWhileNode(final WhileNode whileNode) {
  69.475 +    public boolean enterWhileNode(final WhileNode whileNode) {
  69.476          return enterDefault(whileNode);
  69.477      }
  69.478  
  69.479 @@ -770,9 +737,9 @@
  69.480       * Callback for entering a WithNode
  69.481       *
  69.482       * @param  withNode  the node
  69.483 -     * @return processed node, null if traversal should end
  69.484 +     * @return true if traversal should continue and node children be traversed, false otherwise
  69.485       */
  69.486 -    public Node enterWithNode(final WithNode withNode) {
  69.487 +    public boolean enterWithNode(final WithNode withNode) {
  69.488          return enterDefault(withNode);
  69.489      }
  69.490  
  69.491 @@ -786,74 +753,5 @@
  69.492          return leaveDefault(withNode);
  69.493      }
  69.494  
  69.495 -    /**
  69.496 -     * Get the current function node for this NodeVisitor
  69.497 -     * @see FunctionNode
  69.498 -     * @return the function node being visited
  69.499 -     */
  69.500 -    public FunctionNode getCurrentFunctionNode() {
  69.501 -        return currentFunctionNode;
  69.502 -    }
  69.503 -
  69.504 -    /**
  69.505 -     * Reset the current function node being visited for this NodeVisitor
  69.506 -     * @see FunctionNode
  69.507 -     * @param currentFunctionNode a new function node to traverse
  69.508 -     */
  69.509 -    public void setCurrentFunctionNode(final FunctionNode currentFunctionNode) {
  69.510 -        this.currentFunctionNode = currentFunctionNode;
  69.511 -    }
  69.512 -
  69.513 -    /**
  69.514 -     * Get the current compile unit for this NodeVisitor
  69.515 -     * @see CompileUnit
  69.516 -     * @return a compile unit, or null if not a compiling NodeVisitor
  69.517 -     */
  69.518 -    public CompileUnit getCurrentCompileUnit() {
  69.519 -        return compileUnit;
  69.520 -    }
  69.521 -
  69.522 -    /**
  69.523 -     * Set the current compile unit for this NodeVisitor
  69.524 -     * @see CompileUnit
  69.525 -     * @param compileUnit a new compile unit
  69.526 -     */
  69.527 -    public void setCurrentCompileUnit(final CompileUnit compileUnit) {
  69.528 -        this.compileUnit = compileUnit;
  69.529 -    }
  69.530 -
  69.531 -    /**
  69.532 -     * Get the current method emitter for this NodeVisitor
  69.533 -     * @see MethodEmitter
  69.534 -     * @return the method emitter
  69.535 -     */
  69.536 -    public MethodEmitter getCurrentMethodEmitter() {
  69.537 -        return method;
  69.538 -    }
  69.539 -
  69.540 -    /**
  69.541 -     * Reset the current method emitter for this NodeVisitor
  69.542 -     * @see MethodEmitter
  69.543 -     * @param method a new method emitter
  69.544 -     */
  69.545 -    public void setCurrentMethodEmitter(final MethodEmitter method) {
  69.546 -        this.method = method;
  69.547 -    }
  69.548 -
  69.549 -    /**
  69.550 -     * Get the current Block being traversed for this NodeVisitor
  69.551 -     * @return the current block
  69.552 -     */
  69.553 -    public Block getCurrentBlock() {
  69.554 -        return currentBlock;
  69.555 -    }
  69.556 -
  69.557 -    /**
  69.558 -     * Reset the Block to be traversed for this NodeVisitor
  69.559 -     * @param currentBlock the new current block
  69.560 -     */
  69.561 -    public void setCurrentBlock(final Block currentBlock) {
  69.562 -        this.currentBlock = currentBlock;
  69.563 -    }
  69.564  
  69.565  }
    70.1 --- a/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java	Fri Apr 19 18:23:00 2013 +0530
    70.2 +++ b/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java	Fri Apr 19 16:11:16 2013 +0200
    70.3 @@ -137,7 +137,7 @@
    70.4       */
    70.5      static Object traceReturn(final DebugLogger logger, final Object value) {
    70.6          final String str = "\treturn: " + stripName(value) + " [type=" + (value == null ? "null" : stripName(value.getClass()) + ']');
    70.7 -        logger.log(str, TRACE_LEVEL);
    70.8 +        logger.log(TRACE_LEVEL, str);
    70.9          return value;
   70.10      }
   70.11  
   70.12 @@ -173,7 +173,7 @@
   70.13          }
   70.14  
   70.15          assert logger != null;
   70.16 -        logger.log(sb.toString(), TRACE_LEVEL);
   70.17 +        logger.log(TRACE_LEVEL, sb);
   70.18          stacktrace(logger);
   70.19      }
   70.20  
   70.21 @@ -184,7 +184,7 @@
   70.22          final ByteArrayOutputStream baos = new ByteArrayOutputStream();
   70.23          final PrintStream ps = new PrintStream(baos);
   70.24          new Throwable().printStackTrace(ps);
   70.25 -        logger.log(baos.toString(), TRACE_LEVEL);
   70.26 +        logger.log(TRACE_LEVEL, baos.toString());
   70.27      }
   70.28  
   70.29      private static String argString(final Object arg) {
   70.30 @@ -614,7 +614,7 @@
   70.31          @Override
   70.32          public SwitchPoint createSwitchPoint() {
   70.33              final SwitchPoint sp = super.createSwitchPoint();
   70.34 -            LOG.log("createSwitchPoint " + sp, TRACE_LEVEL);
   70.35 +            LOG.log(TRACE_LEVEL, "createSwitchPoint ", sp);
   70.36              return sp;
   70.37          }
   70.38  
   70.39 @@ -627,7 +627,7 @@
   70.40          @Override
   70.41          public MethodType type(final Class<?> returnType, final Class<?>... paramTypes) {
   70.42              final MethodType mt = super.type(returnType, paramTypes);
   70.43 -            LOG.log("methodType " + returnType + ' ' + Arrays.toString(paramTypes) + ' ' + mt, TRACE_LEVEL);
   70.44 +            LOG.log(TRACE_LEVEL, "methodType ", returnType, " ", Arrays.toString(paramTypes), " ", mt);
   70.45              return mt;
   70.46          }
   70.47      }
   70.48 @@ -638,7 +638,7 @@
   70.49      private static class TraceCreateMethodHandleFunctionality extends TraceMethodHandleFunctionality {
   70.50          @Override
   70.51          public MethodHandle debug(final MethodHandle master, final String str, final Object... args) {
   70.52 -            LOG.log(str + ' ' + describe(args), TRACE_LEVEL);
   70.53 +            LOG.log(TRACE_LEVEL, str, " ", describe(args));
   70.54              stacktrace(LOG);
   70.55              return master;
   70.56          }
    71.1 --- a/src/jdk/nashorn/internal/objects/NativeString.java	Fri Apr 19 18:23:00 2013 +0530
    71.2 +++ b/src/jdk/nashorn/internal/objects/NativeString.java	Fri Apr 19 16:11:16 2013 +0200
    71.3 @@ -179,7 +179,6 @@
    71.4          return ((ScriptObject) Global.toObject(self)).get(key);
    71.5      }
    71.6  
    71.7 -    @SuppressWarnings("unused")
    71.8      private static Object get(final Object self, final int key) {
    71.9          final CharSequence cs = JSType.toCharSequence(self);
   71.10          if (key >= 0 && key < cs.length()) {
    72.1 --- a/src/jdk/nashorn/internal/parser/AbstractParser.java	Fri Apr 19 18:23:00 2013 +0530
    72.2 +++ b/src/jdk/nashorn/internal/parser/AbstractParser.java	Fri Apr 19 16:11:16 2013 +0200
    72.3 @@ -197,9 +197,10 @@
    72.4       *
    72.5       * @param message    Error message.
    72.6       * @param errorToken Offending token.
    72.7 +     * @return ParserException upon failure. Caller should throw and not ignore
    72.8       */
    72.9 -    protected final void error(final String message, final long errorToken) {
   72.10 -        error(JSErrorType.SYNTAX_ERROR, message, errorToken);
   72.11 +    protected final ParserException error(final String message, final long errorToken) {
   72.12 +        return error(JSErrorType.SYNTAX_ERROR, message, errorToken);
   72.13      }
   72.14  
   72.15      /**
   72.16 @@ -208,22 +209,24 @@
   72.17       * @param errorType  The error type
   72.18       * @param message    Error message.
   72.19       * @param errorToken Offending token.
   72.20 +     * @return ParserException upon failure. Caller should throw and not ignore
   72.21       */
   72.22 -    protected final void error(final JSErrorType errorType, final String message, final long errorToken) {
   72.23 +    protected final ParserException error(final JSErrorType errorType, final String message, final long errorToken) {
   72.24          final int position  = Token.descPosition(errorToken);
   72.25          final int lineNum   = source.getLine(position);
   72.26          final int columnNum = source.getColumn(position);
   72.27          final String formatted = ErrorManager.format(message, source, lineNum, columnNum, errorToken);
   72.28 -        throw new ParserException(errorType, formatted, source, lineNum, columnNum, errorToken);
   72.29 +        return new ParserException(errorType, formatted, source, lineNum, columnNum, errorToken);
   72.30      }
   72.31  
   72.32      /**
   72.33       * Report an error.
   72.34       *
   72.35       * @param message Error message.
   72.36 +     * @return ParserException upon failure. Caller should throw and not ignore
   72.37       */
   72.38 -    protected final void error(final String message) {
   72.39 -        error(JSErrorType.SYNTAX_ERROR, message);
   72.40 +    protected final ParserException error(final String message) {
   72.41 +        return error(JSErrorType.SYNTAX_ERROR, message);
   72.42      }
   72.43  
   72.44      /**
   72.45 @@ -231,13 +234,14 @@
   72.46       *
   72.47       * @param errorType  The error type
   72.48       * @param message    Error message.
   72.49 +     * @return ParserException upon failure. Caller should throw and not ignore
   72.50       */
   72.51 -    protected final void error(final JSErrorType errorType, final String message) {
   72.52 +    protected final ParserException error(final JSErrorType errorType, final String message) {
   72.53          // TODO - column needs to account for tabs.
   72.54          final int position = Token.descPosition(token);
   72.55          final int column = position - linePosition;
   72.56          final String formatted = ErrorManager.format(message, source, line, column, token);
   72.57 -        throw new ParserException(errorType, formatted, source, line, column, token);
   72.58 +        return new ParserException(errorType, formatted, source, line, column, token);
   72.59      }
   72.60  
   72.61      /**
   72.62 @@ -270,7 +274,7 @@
   72.63       */
   72.64      protected final void expect(final TokenType expected) throws ParserException {
   72.65          if (type != expected) {
   72.66 -            error(expectMessage(expected));
   72.67 +            throw error(expectMessage(expected));
   72.68          }
   72.69  
   72.70          next();
   72.71 @@ -285,7 +289,7 @@
   72.72       */
   72.73      protected final Object expectValue(final TokenType expected) throws ParserException {
   72.74          if (type != expected) {
   72.75 -            error(expectMessage(expected));
   72.76 +            throw error(expectMessage(expected));
   72.77          }
   72.78  
   72.79          final Object value = getValue();
   72.80 @@ -429,7 +433,7 @@
   72.81                  try {
   72.82                      RegExpFactory.validate(regex.getExpression(), regex.getOptions());
   72.83                  } catch (final ParserException e) {
   72.84 -                    error(e.getMessage());
   72.85 +                    throw error(e.getMessage());
   72.86                  }
   72.87              }
   72.88              node = LiteralNode.newInstance(source, literalToken, finish, (LexerToken)value);
    73.1 --- a/src/jdk/nashorn/internal/parser/JSONParser.java	Fri Apr 19 18:23:00 2013 +0530
    73.2 +++ b/src/jdk/nashorn/internal/parser/JSONParser.java	Fri Apr 19 16:11:16 2013 +0200
    73.3 @@ -170,8 +170,7 @@
    73.4                  }
    73.5              case '"':
    73.6              case '\\':
    73.7 -                error(AbstractParser.message("unexpected.token", str));
    73.8 -                break;
    73.9 +                throw error(AbstractParser.message("unexpected.token", str));
   73.10              }
   73.11          }
   73.12  
   73.13 @@ -222,14 +221,12 @@
   73.14                  return new UnaryNode(source, literalToken, LiteralNode.newInstance(source, realToken, finish, (Number)value));
   73.15              }
   73.16  
   73.17 -            error(AbstractParser.message("expected", "number", type.getNameOrType()));
   73.18 -            break;
   73.19 +            throw error(AbstractParser.message("expected", "number", type.getNameOrType()));
   73.20          default:
   73.21              break;
   73.22          }
   73.23  
   73.24 -        error(AbstractParser.message("expected", "json literal", type.getNameOrType()));
   73.25 -        return null;
   73.26 +        throw error(AbstractParser.message("expected", "json literal", type.getNameOrType()));
   73.27      }
   73.28  
   73.29      /**
   73.30 @@ -265,7 +262,7 @@
   73.31                  elements.add(jsonLiteral());
   73.32                  // Comma between array elements is mandatory in JSON.
   73.33                  if (type != COMMARIGHT && type != RBRACKET) {
   73.34 -                    error(AbstractParser.message("expected", ", or ]", type.getNameOrType()));
   73.35 +                   throw error(AbstractParser.message("expected", ", or ]", type.getNameOrType()));
   73.36                  }
   73.37                  break;
   73.38              }
   73.39 @@ -306,7 +303,7 @@
   73.40  
   73.41                  // Comma between property assigments is mandatory in JSON.
   73.42                  if (type != RBRACE && type != COMMARIGHT) {
   73.43 -                    error(AbstractParser.message("expected", ", or }", type.getNameOrType()));
   73.44 +                    throw error(AbstractParser.message("expected", ", or }", type.getNameOrType()));
   73.45                  }
   73.46                  break;
   73.47              }
   73.48 @@ -334,13 +331,11 @@
   73.49          if (name != null) {
   73.50              expect(COLON);
   73.51              final Node value = jsonLiteral();
   73.52 -            return new PropertyNode(source, propertyToken, value.getFinish(), name, value);
   73.53 +            return new PropertyNode(source, propertyToken, value.getFinish(), name, value, null, null);
   73.54          }
   73.55  
   73.56          // Raise an error.
   73.57 -        error(AbstractParser.message("expected", "string", type.getNameOrType()));
   73.58 -
   73.59 -        return null;
   73.60 +        throw error(AbstractParser.message("expected", "string", type.getNameOrType()));
   73.61      }
   73.62  
   73.63  }
    74.1 --- a/src/jdk/nashorn/internal/parser/Parser.java	Fri Apr 19 18:23:00 2013 +0530
    74.2 +++ b/src/jdk/nashorn/internal/parser/Parser.java	Fri Apr 19 16:11:16 2013 +0200
    74.3 @@ -54,24 +54,23 @@
    74.4  import static jdk.nashorn.internal.parser.TokenType.WHILE;
    74.5  
    74.6  import java.util.ArrayList;
    74.7 -import java.util.HashMap;
    74.8  import java.util.HashSet;
    74.9  import java.util.Iterator;
   74.10 +import java.util.LinkedHashMap;
   74.11  import java.util.List;
   74.12  import java.util.Map;
   74.13 -import java.util.Stack;
   74.14  import jdk.nashorn.internal.codegen.CompilerConstants;
   74.15  import jdk.nashorn.internal.codegen.Namespace;
   74.16  import jdk.nashorn.internal.ir.AccessNode;
   74.17  import jdk.nashorn.internal.ir.BinaryNode;
   74.18  import jdk.nashorn.internal.ir.Block;
   74.19 +import jdk.nashorn.internal.ir.BlockLexicalContext;
   74.20  import jdk.nashorn.internal.ir.BreakNode;
   74.21  import jdk.nashorn.internal.ir.BreakableNode;
   74.22  import jdk.nashorn.internal.ir.CallNode;
   74.23  import jdk.nashorn.internal.ir.CaseNode;
   74.24  import jdk.nashorn.internal.ir.CatchNode;
   74.25  import jdk.nashorn.internal.ir.ContinueNode;
   74.26 -import jdk.nashorn.internal.ir.DoWhileNode;
   74.27  import jdk.nashorn.internal.ir.EmptyNode;
   74.28  import jdk.nashorn.internal.ir.ExecuteNode;
   74.29  import jdk.nashorn.internal.ir.ForNode;
   74.30 @@ -84,6 +83,7 @@
   74.31  import jdk.nashorn.internal.ir.LexicalContext;
   74.32  import jdk.nashorn.internal.ir.LineNumberNode;
   74.33  import jdk.nashorn.internal.ir.LiteralNode;
   74.34 +import jdk.nashorn.internal.ir.LoopNode;
   74.35  import jdk.nashorn.internal.ir.Node;
   74.36  import jdk.nashorn.internal.ir.ObjectNode;
   74.37  import jdk.nashorn.internal.ir.PropertyKey;
   74.38 @@ -117,9 +117,10 @@
   74.39      /** Is scripting mode. */
   74.40      private final boolean scripting;
   74.41  
   74.42 -    private final LexicalContext lexicalContext = new LexicalContext();
   74.43      private List<Node> functionDeclarations;
   74.44  
   74.45 +    private final BlockLexicalContext lc = new BlockLexicalContext();
   74.46 +
   74.47      /** Namespace for function names where not explicitly given */
   74.48      private final Namespace namespace;
   74.49  
   74.50 @@ -146,7 +147,7 @@
   74.51       */
   74.52      public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict) {
   74.53          super(source, errors, strict);
   74.54 -        this.env   = env;
   74.55 +        this.env       = env;
   74.56          this.namespace = new Namespace(env.getNamespace());
   74.57          this.scripting = env._scripting;
   74.58      }
   74.59 @@ -162,7 +163,7 @@
   74.60       * @return function node resulting from successful parse
   74.61       */
   74.62      public FunctionNode parse() {
   74.63 -        return parse(RUN_SCRIPT.tag());
   74.64 +        return parse(RUN_SCRIPT.symbolName());
   74.65      }
   74.66  
   74.67      /**
   74.68 @@ -176,7 +177,7 @@
   74.69       */
   74.70      public FunctionNode parse(final String scriptName) {
   74.71          final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
   74.72 -        LOG.info(this + " begin for '" + scriptName + "'");
   74.73 +        LOG.info(this, " begin for '", scriptName, "'");
   74.74  
   74.75          try {
   74.76              stream = new TokenStream();
   74.77 @@ -214,7 +215,7 @@
   74.78               final String end = this + " end '" + scriptName + "'";
   74.79               if (Timing.isEnabled()) {
   74.80                   Timing.accumulateTime(toString(), System.currentTimeMillis() - t0);
   74.81 -                 LOG.info(end + "' in " + (System.currentTimeMillis() - t0) + " ms");
   74.82 +                 LOG.info(end, "' in ", (System.currentTimeMillis() - t0), " ms");
   74.83               } else {
   74.84                   LOG.info(end);
   74.85               }
   74.86 @@ -275,8 +276,7 @@
   74.87       */
   74.88      private Block newBlock() {
   74.89          final Block block = new Block(source, token, Token.descPosition(token));
   74.90 -        lexicalContext.push(block);
   74.91 -        return block;
   74.92 +        return lc.push(block);
   74.93      }
   74.94  
   74.95      /**
   74.96 @@ -285,36 +285,60 @@
   74.97       * @param ident Name of function.
   74.98       * @return New block.
   74.99       */
  74.100 -    private FunctionNode newFunctionBlock(final IdentNode ident) {
  74.101 +    private FunctionNode newFunctionNode(final long startToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind) {
  74.102          // Build function name.
  74.103          final StringBuilder sb = new StringBuilder();
  74.104  
  74.105 -        final FunctionNode parentFunction = getFunction();
  74.106 -        if(parentFunction != null && !parentFunction.isProgram()) {
  74.107 +        final FunctionNode parentFunction = lc.getCurrentFunction();
  74.108 +        if (parentFunction != null && !parentFunction.isProgram()) {
  74.109              sb.append(parentFunction.getName()).append('$');
  74.110          }
  74.111  
  74.112 -        sb.append(ident != null ? ident.getName() : FUNCTION_PREFIX.tag());
  74.113 +        sb.append(ident != null ? ident.getName() : FUNCTION_PREFIX.symbolName());
  74.114          final String name = namespace.uniqueName(sb.toString());
  74.115 -        assert parentFunction != null || name.equals(RUN_SCRIPT.tag())  : "name = " + name;// must not rename runScript().
  74.116 +        assert parentFunction != null || name.equals(RUN_SCRIPT.symbolName())  : "name = " + name;// must not rename runScript().
  74.117 +
  74.118 +        int flags = 0;
  74.119 +        if (parentFunction == null) {
  74.120 +            flags |= FunctionNode.IS_PROGRAM;
  74.121 +        }
  74.122 +        if (isStrictMode) {
  74.123 +            flags |= FunctionNode.IS_STRICT;
  74.124 +        }
  74.125  
  74.126          // Start new block.
  74.127 -        final FunctionNode functionBlock = new FunctionNode(source, token, Token.descPosition(token), namespace, ident, name);
  74.128 -        if(parentFunction == null) {
  74.129 -            functionBlock.setProgram();
  74.130 -        }
  74.131 -        functionBlock.setStrictMode(isStrictMode);
  74.132 -        functionBlock.setState(errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED);
  74.133 -        lexicalContext.push(functionBlock);
  74.134 -
  74.135 -        return functionBlock;
  74.136 +        FunctionNode functionNode =
  74.137 +            new FunctionNode(
  74.138 +                source,
  74.139 +                token,
  74.140 +                Token.descPosition(token),
  74.141 +                startToken,
  74.142 +                namespace,
  74.143 +                ident,
  74.144 +                name,
  74.145 +                parameters,
  74.146 +                kind,
  74.147 +                flags);
  74.148 +
  74.149 +        functionNode = functionNode.setState(lc, errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED);
  74.150 +        lc.push(functionNode);
  74.151 +        // Create new block, and just put it on the context stack, restoreFunctionNode() will associate it with the
  74.152 +        // FunctionNode.
  74.153 +        newBlock();
  74.154 +        return functionNode;
  74.155      }
  74.156  
  74.157      /**
  74.158       * Restore the current block.
  74.159       */
  74.160 -    private void restoreBlock(Block block) {
  74.161 -        lexicalContext.pop(block);
  74.162 +    private Block restoreBlock(final Block block) {
  74.163 +        return lc.pop(block);//.setFlag(lc, flags);
  74.164 +    }
  74.165 +
  74.166 +    private FunctionNode restoreFunctionNode(final FunctionNode functionNode, final long lastToken) {
  74.167 +        final Block newBody = restoreBlock(lc.getFunctionBody(functionNode));
  74.168 +
  74.169 +        return lc.pop(functionNode).setBody(lc, newBody).setLastToken(lc, lastToken);
  74.170      }
  74.171  
  74.172      /**
  74.173 @@ -323,23 +347,17 @@
  74.174       */
  74.175      private Block getBlock(final boolean needsBraces) {
  74.176          // Set up new block. Captures LBRACE.
  74.177 -        final Block newBlock = newBlock();
  74.178 +        Block newBlock = newBlock();
  74.179          try {
  74.180 -            pushControlNode(newBlock);
  74.181 -
  74.182              // Block opening brace.
  74.183              if (needsBraces) {
  74.184                  expect(LBRACE);
  74.185              }
  74.186 -
  74.187 -            try {
  74.188 -                // Accumulate block statements.
  74.189 -                statementList();
  74.190 -            } finally {
  74.191 -                popControlNode();
  74.192 -            }
  74.193 +            // Accumulate block statements.
  74.194 +            statementList();
  74.195 +
  74.196          } finally {
  74.197 -            restoreBlock(newBlock);
  74.198 +            newBlock = restoreBlock(newBlock);
  74.199          }
  74.200  
  74.201          final int possibleEnd = Token.descPosition(token) + Token.descLength(token);
  74.202 @@ -363,15 +381,12 @@
  74.203              return getBlock(true);
  74.204          }
  74.205          // Set up new block. Captures first token.
  74.206 -        final Block newBlock = newBlock();
  74.207 -
  74.208 +        Block newBlock = newBlock();
  74.209          try {
  74.210 -            // Accumulate statements.
  74.211              statement();
  74.212          } finally {
  74.213 -            restoreBlock(newBlock);
  74.214 +            newBlock = restoreBlock(newBlock);
  74.215          }
  74.216 -
  74.217          return newBlock;
  74.218      }
  74.219  
  74.220 @@ -382,11 +397,8 @@
  74.221      private void detectSpecialFunction(final IdentNode ident) {
  74.222          final String name = ident.getName();
  74.223  
  74.224 -        if (EVAL.tag().equals(name)) {
  74.225 -            final Iterator<FunctionNode> it = lexicalContext.getFunctions();
  74.226 -            if(it.hasNext()) {
  74.227 -                it.next().setHasEval(it);
  74.228 -            }
  74.229 +        if (EVAL.symbolName().equals(name)) {
  74.230 +            markWithOrEval(lc, FunctionNode.HAS_EVAL);
  74.231          }
  74.232      }
  74.233  
  74.234 @@ -397,8 +409,8 @@
  74.235      private void detectSpecialProperty(final IdentNode ident) {
  74.236          final String name = ident.getName();
  74.237  
  74.238 -        if (ARGUMENTS.tag().equals(name)) {
  74.239 -            getFunction().setUsesArguments();
  74.240 +        if (ARGUMENTS.symbolName().equals(name)) {
  74.241 +            lc.setFlag(lc.getCurrentFunction(), FunctionNode.USES_ARGUMENTS);
  74.242          }
  74.243      }
  74.244  
  74.245 @@ -439,7 +451,7 @@
  74.246                    lhs instanceof IndexNode ||
  74.247                    lhs instanceof IdentNode)) {
  74.248                  if (env._early_lvalue_error) {
  74.249 -                    error(JSErrorType.REFERENCE_ERROR, AbstractParser.message("invalid.lvalue"), lhs.getToken());
  74.250 +                    throw error(JSErrorType.REFERENCE_ERROR, AbstractParser.message("invalid.lvalue"), lhs.getToken());
  74.251                  }
  74.252                  return referenceError(lhs, rhs);
  74.253              }
  74.254 @@ -469,144 +481,14 @@
  74.255       * @return           Reduced expression.
  74.256       */
  74.257      private Node incDecExpression(final long firstToken, final TokenType tokenType, final Node expression, final boolean isPostfix) {
  74.258 -        long incDecToken = firstToken;
  74.259          if (isPostfix) {
  74.260 -            incDecToken = Token.recast(incDecToken, tokenType == DECPREFIX ? DECPOSTFIX : INCPOSTFIX);
  74.261 +            return new UnaryNode(source, Token.recast(firstToken, tokenType == DECPREFIX ? DECPOSTFIX : INCPOSTFIX), expression.getStart(), Token.descPosition(firstToken) + Token.descLength(firstToken), expression);
  74.262          }
  74.263  
  74.264 -        final UnaryNode node = new UnaryNode(source, incDecToken, expression);
  74.265 -        if (isPostfix) {
  74.266 -            node.setStart(expression.getStart());
  74.267 -            node.setFinish(Token.descPosition(incDecToken) + Token.descLength(incDecToken));
  74.268 -        }
  74.269 -
  74.270 -        return node;
  74.271 +        return new UnaryNode(source, firstToken, expression);
  74.272      }
  74.273  
  74.274      /**
  74.275 -     * Find a label node in the label stack.
  74.276 -     * @param ident Ident to find.
  74.277 -     * @return null or the found label node.
  74.278 -     */
  74.279 -    private LabelNode findLabel(final IdentNode ident) {
  74.280 -        for (final LabelNode labelNode : getFunction().getLabelStack()) {
  74.281 -            if (labelNode.getLabel().equals(ident)) {
  74.282 -                return labelNode;
  74.283 -            }
  74.284 -        }
  74.285 -
  74.286 -        return null;
  74.287 -    }
  74.288 -
  74.289 -    /**
  74.290 -     * Add a label to the label stack.
  74.291 -     * @param labelNode Label to add.
  74.292 -     */
  74.293 -    private void pushLabel(final LabelNode labelNode) {
  74.294 -        getFunction().getLabelStack().push(labelNode);
  74.295 -    }
  74.296 -
  74.297 -    /**
  74.298 -      * Remove a label from the label stack.
  74.299 -      */
  74.300 -    private void popLabel() {
  74.301 -        getFunction().getLabelStack().pop();
  74.302 -    }
  74.303 -
  74.304 -    /**
  74.305 -     * Track the current nesting of controls for break and continue.
  74.306 -     * @param node For, while, do or switch node.
  74.307 -     */
  74.308 -    private void pushControlNode(final Node node) {
  74.309 -        final boolean isLoop = node instanceof WhileNode;
  74.310 -        final boolean isBreakable = node instanceof BreakableNode || node instanceof Block;
  74.311 -        final FunctionNode function = getFunction();
  74.312 -        function.getControlStack().push(node);
  74.313 -
  74.314 -        for (final LabelNode labelNode : function.getLabelStack()) {
  74.315 -            if (isBreakable && labelNode.getBreakNode() == null) {
  74.316 -                labelNode.setBreakNode(node);
  74.317 -            }
  74.318 -
  74.319 -            if (isLoop && labelNode.getContinueNode() == null) {
  74.320 -                labelNode.setContinueNode(node);
  74.321 -            }
  74.322 -        }
  74.323 -    }
  74.324 -
  74.325 -    /**
  74.326 -     * Finish with control.
  74.327 -     */
  74.328 -    private void popControlNode() {
  74.329 -        // Get control stack.
  74.330 -        final Stack<Node> controlStack = getFunction().getControlStack();
  74.331 -
  74.332 -        // Can be empty if missing brace.
  74.333 -        if (!controlStack.isEmpty()) {
  74.334 -            controlStack.pop();
  74.335 -        }
  74.336 -    }
  74.337 -
  74.338 -    private void popControlNode(final Node node) {
  74.339 -        // Get control stack.
  74.340 -        final Stack<Node> controlStack = getFunction().getControlStack();
  74.341 -
  74.342 -        // Can be empty if missing brace.
  74.343 -        if (!controlStack.isEmpty() && controlStack.peek() == node) {
  74.344 -            controlStack.pop();
  74.345 -        }
  74.346 -    }
  74.347 -
  74.348 -    private boolean isInWithBlock() {
  74.349 -        final Stack<Node> controlStack = getFunction().getControlStack();
  74.350 -        for (int i = controlStack.size() - 1; i >= 0; i--) {
  74.351 -            final Node node = controlStack.get(i);
  74.352 -
  74.353 -            if (node instanceof WithNode) {
  74.354 -                return true;
  74.355 -            }
  74.356 -        }
  74.357 -
  74.358 -        return false;
  74.359 -    }
  74.360 -
  74.361 -    private <T extends Node> T findControl(final Class<T> ctype) {
  74.362 -        final Stack<Node> controlStack = getFunction().getControlStack();
  74.363 -        for (int i = controlStack.size() - 1; i >= 0; i--) {
  74.364 -            final Node node = controlStack.get(i);
  74.365 -
  74.366 -            if (ctype.isAssignableFrom(node.getClass())) {
  74.367 -                return ctype.cast(node);
  74.368 -            }
  74.369 -        }
  74.370 -
  74.371 -        return null;
  74.372 -    }
  74.373 -
  74.374 -    private <T extends Node> List<T> findControls(final Class<T> ctype, final Node to) {
  74.375 -        final List<T> nodes = new ArrayList<>();
  74.376 -        final Stack<Node> controlStack = getFunction().getControlStack();
  74.377 -        for (int i = controlStack.size() - 1; i >= 0; i--) {
  74.378 -            final Node node = controlStack.get(i);
  74.379 -
  74.380 -            if (to == node) {
  74.381 -                break; //stop looking
  74.382 -            }
  74.383 -
  74.384 -            if (ctype.isAssignableFrom(node.getClass())) {
  74.385 -                nodes.add(ctype.cast(node));
  74.386 -            }
  74.387 -        }
  74.388 -
  74.389 -        return nodes;
  74.390 -    }
  74.391 -
  74.392 -    private <T extends Node> int countControls(final Class<T> ctype, final Node to) {
  74.393 -        return findControls(ctype, to).size();
  74.394 -    }
  74.395 -
  74.396 -
  74.397 -    /**
  74.398       * -----------------------------------------------------------------------
  74.399       *
  74.400       * Grammar based on
  74.401 @@ -630,18 +512,23 @@
  74.402          final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength());
  74.403          // Set up the script to append elements.
  74.404  
  74.405 -        final FunctionNode script = newFunctionBlock(new IdentNode(source, functionToken, Token.descPosition(functionToken), scriptName));
  74.406 -
  74.407 -        script.setKind(FunctionNode.Kind.SCRIPT);
  74.408 -        script.setFirstToken(functionToken);
  74.409 +        FunctionNode script = newFunctionNode(
  74.410 +            functionToken,
  74.411 +            new IdentNode(source, functionToken, Token.descPosition(functionToken), scriptName),
  74.412 +            new ArrayList<IdentNode>(),
  74.413 +            FunctionNode.Kind.SCRIPT);
  74.414 +
  74.415          functionDeclarations = new ArrayList<>();
  74.416          sourceElements();
  74.417 -        script.prependStatements(functionDeclarations);
  74.418 +        addFunctionDeclarations(script);
  74.419          functionDeclarations = null;
  74.420 +
  74.421          expect(EOF);
  74.422 -        script.setLastToken(token);
  74.423 +
  74.424          script.setFinish(source.getLength() - 1);
  74.425  
  74.426 +        script = restoreFunctionNode(script, token); //commit code
  74.427 +        script = script.setBody(lc, script.getBody().setNeedsScope(lc));
  74.428          return script;
  74.429      }
  74.430  
  74.431 @@ -670,24 +557,6 @@
  74.432      }
  74.433  
  74.434      /**
  74.435 -     * Return last node in a statement list.
  74.436 -     *
  74.437 -     * @param statements Statement list.
  74.438 -     *
  74.439 -     * @return Last (non-debug) statement or null if empty block.
  74.440 -     */
  74.441 -    private static Node lastStatement(final List<Node> statements) {
  74.442 -        for (int lastIndex = statements.size() - 1; lastIndex >= 0; lastIndex--) {
  74.443 -            final Node node = statements.get(lastIndex);
  74.444 -            if (!node.isDebug()) {
  74.445 -                return node;
  74.446 -            }
  74.447 -        }
  74.448 -
  74.449 -        return null;
  74.450 -    }
  74.451 -
  74.452 -    /**
  74.453       * SourceElements :
  74.454       *      SourceElement
  74.455       *      SourceElements SourceElement
  74.456 @@ -716,7 +585,7 @@
  74.457                      // check for directive prologues
  74.458                      if (checkDirective) {
  74.459                          // skip any debug statement like line number to get actual first line
  74.460 -                        final Node lastStatement = lastStatement(getBlock().getStatements());
  74.461 +                        final Node lastStatement = lc.getLastStatement();
  74.462  
  74.463                          // get directive prologue, if any
  74.464                          final String directive = getDirective(lastStatement);
  74.465 @@ -736,8 +605,8 @@
  74.466                              // handle use strict directive
  74.467                              if ("use strict".equals(directive)) {
  74.468                                  isStrictMode = true;
  74.469 -                                final FunctionNode function = getFunction();
  74.470 -                                function.setStrictMode(true);
  74.471 +                                final FunctionNode function = lc.getCurrentFunction();
  74.472 +                                lc.setFlag(lc.getCurrentFunction(), FunctionNode.IS_STRICT);
  74.473  
  74.474                                  // We don't need to check these, if lexical environment is already strict
  74.475                                  if (!oldStrictMode && directiveStmts != null) {
  74.476 @@ -759,7 +628,7 @@
  74.477                          }
  74.478                      }
  74.479                  } catch (final Exception e) {
  74.480 -                    // Recover parsing.
  74.481 +                    //recover parsing
  74.482                      recover(e);
  74.483                  }
  74.484  
  74.485 @@ -807,13 +676,15 @@
  74.486              // As per spec (ECMA section 12), function declarations as arbitrary statement
  74.487              // is not "portable". Implementation can issue a warning or disallow the same.
  74.488              if (isStrictMode && !topLevel) {
  74.489 -                error(AbstractParser.message("strict.no.func.here"), token);
  74.490 +                throw error(AbstractParser.message("strict.no.func.here"), token);
  74.491              }
  74.492              functionExpression(true, topLevel);
  74.493              return;
  74.494          }
  74.495  
  74.496 -        getBlock().addStatement(lineNumberNode);
  74.497 +        if (lineNumberNode != null) {
  74.498 +            appendStatement(lineNumberNode);
  74.499 +        }
  74.500  
  74.501          switch (type) {
  74.502          case LBRACE:
  74.503 @@ -893,13 +764,9 @@
  74.504       * Parse a statement block.
  74.505       */
  74.506      private void block() {
  74.507 -        // Get statements in block.
  74.508          final Block newBlock = getBlock(true);
  74.509 -
  74.510          // Force block execution.
  74.511 -        final ExecuteNode executeNode = new ExecuteNode(source, newBlock.getToken(), finish, newBlock);
  74.512 -
  74.513 -        getBlock().addStatement(executeNode);
  74.514 +        appendStatement(new ExecuteNode(source, newBlock.getToken(), finish, newBlock));
  74.515      }
  74.516  
  74.517      /**
  74.518 @@ -942,7 +809,7 @@
  74.519              switch (ident.getName()) {
  74.520              case "eval":
  74.521              case "arguments":
  74.522 -                error(AbstractParser.message("strict.name", ident.getName(), contextString), ident.getToken());
  74.523 +                throw error(AbstractParser.message("strict.name", ident.getName(), contextString), ident.getToken());
  74.524              default:
  74.525                  break;
  74.526              }
  74.527 @@ -995,8 +862,7 @@
  74.528              // Allocate var node.
  74.529              final VarNode var = new VarNode(source, varToken, finish, name, init);
  74.530              vars.add(var);
  74.531 -            // Add to current block.
  74.532 -            getBlock().addStatement(var);
  74.533 +            appendStatement(var);
  74.534  
  74.535              if (type != COMMARIGHT) {
  74.536                  break;
  74.537 @@ -1009,7 +875,7 @@
  74.538              boolean semicolon = type == SEMICOLON;
  74.539              endOfLine();
  74.540              if (semicolon) {
  74.541 -                getBlock().setFinish(finish);
  74.542 +                lc.getCurrentBlock().setFinish(finish);
  74.543              }
  74.544          }
  74.545  
  74.546 @@ -1026,8 +892,7 @@
  74.547       */
  74.548      private void emptyStatement() {
  74.549          if (env._empty_statements) {
  74.550 -            getBlock().addStatement(new EmptyNode(source, token,
  74.551 -                    Token.descPosition(token) + Token.descLength(token)));
  74.552 +            appendStatement(new EmptyNode(source, token, Token.descPosition(token) + Token.descLength(token)));
  74.553          }
  74.554  
  74.555          // SEMICOLON checked in caller.
  74.556 @@ -1052,7 +917,7 @@
  74.557          ExecuteNode executeNode = null;
  74.558          if (expression != null) {
  74.559              executeNode = new ExecuteNode(source, expressionToken, finish, expression);
  74.560 -            getBlock().addStatement(executeNode);
  74.561 +            appendStatement(executeNode);
  74.562          } else {
  74.563              expect(null);
  74.564          }
  74.565 @@ -1061,7 +926,7 @@
  74.566  
  74.567          if (executeNode != null) {
  74.568              executeNode.setFinish(finish);
  74.569 -            getBlock().setFinish(finish);
  74.570 +            lc.getCurrentBlock().setFinish(finish);
  74.571          }
  74.572      }
  74.573  
  74.574 @@ -1081,29 +946,17 @@
  74.575          next();
  74.576  
  74.577          expect(LPAREN);
  74.578 -
  74.579 -        // Get the test expression.
  74.580          final Node test = expression();
  74.581 -
  74.582          expect(RPAREN);
  74.583 -
  74.584 -        // Get the pass statement.
  74.585          final Block pass = getStatement();
  74.586  
  74.587 -        // Assume no else.
  74.588          Block fail = null;
  74.589 -
  74.590          if (type == ELSE) {
  74.591              next();
  74.592 -
  74.593 -            // Get the else block.
  74.594              fail = getStatement();
  74.595          }
  74.596  
  74.597 -        // Construct and add new if node.
  74.598 -        final IfNode ifNode = new IfNode(source, ifToken, fail != null ? fail.getFinish() : pass.getFinish(), test, pass, fail);
  74.599 -
  74.600 -        getBlock().addStatement(ifNode);
  74.601 +        appendStatement(new IfNode(source, ifToken, fail != null ? fail.getFinish() : pass.getFinish(), test, pass, fail));
  74.602      }
  74.603  
  74.604      /**
  74.605 @@ -1120,12 +973,12 @@
  74.606       */
  74.607      private void forStatement() {
  74.608          // Create FOR node, capturing FOR token.
  74.609 -        final ForNode forNode = new ForNode(source, token, Token.descPosition(token));
  74.610 -
  74.611 -        pushControlNode(forNode);
  74.612 +        ForNode forNode = new ForNode(source, token, Token.descPosition(token), null, null, null, null, ForNode.IS_FOR);
  74.613 +
  74.614  
  74.615          // Set up new block for scope of vars. Captures first token.
  74.616 -        final Block outer = newBlock();
  74.617 +        Block outer = newBlock();
  74.618 +        lc.push(forNode);
  74.619  
  74.620          try {
  74.621              // FOR tested in caller.
  74.622 @@ -1134,31 +987,97 @@
  74.623              // Nashorn extension: for each expression.
  74.624              // iterate property values rather than property names.
  74.625              if (!env._no_syntax_extensions && type == IDENT && "each".equals(getValue())) {
  74.626 -                forNode.setIsForEach();
  74.627 +                forNode = forNode.setIsForEach(lc);
  74.628                  next();
  74.629              }
  74.630  
  74.631              expect(LPAREN);
  74.632  
  74.633 -            /// Capture control information.
  74.634 -            forControl(forNode);
  74.635 +            List<VarNode> vars = null;
  74.636 +
  74.637 +            switch (type) {
  74.638 +            case VAR:
  74.639 +                // Var statements captured in for outer block.
  74.640 +                vars = variableStatement(false);
  74.641 +                break;
  74.642 +            case SEMICOLON:
  74.643 +                break;
  74.644 +            default:
  74.645 +                final Node expression = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true);
  74.646 +                forNode = forNode.setInit(lc, expression);
  74.647 +                break;
  74.648 +            }
  74.649 +
  74.650 +            switch (type) {
  74.651 +            case SEMICOLON:
  74.652 +                // for (init; test; modify)
  74.653 +                expect(SEMICOLON);
  74.654 +                if (type != SEMICOLON) {
  74.655 +                    forNode = forNode.setTest(lc, expression());
  74.656 +                }
  74.657 +                expect(SEMICOLON);
  74.658 +                if (type != RPAREN) {
  74.659 +                    forNode = forNode.setModify(lc, expression());
  74.660 +                }
  74.661 +                break;
  74.662 +
  74.663 +            case IN:
  74.664 +                forNode = forNode.setIsForIn(lc);
  74.665 +                if (vars != null) {
  74.666 +                    // for (var i in obj)
  74.667 +                    if (vars.size() == 1) {
  74.668 +                        forNode = forNode.setInit(lc, new IdentNode(vars.get(0).getName()));
  74.669 +                    } else {
  74.670 +                        // for (var i, j in obj) is invalid
  74.671 +                        throw error(AbstractParser.message("many.vars.in.for.in.loop"), vars.get(1).getToken());
  74.672 +                    }
  74.673 +
  74.674 +                } else {
  74.675 +                    // for (expr in obj)
  74.676 +                    final Node init = forNode.getInit();
  74.677 +                    assert init != null : "for..in init expression can not be null here";
  74.678 +
  74.679 +                    // check if initial expression is a valid L-value
  74.680 +                    if (!(init instanceof AccessNode ||
  74.681 +                          init instanceof IndexNode ||
  74.682 +                          init instanceof IdentNode)) {
  74.683 +                        throw error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
  74.684 +                    }
  74.685 +
  74.686 +                    if (init instanceof IdentNode) {
  74.687 +                        if (!checkIdentLValue((IdentNode)init)) {
  74.688 +                            throw error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
  74.689 +                        }
  74.690 +                        verifyStrictIdent((IdentNode)init, "for-in iterator");
  74.691 +                    }
  74.692 +                }
  74.693 +
  74.694 +                next();
  74.695 +
  74.696 +                // Get the collection expression.
  74.697 +                forNode = forNode.setModify(lc, expression());
  74.698 +                break;
  74.699 +
  74.700 +            default:
  74.701 +                expect(SEMICOLON);
  74.702 +                break;
  74.703 +            }
  74.704  
  74.705              expect(RPAREN);
  74.706  
  74.707              // Set the for body.
  74.708              final Block body = getStatement();
  74.709 -            forNode.setBody(body);
  74.710 +            forNode = forNode.setBody(lc, body);
  74.711              forNode.setFinish(body.getFinish());
  74.712              outer.setFinish(body.getFinish());
  74.713  
  74.714 -            // Add for to current block.
  74.715 -            getBlock().addStatement(forNode);
  74.716 +            appendStatement(forNode);
  74.717          } finally {
  74.718 -            restoreBlock(outer);
  74.719 -            popControlNode();
  74.720 +            lc.pop(forNode);
  74.721 +            outer = restoreBlock(outer);
  74.722          }
  74.723  
  74.724 -        getBlock().addStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
  74.725 +        appendStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
  74.726       }
  74.727  
  74.728      /**
  74.729 @@ -1175,87 +1094,7 @@
  74.730       * comprehensions.
  74.731       * @param forNode Owning FOR.
  74.732       */
  74.733 -    private void forControl(final ForNode forNode) {
  74.734 -        List<VarNode> vars = null;
  74.735 -
  74.736 -        switch (type) {
  74.737 -        case VAR:
  74.738 -            // Var statements captured in for outer block.
  74.739 -            vars = variableStatement(false);
  74.740 -            break;
  74.741 -
  74.742 -        case SEMICOLON:
  74.743 -            break;
  74.744 -
  74.745 -        default:
  74.746 -            final Node expression = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true);
  74.747 -            forNode.setInit(expression);
  74.748 -        }
  74.749 -
  74.750 -        switch (type) {
  74.751 -        case SEMICOLON:
  74.752 -            // for (init; test; modify)
  74.753 -            expect(SEMICOLON);
  74.754 -
  74.755 -            // Get the test expression.
  74.756 -            if (type != SEMICOLON) {
  74.757 -                forNode.setTest(expression());
  74.758 -            }
  74.759 -
  74.760 -            expect(SEMICOLON);
  74.761 -
  74.762 -            // Get the modify expression.
  74.763 -            if (type != RPAREN) {
  74.764 -                final Node expression = expression();
  74.765 -                forNode.setModify(expression);
  74.766 -            }
  74.767 -
  74.768 -            break;
  74.769 -
  74.770 -        case IN:
  74.771 -            forNode.setIsForIn();
  74.772 -
  74.773 -            if (vars != null) {
  74.774 -                // for (var i in obj)
  74.775 -                if (vars.size() == 1) {
  74.776 -                    forNode.setInit(new IdentNode(vars.get(0).getName()));
  74.777 -                } else {
  74.778 -                    // for (var i, j in obj) is invalid
  74.779 -                    error(AbstractParser.message("many.vars.in.for.in.loop"), vars.get(1).getToken());
  74.780 -                }
  74.781 -
  74.782 -            } else {
  74.783 -                // for (expr in obj)
  74.784 -                final Node init = forNode.getInit();
  74.785 -                assert init != null : "for..in init expression can not be null here";
  74.786 -
  74.787 -                // check if initial expression is a valid L-value
  74.788 -                if (!(init instanceof AccessNode ||
  74.789 -                      init instanceof IndexNode ||
  74.790 -                      init instanceof IdentNode)) {
  74.791 -                    error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
  74.792 -                }
  74.793 -
  74.794 -                if (init instanceof IdentNode) {
  74.795 -                    if (!checkIdentLValue((IdentNode)init)) {
  74.796 -                        error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
  74.797 -                    }
  74.798 -                    verifyStrictIdent((IdentNode)init, "for-in iterator");
  74.799 -                }
  74.800 -            }
  74.801 -
  74.802 -            next();
  74.803 -
  74.804 -            // Get the collection expression.
  74.805 -            forNode.setModify(expression());
  74.806 -            break;
  74.807 -
  74.808 -        default:
  74.809 -            expect(SEMICOLON);
  74.810 -            break;
  74.811 -        }
  74.812 -
  74.813 -    }
  74.814 +
  74.815  
  74.816      /**
  74.817       * ...IterationStatement :
  74.818 @@ -1274,27 +1113,17 @@
  74.819          next();
  74.820  
  74.821          // Construct WHILE node.
  74.822 -        final WhileNode whileNode = new WhileNode(source, whileToken, Token.descPosition(whileToken));
  74.823 -        pushControlNode(whileNode);
  74.824 +        WhileNode whileNode = new WhileNode(source, whileToken, Token.descPosition(whileToken), false);
  74.825 +        lc.push(whileNode);
  74.826  
  74.827          try {
  74.828              expect(LPAREN);
  74.829 -
  74.830 -            // Get the test expression.
  74.831 -            final Node test = expression();
  74.832 -            whileNode.setTest(test);
  74.833 -
  74.834 +            whileNode = whileNode.setTest(lc, expression());
  74.835              expect(RPAREN);
  74.836 -
  74.837 -            // Get WHILE body.
  74.838 -            final Block statements = getStatement();
  74.839 -            whileNode.setBody(statements);
  74.840 -            whileNode.setFinish(statements.getFinish());
  74.841 -
  74.842 -            // Add WHILE node.
  74.843 -            getBlock().addStatement(whileNode);
  74.844 +            whileNode = whileNode.setBody(lc, getStatement());
  74.845 +            appendStatement(whileNode);
  74.846          } finally {
  74.847 -            popControlNode();
  74.848 +            lc.pop(whileNode);
  74.849          }
  74.850      }
  74.851  
  74.852 @@ -1314,34 +1143,25 @@
  74.853          // DO tested in the caller.
  74.854          next();
  74.855  
  74.856 -        final WhileNode doWhileNode = new DoWhileNode(source, doToken, Token.descPosition(doToken));
  74.857 -        pushControlNode(doWhileNode);
  74.858 +        WhileNode doWhileNode = new WhileNode(source, doToken, Token.descPosition(doToken), true);
  74.859 +        lc.push(doWhileNode);
  74.860  
  74.861          try {
  74.862             // Get DO body.
  74.863 -            final Block statements = getStatement();
  74.864 -            doWhileNode.setBody(statements);
  74.865 +            doWhileNode = doWhileNode.setBody(lc, getStatement());
  74.866  
  74.867              expect(WHILE);
  74.868 -
  74.869              expect(LPAREN);
  74.870 -
  74.871 -            // Get the test expression.
  74.872 -            final Node test = expression();
  74.873 -            doWhileNode.setTest(test);
  74.874 -
  74.875 +            doWhileNode = doWhileNode.setTest(lc, expression());
  74.876              expect(RPAREN);
  74.877  
  74.878              if (type == SEMICOLON) {
  74.879                  endOfLine();
  74.880              }
  74.881 -
  74.882              doWhileNode.setFinish(finish);
  74.883 -
  74.884 -            // Add DO node.
  74.885 -            getBlock().addStatement(doWhileNode);
  74.886 +            appendStatement(doWhileNode);
  74.887          } finally {
  74.888 -            popControlNode();
  74.889 +            lc.pop(doWhileNode);
  74.890          }
  74.891      }
  74.892  
  74.893 @@ -1370,28 +1190,26 @@
  74.894  
  74.895          default:
  74.896              final IdentNode ident = getIdent();
  74.897 -            labelNode = findLabel(ident);
  74.898 +            labelNode = lc.findLabel(ident.getName());
  74.899  
  74.900              if (labelNode == null) {
  74.901 -                error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
  74.902 +                throw error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
  74.903              }
  74.904  
  74.905              break;
  74.906          }
  74.907  
  74.908 -        final Node targetNode = labelNode != null ? labelNode.getContinueNode() : findControl(WhileNode.class);
  74.909 +        final IdentNode label = labelNode == null ? null : labelNode.getLabel();
  74.910 +        final LoopNode targetNode = lc.getContinueTo(label);
  74.911  
  74.912          if (targetNode == null) {
  74.913 -            error(AbstractParser.message("illegal.continue.stmt"), continueToken);
  74.914 +            throw error(AbstractParser.message("illegal.continue.stmt"), continueToken);
  74.915          }
  74.916  
  74.917          endOfLine();
  74.918  
  74.919          // Construct and add CONTINUE node.
  74.920 -        final ContinueNode continueNode = new ContinueNode(source, continueToken, finish, labelNode, targetNode, findControl(TryNode.class));
  74.921 -        continueNode.setScopeNestingLevel(countControls(WithNode.class, targetNode));
  74.922 -
  74.923 -        getBlock().addStatement(continueNode);
  74.924 +        appendStatement(new ContinueNode(source, continueToken, finish, label == null ? null : new IdentNode(label)));
  74.925      }
  74.926  
  74.927      /**
  74.928 @@ -1418,28 +1236,27 @@
  74.929  
  74.930          default:
  74.931              final IdentNode ident = getIdent();
  74.932 -            labelNode = findLabel(ident);
  74.933 +            labelNode = lc.findLabel(ident.getName());
  74.934  
  74.935              if (labelNode == null) {
  74.936 -                error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
  74.937 +                throw error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
  74.938              }
  74.939  
  74.940              break;
  74.941          }
  74.942  
  74.943 -        final Node targetNode = labelNode != null ? labelNode.getBreakNode() : findControl(BreakableNode.class);
  74.944 -
  74.945 +        //either an explicit label - then get its node or just a "break" - get first breakable
  74.946 +        //targetNode is what we are breaking out from.
  74.947 +        final IdentNode label = labelNode == null ? null : labelNode.getLabel();
  74.948 +        final BreakableNode targetNode = lc.getBreakable(label);
  74.949          if (targetNode == null) {
  74.950 -            error(AbstractParser.message("illegal.break.stmt"), breakToken);
  74.951 +            throw error(AbstractParser.message("illegal.break.stmt"), breakToken);
  74.952          }
  74.953  
  74.954          endOfLine();
  74.955  
  74.956          // Construct and add BREAK node.
  74.957 -        final BreakNode breakNode = new BreakNode(source, breakToken, finish, labelNode, targetNode, findControl(TryNode.class));
  74.958 -        breakNode.setScopeNestingLevel(countControls(WithNode.class, targetNode));
  74.959 -
  74.960 -        getBlock().addStatement(breakNode);
  74.961 +        appendStatement(new BreakNode(source, breakToken, finish, label == null ? null : new IdentNode(label)));
  74.962      }
  74.963  
  74.964      /**
  74.965 @@ -1452,8 +1269,8 @@
  74.966       */
  74.967      private void returnStatement() {
  74.968          // check for return outside function
  74.969 -        if (getFunction().getKind() == FunctionNode.Kind.SCRIPT) {
  74.970 -            error(AbstractParser.message("invalid.return"));
  74.971 +        if (lc.getCurrentFunction().getKind() == FunctionNode.Kind.SCRIPT) {
  74.972 +            throw error(AbstractParser.message("invalid.return"));
  74.973          }
  74.974  
  74.975          // Capture RETURN token.
  74.976 @@ -1478,8 +1295,7 @@
  74.977          endOfLine();
  74.978  
  74.979          // Construct and add RETURN node.
  74.980 -        final ReturnNode returnNode = new ReturnNode(source, returnToken, finish, expression, findControl(TryNode.class));
  74.981 -        getBlock().addStatement(returnNode);
  74.982 +        appendStatement(new ReturnNode(source, returnToken, finish, expression));
  74.983      }
  74.984  
  74.985      /**
  74.986 @@ -1513,8 +1329,7 @@
  74.987          endOfLine();
  74.988  
  74.989          // Construct and add YIELD node.
  74.990 -        final ReturnNode yieldNode = new ReturnNode(source, yieldToken, finish, expression, findControl(TryNode.class));
  74.991 -        getBlock().addStatement(yieldNode);
  74.992 +        appendStatement(new ReturnNode(source, yieldToken, finish, expression));
  74.993      }
  74.994  
  74.995      /**
  74.996 @@ -1533,35 +1348,24 @@
  74.997  
  74.998          // ECMA 12.10.1 strict mode restrictions
  74.999          if (isStrictMode) {
 74.1000 -            error(AbstractParser.message("strict.no.with"), withToken);
 74.1001 +            throw error(AbstractParser.message("strict.no.with"), withToken);
 74.1002          }
 74.1003  
 74.1004          // Get WITH expression.
 74.1005 -        final WithNode withNode = new WithNode(source, withToken, finish, null, null);
 74.1006 -        final Iterator<FunctionNode> it = lexicalContext.getFunctions();
 74.1007 -        if(it.hasNext()) {
 74.1008 -            it.next().setHasWith(it);
 74.1009 +        WithNode withNode = new WithNode(source, withToken, finish);
 74.1010 +        markWithOrEval(lc, FunctionNode.HAS_WITH);
 74.1011 +
 74.1012 +        try {
 74.1013 +            lc.push(withNode);
 74.1014 +            expect(LPAREN);
 74.1015 +            withNode = withNode.setExpression(lc, expression());
 74.1016 +            expect(RPAREN);
 74.1017 +            withNode = withNode.setBody(lc, getStatement());
 74.1018 +        } finally {
 74.1019 +            lc.pop(withNode);
 74.1020          }
 74.1021  
 74.1022 -        try {
 74.1023 -            pushControlNode(withNode);
 74.1024 -
 74.1025 -            expect(LPAREN);
 74.1026 -
 74.1027 -            final Node expression = expression();
 74.1028 -            withNode.setExpression(expression);
 74.1029 -
 74.1030 -            expect(RPAREN);
 74.1031 -
 74.1032 -            // Get WITH body.
 74.1033 -            final Block statements = getStatement();
 74.1034 -            withNode.setBody(statements);
 74.1035 -            withNode.setFinish(finish);
 74.1036 -        } finally {
 74.1037 -            popControlNode(withNode);
 74.1038 -        }
 74.1039 -
 74.1040 -        getBlock().addStatement(withNode);
 74.1041 +        appendStatement(withNode);
 74.1042      }
 74.1043  
 74.1044      /**
 74.1045 @@ -1587,22 +1391,17 @@
 74.1046       * Parse SWITCH statement.
 74.1047       */
 74.1048      private void switchStatement() {
 74.1049 -        // Capture SWITCH token.
 74.1050          final long switchToken = token;
 74.1051          // SWITCH tested in caller.
 74.1052          next();
 74.1053  
 74.1054          // Create and add switch statement.
 74.1055 -        final SwitchNode switchNode = new SwitchNode(source, switchToken, Token.descPosition(switchToken));
 74.1056 -        pushControlNode(switchNode);
 74.1057 +        SwitchNode switchNode = new SwitchNode(source, switchToken, Token.descPosition(switchToken), null, new ArrayList<CaseNode>(), null);
 74.1058 +        lc.push(switchNode);
 74.1059  
 74.1060          try {
 74.1061              expect(LPAREN);
 74.1062 -
 74.1063 -            // Get switch expression.
 74.1064 -            final Node switchExpression = expression();
 74.1065 -            switchNode.setExpression(switchExpression);
 74.1066 -
 74.1067 +            switchNode = switchNode.setExpression(lc, expression());
 74.1068              expect(RPAREN);
 74.1069  
 74.1070              expect(LBRACE);
 74.1071 @@ -1619,19 +1418,14 @@
 74.1072                  switch (type) {
 74.1073                  case CASE:
 74.1074                      next();
 74.1075 -
 74.1076 -                    // Get case expression.
 74.1077                      caseExpression = expression();
 74.1078 -
 74.1079                      break;
 74.1080  
 74.1081                  case DEFAULT:
 74.1082                      if (defaultCase != null) {
 74.1083 -                        error(AbstractParser.message("duplicate.default.in.switch"));
 74.1084 +                        throw error(AbstractParser.message("duplicate.default.in.switch"));
 74.1085                      }
 74.1086 -
 74.1087                      next();
 74.1088 -
 74.1089                      break;
 74.1090  
 74.1091                  default:
 74.1092 @@ -1654,16 +1448,13 @@
 74.1093                  cases.add(caseNode);
 74.1094              }
 74.1095  
 74.1096 -            switchNode.setCases(cases);
 74.1097 -            switchNode.setDefaultCase(defaultCase);
 74.1098 -
 74.1099 +            switchNode = switchNode.setCases(lc, cases, defaultCase);
 74.1100              next();
 74.1101 -
 74.1102              switchNode.setFinish(finish);
 74.1103  
 74.1104 -            getBlock().addStatement(switchNode);
 74.1105 +            appendStatement(switchNode);
 74.1106          } finally {
 74.1107 -            popControlNode();
 74.1108 +            lc.pop(switchNode);
 74.1109          }
 74.1110      }
 74.1111  
 74.1112 @@ -1683,23 +1474,19 @@
 74.1113  
 74.1114          expect(COLON);
 74.1115  
 74.1116 -        if (findLabel(ident) != null) {
 74.1117 -            error(AbstractParser.message("duplicate.label", ident.getName()), labelToken);
 74.1118 +        if (lc.findLabel(ident.getName()) != null) {
 74.1119 +            throw error(AbstractParser.message("duplicate.label", ident.getName()), labelToken);
 74.1120          }
 74.1121  
 74.1122 +        LabelNode labelNode = new LabelNode(source, labelToken, finish, ident, null);
 74.1123          try {
 74.1124 -            // Create and add label.
 74.1125 -            final LabelNode labelNode = new LabelNode(source, labelToken, finish, ident, null);
 74.1126 -            pushLabel(labelNode);
 74.1127 -            // Get and save body.
 74.1128 -            final Block statements = getStatement();
 74.1129 -            labelNode.setBody(statements);
 74.1130 +            lc.push(labelNode);
 74.1131 +            labelNode = labelNode.setBody(lc, getStatement());
 74.1132              labelNode.setFinish(finish);
 74.1133 -
 74.1134 -            getBlock().addStatement(labelNode);
 74.1135 +            appendStatement(labelNode);
 74.1136          } finally {
 74.1137 -            // Remove label.
 74.1138 -            popLabel();
 74.1139 +            assert lc.peek() instanceof LabelNode;
 74.1140 +            lc.pop(labelNode);
 74.1141          }
 74.1142      }
 74.1143  
 74.1144 @@ -1732,14 +1519,12 @@
 74.1145          }
 74.1146  
 74.1147          if (expression == null) {
 74.1148 -            error(AbstractParser.message("expected.operand", type.getNameOrType()));
 74.1149 +            throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
 74.1150          }
 74.1151  
 74.1152          endOfLine();
 74.1153  
 74.1154 -        // Construct and add THROW node.
 74.1155 -        final ThrowNode throwNode = new ThrowNode(source, throwToken, finish, expression, findControl(TryNode.class));
 74.1156 -        getBlock().addStatement(throwNode);
 74.1157 +        appendStatement(new ThrowNode(source, throwToken, finish, expression));
 74.1158      }
 74.1159  
 74.1160      /**
 74.1161 @@ -1766,28 +1551,18 @@
 74.1162          next();
 74.1163  
 74.1164          // Container block needed to act as target for labeled break statements
 74.1165 -        final Block outer = newBlock();
 74.1166 -        pushControlNode(outer);
 74.1167 +        Block outer = newBlock();
 74.1168  
 74.1169          // Create try.
 74.1170 -        final TryNode tryNode = new TryNode(source, tryToken, Token.descPosition(tryToken), findControl(TryNode.class));
 74.1171 -        pushControlNode(tryNode);
 74.1172  
 74.1173          try {
 74.1174 -            // Get TRY body.
 74.1175 -            final Block tryBody = getBlock(true);
 74.1176 -
 74.1177 -            // Prepare to accumulate catches.
 74.1178 +            final Block       tryBody     = getBlock(true);
 74.1179              final List<Block> catchBlocks = new ArrayList<>();
 74.1180  
 74.1181              while (type == CATCH) {
 74.1182 -                // Capture CATCH token.
 74.1183                  final long catchToken = token;
 74.1184                  next();
 74.1185 -
 74.1186                  expect(LPAREN);
 74.1187 -
 74.1188 -                // Get exception ident.
 74.1189                  final IdentNode exception = getIdent();
 74.1190  
 74.1191                  // ECMA 12.4.1 strict mode restrictions
 74.1192 @@ -1795,28 +1570,23 @@
 74.1193  
 74.1194                  // Check for conditional catch.
 74.1195                  Node ifExpression = null;
 74.1196 -
 74.1197                  if (type == IF) {
 74.1198                      next();
 74.1199 -
 74.1200                      // Get the exception condition.
 74.1201                      ifExpression = expression();
 74.1202                  }
 74.1203  
 74.1204                  expect(RPAREN);
 74.1205  
 74.1206 -                final Block catchBlock = newBlock();
 74.1207 +                Block catchBlock = newBlock();
 74.1208                  try {
 74.1209 -
 74.1210                      // Get CATCH body.
 74.1211                      final Block catchBody = getBlock(true);
 74.1212 -
 74.1213 -                    // Create and add catch.
 74.1214                      final CatchNode catchNode = new CatchNode(source, catchToken, finish, exception, ifExpression, catchBody);
 74.1215 -                    getBlock().addStatement(catchNode);
 74.1216 +                    appendStatement(catchNode);
 74.1217 +                } finally {
 74.1218 +                    catchBlock = restoreBlock(catchBlock);
 74.1219                      catchBlocks.add(catchBlock);
 74.1220 -                } finally {
 74.1221 -                    restoreBlock(catchBlock);
 74.1222                  }
 74.1223  
 74.1224                  // If unconditional catch then should to be the end.
 74.1225 @@ -1825,38 +1595,32 @@
 74.1226                  }
 74.1227              }
 74.1228  
 74.1229 -            popControlNode();
 74.1230 -
 74.1231              // Prepare to capture finally statement.
 74.1232              Block finallyStatements = null;
 74.1233  
 74.1234              if (type == FINALLY) {
 74.1235                  next();
 74.1236 -
 74.1237 -                // Get FINALLY body.
 74.1238                  finallyStatements = getBlock(true);
 74.1239              }
 74.1240  
 74.1241              // Need at least one catch or a finally.
 74.1242              if (catchBlocks.isEmpty() && finallyStatements == null) {
 74.1243 -                error(AbstractParser.message("missing.catch.or.finally"), tryToken);
 74.1244 +                throw error(AbstractParser.message("missing.catch.or.finally"), tryToken);
 74.1245              }
 74.1246  
 74.1247 -            tryNode.setBody(tryBody);
 74.1248 -            tryNode.setCatchBlocks(catchBlocks);
 74.1249 -            tryNode.setFinallyBody(finallyStatements);
 74.1250 +            final TryNode tryNode = new TryNode(source, tryToken, Token.descPosition(tryToken), tryBody, catchBlocks, finallyStatements);
 74.1251 +            // Add try.
 74.1252 +            assert lc.peek() == outer;
 74.1253 +            appendStatement(tryNode);
 74.1254 +
 74.1255              tryNode.setFinish(finish);
 74.1256              outer.setFinish(finish);
 74.1257  
 74.1258 -            // Add try.
 74.1259 -            outer.addStatement(tryNode);
 74.1260          } finally {
 74.1261 -            popControlNode(tryNode);
 74.1262 -            restoreBlock(outer);
 74.1263 -            popControlNode(outer);
 74.1264 +            outer = restoreBlock(outer);
 74.1265          }
 74.1266  
 74.1267 -        getBlock().addStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
 74.1268 +        appendStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
 74.1269      }
 74.1270  
 74.1271      /**
 74.1272 @@ -1872,11 +1636,8 @@
 74.1273          final long debuggerToken = token;
 74.1274          // DEBUGGER tested in caller.
 74.1275          next();
 74.1276 -
 74.1277          endOfLine();
 74.1278 -
 74.1279 -        final RuntimeNode runtimeNode = new RuntimeNode(source, debuggerToken, finish, RuntimeNode.Request.DEBUGGER, new ArrayList<Node>());
 74.1280 -        getBlock().addStatement(runtimeNode);
 74.1281 +        appendStatement(new RuntimeNode(source, debuggerToken, finish, RuntimeNode.Request.DEBUGGER, new ArrayList<Node>()));
 74.1282      }
 74.1283  
 74.1284      /**
 74.1285 @@ -1912,7 +1673,7 @@
 74.1286              return ident;
 74.1287          case OCTAL:
 74.1288              if (isStrictMode) {
 74.1289 -               error(AbstractParser.message("strict.no.octal"), token);
 74.1290 +               throw error(AbstractParser.message("strict.no.octal"), token);
 74.1291              }
 74.1292          case STRING:
 74.1293          case ESCSTRING:
 74.1294 @@ -1981,7 +1742,7 @@
 74.1295          // Skip ending of edit string expression.
 74.1296          expect(RBRACE);
 74.1297  
 74.1298 -        return new CallNode(source, primaryToken, finish, execIdent, arguments);
 74.1299 +        return new CallNode(source, primaryToken, finish, execIdent, arguments, 0);
 74.1300      }
 74.1301  
 74.1302      /**
 74.1303 @@ -2036,7 +1797,7 @@
 74.1304  
 74.1305              default:
 74.1306                  if (!elision) {
 74.1307 -                    error(AbstractParser.message("expected.comma", type.getNameOrType()));
 74.1308 +                    throw error(AbstractParser.message("expected.comma", type.getNameOrType()));
 74.1309                  }
 74.1310                  // Add expression element.
 74.1311                  final Node expression = assignmentExpression(false);
 74.1312 @@ -2077,8 +1838,8 @@
 74.1313  
 74.1314          // Object context.
 74.1315          // Prepare to accumulate elements.
 74.1316 -        final List<Node> elements = new ArrayList<>();
 74.1317 -        final Map<Object, PropertyNode> map = new HashMap<>();
 74.1318 +       // final List<Node> elements = new ArrayList<>();
 74.1319 +        final Map<String, PropertyNode> map = new LinkedHashMap<>();
 74.1320  
 74.1321          // Create a block for the object literal.
 74.1322              boolean commaSeen = true;
 74.1323 @@ -2096,25 +1857,30 @@
 74.1324  
 74.1325                  default:
 74.1326                      if (!commaSeen) {
 74.1327 -                        error(AbstractParser.message("expected.comma", type.getNameOrType()));
 74.1328 -                }
 74.1329 -
 74.1330 -                commaSeen = false;
 74.1331 -                // Get and add the next property.
 74.1332 -                final PropertyNode property = propertyAssignment();
 74.1333 -                final Object key = property.getKeyName();
 74.1334 -                final PropertyNode existingProperty = map.get(key);
 74.1335 -
 74.1336 -                if (existingProperty != null) {
 74.1337 +                        throw error(AbstractParser.message("expected.comma", type.getNameOrType()));
 74.1338 +                    }
 74.1339 +
 74.1340 +                    commaSeen = false;
 74.1341 +                    // Get and add the next property.
 74.1342 +                    final PropertyNode property = propertyAssignment();
 74.1343 +                    final String key = property.getKeyName();
 74.1344 +                    final PropertyNode existingProperty = map.get(key);
 74.1345 +
 74.1346 +                    if (existingProperty == null) {
 74.1347 +                        map.put(key, property);
 74.1348 +                       // elements.add(property);
 74.1349 +                        break;
 74.1350 +                    }
 74.1351 +
 74.1352                      // ECMA section 11.1.5 Object Initialiser
 74.1353                      // point # 4 on property assignment production
 74.1354 -                    final Node value  = property.getValue();
 74.1355 -                    final Node getter = property.getGetter();
 74.1356 -                    final Node setter = property.getSetter();
 74.1357 -
 74.1358 -                    final Node prevValue  = existingProperty.getValue();
 74.1359 -                    final Node prevGetter = existingProperty.getGetter();
 74.1360 -                    final Node prevSetter = existingProperty.getSetter();
 74.1361 +                    final Node         value  = property.getValue();
 74.1362 +                    final FunctionNode getter = property.getGetter();
 74.1363 +                    final FunctionNode setter = property.getSetter();
 74.1364 +
 74.1365 +                    final Node         prevValue  = existingProperty.getValue();
 74.1366 +                    final FunctionNode prevGetter = existingProperty.getGetter();
 74.1367 +                    final FunctionNode prevSetter = existingProperty.getSetter();
 74.1368  
 74.1369                      boolean redefinitionOk = true;
 74.1370                      // ECMA 11.1.5 strict mode restrictions
 74.1371 @@ -2125,7 +1891,7 @@
 74.1372                      }
 74.1373  
 74.1374                      final boolean isPrevAccessor = prevGetter != null || prevSetter != null;
 74.1375 -                    final boolean isAccessor = getter != null || setter != null;
 74.1376 +                    final boolean isAccessor     = getter != null     || setter != null;
 74.1377  
 74.1378                      // data property redefined as accessor property
 74.1379                      if (prevValue != null && isAccessor) {
 74.1380 @@ -2145,40 +1911,33 @@
 74.1381                      }
 74.1382  
 74.1383                      if (!redefinitionOk) {
 74.1384 -                        error(AbstractParser.message("property.redefinition", key.toString()), property.getToken());
 74.1385 +                        throw error(AbstractParser.message("property.redefinition", key.toString()), property.getToken());
 74.1386                      }
 74.1387  
 74.1388 +                    PropertyNode newProperty = existingProperty;
 74.1389                      if (value != null) {
 74.1390 -                        final Node existingValue = existingProperty.getValue();
 74.1391 -
 74.1392 -                        if (existingValue == null) {
 74.1393 -                            existingProperty.setValue(value);
 74.1394 +                        if (prevValue == null) {
 74.1395 +                            map.put(key, newProperty = newProperty.setValue(value));
 74.1396                          } else {
 74.1397 -                            final long propertyToken = Token.recast(existingProperty.getToken(), COMMARIGHT);
 74.1398 -                            existingProperty.setValue(new BinaryNode(source, propertyToken, existingValue, value));
 74.1399 +                            final long propertyToken = Token.recast(newProperty.getToken(), COMMARIGHT);
 74.1400 +                            map.put(key, newProperty = newProperty.setValue(new BinaryNode(source, propertyToken, prevValue, value)));
 74.1401                          }
 74.1402  
 74.1403 -                        existingProperty.setGetter(null);
 74.1404 -                        existingProperty.setSetter(null);
 74.1405 +                        map.put(key, newProperty = newProperty.setGetter(null).setSetter(null));
 74.1406                      }
 74.1407  
 74.1408                      if (getter != null) {
 74.1409 -                        existingProperty.setGetter(getter);
 74.1410 +                        map.put(key, newProperty = newProperty.setGetter(getter));
 74.1411                      }
 74.1412  
 74.1413                      if (setter != null) {
 74.1414 -                        existingProperty.setSetter(setter);
 74.1415 +                        map.put(key, newProperty = newProperty.setSetter(setter));
 74.1416                      }
 74.1417 -                } else {
 74.1418 -                    map.put(key, property);
 74.1419 -                    elements.add(property);
 74.1420 -                }
 74.1421 -
 74.1422 -                break;
 74.1423 +                    break;
 74.1424              }
 74.1425          }
 74.1426  
 74.1427 -        return new ObjectNode(source, objectToken, finish, elements);
 74.1428 +        return new ObjectNode(source, objectToken, finish, new ArrayList<Node>(map.values()));
 74.1429      }
 74.1430  
 74.1431      /**
 74.1432 @@ -2198,7 +1957,7 @@
 74.1433              return getIdent();
 74.1434          case OCTAL:
 74.1435              if (isStrictMode) {
 74.1436 -                error(AbstractParser.message("strict.no.octal"), token);
 74.1437 +                throw error(AbstractParser.message("strict.no.octal"), token);
 74.1438              }
 74.1439          case STRING:
 74.1440          case ESCSTRING:
 74.1441 @@ -2235,8 +1994,6 @@
 74.1442          final long propertyToken = token;
 74.1443  
 74.1444          FunctionNode functionNode;
 74.1445 -        List<IdentNode> parameters;
 74.1446 -        PropertyNode propertyNode;
 74.1447          PropertyKey propertyName;
 74.1448  
 74.1449          if (type == IDENT) {
 74.1450 @@ -2253,11 +2010,8 @@
 74.1451                      final IdentNode getNameNode = new IdentNode(source, ((Node)getIdent).getToken(), finish, "get " + getterName);
 74.1452                      expect(LPAREN);
 74.1453                      expect(RPAREN);
 74.1454 -                    parameters = new ArrayList<>();
 74.1455 -                    functionNode = functionBody(getSetToken, getNameNode, parameters, FunctionNode.Kind.GETTER);
 74.1456 -                    propertyNode = new PropertyNode(source, propertyToken, finish, getIdent, null);
 74.1457 -                    propertyNode.setGetter(functionNode);
 74.1458 -                    return propertyNode;
 74.1459 +                    functionNode = functionBody(getSetToken, getNameNode, new ArrayList<IdentNode>(), FunctionNode.Kind.GETTER);
 74.1460 +                    return new PropertyNode(source, propertyToken, finish, getIdent, null, functionNode, null);
 74.1461  
 74.1462                  case "set":
 74.1463                      final PropertyKey setIdent = propertyName();
 74.1464 @@ -2267,12 +2021,10 @@
 74.1465                      final IdentNode argIdent = getIdent();
 74.1466                      verifyStrictIdent(argIdent, "setter argument");
 74.1467                      expect(RPAREN);
 74.1468 -                    parameters = new ArrayList<>();
 74.1469 +                    List<IdentNode> parameters = new ArrayList<>();
 74.1470                      parameters.add(argIdent);
 74.1471                      functionNode = functionBody(getSetToken, setNameNode, parameters, FunctionNode.Kind.SETTER);
 74.1472 -                    propertyNode = new PropertyNode(source, propertyToken, finish, setIdent, null);
 74.1473 -                    propertyNode.setSetter(functionNode);
 74.1474 -                    return propertyNode;
 74.1475 +                    return new PropertyNode(source, propertyToken, finish, setIdent, null, null, functionNode);
 74.1476  
 74.1477                  default:
 74.1478                      break;
 74.1479 @@ -2286,9 +2038,11 @@
 74.1480  
 74.1481          expect(COLON);
 74.1482  
 74.1483 -        final Node value = assignmentExpression(false);
 74.1484 -        propertyNode =  new PropertyNode(source, propertyToken, finish, propertyName, value);
 74.1485 -        return propertyNode;
 74.1486 +        return new PropertyNode(source, propertyToken, finish, propertyName, assignmentExpression(false), null, null);
 74.1487 +    }
 74.1488 +
 74.1489 +    private int callNodeFlags() {
 74.1490 +        return lc.inWith() ? CallNode.IN_WITH_BLOCK : 0;
 74.1491      }
 74.1492  
 74.1493      /**
 74.1494 @@ -2320,10 +2074,7 @@
 74.1495                  detectSpecialFunction((IdentNode)lhs);
 74.1496              }
 74.1497  
 74.1498 -            lhs = new CallNode(source, callToken, finish, lhs, arguments);
 74.1499 -            if (isInWithBlock()) {
 74.1500 -                ((CallNode)lhs).setInWithBlock();
 74.1501 -            }
 74.1502 +            lhs = new CallNode(source, callToken, finish, lhs, arguments, callNodeFlags());
 74.1503          }
 74.1504  
 74.1505  loop:
 74.1506 @@ -2337,10 +2088,7 @@
 74.1507                  final List<Node> arguments = argumentList();
 74.1508  
 74.1509                  // Create call node.
 74.1510 -                lhs = new CallNode(source, callToken, finish, lhs, arguments);
 74.1511 -                if (isInWithBlock()) {
 74.1512 -                    ((CallNode)lhs).setInWithBlock();
 74.1513 -                }
 74.1514 +                lhs = new CallNode(source, callToken, finish, lhs, arguments, callNodeFlags());
 74.1515  
 74.1516                  break;
 74.1517  
 74.1518 @@ -2419,10 +2167,7 @@
 74.1519              arguments.add(objectLiteral());
 74.1520          }
 74.1521  
 74.1522 -        final CallNode callNode = new CallNode(source, constructor.getToken(), finish, constructor, arguments);
 74.1523 -        if (isInWithBlock()) {
 74.1524 -            callNode.setInWithBlock();
 74.1525 -        }
 74.1526 +        final CallNode callNode = new CallNode(source, constructor.getToken(), finish, constructor, arguments, callNodeFlags());
 74.1527  
 74.1528          return new UnaryNode(source, newToken, callNode);
 74.1529      }
 74.1530 @@ -2482,8 +2227,7 @@
 74.1531  
 74.1532              case PERIOD:
 74.1533                  if (lhs == null) {
 74.1534 -                    error(AbstractParser.message("expected.operand", type.getNameOrType()));
 74.1535 -                    return null;
 74.1536 +                    throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
 74.1537                  }
 74.1538  
 74.1539                  next();
 74.1540 @@ -2585,29 +2329,27 @@
 74.1541          }
 74.1542  
 74.1543          expect(LPAREN);
 74.1544 -
 74.1545          final List<IdentNode> parameters = formalParameterList();
 74.1546 -
 74.1547          expect(RPAREN);
 74.1548  
 74.1549 -        final FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL);
 74.1550 +        FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL);
 74.1551  
 74.1552          if (isStatement) {
 74.1553 -            if(topLevel) {
 74.1554 -                functionNode.setIsDeclared();
 74.1555 +            if (topLevel) {
 74.1556 +                functionNode = functionNode.setFlag(lc, FunctionNode.IS_DECLARED);
 74.1557              }
 74.1558 -            if(ARGUMENTS.tag().equals(name.getName())) {
 74.1559 -                getFunction().setDefinesArguments();
 74.1560 +            if (ARGUMENTS.symbolName().equals(name.getName())) {
 74.1561 +                functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS);
 74.1562              }
 74.1563          }
 74.1564  
 74.1565          if (isAnonymous) {
 74.1566 -            functionNode.setIsAnonymous();
 74.1567 +            functionNode = functionNode.setFlag(lc, FunctionNode.IS_ANONYMOUS);
 74.1568          }
 74.1569  
 74.1570          final int arity = parameters.size();
 74.1571  
 74.1572 -        final boolean strict = functionNode.isStrictMode();
 74.1573 +        final boolean strict = functionNode.isStrict();
 74.1574          if (arity > 1) {
 74.1575              final HashSet<String> parametersSet = new HashSet<>(arity);
 74.1576  
 74.1577 @@ -2615,39 +2357,37 @@
 74.1578                  final IdentNode parameter = parameters.get(i);
 74.1579                  String parameterName = parameter.getName();
 74.1580  
 74.1581 -                if (ARGUMENTS.tag().equals(parameterName)) {
 74.1582 -                    functionNode.setDefinesArguments();
 74.1583 +                if (ARGUMENTS.symbolName().equals(parameterName)) {
 74.1584 +                    functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS);
 74.1585                  }
 74.1586  
 74.1587                  if (parametersSet.contains(parameterName)) {
 74.1588                      // redefinition of parameter name
 74.1589                      if (strict) {
 74.1590 -                        error(AbstractParser.message("strict.param.redefinition", parameterName), parameter.getToken());
 74.1591 -                    } else {
 74.1592 -                        // rename in non-strict mode
 74.1593 -                        parameterName = functionNode.uniqueName(parameterName);
 74.1594 -                        final long parameterToken = parameter.getToken();
 74.1595 -                        parameters.set(i, new IdentNode(source, parameterToken, Token.descPosition(parameterToken), functionNode.uniqueName(parameterName)));
 74.1596 +                        throw error(AbstractParser.message("strict.param.redefinition", parameterName), parameter.getToken());
 74.1597                      }
 74.1598 +                    // rename in non-strict mode
 74.1599 +                    parameterName = functionNode.uniqueName(parameterName);
 74.1600 +                    final long parameterToken = parameter.getToken();
 74.1601 +                    parameters.set(i, new IdentNode(source, parameterToken, Token.descPosition(parameterToken), functionNode.uniqueName(parameterName)));
 74.1602                  }
 74.1603  
 74.1604                  parametersSet.add(parameterName);
 74.1605              }
 74.1606          } else if (arity == 1) {
 74.1607 -            if (ARGUMENTS.tag().equals(parameters.get(0).getName())) {
 74.1608 -                functionNode.setDefinesArguments();
 74.1609 +            if (ARGUMENTS.symbolName().equals(parameters.get(0).getName())) {
 74.1610 +                functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS);
 74.1611              }
 74.1612          }
 74.1613  
 74.1614          if (isStatement) {
 74.1615 -            final VarNode varNode = new VarNode(source, functionToken, finish, name, functionNode, true);
 74.1616 -            if(topLevel) {
 74.1617 +            final VarNode varNode = new VarNode(source, functionToken, finish, name, functionNode, VarNode.IS_STATEMENT);
 74.1618 +            if (topLevel) {
 74.1619                  functionDeclarations.add(lineNumber);
 74.1620                  functionDeclarations.add(varNode);
 74.1621              } else {
 74.1622 -                final Block block = getBlock();
 74.1623 -                block.addStatement(lineNumber);
 74.1624 -                block.addStatement(varNode);
 74.1625 +                appendStatement(lineNumber);
 74.1626 +                appendStatement(varNode);
 74.1627              }
 74.1628          }
 74.1629  
 74.1630 @@ -2701,13 +2441,11 @@
 74.1631       */
 74.1632      private FunctionNode functionBody(final long firstToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind) {
 74.1633          FunctionNode functionNode = null;
 74.1634 +        long lastToken = 0L;
 74.1635  
 74.1636          try {
 74.1637              // Create a new function block.
 74.1638 -            functionNode = newFunctionBlock(ident);
 74.1639 -            functionNode.setParameters(parameters);
 74.1640 -            functionNode.setKind(kind);
 74.1641 -            functionNode.setFirstToken(firstToken);
 74.1642 +            functionNode = newFunctionNode(firstToken, ident, parameters, kind);
 74.1643  
 74.1644              // Nashorn extension: expression closures
 74.1645              if (!env._no_syntax_extensions && type != LBRACE) {
 74.1646 @@ -2720,14 +2458,12 @@
 74.1647  
 74.1648                  // just expression as function body
 74.1649                  final Node expr = expression();
 74.1650 -
 74.1651 +                assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode);
 74.1652                  // create a return statement - this creates code in itself and does not need to be
 74.1653                  // wrapped into an ExecuteNode
 74.1654 -                final ReturnNode  returnNode = new ReturnNode(source, expr.getToken(), finish, expr, null);
 74.1655 -
 74.1656 -                // add the return statement
 74.1657 -                functionNode.addStatement(returnNode);
 74.1658 -                functionNode.setLastToken(token);
 74.1659 +                final ReturnNode returnNode = new ReturnNode(source, expr.getToken(), finish, expr);
 74.1660 +                appendStatement(returnNode);
 74.1661 +                lastToken = token;
 74.1662                  functionNode.setFinish(Token.descPosition(token) + Token.descLength(token));
 74.1663  
 74.1664              } else {
 74.1665 @@ -2738,23 +2474,35 @@
 74.1666                  functionDeclarations = new ArrayList<>();
 74.1667                  try {
 74.1668                      sourceElements();
 74.1669 -                    functionNode.prependStatements(functionDeclarations);
 74.1670 +                    addFunctionDeclarations(functionNode);
 74.1671                  } finally {
 74.1672                      functionDeclarations = prevFunctionDecls;
 74.1673                  }
 74.1674  
 74.1675 -                functionNode.setLastToken(token);
 74.1676 +                lastToken = token;
 74.1677                  expect(RBRACE);
 74.1678                  functionNode.setFinish(finish);
 74.1679  
 74.1680              }
 74.1681          } finally {
 74.1682 -            restoreBlock(functionNode);
 74.1683 +            functionNode = restoreFunctionNode(functionNode, lastToken);
 74.1684          }
 74.1685 -
 74.1686          return functionNode;
 74.1687      }
 74.1688  
 74.1689 +    private void addFunctionDeclarations(final FunctionNode functionNode) {
 74.1690 +        assert lc.peek() == lc.getFunctionBody(functionNode);
 74.1691 +        VarNode lastDecl = null;
 74.1692 +        for (int i = functionDeclarations.size() - 1; i >= 0; i--) {
 74.1693 +            Node decl = functionDeclarations.get(i);
 74.1694 +            if (lastDecl == null && decl instanceof VarNode) {
 74.1695 +                decl = lastDecl = ((VarNode)decl).setFlag(VarNode.IS_LAST_FUNCTION_DECLARATION);
 74.1696 +                lc.setFlag(functionNode, FunctionNode.HAS_FUNCTION_DECLARATIONS);
 74.1697 +            }
 74.1698 +            prependStatement(decl);
 74.1699 +        }
 74.1700 +    }
 74.1701 +
 74.1702      private RuntimeNode referenceError(final Node lhs, final Node rhs) {
 74.1703          final ArrayList<Node> args = new ArrayList<>();
 74.1704          args.add(lhs);
 74.1705 @@ -2764,9 +2512,7 @@
 74.1706              args.add(rhs);
 74.1707          }
 74.1708          args.add(LiteralNode.newInstance(source, lhs.getToken(), lhs.getFinish(), lhs.toString()));
 74.1709 -        final RuntimeNode runtimeNode = new RuntimeNode(source, lhs.getToken(),
 74.1710 -                      lhs.getFinish(), RuntimeNode.Request.REFERENCE_ERROR, args);
 74.1711 -        return runtimeNode;
 74.1712 +        return new RuntimeNode(source, lhs.getToken(), lhs.getFinish(), RuntimeNode.Request.REFERENCE_ERROR, args);
 74.1713      }
 74.1714  
 74.1715      /*
 74.1716 @@ -2815,19 +2561,7 @@
 74.1717          case BIT_NOT:
 74.1718          case NOT:
 74.1719              next();
 74.1720 -
 74.1721              final Node expr = unaryExpression();
 74.1722 -
 74.1723 -            /*
 74.1724 -             // Not sure if "delete <ident>" is a compile-time error or a
 74.1725 -             // runtime error in strict mode.
 74.1726 -
 74.1727 -             if (isStrictMode) {
 74.1728 -                 if (unaryTokenType == DELETE && expr instanceof IdentNode) {
 74.1729 -                     error(message("strict.cant.delete.ident", ((IdentNode)expr).getName()), expr.getToken());
 74.1730 -                 }
 74.1731 -             }
 74.1732 -             */
 74.1733              return new UnaryNode(source, unaryToken, expr);
 74.1734  
 74.1735          case INCPREFIX:
 74.1736 @@ -2890,7 +2624,7 @@
 74.1737          }
 74.1738  
 74.1739          if (expression == null) {
 74.1740 -            error(AbstractParser.message("expected.operand", type.getNameOrType()));
 74.1741 +            throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
 74.1742          }
 74.1743  
 74.1744          return expression;
 74.1745 @@ -2992,6 +2726,7 @@
 74.1746          // Include commas in expression parsing.
 74.1747          return expression(unaryExpression(), COMMARIGHT.getPrecedence(), false);
 74.1748      }
 74.1749 +
 74.1750      private Node expression(final Node exprLhs, final int minPrecedence, final boolean noIn) {
 74.1751          // Get the precedence of the next operator.
 74.1752          int precedence = type.getPrecedence();
 74.1753 @@ -3087,11 +2822,26 @@
 74.1754          return "[JavaScript Parsing]";
 74.1755      }
 74.1756  
 74.1757 -    private Block getBlock() {
 74.1758 -        return lexicalContext.getCurrentBlock();
 74.1759 +    private static void markWithOrEval(final LexicalContext lc, int flag) {
 74.1760 +        final Iterator<FunctionNode> iter = lc.getFunctions();
 74.1761 +        boolean flaggedCurrentFn = false;
 74.1762 +        while (iter.hasNext()) {
 74.1763 +            final FunctionNode fn = iter.next();
 74.1764 +            if (!flaggedCurrentFn) {
 74.1765 +                lc.setFlag(fn, flag);
 74.1766 +                flaggedCurrentFn = true;
 74.1767 +            } else {
 74.1768 +                lc.setFlag(fn, FunctionNode.HAS_DESCENDANT_WITH_OR_EVAL);
 74.1769 +            }
 74.1770 +            lc.setFlag(lc.getFunctionBody(fn), Block.NEEDS_SCOPE);
 74.1771 +        }
 74.1772      }
 74.1773  
 74.1774 -    private FunctionNode getFunction() {
 74.1775 -        return lexicalContext.getCurrentFunction();
 74.1776 +    private void prependStatement(final Node statement) {
 74.1777 +        lc.prependStatement(statement);
 74.1778 +    }
 74.1779 +
 74.1780 +    private void appendStatement(final Node statement) {
 74.1781 +        lc.appendStatement(statement);
 74.1782      }
 74.1783  }
    75.1 --- a/src/jdk/nashorn/internal/parser/TokenType.java	Fri Apr 19 18:23:00 2013 +0530
    75.2 +++ b/src/jdk/nashorn/internal/parser/TokenType.java	Fri Apr 19 16:11:16 2013 +0200
    75.3 @@ -280,6 +280,11 @@
    75.4         return values;
    75.5      }
    75.6  
    75.7 +    @Override
    75.8 +    public String toString() {
    75.9 +        return name;
   75.10 +    }
   75.11 +
   75.12      static {
   75.13          // Avoid cloning of enumeration.
   75.14          values = TokenType.values();
    76.1 --- a/src/jdk/nashorn/internal/runtime/Context.java	Fri Apr 19 18:23:00 2013 +0530
    76.2 +++ b/src/jdk/nashorn/internal/runtime/Context.java	Fri Apr 19 16:11:16 2013 +0200
    76.3 @@ -382,7 +382,7 @@
    76.4              // We need to get strict mode flag from compiled class. This is
    76.5              // because eval code may start with "use strict" directive.
    76.6              try {
    76.7 -                strictFlag = clazz.getField(STRICT_MODE.tag()).getBoolean(null);
    76.8 +                strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null);
    76.9              } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
   76.10                  //ignored
   76.11                  strictFlag = false;
   76.12 @@ -696,7 +696,7 @@
   76.13                  MH.findStatic(
   76.14                      MethodHandles.lookup(),
   76.15                      script,
   76.16 -                    RUN_SCRIPT.tag(),
   76.17 +                    RUN_SCRIPT.symbolName(),
   76.18                      MH.type(
   76.19                          Object.class,
   76.20                          ScriptFunction.class,
   76.21 @@ -705,13 +705,13 @@
   76.22          boolean strict;
   76.23  
   76.24          try {
   76.25 -            strict = script.getField(STRICT_MODE.tag()).getBoolean(null);
   76.26 +            strict = script.getField(STRICT_MODE.symbolName()).getBoolean(null);
   76.27          } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
   76.28              strict = false;
   76.29          }
   76.30  
   76.31          // Package as a JavaScript function and pass function back to shell.
   76.32 -        return ((GlobalObject)Context.getGlobalTrusted()).newScriptFunction(RUN_SCRIPT.tag(), runMethodHandle, scope, strict);
   76.33 +        return ((GlobalObject)Context.getGlobalTrusted()).newScriptFunction(RUN_SCRIPT.symbolName(), runMethodHandle, scope, strict);
   76.34      }
   76.35  
   76.36      private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) {
   76.37 @@ -729,13 +729,13 @@
   76.38              global = (GlobalObject)Context.getGlobalTrusted();
   76.39              script = global.findCachedClass(source);
   76.40              if (script != null) {
   76.41 -                Compiler.LOG.fine("Code cache hit for " + source + " avoiding recompile.");
   76.42 +                Compiler.LOG.fine("Code cache hit for ", source, " avoiding recompile.");
   76.43                  return script;
   76.44              }
   76.45          }
   76.46  
   76.47          final FunctionNode functionNode = new Parser(env, source, errMan, strict).parse();
   76.48 -        if (errors.hasErrors() || env._parse_only) {
   76.49 +        if (errors.hasErrors()) {
   76.50              return null;
   76.51          }
   76.52  
   76.53 @@ -747,6 +747,10 @@
   76.54              getErr().println(new PrintVisitor(functionNode));
   76.55          }
   76.56  
   76.57 +        if (env._parse_only) {
   76.58 +            return null;
   76.59 +        }
   76.60 +
   76.61          final URL          url    = source.getURL();
   76.62          final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
   76.63          final CodeSource   cs     = url == null ? null : new CodeSource(url, (CodeSigner[])null);
    77.1 --- a/src/jdk/nashorn/internal/runtime/DebugLogger.java	Fri Apr 19 18:23:00 2013 +0530
    77.2 +++ b/src/jdk/nashorn/internal/runtime/DebugLogger.java	Fri Apr 19 16:11:16 2013 +0200
    77.3 @@ -135,7 +135,16 @@
    77.4       * @param str the string to log
    77.5       */
    77.6      public void finest(final String str) {
    77.7 -        log(str, Level.FINEST);
    77.8 +        log(Level.FINEST, str);
    77.9 +    }
   77.10 +
   77.11 +    /**
   77.12 +     * Shorthand for outputting a log string as log level
   77.13 +     * {@link java.util.logging.Level#FINEST} on this logger
   77.14 +     * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
   77.15 +     */
   77.16 +    public void finest(final Object... objs) {
   77.17 +        log(Level.FINEST, objs);
   77.18      }
   77.19  
   77.20      /**
   77.21 @@ -144,7 +153,16 @@
   77.22       * @param str the string to log
   77.23       */
   77.24      public void finer(final String str) {
   77.25 -        log(str, Level.FINER);
   77.26 +        log(Level.FINER, str);
   77.27 +    }
   77.28 +
   77.29 +    /**
   77.30 +     * Shorthand for outputting a log string as log level
   77.31 +     * {@link java.util.logging.Level#FINER} on this logger
   77.32 +     * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
   77.33 +     */
   77.34 +    public void finer(final Object... objs) {
   77.35 +        log(Level.FINER, objs);
   77.36      }
   77.37  
   77.38      /**
   77.39 @@ -153,7 +171,16 @@
   77.40       * @param str the string to log
   77.41       */
   77.42      public void fine(final String str) {
   77.43 -        log(str, Level.FINE);
   77.44 +        log(Level.FINE, str);
   77.45 +    }
   77.46 +
   77.47 +    /**
   77.48 +     * Shorthand for outputting a log string as log level
   77.49 +     * {@link java.util.logging.Level#FINE} on this logger
   77.50 +     * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
   77.51 +     */
   77.52 +    public void fine(final Object... objs) {
   77.53 +        log(Level.FINE, objs);
   77.54      }
   77.55  
   77.56      /**
   77.57 @@ -162,7 +189,16 @@
   77.58       * @param str the string to log
   77.59       */
   77.60      public void config(final String str) {
   77.61 -        log(str, Level.CONFIG);
   77.62 +        log(Level.CONFIG, str);
   77.63 +    }
   77.64 +
   77.65 +    /**
   77.66 +     * Shorthand for outputting a log string as log level
   77.67 +     * {@link java.util.logging.Level#CONFIG} on this logger
   77.68 +     * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
   77.69 +     */
   77.70 +    public void config(final Object... objs) {
   77.71 +        log(Level.CONFIG, objs);
   77.72      }
   77.73  
   77.74      /**
   77.75 @@ -171,7 +207,16 @@
   77.76       * @param str the string to log
   77.77       */
   77.78      public void info(final String str) {
   77.79 -        log(str, Level.INFO);
   77.80 +        log(Level.INFO, str);
   77.81 +    }
   77.82 +
   77.83 +    /**
   77.84 +     * Shorthand for outputting a log string as log level
   77.85 +     * {@link java.util.logging.Level#FINE} on this logger
   77.86 +     * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
   77.87 +     */
   77.88 +    public void info(final Object... objs) {
   77.89 +        log(Level.INFO, objs);
   77.90      }
   77.91  
   77.92      /**
   77.93 @@ -180,7 +225,16 @@
   77.94       * @param str the string to log
   77.95       */
   77.96      public void warning(final String str) {
   77.97 -        log(str, Level.WARNING);
   77.98 +        log(Level.WARNING, str);
   77.99 +    }
  77.100 +
  77.101 +    /**
  77.102 +     * Shorthand for outputting a log string as log level
  77.103 +     * {@link java.util.logging.Level#FINE} on this logger
  77.104 +     * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
  77.105 +     */
  77.106 +    public void warning(final Object... objs) {
  77.107 +        log(Level.WARNING, objs);
  77.108      }
  77.109  
  77.110      /**
  77.111 @@ -189,20 +243,28 @@
  77.112       * @param str the string to log
  77.113       */
  77.114      public void severe(final String str) {
  77.115 -        log(str, Level.SEVERE);
  77.116 +        log(Level.SEVERE, str);
  77.117 +    }
  77.118 +
  77.119 +    /**
  77.120 +     * Shorthand for outputting a log string as log level
  77.121 +     * {@link java.util.logging.Level#FINE} on this logger
  77.122 +     * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
  77.123 +     */
  77.124 +    public void severe(final Object... objs) {
  77.125 +        log(Level.SEVERE, objs);
  77.126      }
  77.127  
  77.128      /**
  77.129       * Output log line on this logger at a given level of verbosity
  77.130       * @see java.util.logging.Level
  77.131       *
  77.132 +     * @param level minimum log level required for logging to take place
  77.133       * @param str   string to log
  77.134 -     * @param level minimum log level required for logging to take place
  77.135       */
  77.136 -    public void log(final String str, final Level level) {
  77.137 +    public void log(final Level level, final String str) {
  77.138          if (isEnabled) {
  77.139              final StringBuilder sb = new StringBuilder();
  77.140 -
  77.141              for (int i = 0 ; i < indent ; i++) {
  77.142                  sb.append(' ');
  77.143              }
  77.144 @@ -210,4 +272,24 @@
  77.145              logger.log(level, sb.toString());
  77.146          }
  77.147      }
  77.148 +
  77.149 +    /**
  77.150 +     * Output log line on this logger at a given level of verbosity
  77.151 +     * @see java.util.logging.Level
  77.152 +     *
  77.153 +     * @param level minimum log level required for logging to take place
  77.154 +     * @param objs  objects for which to invoke toString and concatenate to log
  77.155 +     */
  77.156 +    public void log(final Level level, final Object... objs) {
  77.157 +        if (isEnabled) {
  77.158 +            final StringBuilder sb = new StringBuilder();
  77.159 +            for (int i = 0 ; i < indent ; i++) {
  77.160 +                sb.append(' ');
  77.161 +            }
  77.162 +            for (final Object obj : objs) {
  77.163 +                sb.append(obj);
  77.164 +            }
  77.165 +            logger.log(level, sb.toString());
  77.166 +        }
  77.167 +    }
  77.168  }
    78.1 --- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Fri Apr 19 18:23:00 2013 +0530
    78.2 +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Fri Apr 19 16:11:16 2013 +0200
    78.3 @@ -25,10 +25,11 @@
    78.4  
    78.5  package jdk.nashorn.internal.runtime;
    78.6  
    78.7 +import static jdk.nashorn.internal.lookup.Lookup.MH;
    78.8 +
    78.9  import java.lang.invoke.MethodHandle;
   78.10  import java.lang.invoke.MethodHandles;
   78.11  import java.lang.invoke.MethodType;
   78.12 -
   78.13  import jdk.nashorn.internal.codegen.Compiler;
   78.14  import jdk.nashorn.internal.codegen.CompilerConstants;
   78.15  import jdk.nashorn.internal.codegen.FunctionSignature;
   78.16 @@ -37,8 +38,6 @@
   78.17  import jdk.nashorn.internal.parser.Token;
   78.18  import jdk.nashorn.internal.parser.TokenType;
   78.19  
   78.20 -import static jdk.nashorn.internal.lookup.Lookup.MH;
   78.21 -
   78.22  /**
   78.23   * This is a subclass that represents a script function that may be regenerated,
   78.24   * for example with specialization based on call site types, or lazily generated.
   78.25 @@ -47,7 +46,7 @@
   78.26   */
   78.27  public final class RecompilableScriptFunctionData extends ScriptFunctionData {
   78.28  
   78.29 -    private final FunctionNode functionNode;
   78.30 +    private FunctionNode functionNode;
   78.31      private final PropertyMap  allocatorMap;
   78.32      private final CodeInstaller<ScriptEnvironment> installer;
   78.33      private final String allocatorClassName;
   78.34 @@ -70,7 +69,7 @@
   78.35                  "" :
   78.36                  functionNode.getIdent().getName(),
   78.37                functionNode.getParameters().size(),
   78.38 -              functionNode.isStrictMode(),
   78.39 +              functionNode.isStrict(),
   78.40                false,
   78.41                true);
   78.42  
   78.43 @@ -129,7 +128,7 @@
   78.44  
   78.45      private void ensureHasAllocator() throws ClassNotFoundException {
   78.46          if (allocator == null && allocatorClassName != null) {
   78.47 -            this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.tag(), MH.type(ScriptObject.class, PropertyMap.class));
   78.48 +            this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
   78.49          }
   78.50      }
   78.51  
   78.52 @@ -148,8 +147,11 @@
   78.53           // therefore, currently method specialization is disabled. TODO
   78.54  
   78.55           if (functionNode.isLazy()) {
   78.56 -             Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '" + functionNode.getName() + "'");
   78.57 -             new Compiler(installer, functionNode).compile().install();
   78.58 +             Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'");
   78.59 +             final Compiler compiler = new Compiler(installer, functionNode);
   78.60 +             functionNode = compiler.compile();
   78.61 +             assert !functionNode.isLazy();
   78.62 +             compiler.install();
   78.63  
   78.64               // we don't need to update any flags - varArgs and needsCallee are instrincic
   78.65               // in the function world we need to get a destination node from the compile instead
   78.66 @@ -159,7 +161,7 @@
   78.67           // we can't get here unless we have bytecode, either from eager compilation or from
   78.68           // running a lazy compile on the lines above
   78.69  
   78.70 -         assert functionNode.hasState(CompilationState.INSTALLED);
   78.71 +         assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
   78.72  
   78.73           // code exists - look it up and add it into the automatically sorted invoker list
   78.74           code.add(
    79.1 --- a/src/jdk/nashorn/internal/runtime/StructureLoader.java	Fri Apr 19 18:23:00 2013 +0530
    79.2 +++ b/src/jdk/nashorn/internal/runtime/StructureLoader.java	Fri Apr 19 16:11:16 2013 +0200
    79.3 @@ -47,7 +47,7 @@
    79.4   *
    79.5   */
    79.6  final class StructureLoader extends NashornLoader {
    79.7 -    private static final String JS_OBJECT_PREFIX_EXTERNAL = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_PREFIX.tag();
    79.8 +    private static final String JS_OBJECT_PREFIX_EXTERNAL = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_PREFIX.symbolName();
    79.9      private static final String OBJECTS_PACKAGE_EXTERNAL  = binaryName(OBJECTS_PACKAGE);
   79.10  
   79.11      /**
   79.12 @@ -110,7 +110,7 @@
   79.13      @Override
   79.14      protected Class<?> findClass(final String name) throws ClassNotFoundException {
   79.15          if (name.startsWith(JS_OBJECT_PREFIX_EXTERNAL)) {
   79.16 -            final int start = name.indexOf(JS_OBJECT_PREFIX.tag()) + JS_OBJECT_PREFIX.tag().length();
   79.17 +            final int start = name.indexOf(JS_OBJECT_PREFIX.symbolName()) + JS_OBJECT_PREFIX.symbolName().length();
   79.18              return generateClass(name, name.substring(start, name.length()));
   79.19          }
   79.20          return super.findClass(name);
    80.1 --- a/src/jdk/nashorn/internal/runtime/linker/ClassAndLoader.java	Fri Apr 19 18:23:00 2013 +0530
    80.2 +++ b/src/jdk/nashorn/internal/runtime/linker/ClassAndLoader.java	Fri Apr 19 16:11:16 2013 +0200
    80.3 @@ -185,4 +185,4 @@
    80.4          }
    80.5          return classesAndLoaders.keySet();
    80.6      }
    80.7 -}
    80.8 \ No newline at end of file
    80.9 +}
    81.1 --- a/src/jdk/nashorn/tools/Shell.java	Fri Apr 19 18:23:00 2013 +0530
    81.2 +++ b/src/jdk/nashorn/tools/Shell.java	Fri Apr 19 16:11:16 2013 +0200
    81.3 @@ -42,6 +42,8 @@
    81.4  import jdk.nashorn.api.scripting.NashornException;
    81.5  import jdk.nashorn.internal.codegen.Compiler;
    81.6  import jdk.nashorn.internal.ir.FunctionNode;
    81.7 +import jdk.nashorn.internal.ir.debug.ASTWriter;
    81.8 +import jdk.nashorn.internal.ir.debug.PrintVisitor;
    81.9  import jdk.nashorn.internal.parser.Parser;
   81.10  import jdk.nashorn.internal.runtime.Context;
   81.11  import jdk.nashorn.internal.runtime.ErrorManager;
   81.12 @@ -254,6 +256,14 @@
   81.13                      return COMPILATION_ERROR;
   81.14                  }
   81.15  
   81.16 +                if (env._print_ast) {
   81.17 +                    context.getErr().println(new ASTWriter(functionNode));
   81.18 +                }
   81.19 +
   81.20 +                if (env._print_parse) {
   81.21 +                    context.getErr().println(new PrintVisitor(functionNode));
   81.22 +                }
   81.23 +
   81.24                  //null - pass no code installer - this is compile only
   81.25                  new Compiler(env, functionNode).compile();
   81.26              }
    82.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    82.2 +++ b/test/script/basic/try2.js	Fri Apr 19 16:11:16 2013 +0200
    82.3 @@ -0,0 +1,49 @@
    82.4 +/*
    82.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    82.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    82.7 + * 
    82.8 + * This code is free software; you can redistribute it and/or modify it
    82.9 + * under the terms of the GNU General Public License version 2 only, as
   82.10 + * published by the Free Software Foundation.
   82.11 + * 
   82.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   82.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   82.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   82.15 + * version 2 for more details (a copy is included in the LICENSE file that
   82.16 + * accompanied this code).
   82.17 + * 
   82.18 + * You should have received a copy of the GNU General Public License version
   82.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   82.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   82.21 + * 
   82.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   82.23 + * or visit www.oracle.com if you need additional information or have any
   82.24 + * questions.
   82.25 + */
   82.26 +
   82.27 +/**
   82.28 + * Try throw test - nest finally
   82.29 + *
   82.30 + * @test
   82.31 + * @run 
   82.32 + */
   82.33 +
   82.34 +function f() {
   82.35 +    print("a");
   82.36 +    try {
   82.37 +	print("b");
   82.38 +    } finally {
   82.39 +	print("c");
   82.40 +	try {
   82.41 +	    print("d");
   82.42 +	} finally {
   82.43 +	    print("e");
   82.44 +	}
   82.45 +	print("f");
   82.46 +    }
   82.47 +    print("g");
   82.48 +}
   82.49 +
   82.50 +f();
   82.51 +
   82.52 +print("done");
    83.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    83.2 +++ b/test/script/basic/try2.js.EXPECTED	Fri Apr 19 16:11:16 2013 +0200
    83.3 @@ -0,0 +1,8 @@
    83.4 +a
    83.5 +b
    83.6 +c
    83.7 +d
    83.8 +e
    83.9 +f
   83.10 +g
   83.11 +done

mercurial