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