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