AssignSymbols.java revision 1336:efb5f54092ed
1139749Simp/*
276479Swpaul * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
376479Swpaul * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
476479Swpaul *
576479Swpaul * This code is free software; you can redistribute it and/or modify it
676479Swpaul * under the terms of the GNU General Public License version 2 only, as
776479Swpaul * published by the Free Software Foundation.  Oracle designates this
876479Swpaul * particular file as subject to the "Classpath" exception as provided
976479Swpaul * by Oracle in the LICENSE file that accompanied this code.
1076479Swpaul *
1176479Swpaul * This code is distributed in the hope that it will be useful, but WITHOUT
1276479Swpaul * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1376479Swpaul * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1476479Swpaul * version 2 for more details (a copy is included in the LICENSE file that
1576479Swpaul * accompanied this code).
1676479Swpaul *
1776479Swpaul * You should have received a copy of the GNU General Public License version
1876479Swpaul * 2 along with this work; if not, write to the Free Software Foundation,
1976479Swpaul * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2076479Swpaul *
2176479Swpaul * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2276479Swpaul * or visit www.oracle.com if you need additional information or have any
2376479Swpaul * questions.
2476479Swpaul */
2576479Swpaul
2676479Swpaulpackage jdk.nashorn.internal.codegen;
2776479Swpaul
2876479Swpaulimport static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
2976479Swpaulimport static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
3076479Swpaulimport static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
3176479Swpaulimport static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
3276479Swpaulimport static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
3376479Swpaulimport static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
34119418Sobrienimport static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
35119418Sobrienimport static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
36119418Sobrienimport static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
3776479Swpaulimport static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
3876479Swpaulimport static jdk.nashorn.internal.ir.Symbol.HAS_OBJECT_VALUE;
3976479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_CONST;
4076479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF;
4176479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
4276479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
4376479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_LET;
4476479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
4576479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_PROGRAM_LEVEL;
4676479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
4776479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_THIS;
4876479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_VAR;
4976479Swpaulimport static jdk.nashorn.internal.ir.Symbol.KINDMASK;
5076479Swpaul
5176479Swpaulimport java.util.ArrayDeque;
5276479Swpaulimport java.util.ArrayList;
5376479Swpaulimport java.util.Deque;
5476479Swpaulimport java.util.HashMap;
5576479Swpaulimport java.util.HashSet;
5676479Swpaulimport java.util.Iterator;
5776479Swpaulimport java.util.List;
5876479Swpaulimport java.util.ListIterator;
5976479Swpaulimport java.util.Map;
6076479Swpaulimport java.util.Set;
6176479Swpaulimport jdk.nashorn.internal.ir.AccessNode;
6276479Swpaulimport jdk.nashorn.internal.ir.BinaryNode;
6376479Swpaulimport jdk.nashorn.internal.ir.Block;
6476479Swpaulimport jdk.nashorn.internal.ir.CatchNode;
6576479Swpaulimport jdk.nashorn.internal.ir.Expression;
6676479Swpaulimport jdk.nashorn.internal.ir.ForNode;
6776479Swpaulimport jdk.nashorn.internal.ir.FunctionNode;
6876479Swpaulimport jdk.nashorn.internal.ir.FunctionNode.CompilationState;
6976479Swpaulimport jdk.nashorn.internal.ir.IdentNode;
7076479Swpaulimport jdk.nashorn.internal.ir.IndexNode;
7176479Swpaulimport jdk.nashorn.internal.ir.LexicalContext;
7276479Swpaulimport jdk.nashorn.internal.ir.LexicalContextNode;
7378323Swpaulimport jdk.nashorn.internal.ir.LiteralNode;
7478323Swpaulimport jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
7578323Swpaulimport jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
7678323Swpaulimport jdk.nashorn.internal.ir.Node;
7778323Swpaulimport jdk.nashorn.internal.ir.RuntimeNode;
7878323Swpaulimport jdk.nashorn.internal.ir.RuntimeNode.Request;
7978323Swpaulimport jdk.nashorn.internal.ir.Statement;
8078323Swpaulimport jdk.nashorn.internal.ir.SwitchNode;
8178323Swpaulimport jdk.nashorn.internal.ir.Symbol;
8278323Swpaulimport jdk.nashorn.internal.ir.TryNode;
8378323Swpaulimport jdk.nashorn.internal.ir.UnaryNode;
8478323Swpaulimport jdk.nashorn.internal.ir.VarNode;
8578323Swpaulimport jdk.nashorn.internal.ir.WithNode;
8678323Swpaulimport jdk.nashorn.internal.ir.visitor.NodeVisitor;
8778323Swpaulimport jdk.nashorn.internal.parser.TokenType;
8878323Swpaulimport jdk.nashorn.internal.runtime.Context;
8976479Swpaulimport jdk.nashorn.internal.runtime.ECMAErrors;
9076479Swpaulimport jdk.nashorn.internal.runtime.ErrorManager;
91150968Sglebiusimport jdk.nashorn.internal.runtime.JSErrorType;
92150968Sglebiusimport jdk.nashorn.internal.runtime.ParserException;
93150968Sglebiusimport jdk.nashorn.internal.runtime.Source;
94150968Sglebiusimport jdk.nashorn.internal.runtime.logging.DebugLogger;
9576479Swpaulimport jdk.nashorn.internal.runtime.logging.Loggable;
9676479Swpaulimport jdk.nashorn.internal.runtime.logging.Logger;
97192506Syongari
98192506Syongari/**
99192506Syongari * This visitor assigns symbols to identifiers denoting variables. It does few more minor calculations that are only
100192506Syongari * possible after symbols have been assigned; such is the transformation of "delete" and "typeof" operators into runtime
101192506Syongari * nodes and counting of number of properties assigned to "this" in constructor functions. This visitor is also notable
10276479Swpaul * for what it doesn't do, most significantly it does no type calculations as in JavaScript variables can change types
103129879Sphk * during runtime and as such symbols don't have types. Calculation of expression types is performed by a separate
104192506Syongari * visitor.
105192506Syongari */
10676479Swpaul@Logger(name="symbols")
107192506Syongarifinal class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggable {
108192506Syongari    private final DebugLogger log;
10976479Swpaul    private final boolean     debug;
110192506Syongari
11176479Swpaul    private static boolean isParamOrVar(final IdentNode identNode) {
11276479Swpaul        final Symbol symbol = identNode.getSymbol();
11376479Swpaul        return symbol.isParam() || symbol.isVar();
11476479Swpaul    }
11576479Swpaul
11676479Swpaul    private static String name(final Node node) {
11776479Swpaul        final String cn = node.getClass().getName();
11876479Swpaul        final int lastDot = cn.lastIndexOf('.');
11976479Swpaul        if (lastDot == -1) {
12076479Swpaul            return cn;
12176479Swpaul        }
122119285Simp        return cn.substring(lastDot + 1);
123119285Simp    }
12476479Swpaul
125192506Syongari    /**
12676479Swpaul     * Checks if various symbols that were provisionally marked as needing a slot ended up unused, and marks them as not
12776522Swpaul     * needing a slot after all.
12876479Swpaul     * @param functionNode the function node
129192506Syongari     * @return the passed in node, for easy chaining
130192506Syongari     */
131192506Syongari    private static FunctionNode removeUnusedSlots(final FunctionNode functionNode) {
132113506Smdodd        if (!functionNode.needsCallee()) {
133113506Smdodd            functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
13476479Swpaul        }
13576479Swpaul        if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) {
13676479Swpaul            functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
13776479Swpaul        }
13876479Swpaul        // Named function expressions that end up not referencing themselves won't need a local slot for the self symbol.
13976479Swpaul        if(functionNode.isNamedFunctionExpression() && !functionNode.usesSelfSymbol()) {
14076479Swpaul            final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName());
14176479Swpaul            if(selfSymbol != null && selfSymbol.isFunctionSelf()) {
14276479Swpaul                selfSymbol.setNeedsSlot(false);
14376479Swpaul                selfSymbol.clearFlag(Symbol.IS_VAR);
14476479Swpaul            }
14576479Swpaul        }
14676479Swpaul        return functionNode;
14799497Salfred    }
14899497Salfred
14999497Salfred    private final Deque<Set<String>> thisProperties = new ArrayDeque<>();
150192506Syongari    private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol
151192506Syongari    private final Compiler compiler;
152192506Syongari
15376479Swpaul    public AssignSymbols(final Compiler compiler) {
154192506Syongari        super(new LexicalContext());
155192506Syongari        this.compiler = compiler;
156192506Syongari        this.log   = initLogger(compiler.getContext());
157192506Syongari        this.debug = log.isEnabled();
158192506Syongari    }
159135250Swpaul
160193105Sattilio    @Override
16199497Salfred    public DebugLogger getLogger() {
16299497Salfred        return log;
16399497Salfred    }
164192506Syongari
16599497Salfred    @Override
166135250Swpaul    public DebugLogger initLogger(final Context context) {
16799497Salfred        return context.getLogger(this.getClass());
16899497Salfred    }
169135250Swpaul
170192506Syongari    /**
17199497Salfred     * Define symbols for all variable declarations at the top of the function scope. This way we can get around
172192506Syongari     * problems like
173192506Syongari     *
174192506Syongari     * while (true) {
175192506Syongari     *   break;
17676479Swpaul     *   if (true) {
17799497Salfred     *     var s;
17899497Salfred     *   }
17999497Salfred     * }
180192294Syongari     *
181192506Syongari     * to an arbitrary nesting depth.
18276479Swpaul     *
18399497Salfred     * see NASHORN-73
184192294Syongari     *
18599497Salfred     * @param functionNode the FunctionNode we are entering
18699497Salfred     * @param body the body of the FunctionNode we are entering
18776479Swpaul     */
18899497Salfred    private void acceptDeclarations(final FunctionNode functionNode, final Block body) {
18999497Salfred        // This visitor will assign symbol to all declared variables.
19099497Salfred        body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
19176479Swpaul            @Override
192192506Syongari            protected boolean enterDefault(final Node node) {
19399497Salfred                // Don't bother visiting expressions; var is a statement, it can't be inside an expression.
194192506Syongari                // This will also prevent visiting nested functions (as FunctionNode is an expression).
195192506Syongari                return !(node instanceof Expression);
196192506Syongari            }
19799497Salfred
19899497Salfred            @Override
199192506Syongari            public Node leaveVarNode(final VarNode varNode) {
200192506Syongari                final IdentNode ident  = varNode.getName();
201192506Syongari                final boolean blockScoped = varNode.isBlockScoped();
20276479Swpaul                if (blockScoped && lc.inUnprotectedSwitchContext()) {
20376479Swpaul                    throwUnprotectedSwitchError(varNode);
20476479Swpaul                }
20576479Swpaul                final Block block = blockScoped ? lc.getCurrentBlock() : body;
20676479Swpaul                final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags());
20776479Swpaul                if (varNode.isFunctionDeclaration()) {
20876479Swpaul                    symbol.setIsFunctionDeclaration();
209192506Syongari                }
210192506Syongari                return varNode.setName(ident.setSymbol(symbol));
21176479Swpaul            }
21276479Swpaul        });
21376479Swpaul    }
21476479Swpaul
21576479Swpaul    private IdentNode compilerConstantIdentifier(final CompilerConstants cc) {
21676479Swpaul        return createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc));
21776479Swpaul    }
21876479Swpaul
21976479Swpaul    /**
22076479Swpaul     * Creates an ident node for an implicit identifier within the function (one not declared in the script source
221192506Syongari     * code). These identifiers are defined with function's token and finish.
22276479Swpaul     * @param name the name of the identifier
22376479Swpaul     * @return an ident node representing the implicit identifier.
22476479Swpaul     */
22576479Swpaul    private IdentNode createImplicitIdentifier(final String name) {
22676479Swpaul        final FunctionNode fn = lc.getCurrentFunction();
22776479Swpaul        return new IdentNode(fn.getToken(), fn.getFinish(), name);
22876479Swpaul    }
22976479Swpaul
23076479Swpaul    private Symbol createSymbol(final String name, final int flags) {
23176479Swpaul        if ((flags & Symbol.KINDMASK) == IS_GLOBAL) {
232113506Smdodd            //reuse global symbols so they can be hashed
23376479Swpaul            Symbol global = globalSymbols.get(name);
23476479Swpaul            if (global == null) {
23576479Swpaul                global = new Symbol(name, flags);
23676479Swpaul                globalSymbols.put(name, global);
23776479Swpaul            }
23876479Swpaul            return global;
23976479Swpaul        }
24076479Swpaul        return new Symbol(name, flags);
24176479Swpaul    }
24276479Swpaul
24376479Swpaul    /**
244106696Salfred     * Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically
24576479Swpaul     * used to create assignmnent of {@code :callee} to the function name symbol in self-referential function
24676479Swpaul     * expressions as well as for assignment of {@code :arguments} to {@code arguments}.
247106696Salfred     *
24876479Swpaul     * @param name the ident node identifying the variable to initialize
24999497Salfred     * @param initConstant the compiler constant it is initialized to
250192288Syongari     * @param fn the function node the assignment is for
25176479Swpaul     * @return a var node with the appropriate assignment
252192297Syongari     */
25376479Swpaul    private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) {
25476479Swpaul        final IdentNode init = compilerConstantIdentifier(initConstant);
25576479Swpaul        assert init.getSymbol() != null && init.getSymbol().isBytecodeLocal();
25676479Swpaul
25776479Swpaul        final VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init);
25899497Salfred
259192288Syongari        final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName());
26076479Swpaul        assert nameSymbol != null;
261192297Syongari
26276479Swpaul        return (VarNode)synthVar.setName(name.setSymbol(nameSymbol)).accept(this);
26376479Swpaul    }
26476479Swpaul
26576479Swpaul    private FunctionNode createSyntheticInitializers(final FunctionNode functionNode) {
26676479Swpaul        final List<VarNode> syntheticInitializers = new ArrayList<>(2);
26776479Swpaul
26876479Swpaul        // Must visit the new var nodes in the context of the body. We could also just set the new statements into the
26976479Swpaul        // block and then revisit the entire block, but that seems to be too much double work.
27076479Swpaul        final Block body = functionNode.getBody();
27176479Swpaul        lc.push(body);
27276479Swpaul        try {
27376479Swpaul            if (functionNode.usesSelfSymbol()) {
27476479Swpaul                // "var fn = :callee"
27576479Swpaul                syntheticInitializers.add(createSyntheticInitializer(functionNode.getIdent(), CALLEE, functionNode));
27676479Swpaul            }
27776479Swpaul
27876479Swpaul            if (functionNode.needsArguments()) {
27976479Swpaul                // "var arguments = :arguments"
28076479Swpaul                syntheticInitializers.add(createSyntheticInitializer(createImplicitIdentifier(ARGUMENTS_VAR.symbolName()),
28176479Swpaul                        ARGUMENTS, functionNode));
28276479Swpaul            }
28376479Swpaul
28476479Swpaul            if (syntheticInitializers.isEmpty()) {
28599497Salfred                return functionNode;
286192288Syongari            }
28776479Swpaul
288192297Syongari            for(final ListIterator<VarNode> it = syntheticInitializers.listIterator(); it.hasNext();) {
28976479Swpaul                it.set((VarNode)it.next().accept(this));
29076479Swpaul            }
29176479Swpaul        } finally {
29276479Swpaul            lc.pop(body);
29376479Swpaul        }
29476479Swpaul
29576479Swpaul        final List<Statement> stmts = body.getStatements();
29676479Swpaul        final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size());
29776479Swpaul        newStatements.addAll(syntheticInitializers);
29876479Swpaul        newStatements.addAll(stmts);
29976479Swpaul        return functionNode.setBody(lc, body.setStatements(lc, newStatements));
30076479Swpaul    }
30176479Swpaul
30276479Swpaul    /**
30376479Swpaul     * Defines a new symbol in the given block.
30476479Swpaul     *
30576479Swpaul     * @param block        the block in which to define the symbol
30676479Swpaul     * @param name         name of symbol.
30776479Swpaul     * @param origin       origin node
30876479Swpaul     * @param symbolFlags  Symbol flags.
30976479Swpaul     *
31076479Swpaul     * @return Symbol for given name or null for redefinition.
31176479Swpaul     */
31299497Salfred    private Symbol defineSymbol(final Block block, final String name, final Node origin, final int symbolFlags) {
313192294Syongari        int    flags  = symbolFlags;
31476479Swpaul        final boolean isBlockScope = (flags & IS_LET) != 0 || (flags & IS_CONST) != 0;
315192297Syongari        final boolean isGlobal     = (flags & KINDMASK) == IS_GLOBAL;
316192297Syongari
31776479Swpaul        Symbol symbol;
31876479Swpaul        final FunctionNode function;
31976479Swpaul        if (isBlockScope) {
32076479Swpaul            // block scoped variables always live in current block, no need to look for existing symbols in parent blocks.
32176479Swpaul            symbol = block.getExistingSymbol(name);
32276479Swpaul            function = lc.getCurrentFunction();
32376479Swpaul        } else {
32476479Swpaul            symbol = findSymbol(block, name);
32576479Swpaul            function = lc.getFunction(block);
32676479Swpaul        }
32776479Swpaul
32876479Swpaul        // Global variables are implicitly always scope variables too.
32976479Swpaul        if (isGlobal) {
33076479Swpaul            flags |= IS_SCOPE;
33176479Swpaul        }
33276479Swpaul
33376479Swpaul        if (lc.getCurrentFunction().isProgram()) {
33476479Swpaul            flags |= IS_PROGRAM_LEVEL;
33576479Swpaul        }
33676479Swpaul
33776479Swpaul        final boolean isParam = (flags & KINDMASK) == IS_PARAM;
33876479Swpaul        final boolean isVar =   (flags & KINDMASK) == IS_VAR;
33976479Swpaul
34076479Swpaul        if (symbol != null) {
34176479Swpaul            // Symbol was already defined. Check if it needs to be redefined.
34276479Swpaul            if (isParam) {
34376479Swpaul                if (!isLocal(function, symbol)) {
34476479Swpaul                    // Not defined in this function. Create a new definition.
34576479Swpaul                    symbol = null;
34676479Swpaul                } else if (symbol.isParam()) {
34776479Swpaul                    // Duplicate parameter. Null return will force an error.
34876479Swpaul                    throw new AssertionError("duplicate parameter");
34976479Swpaul                }
35076479Swpaul            } else if (isVar) {
35176479Swpaul                if (isBlockScope) {
35276479Swpaul                    // Check redeclaration in same block
35376479Swpaul                    if (symbol.hasBeenDeclared()) {
35476479Swpaul                        throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin);
35599497Salfred                    } else {
356192506Syongari                        symbol.setHasBeenDeclared();
35776479Swpaul                        // Set scope flag on top-level block scoped symbols
358192297Syongari                        if (function.isProgram() && function.getBody() == block) {
359192297Syongari                            symbol.setIsScope();
36076479Swpaul                        }
36176479Swpaul                    }
36276479Swpaul                } else if ((flags & IS_INTERNAL) != 0) {
363192294Syongari                    // Always create a new definition.
364192506Syongari                    symbol = null;
36576479Swpaul                } else {
36676479Swpaul                    // Found LET or CONST in parent scope of same function - s SyntaxError
36776479Swpaul                    if (symbol.isBlockScoped() && isLocal(lc.getCurrentFunction(), symbol)) {
36876479Swpaul                        throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin);
36976479Swpaul                    }
37076479Swpaul                    // Not defined in this function. Create a new definition.
37199497Salfred                    if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
372192288Syongari                        symbol = null;
37376479Swpaul                    }
374192297Syongari                }
37576479Swpaul            }
37676479Swpaul        }
37776479Swpaul
37876479Swpaul        if (symbol == null) {
37976479Swpaul            // If not found, then create a new one.
38076479Swpaul            final Block symbolBlock;
38176479Swpaul
38276479Swpaul            // Determine where to create it.
38376479Swpaul            if (isVar && ((flags & IS_INTERNAL) != 0 || isBlockScope)) {
38476479Swpaul                symbolBlock = block; //internal vars are always defined in the block closest to them
38576479Swpaul            } else if (isGlobal) {
38676479Swpaul                symbolBlock = lc.getOutermostFunction().getBody();
38776479Swpaul            } else {
38876479Swpaul                symbolBlock = lc.getFunctionBody(function);
38999497Salfred            }
390192294Syongari
39176479Swpaul            // Create and add to appropriate block.
392192297Syongari            symbol = createSymbol(name, flags);
39376479Swpaul            symbolBlock.putSymbol(lc, symbol);
39476479Swpaul
39576479Swpaul            if ((flags & IS_SCOPE) == 0) {
39676479Swpaul                // Initial assumption; symbol can lose its slot later
397192298Syongari                symbol.setNeedsSlot(true);
39876479Swpaul            }
399192298Syongari        } else if (symbol.less(flags)) {
40076479Swpaul            symbol.setFlags(flags);
401192298Syongari        }
40276479Swpaul
40376479Swpaul        return symbol;
40476479Swpaul    }
40576479Swpaul
40676479Swpaul    private <T extends Node> T end(final T node) {
40776479Swpaul        return end(node, true);
40876479Swpaul    }
40976479Swpaul
41076479Swpaul    private <T extends Node> T end(final T node, final boolean printNode) {
41176479Swpaul        if (debug) {
41299497Salfred            final StringBuilder sb = new StringBuilder();
413192288Syongari
41476479Swpaul            sb.append("[LEAVE ").
415192297Syongari                append(name(node)).
41676479Swpaul                append("] ").
41776479Swpaul                append(printNode ? node.toString() : "").
41876479Swpaul                append(" in '").
41976479Swpaul                append(lc.getCurrentFunction().getName()).
42076479Swpaul                append('\'');
42176479Swpaul
42276479Swpaul            if (node instanceof IdentNode) {
42376479Swpaul                final Symbol symbol = ((IdentNode)node).getSymbol();
424192290Syongari                if (symbol == null) {
42576479Swpaul                    sb.append(" <NO SYMBOL>");
42676479Swpaul                } else {
42776479Swpaul                    sb.append(" <symbol=").append(symbol).append('>');
42876479Swpaul                }
42976479Swpaul            }
43076479Swpaul
43176479Swpaul            log.unindent();
43276479Swpaul            log.info(sb);
43376479Swpaul        }
43476479Swpaul
43576479Swpaul        return node;
43676479Swpaul    }
43776479Swpaul
43876479Swpaul    @Override
43976479Swpaul    public boolean enterBlock(final Block block) {
44076479Swpaul        start(block);
44176479Swpaul
44276479Swpaul        if (lc.isFunctionBody()) {
44376479Swpaul            block.clearSymbols();
44476479Swpaul            final FunctionNode fn = lc.getCurrentFunction();
44576479Swpaul            if (isUnparsedFunction(fn)) {
44676479Swpaul                // It's a skipped nested function. Just mark the symbols being used by it as being in use.
44776479Swpaul                for(final String name: compiler.getScriptFunctionData(fn.getId()).getExternalSymbolNames()) {
44876479Swpaul                    nameIsUsed(name, null);
44976479Swpaul                }
45076479Swpaul                // Don't bother descending into it, it must be empty anyway.
45176479Swpaul                assert block.getStatements().isEmpty();
45276479Swpaul                return false;
453109058Smbr            }
45476479Swpaul
45576479Swpaul            enterFunctionBody();
45676479Swpaul        }
45776479Swpaul
45876479Swpaul        return true;
45976479Swpaul    }
46076479Swpaul
46176479Swpaul    private boolean isUnparsedFunction(final FunctionNode fn) {
462192292Syongari        return compiler.isOnDemandCompilation() && fn != lc.getOutermostFunction();
46376479Swpaul    }
46476479Swpaul
46576479Swpaul    @Override
46676479Swpaul    public boolean enterCatchNode(final CatchNode catchNode) {
46776479Swpaul        final IdentNode exception = catchNode.getException();
46876479Swpaul        final Block     block     = lc.getCurrentBlock();
46976479Swpaul
47076479Swpaul        start(catchNode);
47176479Swpaul
47276479Swpaul        // define block-local exception variable
47376479Swpaul        final String exname = exception.getName();
47476479Swpaul        // If the name of the exception starts with ":e", this is a synthetic catch block, likely a catch-all. Its
47576479Swpaul        // symbol is naturally internal, and should be treated as such.
47676479Swpaul        final boolean isInternal = exname.startsWith(EXCEPTION_PREFIX.symbolName());
47776479Swpaul        // IS_LET flag is required to make sure symbol is not visible outside catch block. However, we need to
47876479Swpaul        // clear the IS_LET flag after creation to allow redefinition of symbol inside the catch block.
47976479Swpaul        final Symbol symbol = defineSymbol(block, exname, catchNode, IS_VAR | IS_LET | (isInternal ? IS_INTERNAL : 0) | HAS_OBJECT_VALUE);
48076479Swpaul        symbol.clearFlag(IS_LET);
48176479Swpaul
48276479Swpaul        return true;
48376479Swpaul    }
48476479Swpaul
48576479Swpaul    private void enterFunctionBody() {
48676479Swpaul        final FunctionNode functionNode = lc.getCurrentFunction();
48776479Swpaul        final Block body = lc.getCurrentBlock();
48876479Swpaul
48976479Swpaul        initFunctionWideVariables(functionNode, body);
49076479Swpaul        acceptDeclarations(functionNode, body);
491192292Syongari        defineFunctionSelfSymbol(functionNode, body);
492192292Syongari    }
49376479Swpaul
49476479Swpaul    private void defineFunctionSelfSymbol(final FunctionNode functionNode, final Block body) {
49576479Swpaul        // Function self-symbol is only declared as a local variable for named function expressions. Declared functions
49676479Swpaul        // don't need it as they are local variables in their declaring scope.
49776479Swpaul        if (!functionNode.isNamedFunctionExpression()) {
49899497Salfred            return;
499192288Syongari        }
50076479Swpaul
50176479Swpaul        final String name = functionNode.getIdent().getName();
50276479Swpaul        assert name != null; // As it's a named function expression.
50376479Swpaul
50476479Swpaul        if (body.getExistingSymbol(name) != null) {
50576479Swpaul            // Body already has a declaration for the name. It's either a parameter "function x(x)" or a
50676479Swpaul            // top-level variable "function x() { ... var x; ... }".
50776479Swpaul            return;
50876479Swpaul        }
509192290Syongari
51076479Swpaul        defineSymbol(body, name, functionNode, IS_VAR | IS_FUNCTION_SELF | HAS_OBJECT_VALUE);
51176479Swpaul        if(functionNode.allVarsInScope()) { // basically, has deep eval
51276479Swpaul            // We must conservatively presume that eval'd code can dynamically use the function symbol.
51376479Swpaul            lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
51476479Swpaul        }
51576479Swpaul    }
51676479Swpaul
51776479Swpaul    @Override
51876479Swpaul    public boolean enterFunctionNode(final FunctionNode functionNode) {
51976479Swpaul        start(functionNode, false);
52076479Swpaul
52176479Swpaul        thisProperties.push(new HashSet<String>());
52276479Swpaul
52376479Swpaul        // Every function has a body, even the ones skipped on reparse (they have an empty one). We're
52476479Swpaul        // asserting this as even for those, enterBlock() must be invoked to correctly process symbols that
52576479Swpaul        // are used in them.
52676479Swpaul        assert functionNode.getBody() != null;
52776479Swpaul
52876479Swpaul        return true;
52976479Swpaul    }
53076479Swpaul
53176479Swpaul    @Override
53276479Swpaul    public boolean enterVarNode(final VarNode varNode) {
53376479Swpaul        start(varNode);
53476479Swpaul        // Normally, a symbol assigned in a var statement is not live for its RHS. Since we also represent function
535192292Syongari        // declarations as VarNodes, they are exception to the rule, as they need to have the symbol visible to the
53676479Swpaul        // body of the declared function for self-reference.
53776479Swpaul        if (varNode.isFunctionDeclaration()) {
53899497Salfred            defineVarIdent(varNode);
539192288Syongari        }
54076479Swpaul        return true;
541192297Syongari    }
542192297Syongari
543192506Syongari    @Override
54476479Swpaul    public Node leaveVarNode(final VarNode varNode) {
54576479Swpaul        if (!varNode.isFunctionDeclaration()) {
546192506Syongari            defineVarIdent(varNode);
547192506Syongari        }
548192506Syongari        return super.leaveVarNode(varNode);
549192506Syongari    }
550192506Syongari
551192506Syongari    private void defineVarIdent(final VarNode varNode) {
552192506Syongari        final IdentNode ident = varNode.getName();
553192506Syongari        final int flags;
554192506Syongari        if (!varNode.isBlockScoped() && lc.getCurrentFunction().isProgram()) {
555192506Syongari            flags = IS_SCOPE;
556192506Syongari        } else {
557192506Syongari            flags = 0;
558192506Syongari        }
559192506Syongari        defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | flags);
560192506Syongari    }
561192506Syongari
562192506Syongari    private Symbol exceptionSymbol() {
563192506Syongari        return newObjectInternal(EXCEPTION_PREFIX);
564192506Syongari    }
565192506Syongari
566192506Syongari    /**
567192506Syongari     * This has to run before fix assignment types, store any type specializations for
568192506Syongari     * parameters, then turn them into objects for the generic version of this method.
569192506Syongari     *
570192506Syongari     * @param functionNode functionNode
571192506Syongari     */
572192506Syongari    private FunctionNode finalizeParameters(final FunctionNode functionNode) {
573192506Syongari        final List<IdentNode> newParams = new ArrayList<>();
574192506Syongari        final boolean isVarArg = functionNode.isVarArg();
575192506Syongari
576192506Syongari        final Block body = functionNode.getBody();
577192506Syongari        for (final IdentNode param : functionNode.getParameters()) {
578192506Syongari            final Symbol paramSymbol = body.getExistingSymbol(param.getName());
579192506Syongari            assert paramSymbol != null;
580192506Syongari            assert paramSymbol.isParam() : paramSymbol + " " + paramSymbol.getFlags();
581192506Syongari            newParams.add(param.setSymbol(paramSymbol));
582192506Syongari
583192506Syongari            // parameters should not be slots for a function that uses variable arity signature
584192506Syongari            if (isVarArg) {
58576479Swpaul                paramSymbol.setNeedsSlot(false);
58676479Swpaul            }
58776479Swpaul        }
58876479Swpaul
58976479Swpaul        return functionNode.setParameters(lc, newParams);
59076479Swpaul    }
59176479Swpaul
592192292Syongari    /**
59376479Swpaul     * Search for symbol in the lexical context starting from the given block.
59476479Swpaul     * @param name Symbol name.
59599497Salfred     * @return Found symbol or null if not found.
596192288Syongari     */
59776479Swpaul    private Symbol findSymbol(final Block block, final String name) {
598192297Syongari        for (final Iterator<Block> blocks = lc.getBlocks(block); blocks.hasNext();) {
599192297Syongari            final Symbol symbol = blocks.next().getExistingSymbol(name);
60076479Swpaul            if (symbol != null) {
60176479Swpaul                return symbol;
602192506Syongari            }
603192506Syongari        }
604192506Syongari        return null;
605192506Syongari    }
606192506Syongari
607192506Syongari    /**
608192506Syongari     * Marks the current function as one using any global symbol. The function and all its parent functions will all be
609192506Syongari     * marked as needing parent scope.
610192506Syongari     * @see FunctionNode#needsParentScope()
611192506Syongari     */
612192506Syongari    private void functionUsesGlobalSymbol() {
613192506Syongari        for (final Iterator<FunctionNode> fns = lc.getFunctions(); fns.hasNext();) {
614192506Syongari            lc.setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
615192506Syongari        }
616192506Syongari    }
617192506Syongari
618192506Syongari    /**
619192506Syongari     * Marks the current function as one using a scoped symbol. The block defining the symbol will be marked as needing
620192506Syongari     * its own scope to hold the variable. If the symbol is defined outside of the current function, it and all
621192506Syongari     * functions up to (but not including) the function containing the defining block will be marked as needing parent
622192506Syongari     * function scope.
623192506Syongari     * @see FunctionNode#needsParentScope()
624192506Syongari     */
625192506Syongari    private void functionUsesScopeSymbol(final Symbol symbol) {
626192506Syongari        final String name = symbol.getName();
627192506Syongari        for (final Iterator<LexicalContextNode> contextNodeIter = lc.getAllNodes(); contextNodeIter.hasNext(); ) {
628192506Syongari            final LexicalContextNode node = contextNodeIter.next();
629192506Syongari            if (node instanceof Block) {
630192506Syongari                final Block block = (Block)node;
631192506Syongari                if (block.getExistingSymbol(name) != null) {
632192506Syongari                    assert lc.contains(block);
633192506Syongari                    lc.setBlockNeedsScope(block);
634192506Syongari                    break;
63576479Swpaul                }
63676479Swpaul            } else if (node instanceof FunctionNode) {
63776479Swpaul                lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE);
63876479Swpaul            }
63976479Swpaul        }
64076479Swpaul    }
64176479Swpaul
64276479Swpaul    /**
643192292Syongari     * Declares that the current function is using the symbol.
64476479Swpaul     * @param symbol the symbol used by the current function.
64576479Swpaul     */
646192506Syongari    private void functionUsesSymbol(final Symbol symbol) {
647192506Syongari        assert symbol != null;
648192506Syongari        if (symbol.isScope()) {
64999497Salfred            if (symbol.isGlobal()) {
650192288Syongari                functionUsesGlobalSymbol();
65176479Swpaul            } else {
652192297Syongari                functionUsesScopeSymbol(symbol);
653192297Syongari            }
654192506Syongari        } else {
655192506Syongari            assert !symbol.isGlobal(); // Every global is also scope
656192506Syongari        }
657192506Syongari    }
65876479Swpaul
65976479Swpaul    private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) {
660192506Syongari        defineSymbol(block, cc.symbolName(), null, flags).setNeedsSlot(true);
66176479Swpaul    }
662192506Syongari
663192506Syongari    private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
664192506Syongari        initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
665192506Syongari        initCompileConstant(THIS, body, IS_PARAM | IS_THIS | HAS_OBJECT_VALUE);
666192506Syongari
667192506Syongari        if (functionNode.isVarArg()) {
668192506Syongari            initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
669192506Syongari            if (functionNode.needsArguments()) {
670192506Syongari                initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE);
671192506Syongari                defineSymbol(body, ARGUMENTS_VAR.symbolName(), null, IS_VAR | HAS_OBJECT_VALUE);
672192506Syongari            }
673192506Syongari        }
674192506Syongari
675192506Syongari        initParameters(functionNode, body);
676192506Syongari        initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE);
677192506Syongari        initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL);
678192506Syongari    }
679192506Syongari
680192506Syongari    /**
681192506Syongari     * Initialize parameters for function node.
682101540Sambrisko     * @param functionNode the function node
683192506Syongari     */
68476479Swpaul    private void initParameters(final FunctionNode functionNode, final Block body) {
685192506Syongari        final boolean isVarArg = functionNode.isVarArg();
686192506Syongari        final boolean scopeParams = functionNode.allVarsInScope() || isVarArg;
687192506Syongari        for (final IdentNode param : functionNode.getParameters()) {
688192506Syongari            final Symbol symbol = defineSymbol(body, param.getName(), param, IS_PARAM);
689192506Syongari            if(scopeParams) {
690192506Syongari                // NOTE: this "set is scope" is a poor substitute for clear expression of where the symbol is stored.
691192506Syongari                // It will force creation of scopes where they would otherwise not necessarily be needed (functions
692192506Syongari                // using arguments object and other variable arity functions). Tracked by JDK-8038942.
693192506Syongari                symbol.setIsScope();
694192506Syongari                assert symbol.hasSlot();
695192506Syongari                if(isVarArg) {
696192506Syongari                    symbol.setNeedsSlot(false);
697192506Syongari                }
698192506Syongari            }
699192506Syongari        }
700192506Syongari    }
701192506Syongari
702192506Syongari    /**
703192506Syongari     * Is the symbol local to (that is, defined in) the specified function?
704192506Syongari     * @param function the function
705192506Syongari     * @param symbol the symbol
706192506Syongari     * @return true if the symbol is defined in the specified function
707192506Syongari     */
708192506Syongari    private boolean isLocal(final FunctionNode function, final Symbol symbol) {
709192506Syongari        final FunctionNode definingFn = lc.getDefiningFunction(symbol);
710192506Syongari        assert definingFn != null;
711192506Syongari        return definingFn == function;
712192506Syongari    }
713192506Syongari
714101540Sambrisko    @Override
715192506Syongari    public Node leaveBinaryNode(final BinaryNode binaryNode) {
716192506Syongari        if (binaryNode.isTokenType(TokenType.ASSIGN)) {
717192506Syongari            return leaveASSIGN(binaryNode);
718192506Syongari        }
719192506Syongari        return super.leaveBinaryNode(binaryNode);
720192506Syongari    }
721192506Syongari
722101540Sambrisko    private Node leaveASSIGN(final BinaryNode binaryNode) {
723101540Sambrisko        // If we're assigning a property of the this object ("this.foo = ..."), record it.
724192506Syongari        final Expression lhs = binaryNode.lhs();
725101540Sambrisko        if (lhs instanceof AccessNode) {
726192506Syongari            final AccessNode accessNode = (AccessNode) lhs;
727101540Sambrisko            final Expression base = accessNode.getBase();
728101540Sambrisko            if (base instanceof IdentNode) {
729192506Syongari                final Symbol symbol = ((IdentNode)base).getSymbol();
730192506Syongari                if(symbol.isThis()) {
731192506Syongari                    thisProperties.peek().add(accessNode.getProperty());
732192506Syongari                }
733192506Syongari            }
734192506Syongari        }
735192506Syongari        return binaryNode;
736192506Syongari    }
737192506Syongari
738192506Syongari    @Override
739192506Syongari    public Node leaveUnaryNode(final UnaryNode unaryNode) {
740101540Sambrisko        switch (unaryNode.tokenType()) {
741192506Syongari        case DELETE:
742192506Syongari            return leaveDELETE(unaryNode);
743192506Syongari        case TYPEOF:
744192506Syongari            return leaveTYPEOF(unaryNode);
745192506Syongari        default:
746192506Syongari            return super.leaveUnaryNode(unaryNode);
747192506Syongari        }
748192506Syongari    }
749192506Syongari
750192506Syongari    @Override
751192506Syongari    public Node leaveBlock(final Block block) {
752192506Syongari        // It's not necessary to guard the marking of symbols as locals with this "if" condition for
753192506Syongari        // correctness, it's just an optimization -- runtime type calculation is not used when the compilation
754192506Syongari        // is not an on-demand optimistic compilation, so we can skip locals marking then.
755192506Syongari        if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) {
756192506Syongari            // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand
757192506Syongari            // compilation, and we're skipping parsing the function bodies for nested functions, this
758192506Syongari            // basically only means their parameters. It'd be enough to mistakenly declare to be a local a
759192506Syongari            // symbol in the outer function named the same as one of the parameters, though.
760192506Syongari            if (lc.getFunction(block) == lc.getOutermostFunction()) {
761192506Syongari                for (final Symbol symbol: block.getSymbols()) {
762192506Syongari                    if (!symbol.isScope()) {
763192506Syongari                        assert symbol.isVar() || symbol.isParam();
764192506Syongari                        compiler.declareLocalSymbol(symbol.getName());
765192506Syongari                    }
766192506Syongari                }
767192506Syongari            }
768192506Syongari        }
769192506Syongari        return block;
770192506Syongari    }
771192506Syongari
772192506Syongari    private Node leaveDELETE(final UnaryNode unaryNode) {
773192506Syongari        final FunctionNode currentFunctionNode = lc.getCurrentFunction();
774192506Syongari        final boolean      strictMode          = currentFunctionNode.isStrict();
775192506Syongari        final Expression   rhs                 = unaryNode.getExpression();
776192506Syongari        final Expression   strictFlagNode      = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this);
777192506Syongari
778192506Syongari        Request request = Request.DELETE;
779192506Syongari        final List<Expression> args = new ArrayList<>();
780192506Syongari
781192506Syongari        if (rhs instanceof IdentNode) {
782192506Syongari            final IdentNode ident = (IdentNode)rhs;
783192506Syongari            // If this is a declared variable or a function parameter, delete always fails (except for globals).
784192506Syongari            final String name = ident.getName();
785192506Syongari            final Symbol symbol = ident.getSymbol();
786192506Syongari            final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel())));
787192506Syongari
788192506Syongari            if (failDelete && symbol.isThis()) {
789192506Syongari                return LiteralNode.newInstance(unaryNode, true).accept(this);
790192506Syongari            }
791192506Syongari            final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this);
792192506Syongari
79379424Swpaul            if (!failDelete) {
794192506Syongari                args.add(compilerConstantIdentifier(SCOPE));
795192506Syongari            }
796192506Syongari            args.add(literalNode);
797192506Syongari            args.add(strictFlagNode);
798192506Syongari
79976479Swpaul            if (failDelete) {
80076479Swpaul                request = Request.FAIL_DELETE;
80199497Salfred            }
802192506Syongari        } else if (rhs instanceof AccessNode) {
80376479Swpaul            final Expression base     = ((AccessNode)rhs).getBase();
804192297Syongari            final String     property = ((AccessNode)rhs).getProperty();
805192297Syongari
806192506Syongari            args.add(base);
807192297Syongari            args.add((Expression)LiteralNode.newInstance(unaryNode, property).accept(this));
80876479Swpaul            args.add(strictFlagNode);
809135250Swpaul
810147256Sbrooks        } else if (rhs instanceof IndexNode) {
81176479Swpaul            final IndexNode indexNode = (IndexNode)rhs;
812192506Syongari            final Expression base  = indexNode.getBase();
813192506Syongari            final Expression index = indexNode.getIndex();
814192506Syongari
815192506Syongari            args.add(base);
816192506Syongari            args.add(index);
817192506Syongari            args.add(strictFlagNode);
818192506Syongari
819192506Syongari        } else {
820192506Syongari            return LiteralNode.newInstance(unaryNode, true).accept(this);
821192506Syongari        }
822192506Syongari        return new RuntimeNode(unaryNode, request, args).accept(this);
823192506Syongari    }
824192506Syongari
825192506Syongari    @Override
826192506Syongari    public Node leaveForNode(final ForNode forNode) {
827192506Syongari        if (forNode.isForIn()) {
828192506Syongari            forNode.setIterator(newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73
829192506Syongari        }
830192506Syongari
831192506Syongari        return end(forNode);
832192506Syongari    }
833192506Syongari
834192506Syongari    @Override
835192506Syongari    public Node leaveFunctionNode(final FunctionNode functionNode) {
836192506Syongari        final FunctionNode finalizedFunction;
837192506Syongari        if (isUnparsedFunction(functionNode)) {
838192506Syongari            finalizedFunction = functionNode;
839192506Syongari        } else {
840192506Syongari            finalizedFunction =
841192506Syongari               markProgramBlock(
842192506Syongari               removeUnusedSlots(
843192506Syongari               createSyntheticInitializers(
844192506Syongari               finalizeParameters(
845192506Syongari                       lc.applyTopFlags(functionNode))))
84676479Swpaul                       .setThisProperties(lc, thisProperties.pop().size()));
84776479Swpaul        }
84876479Swpaul        return finalizedFunction.setState(lc, CompilationState.SYMBOLS_ASSIGNED);
84976479Swpaul    }
85076479Swpaul
85176479Swpaul    @Override
852192506Syongari    public Node leaveIdentNode(final IdentNode identNode) {
85376479Swpaul        if (identNode.isPropertyName()) {
85476479Swpaul            return identNode;
85576479Swpaul        }
85676479Swpaul
85776479Swpaul        final Symbol symbol = nameIsUsed(identNode.getName(), identNode);
85876479Swpaul
85976479Swpaul        if (!identNode.isInitializedHere()) {
86076479Swpaul            symbol.increaseUseCount();
86176479Swpaul        }
86276479Swpaul
86376479Swpaul        IdentNode newIdentNode = identNode.setSymbol(symbol);
86476479Swpaul
86576479Swpaul        // If a block-scoped var is used before its declaration mark it as dead.
866195049Srwatson        // We can only statically detect this for local vars, cross-function symbols require runtime checks.
86776479Swpaul        if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) {
86876479Swpaul            newIdentNode = newIdentNode.markDead();
86976479Swpaul        }
870130270Snaddy
871130270Snaddy        return end(newIdentNode);
87276479Swpaul    }
87376479Swpaul
87476479Swpaul    private Symbol nameIsUsed(final String name, final IdentNode origin) {
87576479Swpaul        final Block block = lc.getCurrentBlock();
87676479Swpaul
87776479Swpaul        Symbol symbol = findSymbol(block, name);
878195049Srwatson
87976479Swpaul        //If an existing symbol with the name is found, use that otherwise, declare a new one
880192506Syongari        if (symbol != null) {
881192506Syongari            log.info("Existing symbol = ", symbol);
882192506Syongari            if (symbol.isFunctionSelf()) {
883192506Syongari                final FunctionNode functionNode = lc.getDefiningFunction(symbol);
884192506Syongari                assert functionNode != null;
885192506Syongari                assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
88676479Swpaul                lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
88776479Swpaul            }
88899497Salfred
889192288Syongari            // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already)
89076479Swpaul            maybeForceScope(symbol);
891192506Syongari        } else {
892192297Syongari            log.info("No symbol exists. Declare as global: ", name);
89376479Swpaul            symbol = defineSymbol(block, name, origin, IS_GLOBAL | IS_SCOPE);
89476479Swpaul        }
89576479Swpaul
89676479Swpaul        functionUsesSymbol(symbol);
89776479Swpaul        return symbol;
89876479Swpaul    }
899192506Syongari
90076479Swpaul    @Override
90176479Swpaul    public Node leaveSwitchNode(final SwitchNode switchNode) {
90276479Swpaul        // We only need a symbol for the tag if it's not an integer switch node
903162321Sglebius        if(!switchNode.isUniqueInteger()) {
90476479Swpaul            switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX));
90576479Swpaul        }
90676479Swpaul        return switchNode;
90776479Swpaul    }
90876479Swpaul
90976479Swpaul    @Override
91076479Swpaul    public Node leaveTryNode(final TryNode tryNode) {
91176479Swpaul        tryNode.setException(exceptionSymbol());
91276479Swpaul        assert tryNode.getFinallyBody() == null;
91376479Swpaul
914192506Syongari        end(tryNode);
915192506Syongari
916192506Syongari        return tryNode;
917192506Syongari    }
918192506Syongari
919192506Syongari    private Node leaveTYPEOF(final UnaryNode unaryNode) {
920192506Syongari        final Expression rhs = unaryNode.getExpression();
921192506Syongari
922192506Syongari        final List<Expression> args = new ArrayList<>();
923192506Syongari        if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) {
924192506Syongari            args.add(compilerConstantIdentifier(SCOPE));
925192506Syongari            args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null
92676479Swpaul        } else {
92776479Swpaul            args.add(rhs);
92876479Swpaul            args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
929108470Sschweikh        }
93076479Swpaul
93176479Swpaul        final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args).accept(this);
93299497Salfred
933192288Syongari        end(unaryNode);
93476479Swpaul
935192297Syongari        return runtimeNode;
93676479Swpaul    }
93776479Swpaul
93876479Swpaul    private FunctionNode markProgramBlock(final FunctionNode functionNode) {
939192292Syongari        if (compiler.isOnDemandCompilation() || !functionNode.isProgram()) {
94076479Swpaul            return functionNode;
94176479Swpaul        }
94276479Swpaul
943192292Syongari        return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE));
94476479Swpaul    }
94576479Swpaul
94676479Swpaul    /**
94776479Swpaul     * If the symbol isn't already a scope symbol, but it needs to be (see {@link #symbolNeedsToBeScope(Symbol)}, it is
948192292Syongari     * promoted to a scope symbol and its block marked as needing a scope.
94976479Swpaul     * @param symbol the symbol that might be scoped
95076479Swpaul     */
95176479Swpaul    private void maybeForceScope(final Symbol symbol) {
95276479Swpaul        if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
95376479Swpaul            Symbol.setSymbolIsScope(lc, symbol);
95476479Swpaul        }
95599497Salfred    }
956192288Syongari
95776479Swpaul    private Symbol newInternal(final CompilerConstants cc, final int flags) {
958192506Syongari        return defineSymbol(lc.getCurrentBlock(), lc.getCurrentFunction().uniqueName(cc.symbolName()), null, IS_VAR | IS_INTERNAL | flags); //NASHORN-73
959192506Syongari    }
960192297Syongari
961192506Syongari    private Symbol newObjectInternal(final CompilerConstants cc) {
962192506Syongari        return newInternal(cc, HAS_OBJECT_VALUE);
96376479Swpaul    }
964192506Syongari
96576479Swpaul    private boolean start(final Node node) {
966162321Sglebius        return start(node, true);
96776479Swpaul    }
968135250Swpaul
969151296Sjhb    private boolean start(final Node node, final boolean printNode) {
970151296Sjhb        if (debug) {
97176479Swpaul            final StringBuilder sb = new StringBuilder();
97276479Swpaul
97376479Swpaul            sb.append("[ENTER ").
97476479Swpaul                append(name(node)).
97576479Swpaul                append("] ").
976192506Syongari                append(printNode ? node.toString() : "").
977192506Syongari                append(" in '").
978192506Syongari                append(lc.getCurrentFunction().getName()).
979192506Syongari                append("'");
980192506Syongari            log.info(sb);
981192506Syongari            log.indent();
982192506Syongari        }
983192506Syongari
984192506Syongari        return true;
98576479Swpaul    }
98676479Swpaul
987192506Syongari    /**
988192506Syongari     * Determines if the symbol has to be a scope symbol. In general terms, it has to be a scope symbol if it can only
989192506Syongari     * be reached from the current block by traversing a function node, a split node, or a with node.
990192506Syongari     * @param symbol the symbol checked for needing to be a scope symbol
991192506Syongari     * @return true if the symbol has to be a scope symbol.
992192506Syongari     */
993192506Syongari    private boolean symbolNeedsToBeScope(final Symbol symbol) {
994192506Syongari        if (symbol.isThis() || symbol.isInternal()) {
995192506Syongari            return false;
996192506Syongari        }
997192506Syongari
998192506Syongari        final FunctionNode func = lc.getCurrentFunction();
999192506Syongari        if ( func.allVarsInScope() || (!symbol.isBlockScoped() && func.isProgram())) {
1000192506Syongari            return true;
1001192506Syongari        }
1002192506Syongari
100376479Swpaul        boolean previousWasBlock = false;
100476479Swpaul        for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
100576479Swpaul            final LexicalContextNode node = it.next();
100676479Swpaul            if (node instanceof FunctionNode || isSplitArray(node)) {
1007127135Snjl                // We reached the function boundary or a splitting boundary without seeing a definition for the symbol.
100876479Swpaul                // It needs to be in scope.
100976479Swpaul                return true;
101076479Swpaul            } else if (node instanceof WithNode) {
1011151296Sjhb                if (previousWasBlock) {
101276479Swpaul                    // We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately
101376479Swpaul                    // preceded by a block, this means we're currently processing its expression, not its body,
101476479Swpaul                    // therefore it doesn't count.
101576479Swpaul                    return true;
1016192506Syongari                }
1017192506Syongari                previousWasBlock = false;
1018192506Syongari            } else if (node instanceof Block) {
1019192506Syongari                if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) {
1020192506Syongari                    // We reached the block that defines the symbol without reaching either the function boundary, or a
102176479Swpaul                    // WithNode. The symbol need not be scoped.
102276479Swpaul                    return false;
102376479Swpaul                }
102476479Swpaul                previousWasBlock = true;
102576479Swpaul            } else {
102676479Swpaul                previousWasBlock = false;
1027192506Syongari            }
1028192506Syongari        }
1029192506Syongari        throw new AssertionError();
1030192506Syongari    }
1031192506Syongari
1032192506Syongari    private static boolean isSplitArray(final LexicalContextNode expr) {
1033192506Syongari        if(!(expr instanceof ArrayLiteralNode)) {
103476479Swpaul            return false;
1035192506Syongari        }
103676479Swpaul        final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits();
103776479Swpaul        return !(units == null || units.isEmpty());
103876479Swpaul    }
103976479Swpaul
1040192506Syongari    private void throwUnprotectedSwitchError(final VarNode varNode) {
1041192506Syongari        // Block scoped declarations in switch statements without explicit blocks should be declared
1042147256Sbrooks        // in a common block that contains all the case clauses. We cannot support this without a
1043147256Sbrooks        // fundamental rewrite of how switch statements are handled (case nodes contain blocks and are
1044192506Syongari        // directly contained by switch node). As a temporary solution we throw a reference error here.
1045147256Sbrooks        final String msg = ECMAErrors.getMessage("syntax.error.unprotected.switch.declaration", varNode.isLet() ? "let" : "const");
1046147256Sbrooks        throwParserException(msg, varNode);
1047147256Sbrooks    }
104876479Swpaul
1049121816Sbrooks    private void throwParserException(final String message, final Node origin) {
1050135250Swpaul        if (origin == null) {
105176479Swpaul            throw new ParserException(message);
105276479Swpaul        }
105376479Swpaul        final Source source = compiler.getSource();
1054192506Syongari        final long token = origin.getToken();
1055192506Syongari        final int line = source.getLine(origin.getStart());
1056192506Syongari        final int column = source.getColumn(origin.getStart());
105776479Swpaul        final String formatted = ErrorManager.format(message, source, line, column, token);
1058192506Syongari        throw new ParserException(JSErrorType.SYNTAX_ERROR, formatted, source, line, column, token);
1059192506Syongari    }
1060192506Syongari}
1061192506Syongari