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