AssignSymbols.java revision 1444:f436923c391f
1/*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.nashorn.internal.codegen;
27
28import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
29import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
30import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
31import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
32import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
33import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
34import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
35import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
36import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
37import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
38import static jdk.nashorn.internal.ir.Symbol.HAS_OBJECT_VALUE;
39import static jdk.nashorn.internal.ir.Symbol.IS_CONST;
40import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF;
41import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
42import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
43import static jdk.nashorn.internal.ir.Symbol.IS_LET;
44import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
45import static jdk.nashorn.internal.ir.Symbol.IS_PROGRAM_LEVEL;
46import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
47import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
48import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
49import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
50
51import java.util.ArrayDeque;
52import java.util.ArrayList;
53import java.util.Deque;
54import java.util.HashMap;
55import java.util.HashSet;
56import java.util.Iterator;
57import java.util.List;
58import java.util.ListIterator;
59import java.util.Map;
60import java.util.Set;
61import jdk.nashorn.internal.ir.AccessNode;
62import jdk.nashorn.internal.ir.BinaryNode;
63import jdk.nashorn.internal.ir.Block;
64import jdk.nashorn.internal.ir.CatchNode;
65import jdk.nashorn.internal.ir.Expression;
66import jdk.nashorn.internal.ir.ForNode;
67import jdk.nashorn.internal.ir.FunctionNode;
68import jdk.nashorn.internal.ir.IdentNode;
69import jdk.nashorn.internal.ir.IndexNode;
70import jdk.nashorn.internal.ir.LexicalContextNode;
71import jdk.nashorn.internal.ir.LiteralNode;
72import jdk.nashorn.internal.ir.Node;
73import jdk.nashorn.internal.ir.RuntimeNode;
74import jdk.nashorn.internal.ir.RuntimeNode.Request;
75import jdk.nashorn.internal.ir.Splittable;
76import jdk.nashorn.internal.ir.Statement;
77import jdk.nashorn.internal.ir.SwitchNode;
78import jdk.nashorn.internal.ir.Symbol;
79import jdk.nashorn.internal.ir.TryNode;
80import jdk.nashorn.internal.ir.UnaryNode;
81import jdk.nashorn.internal.ir.VarNode;
82import jdk.nashorn.internal.ir.WithNode;
83import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor;
84import jdk.nashorn.internal.parser.TokenType;
85import jdk.nashorn.internal.runtime.Context;
86import jdk.nashorn.internal.runtime.ECMAErrors;
87import jdk.nashorn.internal.runtime.ErrorManager;
88import jdk.nashorn.internal.runtime.JSErrorType;
89import jdk.nashorn.internal.runtime.ParserException;
90import jdk.nashorn.internal.runtime.Source;
91import jdk.nashorn.internal.runtime.logging.DebugLogger;
92import jdk.nashorn.internal.runtime.logging.Loggable;
93import jdk.nashorn.internal.runtime.logging.Logger;
94
95/**
96 * This visitor assigns symbols to identifiers denoting variables. It does few more minor calculations that are only
97 * possible after symbols have been assigned; such is the transformation of "delete" and "typeof" operators into runtime
98 * nodes and counting of number of properties assigned to "this" in constructor functions. This visitor is also notable
99 * for what it doesn't do, most significantly it does no type calculations as in JavaScript variables can change types
100 * during runtime and as such symbols don't have types. Calculation of expression types is performed by a separate
101 * visitor.
102 */
103@Logger(name="symbols")
104final class AssignSymbols extends SimpleNodeVisitor implements Loggable {
105    private final DebugLogger log;
106    private final boolean     debug;
107
108    private static boolean isParamOrVar(final IdentNode identNode) {
109        final Symbol symbol = identNode.getSymbol();
110        return symbol.isParam() || symbol.isVar();
111    }
112
113    private static String name(final Node node) {
114        final String cn = node.getClass().getName();
115        final int lastDot = cn.lastIndexOf('.');
116        if (lastDot == -1) {
117            return cn;
118        }
119        return cn.substring(lastDot + 1);
120    }
121
122    /**
123     * Checks if various symbols that were provisionally marked as needing a slot ended up unused, and marks them as not
124     * needing a slot after all.
125     * @param functionNode the function node
126     * @return the passed in node, for easy chaining
127     */
128    private static FunctionNode removeUnusedSlots(final FunctionNode functionNode) {
129        if (!functionNode.needsCallee()) {
130            functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
131        }
132        if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) {
133            functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
134        }
135        // Named function expressions that end up not referencing themselves won't need a local slot for the self symbol.
136        if(functionNode.isNamedFunctionExpression() && !functionNode.usesSelfSymbol()) {
137            final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName());
138            if(selfSymbol != null && selfSymbol.isFunctionSelf()) {
139                selfSymbol.setNeedsSlot(false);
140                selfSymbol.clearFlag(Symbol.IS_VAR);
141            }
142        }
143        return functionNode;
144    }
145
146    private final Deque<Set<String>> thisProperties = new ArrayDeque<>();
147    private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol
148    private final Compiler compiler;
149    private final boolean isOnDemand;
150
151    public AssignSymbols(final Compiler compiler) {
152        this.compiler = compiler;
153        this.log   = initLogger(compiler.getContext());
154        this.debug = log.isEnabled();
155        this.isOnDemand = compiler.isOnDemandCompilation();
156    }
157
158    @Override
159    public DebugLogger getLogger() {
160        return log;
161    }
162
163    @Override
164    public DebugLogger initLogger(final Context context) {
165        return context.getLogger(this.getClass());
166    }
167
168    /**
169     * Define symbols for all variable declarations at the top of the function scope. This way we can get around
170     * problems like
171     *
172     * while (true) {
173     *   break;
174     *   if (true) {
175     *     var s;
176     *   }
177     * }
178     *
179     * to an arbitrary nesting depth.
180     *
181     * see NASHORN-73
182     *
183     * @param functionNode the FunctionNode we are entering
184     * @param body the body of the FunctionNode we are entering
185     */
186    private void acceptDeclarations(final FunctionNode functionNode, final Block body) {
187        // This visitor will assign symbol to all declared variables.
188        body.accept(new SimpleNodeVisitor() {
189            @Override
190            protected boolean enterDefault(final Node node) {
191                // Don't bother visiting expressions; var is a statement, it can't be inside an expression.
192                // This will also prevent visiting nested functions (as FunctionNode is an expression).
193                return !(node instanceof Expression);
194            }
195
196            @Override
197            public Node leaveVarNode(final VarNode varNode) {
198                final IdentNode ident  = varNode.getName();
199                final boolean blockScoped = varNode.isBlockScoped();
200                if (blockScoped && lc.inUnprotectedSwitchContext()) {
201                    throwUnprotectedSwitchError(varNode);
202                }
203                final Block block = blockScoped ? lc.getCurrentBlock() : body;
204                final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags());
205                if (varNode.isFunctionDeclaration()) {
206                    symbol.setIsFunctionDeclaration();
207                }
208                return varNode.setName(ident.setSymbol(symbol));
209            }
210        });
211    }
212
213    private IdentNode compilerConstantIdentifier(final CompilerConstants cc) {
214        return createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc));
215    }
216
217    /**
218     * Creates an ident node for an implicit identifier within the function (one not declared in the script source
219     * code). These identifiers are defined with function's token and finish.
220     * @param name the name of the identifier
221     * @return an ident node representing the implicit identifier.
222     */
223    private IdentNode createImplicitIdentifier(final String name) {
224        final FunctionNode fn = lc.getCurrentFunction();
225        return new IdentNode(fn.getToken(), fn.getFinish(), name);
226    }
227
228    private Symbol createSymbol(final String name, final int flags) {
229        if ((flags & Symbol.KINDMASK) == IS_GLOBAL) {
230            //reuse global symbols so they can be hashed
231            Symbol global = globalSymbols.get(name);
232            if (global == null) {
233                global = new Symbol(name, flags);
234                globalSymbols.put(name, global);
235            }
236            return global;
237        }
238        return new Symbol(name, flags);
239    }
240
241    /**
242     * Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically
243     * used to create assignment of {@code :callee} to the function name symbol in self-referential function
244     * expressions as well as for assignment of {@code :arguments} to {@code arguments}.
245     *
246     * @param name the ident node identifying the variable to initialize
247     * @param initConstant the compiler constant it is initialized to
248     * @param fn the function node the assignment is for
249     * @return a var node with the appropriate assignment
250     */
251    private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) {
252        final IdentNode init = compilerConstantIdentifier(initConstant);
253        assert init.getSymbol() != null && init.getSymbol().isBytecodeLocal();
254
255        final VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init);
256
257        final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName());
258        assert nameSymbol != null;
259
260        return (VarNode)synthVar.setName(name.setSymbol(nameSymbol)).accept(this);
261    }
262
263    private FunctionNode createSyntheticInitializers(final FunctionNode functionNode) {
264        final List<VarNode> syntheticInitializers = new ArrayList<>(2);
265
266        // Must visit the new var nodes in the context of the body. We could also just set the new statements into the
267        // block and then revisit the entire block, but that seems to be too much double work.
268        final Block body = functionNode.getBody();
269        lc.push(body);
270        try {
271            if (functionNode.usesSelfSymbol()) {
272                // "var fn = :callee"
273                syntheticInitializers.add(createSyntheticInitializer(functionNode.getIdent(), CALLEE, functionNode));
274            }
275
276            if (functionNode.needsArguments()) {
277                // "var arguments = :arguments"
278                syntheticInitializers.add(createSyntheticInitializer(createImplicitIdentifier(ARGUMENTS_VAR.symbolName()),
279                        ARGUMENTS, functionNode));
280            }
281
282            if (syntheticInitializers.isEmpty()) {
283                return functionNode;
284            }
285
286            for(final ListIterator<VarNode> it = syntheticInitializers.listIterator(); it.hasNext();) {
287                it.set((VarNode)it.next().accept(this));
288            }
289        } finally {
290            lc.pop(body);
291        }
292
293        final List<Statement> stmts = body.getStatements();
294        final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size());
295        newStatements.addAll(syntheticInitializers);
296        newStatements.addAll(stmts);
297        return functionNode.setBody(lc, body.setStatements(lc, newStatements));
298    }
299
300    /**
301     * Defines a new symbol in the given block.
302     *
303     * @param block        the block in which to define the symbol
304     * @param name         name of symbol.
305     * @param origin       origin node
306     * @param symbolFlags  Symbol flags.
307     *
308     * @return Symbol for given name or null for redefinition.
309     */
310    private Symbol defineSymbol(final Block block, final String name, final Node origin, final int symbolFlags) {
311        int    flags  = symbolFlags;
312        final boolean isBlockScope = (flags & IS_LET) != 0 || (flags & IS_CONST) != 0;
313        final boolean isGlobal     = (flags & KINDMASK) == IS_GLOBAL;
314
315        Symbol symbol;
316        final FunctionNode function;
317        if (isBlockScope) {
318            // block scoped variables always live in current block, no need to look for existing symbols in parent blocks.
319            symbol = block.getExistingSymbol(name);
320            function = lc.getCurrentFunction();
321        } else {
322            symbol = findSymbol(block, name);
323            function = lc.getFunction(block);
324        }
325
326        // Global variables are implicitly always scope variables too.
327        if (isGlobal) {
328            flags |= IS_SCOPE;
329        }
330
331        if (lc.getCurrentFunction().isProgram()) {
332            flags |= IS_PROGRAM_LEVEL;
333        }
334
335        final boolean isParam = (flags & KINDMASK) == IS_PARAM;
336        final boolean isVar =   (flags & KINDMASK) == IS_VAR;
337
338        if (symbol != null) {
339            // Symbol was already defined. Check if it needs to be redefined.
340            if (isParam) {
341                if (!isLocal(function, symbol)) {
342                    // Not defined in this function. Create a new definition.
343                    symbol = null;
344                } else if (symbol.isParam()) {
345                    // Duplicate parameter. Null return will force an error.
346                    throw new AssertionError("duplicate parameter");
347                }
348            } else if (isVar) {
349                if (isBlockScope) {
350                    // Check redeclaration in same block
351                    if (symbol.hasBeenDeclared()) {
352                        throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin);
353                    } else {
354                        symbol.setHasBeenDeclared();
355                        // Set scope flag on top-level block scoped symbols
356                        if (function.isProgram() && function.getBody() == block) {
357                            symbol.setIsScope();
358                        }
359                    }
360                } else if ((flags & IS_INTERNAL) != 0) {
361                    // Always create a new definition.
362                    symbol = null;
363                } else {
364                    // Found LET or CONST in parent scope of same function - s SyntaxError
365                    if (symbol.isBlockScoped() && isLocal(lc.getCurrentFunction(), symbol)) {
366                        throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin);
367                    }
368                    // Not defined in this function. Create a new definition.
369                    if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
370                        symbol = null;
371                    }
372                }
373            }
374        }
375
376        if (symbol == null) {
377            // If not found, then create a new one.
378            final Block symbolBlock;
379
380            // Determine where to create it.
381            if (isVar && ((flags & IS_INTERNAL) != 0 || isBlockScope)) {
382                symbolBlock = block; //internal vars are always defined in the block closest to them
383            } else if (isGlobal) {
384                symbolBlock = lc.getOutermostFunction().getBody();
385            } else {
386                symbolBlock = lc.getFunctionBody(function);
387            }
388
389            // Create and add to appropriate block.
390            symbol = createSymbol(name, flags);
391            symbolBlock.putSymbol(symbol);
392
393            if ((flags & IS_SCOPE) == 0) {
394                // Initial assumption; symbol can lose its slot later
395                symbol.setNeedsSlot(true);
396            }
397        } else if (symbol.less(flags)) {
398            symbol.setFlags(flags);
399        }
400
401        return symbol;
402    }
403
404    private <T extends Node> T end(final T node) {
405        return end(node, true);
406    }
407
408    private <T extends Node> T end(final T node, final boolean printNode) {
409        if (debug) {
410            final StringBuilder sb = new StringBuilder();
411
412            sb.append("[LEAVE ").
413                append(name(node)).
414                append("] ").
415                append(printNode ? node.toString() : "").
416                append(" in '").
417                append(lc.getCurrentFunction().getName()).
418                append('\'');
419
420            if (node instanceof IdentNode) {
421                final Symbol symbol = ((IdentNode)node).getSymbol();
422                if (symbol == null) {
423                    sb.append(" <NO SYMBOL>");
424                } else {
425                    sb.append(" <symbol=").append(symbol).append('>');
426                }
427            }
428
429            log.unindent();
430            log.info(sb);
431        }
432
433        return node;
434    }
435
436    @Override
437    public boolean enterBlock(final Block block) {
438        start(block);
439
440        if (lc.isFunctionBody()) {
441            assert !block.hasSymbols();
442            final FunctionNode fn = lc.getCurrentFunction();
443            if (isUnparsedFunction(fn)) {
444                // It's a skipped nested function. Just mark the symbols being used by it as being in use.
445                for(final String name: compiler.getScriptFunctionData(fn.getId()).getExternalSymbolNames()) {
446                    nameIsUsed(name, null);
447                }
448                // Don't bother descending into it, it must be empty anyway.
449                assert block.getStatements().isEmpty();
450                return false;
451            }
452
453            enterFunctionBody();
454        }
455
456        return true;
457    }
458
459    private boolean isUnparsedFunction(final FunctionNode fn) {
460        return isOnDemand && fn != lc.getOutermostFunction();
461    }
462
463    @Override
464    public boolean enterCatchNode(final CatchNode catchNode) {
465        final IdentNode exception = catchNode.getException();
466        final Block     block     = lc.getCurrentBlock();
467
468        start(catchNode);
469
470        // define block-local exception variable
471        final String exname = exception.getName();
472        // If the name of the exception starts with ":e", this is a synthetic catch block, likely a catch-all. Its
473        // symbol is naturally internal, and should be treated as such.
474        final boolean isInternal = exname.startsWith(EXCEPTION_PREFIX.symbolName());
475        // IS_LET flag is required to make sure symbol is not visible outside catch block. However, we need to
476        // clear the IS_LET flag after creation to allow redefinition of symbol inside the catch block.
477        final Symbol symbol = defineSymbol(block, exname, catchNode, IS_VAR | IS_LET | (isInternal ? IS_INTERNAL : 0) | HAS_OBJECT_VALUE);
478        symbol.clearFlag(IS_LET);
479
480        return true;
481    }
482
483    private void enterFunctionBody() {
484        final FunctionNode functionNode = lc.getCurrentFunction();
485        final Block body = lc.getCurrentBlock();
486
487        initFunctionWideVariables(functionNode, body);
488        acceptDeclarations(functionNode, body);
489        defineFunctionSelfSymbol(functionNode, body);
490    }
491
492    private void defineFunctionSelfSymbol(final FunctionNode functionNode, final Block body) {
493        // Function self-symbol is only declared as a local variable for named function expressions. Declared functions
494        // don't need it as they are local variables in their declaring scope.
495        if (!functionNode.isNamedFunctionExpression()) {
496            return;
497        }
498
499        final String name = functionNode.getIdent().getName();
500        assert name != null; // As it's a named function expression.
501
502        if (body.getExistingSymbol(name) != null) {
503            // Body already has a declaration for the name. It's either a parameter "function x(x)" or a
504            // top-level variable "function x() { ... var x; ... }".
505            return;
506        }
507
508        defineSymbol(body, name, functionNode, IS_VAR | IS_FUNCTION_SELF | HAS_OBJECT_VALUE);
509        if(functionNode.allVarsInScope()) { // basically, has deep eval
510            // We must conservatively presume that eval'd code can dynamically use the function symbol.
511            lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
512        }
513    }
514
515    @Override
516    public boolean enterFunctionNode(final FunctionNode functionNode) {
517        start(functionNode, false);
518
519        thisProperties.push(new HashSet<String>());
520
521        // Every function has a body, even the ones skipped on reparse (they have an empty one). We're
522        // asserting this as even for those, enterBlock() must be invoked to correctly process symbols that
523        // are used in them.
524        assert functionNode.getBody() != null;
525
526        return true;
527    }
528
529    @Override
530    public boolean enterVarNode(final VarNode varNode) {
531        start(varNode);
532        // Normally, a symbol assigned in a var statement is not live for its RHS. Since we also represent function
533        // declarations as VarNodes, they are exception to the rule, as they need to have the symbol visible to the
534        // body of the declared function for self-reference.
535        if (varNode.isFunctionDeclaration()) {
536            defineVarIdent(varNode);
537        }
538        return true;
539    }
540
541    @Override
542    public Node leaveVarNode(final VarNode varNode) {
543        if (!varNode.isFunctionDeclaration()) {
544            defineVarIdent(varNode);
545        }
546        return super.leaveVarNode(varNode);
547    }
548
549    private void defineVarIdent(final VarNode varNode) {
550        final IdentNode ident = varNode.getName();
551        final int flags;
552        if (!varNode.isBlockScoped() && lc.getCurrentFunction().isProgram()) {
553            flags = IS_SCOPE;
554        } else {
555            flags = 0;
556        }
557        defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | flags);
558    }
559
560    private Symbol exceptionSymbol() {
561        return newObjectInternal(EXCEPTION_PREFIX);
562    }
563
564    /**
565     * This has to run before fix assignment types, store any type specializations for
566     * parameters, then turn them into objects for the generic version of this method.
567     *
568     * @param functionNode functionNode
569     */
570    private FunctionNode finalizeParameters(final FunctionNode functionNode) {
571        final List<IdentNode> newParams = new ArrayList<>();
572        final boolean isVarArg = functionNode.isVarArg();
573
574        final Block body = functionNode.getBody();
575        for (final IdentNode param : functionNode.getParameters()) {
576            final Symbol paramSymbol = body.getExistingSymbol(param.getName());
577            assert paramSymbol != null;
578            assert paramSymbol.isParam() : paramSymbol + " " + paramSymbol.getFlags();
579            newParams.add(param.setSymbol(paramSymbol));
580
581            // parameters should not be slots for a function that uses variable arity signature
582            if (isVarArg) {
583                paramSymbol.setNeedsSlot(false);
584            }
585        }
586
587        return functionNode.setParameters(lc, newParams);
588    }
589
590    /**
591     * Search for symbol in the lexical context starting from the given block.
592     * @param name Symbol name.
593     * @return Found symbol or null if not found.
594     */
595    private Symbol findSymbol(final Block block, final String name) {
596        for (final Iterator<Block> blocks = lc.getBlocks(block); blocks.hasNext();) {
597            final Symbol symbol = blocks.next().getExistingSymbol(name);
598            if (symbol != null) {
599                return symbol;
600            }
601        }
602        return null;
603    }
604
605    /**
606     * Marks the current function as one using any global symbol. The function and all its parent functions will all be
607     * marked as needing parent scope.
608     * @see FunctionNode#needsParentScope()
609     */
610    private void functionUsesGlobalSymbol() {
611        for (final Iterator<FunctionNode> fns = lc.getFunctions(); fns.hasNext();) {
612            lc.setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
613        }
614    }
615
616    /**
617     * Marks the current function as one using a scoped symbol. The block defining the symbol will be marked as needing
618     * its own scope to hold the variable. If the symbol is defined outside of the current function, it and all
619     * functions up to (but not including) the function containing the defining block will be marked as needing parent
620     * function scope.
621     * @see FunctionNode#needsParentScope()
622     */
623    private void functionUsesScopeSymbol(final Symbol symbol) {
624        final String name = symbol.getName();
625        for (final Iterator<LexicalContextNode> contextNodeIter = lc.getAllNodes(); contextNodeIter.hasNext(); ) {
626            final LexicalContextNode node = contextNodeIter.next();
627            if (node instanceof Block) {
628                final Block block = (Block)node;
629                if (block.getExistingSymbol(name) != null) {
630                    assert lc.contains(block);
631                    lc.setBlockNeedsScope(block);
632                    break;
633                }
634            } else if (node instanceof FunctionNode) {
635                lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE);
636            }
637        }
638    }
639
640    /**
641     * Declares that the current function is using the symbol.
642     * @param symbol the symbol used by the current function.
643     */
644    private void functionUsesSymbol(final Symbol symbol) {
645        assert symbol != null;
646        if (symbol.isScope()) {
647            if (symbol.isGlobal()) {
648                functionUsesGlobalSymbol();
649            } else {
650                functionUsesScopeSymbol(symbol);
651            }
652        } else {
653            assert !symbol.isGlobal(); // Every global is also scope
654        }
655    }
656
657    private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) {
658        defineSymbol(block, cc.symbolName(), null, flags).setNeedsSlot(true);
659    }
660
661    private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
662        initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
663        initCompileConstant(THIS, body, IS_PARAM | IS_THIS | HAS_OBJECT_VALUE);
664
665        if (functionNode.isVarArg()) {
666            initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
667            if (functionNode.needsArguments()) {
668                initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE);
669                defineSymbol(body, ARGUMENTS_VAR.symbolName(), null, IS_VAR | HAS_OBJECT_VALUE);
670            }
671        }
672
673        initParameters(functionNode, body);
674        initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE);
675        initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL);
676    }
677
678    /**
679     * Initialize parameters for function node.
680     * @param functionNode the function node
681     */
682    private void initParameters(final FunctionNode functionNode, final Block body) {
683        final boolean isVarArg = functionNode.isVarArg();
684        final boolean scopeParams = functionNode.allVarsInScope() || isVarArg;
685        for (final IdentNode param : functionNode.getParameters()) {
686            final Symbol symbol = defineSymbol(body, param.getName(), param, IS_PARAM);
687            if(scopeParams) {
688                // NOTE: this "set is scope" is a poor substitute for clear expression of where the symbol is stored.
689                // It will force creation of scopes where they would otherwise not necessarily be needed (functions
690                // using arguments object and other variable arity functions). Tracked by JDK-8038942.
691                symbol.setIsScope();
692                assert symbol.hasSlot();
693                if(isVarArg) {
694                    symbol.setNeedsSlot(false);
695                }
696            }
697        }
698    }
699
700    /**
701     * Is the symbol local to (that is, defined in) the specified function?
702     * @param function the function
703     * @param symbol the symbol
704     * @return true if the symbol is defined in the specified function
705     */
706    private boolean isLocal(final FunctionNode function, final Symbol symbol) {
707        final FunctionNode definingFn = lc.getDefiningFunction(symbol);
708        assert definingFn != null;
709        return definingFn == function;
710    }
711
712    @Override
713    public Node leaveBinaryNode(final BinaryNode binaryNode) {
714        if (binaryNode.isTokenType(TokenType.ASSIGN)) {
715            return leaveASSIGN(binaryNode);
716        }
717        return super.leaveBinaryNode(binaryNode);
718    }
719
720    private Node leaveASSIGN(final BinaryNode binaryNode) {
721        // If we're assigning a property of the this object ("this.foo = ..."), record it.
722        final Expression lhs = binaryNode.lhs();
723        if (lhs instanceof AccessNode) {
724            final AccessNode accessNode = (AccessNode) lhs;
725            final Expression base = accessNode.getBase();
726            if (base instanceof IdentNode) {
727                final Symbol symbol = ((IdentNode)base).getSymbol();
728                if(symbol.isThis()) {
729                    thisProperties.peek().add(accessNode.getProperty());
730                }
731            }
732        }
733        return binaryNode;
734    }
735
736    @Override
737    public Node leaveUnaryNode(final UnaryNode unaryNode) {
738        switch (unaryNode.tokenType()) {
739        case DELETE:
740            return leaveDELETE(unaryNode);
741        case TYPEOF:
742            return leaveTYPEOF(unaryNode);
743        default:
744            return super.leaveUnaryNode(unaryNode);
745        }
746    }
747
748    private Node leaveDELETE(final UnaryNode unaryNode) {
749        final FunctionNode currentFunctionNode = lc.getCurrentFunction();
750        final boolean      strictMode          = currentFunctionNode.isStrict();
751        final Expression   rhs                 = unaryNode.getExpression();
752        final Expression   strictFlagNode      = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this);
753
754        Request request = Request.DELETE;
755        final List<Expression> args = new ArrayList<>();
756
757        if (rhs instanceof IdentNode) {
758            final IdentNode ident = (IdentNode)rhs;
759            // If this is a declared variable or a function parameter, delete always fails (except for globals).
760            final String name = ident.getName();
761            final Symbol symbol = ident.getSymbol();
762
763            if (symbol.isThis()) {
764                // Can't delete "this", ignore and return true
765                return LiteralNode.newInstance(unaryNode, true);
766            }
767            final Expression literalNode = LiteralNode.newInstance(unaryNode, name);
768            final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel())));
769
770            if (!failDelete) {
771                args.add(compilerConstantIdentifier(SCOPE));
772            }
773            args.add(literalNode);
774            args.add(strictFlagNode);
775
776            if (failDelete) {
777                request = Request.FAIL_DELETE;
778            } else if ((symbol.isGlobal() && !symbol.isFunctionDeclaration()) || symbol.isProgramLevel()) {
779                request = Request.SLOW_DELETE;
780            }
781        } else if (rhs instanceof AccessNode) {
782            final Expression base     = ((AccessNode)rhs).getBase();
783            final String     property = ((AccessNode)rhs).getProperty();
784
785            args.add(base);
786            args.add(LiteralNode.newInstance(unaryNode, property));
787            args.add(strictFlagNode);
788
789        } else if (rhs instanceof IndexNode) {
790            final IndexNode indexNode = (IndexNode)rhs;
791            final Expression base  = indexNode.getBase();
792            final Expression index = indexNode.getIndex();
793
794            args.add(base);
795            args.add(index);
796            args.add(strictFlagNode);
797
798        } else {
799            return LiteralNode.newInstance(unaryNode, true);
800        }
801        return new RuntimeNode(unaryNode, request, args);
802    }
803
804    @Override
805    public Node leaveForNode(final ForNode forNode) {
806        if (forNode.isForIn()) {
807            return forNode.setIterator(lc, newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73
808        }
809
810        return end(forNode);
811    }
812
813    @Override
814    public Node leaveFunctionNode(final FunctionNode functionNode) {
815        final FunctionNode finalizedFunction;
816        if (isUnparsedFunction(functionNode)) {
817            finalizedFunction = functionNode;
818        } else {
819            finalizedFunction =
820               markProgramBlock(
821               removeUnusedSlots(
822               createSyntheticInitializers(
823               finalizeParameters(
824                       lc.applyTopFlags(functionNode))))
825                       .setThisProperties(lc, thisProperties.pop().size()));
826        }
827        return finalizedFunction;
828    }
829
830    @Override
831    public Node leaveIdentNode(final IdentNode identNode) {
832        if (identNode.isPropertyName()) {
833            return identNode;
834        }
835
836        final Symbol symbol = nameIsUsed(identNode.getName(), identNode);
837
838        if (!identNode.isInitializedHere()) {
839            symbol.increaseUseCount();
840        }
841
842        IdentNode newIdentNode = identNode.setSymbol(symbol);
843
844        // If a block-scoped var is used before its declaration mark it as dead.
845        // We can only statically detect this for local vars, cross-function symbols require runtime checks.
846        if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) {
847            newIdentNode = newIdentNode.markDead();
848        }
849
850        return end(newIdentNode);
851    }
852
853    private Symbol nameIsUsed(final String name, final IdentNode origin) {
854        final Block block = lc.getCurrentBlock();
855
856        Symbol symbol = findSymbol(block, name);
857
858        //If an existing symbol with the name is found, use that otherwise, declare a new one
859        if (symbol != null) {
860            log.info("Existing symbol = ", symbol);
861            if (symbol.isFunctionSelf()) {
862                final FunctionNode functionNode = lc.getDefiningFunction(symbol);
863                assert functionNode != null;
864                assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
865                lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
866            }
867
868            // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already)
869            maybeForceScope(symbol);
870        } else {
871            log.info("No symbol exists. Declare as global: ", name);
872            symbol = defineSymbol(block, name, origin, IS_GLOBAL | IS_SCOPE);
873        }
874
875        functionUsesSymbol(symbol);
876        return symbol;
877    }
878
879    @Override
880    public Node leaveSwitchNode(final SwitchNode switchNode) {
881        // We only need a symbol for the tag if it's not an integer switch node
882        if(!switchNode.isUniqueInteger()) {
883            return switchNode.setTag(lc, newObjectInternal(SWITCH_TAG_PREFIX));
884        }
885        return switchNode;
886    }
887
888    @Override
889    public Node leaveTryNode(final TryNode tryNode) {
890        assert tryNode.getFinallyBody() == null;
891
892        end(tryNode);
893
894        return tryNode.setException(lc, exceptionSymbol());
895    }
896
897    private Node leaveTYPEOF(final UnaryNode unaryNode) {
898        final Expression rhs = unaryNode.getExpression();
899
900        final List<Expression> args = new ArrayList<>();
901        if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) {
902            args.add(compilerConstantIdentifier(SCOPE));
903            args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName())); //null
904        } else {
905            args.add(rhs);
906            args.add(LiteralNode.newInstance(unaryNode)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
907        }
908
909        final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
910
911        end(unaryNode);
912
913        return runtimeNode;
914    }
915
916    private FunctionNode markProgramBlock(final FunctionNode functionNode) {
917        if (isOnDemand || !functionNode.isProgram()) {
918            return functionNode;
919        }
920
921        return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE));
922    }
923
924    /**
925     * If the symbol isn't already a scope symbol, but it needs to be (see {@link #symbolNeedsToBeScope(Symbol)}, it is
926     * promoted to a scope symbol and its block marked as needing a scope.
927     * @param symbol the symbol that might be scoped
928     */
929    private void maybeForceScope(final Symbol symbol) {
930        if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
931            Symbol.setSymbolIsScope(lc, symbol);
932        }
933    }
934
935    private Symbol newInternal(final CompilerConstants cc, final int flags) {
936        return defineSymbol(lc.getCurrentBlock(), lc.getCurrentFunction().uniqueName(cc.symbolName()), null, IS_VAR | IS_INTERNAL | flags); //NASHORN-73
937    }
938
939    private Symbol newObjectInternal(final CompilerConstants cc) {
940        return newInternal(cc, HAS_OBJECT_VALUE);
941    }
942
943    private boolean start(final Node node) {
944        return start(node, true);
945    }
946
947    private boolean start(final Node node, final boolean printNode) {
948        if (debug) {
949            final StringBuilder sb = new StringBuilder();
950
951            sb.append("[ENTER ").
952                append(name(node)).
953                append("] ").
954                append(printNode ? node.toString() : "").
955                append(" in '").
956                append(lc.getCurrentFunction().getName()).
957                append("'");
958            log.info(sb);
959            log.indent();
960        }
961
962        return true;
963    }
964
965    /**
966     * Determines if the symbol has to be a scope symbol. In general terms, it has to be a scope symbol if it can only
967     * be reached from the current block by traversing a function node, a split node, or a with node.
968     * @param symbol the symbol checked for needing to be a scope symbol
969     * @return true if the symbol has to be a scope symbol.
970     */
971    private boolean symbolNeedsToBeScope(final Symbol symbol) {
972        if (symbol.isThis() || symbol.isInternal()) {
973            return false;
974        }
975
976        final FunctionNode func = lc.getCurrentFunction();
977        if ( func.allVarsInScope() || (!symbol.isBlockScoped() && func.isProgram())) {
978            return true;
979        }
980
981        boolean previousWasBlock = false;
982        for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
983            final LexicalContextNode node = it.next();
984            if (node instanceof FunctionNode || isSplitLiteral(node)) {
985                // We reached the function boundary or a splitting boundary without seeing a definition for the symbol.
986                // It needs to be in scope.
987                return true;
988            } else if (node instanceof WithNode) {
989                if (previousWasBlock) {
990                    // We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately
991                    // preceded by a block, this means we're currently processing its expression, not its body,
992                    // therefore it doesn't count.
993                    return true;
994                }
995                previousWasBlock = false;
996            } else if (node instanceof Block) {
997                if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) {
998                    // We reached the block that defines the symbol without reaching either the function boundary, or a
999                    // WithNode. The symbol need not be scoped.
1000                    return false;
1001                }
1002                previousWasBlock = true;
1003            } else {
1004                previousWasBlock = false;
1005            }
1006        }
1007        throw new AssertionError();
1008    }
1009
1010    private static boolean isSplitLiteral(final LexicalContextNode expr) {
1011        return expr instanceof Splittable && ((Splittable) expr).getSplitRanges() != null;
1012    }
1013
1014    private void throwUnprotectedSwitchError(final VarNode varNode) {
1015        // Block scoped declarations in switch statements without explicit blocks should be declared
1016        // in a common block that contains all the case clauses. We cannot support this without a
1017        // fundamental rewrite of how switch statements are handled (case nodes contain blocks and are
1018        // directly contained by switch node). As a temporary solution we throw a reference error here.
1019        final String msg = ECMAErrors.getMessage("syntax.error.unprotected.switch.declaration", varNode.isLet() ? "let" : "const");
1020        throwParserException(msg, varNode);
1021    }
1022
1023    private void throwParserException(final String message, final Node origin) {
1024        if (origin == null) {
1025            throw new ParserException(message);
1026        }
1027        final Source source = compiler.getSource();
1028        final long token = origin.getToken();
1029        final int line = source.getLine(origin.getStart());
1030        final int column = source.getColumn(origin.getStart());
1031        final String formatted = ErrorManager.format(message, source, line, column, token);
1032        throw new ParserException(JSErrorType.SYNTAX_ERROR, formatted, source, line, column, token);
1033    }
1034}
1035