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