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