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