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