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