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.LITERAL_PREFIX;
  34 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
  35 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
  36 import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
  37 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
  38 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
  39 import static jdk.nashorn.internal.ir.Symbol.IS_ALWAYS_DEFINED;
  40 import static jdk.nashorn.internal.ir.Symbol.IS_CONSTANT;
  41 import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF;
  42 import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
  43 import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
  44 import static jdk.nashorn.internal.ir.Symbol.IS_LET;
  45 import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
  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.HashSet;
  55 import java.util.Iterator;
  56 import java.util.List;
  57 import java.util.Set;
  58 import jdk.nashorn.internal.codegen.types.Type;
  59 import jdk.nashorn.internal.ir.AccessNode;
  60 import jdk.nashorn.internal.ir.BinaryNode;
  61 import jdk.nashorn.internal.ir.Block;
  62 import jdk.nashorn.internal.ir.CallNode;
  63 import jdk.nashorn.internal.ir.CaseNode;
  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.Node;
  76 import jdk.nashorn.internal.ir.ObjectNode;
  77 import jdk.nashorn.internal.ir.ReturnNode;
  78 import jdk.nashorn.internal.ir.RuntimeNode;
  79 import jdk.nashorn.internal.ir.RuntimeNode.Request;
  80 import jdk.nashorn.internal.ir.Statement;
  81 import jdk.nashorn.internal.ir.SwitchNode;
  82 import jdk.nashorn.internal.ir.Symbol;
  83 import jdk.nashorn.internal.ir.TemporarySymbols;
  84 import jdk.nashorn.internal.ir.TernaryNode;
  85 import jdk.nashorn.internal.ir.TryNode;
  86 import jdk.nashorn.internal.ir.UnaryNode;
  87 import jdk.nashorn.internal.ir.VarNode;
  88 import jdk.nashorn.internal.ir.WithNode;
  89 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
  90 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
  91 import jdk.nashorn.internal.parser.TokenType;
  92 import jdk.nashorn.internal.runtime.Context;
  93 import jdk.nashorn.internal.runtime.Debug;
  94 import jdk.nashorn.internal.runtime.DebugLogger;
  95 import jdk.nashorn.internal.runtime.JSType;
  96 import jdk.nashorn.internal.runtime.Property;
  97 import jdk.nashorn.internal.runtime.PropertyMap;
  98 
  99 /**
 100  * This is the attribution pass of the code generator. Attr takes Lowered IR,
 101  * that is, IR where control flow has been computed and high level to low level
 102  * substitions for operations have been performed.
 103  *
 104  * After Attr, every symbol will have a conservative correct type.
 105  *
 106  * Any expression that requires temporary storage as part of computation will
 107  * also be detected here and give a temporary symbol
 108  *
 109  * Types can be narrowed after Attr by Access Specialization in FinalizeTypes,
 110  * but in general, this is where the main symbol type information is
 111  * computed.
 112  */
 113 
 114 final class Attr extends NodeOperatorVisitor<LexicalContext> {
 115 
 116     /**
 117      * Local definitions in current block (to discriminate from function
 118      * declarations always defined in the function scope. This is for
 119      * "can be undefined" analysis.
 120      */
 121     private final Deque<Set<String>> localDefs;
 122 
 123     /**
 124      * Local definitions in current block to guard against cases like
 125      * NASHORN-467 when things can be undefined as they are used before
 126      * their local var definition. *sigh* JavaScript...
 127      */
 128     private final Deque<Set<String>> localUses;
 129 
 130     private final Deque<Type> returnTypes;
 131 
 132     private int catchNestingLevel;
 133 
 134     private static final DebugLogger LOG   = new DebugLogger("attr");
 135     private static final boolean     DEBUG = LOG.isEnabled();
 136 
 137     private final TemporarySymbols temporarySymbols;
 138 
 139     /**
 140      * Constructor.
 141      */
 142     Attr(final TemporarySymbols temporarySymbols) {
 143         super(new LexicalContext());
 144         this.temporarySymbols = temporarySymbols;
 145         this.localDefs   = new ArrayDeque<>();
 146         this.localUses   = new ArrayDeque<>();
 147         this.returnTypes = new ArrayDeque<>();
 148     }
 149 
 150     @Override
 151     protected boolean enterDefault(final Node node) {
 152         return start(node);
 153     }
 154 
 155     @Override
 156     protected Node leaveDefault(final Node node) {
 157         return end(node);
 158     }
 159 
 160     @Override
 161     public Node leaveAccessNode(final AccessNode accessNode) {
 162         //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this, that
 163         //is why we can't set the access node base to be an object here, that will ruin access specialization
 164         //for example for a.x | 17.
 165         return end(ensureSymbol(Type.OBJECT, accessNode));
 166     }
 167 
 168     private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
 169         initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL);
 170         initCompileConstant(THIS, body, IS_PARAM | IS_THIS, Type.OBJECT);
 171 
 172         if (functionNode.isVarArg()) {
 173             initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL);
 174             if (functionNode.needsArguments()) {
 175                 initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED);
 176                 final String argumentsName = ARGUMENTS_VAR.symbolName();
 177                 newType(defineSymbol(body, argumentsName, IS_VAR | IS_ALWAYS_DEFINED), Type.typeFor(ARGUMENTS_VAR.type()));
 178                 addLocalDef(argumentsName);
 179             }
 180         }
 181 
 182         initParameters(functionNode, body);
 183         initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED);
 184         initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED, Type.OBJECT);
 185     }
 186 
 187 
 188     /**
 189      * This pushes all declarations (except for non-statements, i.e. for
 190      * node temporaries) to the top of the function scope. This way we can
 191      * get around problems like
 192      *
 193      * while (true) {
 194      *   break;
 195      *   if (true) {
 196      *     var s;
 197      *   }
 198      * }
 199      *
 200      * to an arbitrary nesting depth.
 201      *
 202      * see NASHORN-73
 203      *
 204      * @param functionNode the FunctionNode we are entering
 205      * @param body the body of the FunctionNode we are entering
 206      */
 207     private void acceptDeclarations(final FunctionNode functionNode, final Block body) {
 208         // This visitor will assign symbol to all declared variables, except function declarations (which are taken care
 209         // in a separate step above) and "var" declarations in for loop initializers.
 210         //
 211         // It also handles the case that a variable can be undefined, e.g
 212         // if (cond) {
 213         //    x = x.y;
 214         // }
 215         // var x = 17;
 216         //
 217         // by making sure that no identifier has been found earlier in the body than the
 218         // declaration - if such is the case the identifier is flagged as caBeUndefined to
 219         // be safe if it turns into a local var. Otherwise corrupt bytecode results
 220 
 221         body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
 222             private final Set<String> uses = new HashSet<>();
 223             private final Set<String> canBeUndefined = new HashSet<>();
 224 
 225             @Override
 226             public boolean enterFunctionNode(final FunctionNode nestedFn) {
 227                 return false;
 228             }
 229 
 230             @Override
 231             public Node leaveIdentNode(final IdentNode identNode) {
 232                 uses.add(identNode.getName());
 233                 return identNode;
 234             }
 235 
 236             @Override
 237             public boolean enterVarNode(final VarNode varNode) {
 238                 final String name = varNode.getName().getName();
 239                 //if this is used before the var node, the var node symbol needs to be tagged as can be undefined
 240                 if (uses.contains(name)) {
 241                     canBeUndefined.add(name);
 242                 }
 243 
 244                 // all uses of the declared varnode inside the var node are potentially undefined
 245                 // however this is a bit conservative as e.g. var x = 17; var x = 1 + x; does work
 246                 if (!varNode.isFunctionDeclaration() && varNode.getInit() != null) {
 247                     varNode.getInit().accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
 248                        @Override
 249                        public boolean enterIdentNode(final IdentNode identNode) {
 250                            if (name.equals(identNode.getName())) {
 251                               canBeUndefined.add(name);
 252                            }
 253                            return false;
 254                        }
 255                     });
 256                 }
 257 
 258                 return true;
 259             }
 260 
 261             @Override
 262             public Node leaveVarNode(final VarNode varNode) {
 263                 // any declared symbols that aren't visited need to be typed as well, hence the list
 264                 if (varNode.isStatement()) {
 265                     final IdentNode ident  = varNode.getName();
 266                     final Symbol    symbol = defineSymbol(body, ident.getName(), IS_VAR);
 267                     if (canBeUndefined.contains(ident.getName())) {
 268                         symbol.setType(Type.OBJECT);
 269                         symbol.setCanBeUndefined();
 270                     }
 271                     functionNode.addDeclaredSymbol(symbol);
 272                     if (varNode.isFunctionDeclaration()) {
 273                         newType(symbol, FunctionNode.FUNCTION_TYPE);
 274                         symbol.setIsFunctionDeclaration();
 275                     }
 276                     return varNode.setName((IdentNode)ident.setSymbol(lc, symbol));
 277                 }
 278 
 279                 return varNode;
 280             }
 281         });
 282     }
 283 
 284     private void enterFunctionBody() {
 285 
 286         final FunctionNode functionNode = lc.getCurrentFunction();
 287         final Block body = lc.getCurrentBlock();
 288 
 289         initFunctionWideVariables(functionNode, body);
 290 
 291         if (functionNode.isProgram()) {
 292             initFromPropertyMap(body);
 293         } else if (!functionNode.isDeclared()) {
 294             // It's neither declared nor program - it's a function expression then; assign it a self-symbol.
 295             assert functionNode.getSymbol() == null;
 296 
 297             final boolean anonymous = functionNode.isAnonymous();
 298             final String  name      = anonymous ? null : functionNode.getIdent().getName();
 299             if (!(anonymous || body.getExistingSymbol(name) != null)) {
 300                 assert !anonymous && name != null;
 301                 newType(defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF), Type.OBJECT);
 302             }
 303         }
 304 
 305         acceptDeclarations(functionNode, body);
 306     }
 307 
 308     @Override
 309     public boolean enterBlock(final Block block) {
 310         start(block);
 311         //ensure that we don't use information from a previous compile. This is very ugly TODO
 312         //the symbols in the block should really be stateless
 313         block.clearSymbols();
 314 
 315         if (lc.isFunctionBody()) {
 316             enterFunctionBody();
 317         }
 318         pushLocalsBlock();
 319 
 320         return true;
 321     }
 322 
 323     @Override
 324     public Node leaveBlock(final Block block) {
 325         popLocals();
 326         return end(block);
 327     }
 328 
 329     @Override
 330     public boolean enterCallNode(final CallNode callNode) {
 331         return start(callNode);
 332     }
 333 
 334     @Override
 335     public Node leaveCallNode(final CallNode callNode) {
 336         return end(ensureSymbol(callNode.getType(), callNode));
 337     }
 338 
 339     @Override
 340     public boolean enterCatchNode(final CatchNode catchNode) {
 341         final IdentNode exception = catchNode.getException();
 342         final Block     block     = lc.getCurrentBlock();
 343 
 344         start(catchNode);
 345         catchNestingLevel++;
 346 
 347         // define block-local exception variable
 348         final String exname = exception.getName();
 349         final Symbol def = defineSymbol(block, exname, IS_VAR | IS_LET | IS_ALWAYS_DEFINED);
 350         newType(def, Type.OBJECT); //we can catch anything, not just ecma exceptions
 351 
 352         addLocalDef(exname);
 353 
 354         return true;
 355     }
 356 
 357     @Override
 358     public Node leaveCatchNode(final CatchNode catchNode) {
 359         final IdentNode exception = catchNode.getException();
 360         final Block  block        = lc.getCurrentBlock();
 361         final Symbol symbol       = findSymbol(block, exception.getName());
 362 
 363         catchNestingLevel--;
 364 
 365         assert symbol != null;
 366         return end(catchNode.setException((IdentNode)exception.setSymbol(lc, symbol)));
 367     }
 368 
 369     /**
 370      * Declare the definition of a new symbol.
 371      *
 372      * @param name         Name of symbol.
 373      * @param symbolFlags  Symbol flags.
 374      *
 375      * @return Symbol for given name or null for redefinition.
 376      */
 377     private Symbol defineSymbol(final Block block, final String name, final int symbolFlags) {
 378         int    flags  = symbolFlags;
 379         Symbol symbol = findSymbol(block, name); // Locate symbol.
 380 
 381         if ((flags & KINDMASK) == IS_GLOBAL) {
 382             flags |= IS_SCOPE;
 383         }
 384 
 385         final FunctionNode function = lc.getFunction(block);
 386         if (symbol != null) {
 387             // Symbol was already defined. Check if it needs to be redefined.
 388             if ((flags & KINDMASK) == IS_PARAM) {
 389                 if (!isLocal(function, symbol)) {
 390                     // Not defined in this function. Create a new definition.
 391                     symbol = null;
 392                 } else if (symbol.isParam()) {
 393                     // Duplicate parameter. Null return will force an error.
 394                     assert false : "duplicate parameter";
 395                     return null;
 396                 }
 397             } else if ((flags & KINDMASK) == IS_VAR) {
 398                 if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) {
 399                     // Always create a new definition.
 400                     symbol = null;
 401                 } else {
 402                     // Not defined in this function. Create a new definition.
 403                     if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
 404                         symbol = null;
 405                     }
 406                 }
 407             }
 408         }
 409 
 410         if (symbol == null) {
 411             // If not found, then create a new one.
 412             Block symbolBlock;
 413 
 414             // Determine where to create it.
 415             if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
 416                 symbolBlock = block; //internal vars are always defined in the block closest to them
 417             } else {
 418                 symbolBlock = lc.getFunctionBody(function);
 419             }
 420 
 421             // Create and add to appropriate block.
 422             symbol = new Symbol(name, flags);
 423             symbolBlock.putSymbol(lc, symbol);
 424 
 425             if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
 426                 symbol.setNeedsSlot(true);
 427             }
 428         } else if (symbol.less(flags)) {
 429             symbol.setFlags(flags);
 430         }
 431 
 432         return symbol;
 433     }
 434 
 435     @Override
 436     public boolean enterFunctionNode(final FunctionNode functionNode) {
 437         start(functionNode, false);
 438 
 439         if (functionNode.isLazy()) {
 440             return false;
 441         }
 442 
 443         //an outermost function in our lexical context that is not a program (runScript)
 444         //is possible - it is a function being compiled lazily
 445         if (functionNode.isDeclared()) {
 446             final Iterator<Block> blocks = lc.getBlocks();
 447             if (blocks.hasNext()) {
 448                 defineSymbol(blocks.next(), functionNode.getIdent().getName(), IS_VAR);
 449             }
 450         }
 451 
 452         returnTypes.push(functionNode.getReturnType());
 453         pushLocalsFunction();
 454 
 455         return true;
 456     }
 457 
 458     @Override
 459     public Node leaveFunctionNode(final FunctionNode functionNode) {
 460         FunctionNode newFunctionNode = functionNode;
 461 
 462         final Block body = newFunctionNode.getBody();
 463 
 464         //look for this function in the parent block
 465         if (functionNode.isDeclared()) {
 466             final Iterator<Block> blocks = lc.getBlocks();
 467             if (blocks.hasNext()) {
 468                 newFunctionNode = (FunctionNode)newFunctionNode.setSymbol(lc, findSymbol(blocks.next(), functionNode.getIdent().getName()));
 469             }
 470         } else if (!functionNode.isProgram()) {
 471             final boolean anonymous = functionNode.isAnonymous();
 472             final String  name      = anonymous ? null : functionNode.getIdent().getName();
 473             if (anonymous || body.getExistingSymbol(name) != null) {
 474                 newFunctionNode = (FunctionNode)ensureSymbol(FunctionNode.FUNCTION_TYPE, newFunctionNode);
 475             } else {
 476                 assert name != null;
 477                 final Symbol self = body.getExistingSymbol(name);
 478                 assert self != null && self.isFunctionSelf();
 479                 newFunctionNode = (FunctionNode)newFunctionNode.setSymbol(lc, body.getExistingSymbol(name));
 480             }
 481         }
 482 
 483         //unknown parameters are promoted to object type.
 484         if (newFunctionNode.hasLazyChildren()) {
 485             //the final body has already been assigned as we have left the function node block body by now
 486             objectifySymbols(body);
 487         }
 488         newFunctionNode = finalizeParameters(newFunctionNode);
 489         newFunctionNode = finalizeTypes(newFunctionNode);
 490         for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) {
 491             if (symbol.getSymbolType().isUnknown()) {
 492                 symbol.setType(Type.OBJECT);
 493                 symbol.setCanBeUndefined();
 494             }
 495         }
 496 
 497         List<VarNode> syntheticInitializers = null;
 498 
 499         if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) {
 500             syntheticInitializers = new ArrayList<>(2);
 501             LOG.info("Accepting self symbol init for ", newFunctionNode.getName());
 502             // "var fn = :callee"
 503             syntheticInitializers.add(createSyntheticInitializer(newFunctionNode.getIdent(), CALLEE, newFunctionNode));
 504         }
 505 
 506         if (newFunctionNode.needsArguments()) {
 507             if (syntheticInitializers == null) {
 508                 syntheticInitializers = new ArrayList<>(1);
 509             }
 510             // "var arguments = :arguments"
 511             syntheticInitializers.add(createSyntheticInitializer(createImplicitIdentifier(ARGUMENTS_VAR.symbolName()),
 512                     ARGUMENTS, newFunctionNode));
 513         }
 514 
 515         if (syntheticInitializers != null) {
 516             final List<Statement> stmts = newFunctionNode.getBody().getStatements();
 517             final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size());
 518             newStatements.addAll(syntheticInitializers);
 519             newStatements.addAll(stmts);
 520             newFunctionNode = newFunctionNode.setBody(lc, newFunctionNode.getBody().setStatements(lc, newStatements));
 521         }
 522 
 523         if (returnTypes.peek().isUnknown()) {
 524             LOG.info("Unknown return type promoted to object");
 525             newFunctionNode = newFunctionNode.setReturnType(lc, Type.OBJECT);
 526         }
 527         final Type returnType = returnTypes.pop();
 528         newFunctionNode = newFunctionNode.setReturnType(lc, returnType.isUnknown() ? Type.OBJECT : returnType);
 529         newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR);
 530 
 531         popLocals();
 532 
 533         end(newFunctionNode, false);
 534 
 535         return newFunctionNode;
 536     }
 537 
 538     /**
 539      * Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically
 540      * used to create assignmnent of {@code :callee} to the function name symbol in self-referential function
 541      * expressions as well as for assignment of {@code :arguments} to {@code arguments}.
 542      *
 543      * @param name the ident node identifying the variable to initialize
 544      * @param initConstant the compiler constant it is initialized to
 545      * @param fn the function node the assignment is for
 546      * @return a var node with the appropriate assignment
 547      */
 548     private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) {
 549         final IdentNode init = compilerConstant(initConstant);
 550         assert init.getSymbol() != null && init.getSymbol().hasSlot();
 551 
 552         VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init);
 553 
 554         final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName());
 555         assert nameSymbol != null;
 556 
 557         return synthVar.setName((IdentNode)name.setSymbol(lc, nameSymbol));
 558     }
 559 
 560     @Override
 561     public Node leaveIdentNode(final IdentNode identNode) {
 562         final String name = identNode.getName();
 563 
 564         if (identNode.isPropertyName()) {
 565             // assign a pseudo symbol to property name
 566             final Symbol pseudoSymbol = pseudoSymbol(name);
 567             LOG.info("IdentNode is property name -> assigning pseudo symbol ", pseudoSymbol);
 568             LOG.unindent();
 569             return end(identNode.setSymbol(lc, pseudoSymbol));
 570         }
 571 
 572         final Block block = lc.getCurrentBlock();
 573 
 574         Symbol symbol = findSymbol(block, name);
 575 
 576         //If an existing symbol with the name is found, use that otherwise, declare a new one
 577         if (symbol != null) {
 578             LOG.info("Existing symbol = ", symbol);
 579             if (symbol.isFunctionSelf()) {
 580                 final FunctionNode functionNode = lc.getDefiningFunction(symbol);
 581                 assert functionNode != null;
 582                 assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
 583                 lc.setFlag(functionNode.getBody(), Block.NEEDS_SELF_SYMBOL);
 584                 newType(symbol, FunctionNode.FUNCTION_TYPE);
 585             } else if (!identNode.isInitializedHere()) {
 586                 /*
 587                  * See NASHORN-448, JDK-8016235
 588                  *
 589                  * Here is a use outside the local def scope
 590                  * the inCatch check is a conservative approach to handle things that might have only been
 591                  * defined in the try block, but with variable declarations, which due to JavaScript rules
 592                  * have to be lifted up into the function scope outside the try block anyway, but as the
 593                  * flow can fault at almost any place in the try block and get us to the catch block, all we
 594                  * know is that we have a declaration, not a definition. This can be made better and less
 595                  * conservative once we superimpose a CFG onto the AST.
 596                  */
 597                 if (!isLocalDef(name) || inCatch()) {
 598                     newType(symbol, Type.OBJECT);
 599                     symbol.setCanBeUndefined();
 600                 }
 601             }
 602 
 603             // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already)
 604             maybeForceScope(symbol);
 605         } else {
 606             LOG.info("No symbol exists. Declare undefined: ", symbol);
 607             symbol = defineSymbol(block, name, IS_GLOBAL);
 608             // we have never seen this before, it can be undefined
 609             newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway?
 610             symbol.setCanBeUndefined();
 611             Symbol.setSymbolIsScope(lc, symbol);
 612         }
 613 
 614         setBlockScope(name, symbol);
 615 
 616         if (!identNode.isInitializedHere()) {
 617             symbol.increaseUseCount();
 618         }
 619         addLocalUse(identNode.getName());
 620 
 621         return end(identNode.setSymbol(lc, symbol));
 622     }
 623 
 624     private boolean inCatch() {
 625         return catchNestingLevel > 0;
 626     }
 627 
 628     /**
 629      * If the symbol isn't already a scope symbol, and it is either not local to the current function, or it is being
 630      * referenced from within a with block, we force it to be a scope symbol.
 631      * @param symbol the symbol that might be scoped
 632      */
 633     private void maybeForceScope(final Symbol symbol) {
 634         if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
 635             Symbol.setSymbolIsScope(lc, symbol);
 636         }
 637     }
 638 
 639     private boolean symbolNeedsToBeScope(Symbol symbol) {
 640         if (symbol.isThis() || symbol.isInternal()) {
 641             return false;
 642         }
 643         boolean previousWasBlock = false;
 644         for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
 645             final LexicalContextNode node = it.next();
 646             if (node instanceof FunctionNode) {
 647                 // We reached the function boundary without seeing a definition for the symbol - it needs to be in
 648                 // scope.
 649                 return true;
 650             } else if (node instanceof WithNode) {
 651                 if (previousWasBlock) {
 652                     // We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately
 653                     // preceded by a block, this means we're currently processing its expression, not its body,
 654                     // therefore it doesn't count.
 655                     return true;
 656                 }
 657                 previousWasBlock = false;
 658             } else if (node instanceof Block) {
 659                 if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) {
 660                     // We reached the block that defines the symbol without reaching either the function boundary, or a
 661                     // WithNode. The symbol need not be scoped.
 662                     return false;
 663                 }
 664                 previousWasBlock = true;
 665             } else {
 666                 previousWasBlock = false;
 667             }
 668         }
 669         throw new AssertionError();
 670     }
 671 
 672     private void setBlockScope(final String name, final Symbol symbol) {
 673         assert symbol != null;
 674         if (symbol.isGlobal()) {
 675             setUsesGlobalSymbol();
 676             return;
 677         }
 678 
 679         if (symbol.isScope()) {
 680             Block scopeBlock = null;
 681             for (final Iterator<LexicalContextNode> contextNodeIter = lc.getAllNodes(); contextNodeIter.hasNext(); ) {
 682                 final LexicalContextNode node = contextNodeIter.next();
 683                 if (node instanceof Block) {
 684                     if (((Block)node).getExistingSymbol(name) != null) {
 685                         scopeBlock = (Block)node;
 686                         break;
 687                     }
 688                 } else if (node instanceof FunctionNode) {
 689                     lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE);
 690                 }
 691             }
 692 
 693             if (scopeBlock != null) {
 694                 assert lc.contains(scopeBlock);
 695                 lc.setBlockNeedsScope(scopeBlock);
 696             }
 697         }
 698     }
 699 
 700     /**
 701      * Marks the current function as one using any global symbol. The function and all its parent functions will all be
 702      * marked as needing parent scope.
 703      * @see #needsParentScope()
 704      */
 705     private void setUsesGlobalSymbol() {
 706         for (final Iterator<FunctionNode> fns = lc.getFunctions(); fns.hasNext();) {
 707             lc.setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
 708         }
 709     }
 710 
 711     /**
 712      * Search for symbol in the lexical context starting from the given block.
 713      * @param name Symbol name.
 714      * @return Found symbol or null if not found.
 715      */
 716     private Symbol findSymbol(final Block block, final String name) {
 717         // Search up block chain to locate symbol.
 718 
 719         for (final Iterator<Block> blocks = lc.getBlocks(block); blocks.hasNext();) {
 720             // Find name.
 721             final Symbol symbol = blocks.next().getExistingSymbol(name);
 722             // If found then we are good.
 723             if (symbol != null) {
 724                 return symbol;
 725             }
 726         }
 727         return null;
 728     }
 729 
 730     @Override
 731     public Node leaveIndexNode(final IndexNode indexNode) {
 732         return end(ensureSymbol(Type.OBJECT, indexNode));
 733     }
 734 
 735     @SuppressWarnings("rawtypes")
 736     @Override
 737     public Node leaveLiteralNode(final LiteralNode literalNode) {
 738         assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens
 739         assert literalNode instanceof ArrayLiteralNode || !(literalNode.getValue() instanceof Node) : "literals with Node values not supported";
 740         final Symbol symbol = new Symbol(lc.getCurrentFunction().uniqueName(LITERAL_PREFIX.symbolName()), IS_CONSTANT, literalNode.getType());
 741         if (literalNode instanceof ArrayLiteralNode) {
 742             ((ArrayLiteralNode)literalNode).analyze();
 743         }
 744         return end(literalNode.setSymbol(lc, symbol));
 745     }
 746 
 747     @Override
 748     public boolean enterObjectNode(final ObjectNode objectNode) {
 749         return start(objectNode);
 750     }
 751 
 752     @Override
 753     public Node leaveObjectNode(final ObjectNode objectNode) {
 754         return end(ensureSymbol(Type.OBJECT, objectNode));
 755     }
 756 
 757     @Override
 758     public Node leaveReturnNode(final ReturnNode returnNode) {
 759         final Expression expr = returnNode.getExpression();
 760         final Type returnType;
 761 
 762         if (expr != null) {
 763             //we can't do parameter specialization if we return something that hasn't been typed yet
 764             final Symbol symbol = expr.getSymbol();
 765             if (expr.getType().isUnknown() && symbol.isParam()) {
 766                 symbol.setType(Type.OBJECT);
 767             }
 768 
 769             returnType = widestReturnType(returnTypes.pop(), symbol.getSymbolType());
 770         } else {
 771             returnType = Type.OBJECT; //undefined
 772         }
 773         LOG.info("Returntype is now ", returnType);
 774         returnTypes.push(returnType);
 775 
 776         end(returnNode);
 777 
 778         return returnNode;
 779     }
 780 
 781     @Override
 782     public Node leaveSwitchNode(final SwitchNode switchNode) {
 783         Type type = Type.UNKNOWN;
 784 
 785         final List<CaseNode> newCases = new ArrayList<>();
 786         for (final CaseNode caseNode : switchNode.getCases()) {
 787             final Node test = caseNode.getTest();
 788 
 789             CaseNode newCaseNode = caseNode;
 790             if (test != null) {
 791                 if (test instanceof LiteralNode) {
 792                     //go down to integers if we can
 793                     final LiteralNode<?> lit = (LiteralNode<?>)test;
 794                     if (lit.isNumeric() && !(lit.getValue() instanceof Integer)) {
 795                         if (JSType.isRepresentableAsInt(lit.getNumber())) {
 796                             newCaseNode = caseNode.setTest((Expression)LiteralNode.newInstance(lit, lit.getInt32()).accept(this));
 797                         }
 798                     }
 799                 } else {
 800                     // the "all integer" case that CodeGenerator optimizes for currently assumes literals only
 801                     type = Type.OBJECT;
 802                 }
 803 
 804                 final Type newCaseType = newCaseNode.getTest().getType();
 805                 if (newCaseType.isBoolean()) {
 806                     type = Type.OBJECT; //booleans and integers aren't assignment compatible
 807                 } else {
 808                     type = Type.widest(type, newCaseType);
 809                 }
 810             }
 811 
 812             newCases.add(newCaseNode);
 813         }
 814 
 815         //only optimize for all integers
 816         if (!type.isInteger()) {
 817             type = Type.OBJECT;
 818         }
 819 
 820         switchNode.setTag(newInternal(lc.getCurrentFunction().uniqueName(SWITCH_TAG_PREFIX.symbolName()), type));
 821 
 822         end(switchNode);
 823 
 824         return switchNode.setCases(lc, newCases);
 825     }
 826 
 827     @Override
 828     public Node leaveTryNode(final TryNode tryNode) {
 829         tryNode.setException(exceptionSymbol());
 830 
 831         if (tryNode.getFinallyBody() != null) {
 832             tryNode.setFinallyCatchAll(exceptionSymbol());
 833         }
 834 
 835         end(tryNode);
 836 
 837         return tryNode;
 838     }
 839 
 840     @Override
 841     public boolean enterVarNode(final VarNode varNode) {
 842         start(varNode);
 843 
 844         final IdentNode ident = varNode.getName();
 845         final String    name  = ident.getName();
 846 
 847         final Symbol symbol = defineSymbol(lc.getCurrentBlock(), name, IS_VAR);
 848         assert symbol != null;
 849 
 850         // NASHORN-467 - use before definition of vars - conservative
 851         if (isLocalUse(ident.getName())) {
 852             newType(symbol, Type.OBJECT);
 853             symbol.setCanBeUndefined();
 854         }
 855 
 856         return true;
 857     }
 858 
 859     @Override
 860     public Node leaveVarNode(final VarNode varNode) {
 861         final Expression init  = varNode.getInit();
 862         final IdentNode  ident = varNode.getName();
 863         final String     name  = ident.getName();
 864 
 865         final Symbol  symbol = findSymbol(lc.getCurrentBlock(), name);
 866         assert ident.getSymbol() == symbol;
 867 
 868         if (init == null) {
 869             // var x; with no init will be treated like a use of x by
 870             // leaveIdentNode unless we remove the name from the localdef list.
 871             removeLocalDef(name);
 872             return end(varNode);
 873         }
 874 
 875         addLocalDef(name);
 876 
 877         assert symbol != null;
 878 
 879         final IdentNode newIdent = (IdentNode)ident.setSymbol(lc, symbol);
 880 
 881         final VarNode newVarNode = varNode.setName(newIdent);
 882 
 883         final boolean isScript = lc.getDefiningFunction(symbol).isProgram(); //see NASHORN-56
 884         if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) {
 885             // Forbid integers as local vars for now as we have no way to treat them as undefined
 886             newType(symbol, init.getType());
 887         } else {
 888             newType(symbol, Type.OBJECT);
 889         }
 890 
 891         assert newVarNode.getName().hasType() : newVarNode + " has no type";
 892 
 893         return end(newVarNode);
 894     }
 895 
 896     @Override
 897     public Node leaveADD(final UnaryNode unaryNode) {
 898         return end(ensureSymbol(arithType(), unaryNode));
 899     }
 900 
 901     @Override
 902     public Node leaveBIT_NOT(final UnaryNode unaryNode) {
 903         return end(ensureSymbol(Type.INT, unaryNode));
 904     }
 905 
 906     @Override
 907     public Node leaveDECINC(final UnaryNode unaryNode) {
 908         // @see assignOffset
 909         final Type type = arithType();
 910         newType(unaryNode.rhs().getSymbol(), type);
 911         return end(ensureSymbol(type, unaryNode));
 912     }
 913 
 914     @Override
 915     public Node leaveDELETE(final UnaryNode unaryNode) {
 916         final FunctionNode   currentFunctionNode = lc.getCurrentFunction();
 917         final boolean        strictMode          = currentFunctionNode.isStrict();
 918         final Expression     rhs                 = unaryNode.rhs();
 919         final Expression     strictFlagNode      = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this);
 920 
 921         Request request = Request.DELETE;
 922         final List<Expression> args = new ArrayList<>();
 923 
 924         if (rhs instanceof IdentNode) {
 925             // If this is a declared variable or a function parameter, delete always fails (except for globals).
 926             final String name = ((IdentNode)rhs).getName();
 927 
 928             final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !isProgramLevelSymbol(name));
 929 
 930             if (failDelete && rhs.getSymbol().isThis()) {
 931                 return LiteralNode.newInstance(unaryNode, true).accept(this);
 932             }
 933             final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this);
 934 
 935             if (!failDelete) {
 936                 args.add(compilerConstant(SCOPE));
 937             }
 938             args.add(literalNode);
 939             args.add(strictFlagNode);
 940 
 941             if (failDelete) {
 942                 request = Request.FAIL_DELETE;
 943             }
 944         } else if (rhs instanceof AccessNode) {
 945             final Expression base     = ((AccessNode)rhs).getBase();
 946             final IdentNode  property = ((AccessNode)rhs).getProperty();
 947 
 948             args.add(base);
 949             args.add((Expression)LiteralNode.newInstance(unaryNode, property.getName()).accept(this));
 950             args.add(strictFlagNode);
 951 
 952         } else if (rhs instanceof IndexNode) {
 953             final IndexNode indexNode = (IndexNode)rhs;
 954             final Expression base  = indexNode.getBase();
 955             final Expression index = indexNode.getIndex();
 956 
 957             args.add(base);
 958             args.add(index);
 959             args.add(strictFlagNode);
 960 
 961         } else {
 962             return LiteralNode.newInstance(unaryNode, true).accept(this);
 963         }
 964 
 965         final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, request, args);
 966         assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //unary parent constructor should do this
 967 
 968         return leaveRuntimeNode(runtimeNode);
 969     }
 970 
 971     /**
 972      * Is the symbol denoted by the specified name in the current lexical context defined in the program level
 973      * @param name the name of the symbol
 974      * @return true if the symbol denoted by the specified name in the current lexical context defined in the program level.
 975      */
 976     private boolean isProgramLevelSymbol(final String name) {
 977         for(final Iterator<Block> it = lc.getBlocks(); it.hasNext();) {
 978             final Block next = it.next();
 979             if(next.getExistingSymbol(name) != null) {
 980                 return next == lc.getFunctionBody(lc.getOutermostFunction());
 981             }
 982         }
 983         throw new AssertionError("Couldn't find symbol " + name + " in the context");
 984     }
 985 
 986     @Override
 987     public Node leaveNEW(final UnaryNode unaryNode) {
 988         return end(ensureSymbol(Type.OBJECT, unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew())));
 989     }
 990 
 991     @Override
 992     public Node leaveNOT(final UnaryNode unaryNode) {
 993         return end(ensureSymbol(Type.BOOLEAN, unaryNode));
 994     }
 995 
 996     private IdentNode compilerConstant(CompilerConstants cc) {
 997         return (IdentNode)createImplicitIdentifier(cc.symbolName()).setSymbol(lc, lc.getCurrentFunction().compilerConstant(cc));
 998     }
 999 
1000     /**
1001      * Creates an ident node for an implicit identifier within the function (one not declared in the script source
1002      * code). These identifiers are defined with function's token and finish.
1003      * @param name the name of the identifier
1004      * @return an ident node representing the implicit identifier.
1005      */
1006     private IdentNode createImplicitIdentifier(final String name) {
1007         final FunctionNode fn = lc.getCurrentFunction();
1008         return new IdentNode(fn.getToken(), fn.getFinish(), name);
1009     }
1010 
1011     @Override
1012     public Node leaveTYPEOF(final UnaryNode unaryNode) {
1013         final Expression rhs = unaryNode.rhs();
1014 
1015         List<Expression> args = new ArrayList<>();
1016         if (rhs instanceof IdentNode && !rhs.getSymbol().isParam() && !rhs.getSymbol().isVar()) {
1017             args.add(compilerConstant(SCOPE));
1018             args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null
1019         } else {
1020             args.add(rhs);
1021             args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
1022         }
1023 
1024         RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
1025         assert runtimeNode.getSymbol() == unaryNode.getSymbol();
1026 
1027         runtimeNode = (RuntimeNode)leaveRuntimeNode(runtimeNode);
1028 
1029         end(unaryNode);
1030 
1031         return runtimeNode;
1032     }
1033 
1034     @Override
1035     public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
1036         return end(ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode));
1037     }
1038 
1039     @Override
1040     public Node leaveSUB(final UnaryNode unaryNode) {
1041         return end(ensureSymbol(arithType(), unaryNode));
1042     }
1043 
1044     @Override
1045     public Node leaveVOID(final UnaryNode unaryNode) {
1046         return end(ensureSymbol(Type.OBJECT, unaryNode));
1047     }
1048 
1049     /**
1050      * Add is a special binary, as it works not only on arithmetic, but for
1051      * strings etc as well.
1052      */
1053     @Override
1054     public Node leaveADD(final BinaryNode binaryNode) {
1055         final Expression lhs = binaryNode.lhs();
1056         final Expression rhs = binaryNode.rhs();
1057 
1058         ensureTypeNotUnknown(lhs);
1059         ensureTypeNotUnknown(rhs);
1060         //even if we are adding two known types, this can overflow. i.e.
1061         //int and number -> number.
1062         //int and int are also number though.
1063         //something and object is object
1064         return end(ensureSymbol(Type.widest(arithType(), Type.widest(lhs.getType(), rhs.getType())), binaryNode));
1065     }
1066 
1067     @Override
1068     public Node leaveAND(final BinaryNode binaryNode) {
1069         return end(ensureSymbol(Type.OBJECT, binaryNode));
1070     }
1071 
1072     /**
1073      * This is a helper called before an assignment.
1074      * @param binaryNode assignment node
1075      */
1076     private boolean enterAssignmentNode(final BinaryNode binaryNode) {
1077         start(binaryNode);
1078 
1079         return true;
1080     }
1081 
1082 
1083     /**
1084      * This assign helper is called after an assignment, when all children of
1085      * the assign has been processed. It fixes the types and recursively makes
1086      * sure that everyhing has slots that should have them in the chain.
1087      *
1088      * @param binaryNode assignment node
1089      */
1090     private Node leaveAssignmentNode(final BinaryNode binaryNode) {
1091         final Expression lhs = binaryNode.lhs();
1092         final Expression rhs = binaryNode.rhs();
1093         final Type type;
1094 
1095         if (lhs instanceof IdentNode) {
1096             final Block     block = lc.getCurrentBlock();
1097             final IdentNode ident = (IdentNode)lhs;
1098             final String    name  = ident.getName();
1099             final Symbol symbol = findSymbol(block, name);
1100 
1101             if (symbol == null) {
1102                 defineSymbol(block, name, IS_GLOBAL);
1103             } else {
1104                 maybeForceScope(symbol);
1105             }
1106 
1107             addLocalDef(name);
1108         }
1109 
1110         if (rhs.getType().isNumeric()) {
1111             type = Type.widest(lhs.getType(), rhs.getType());
1112         } else {
1113             type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too.
1114         }
1115 
1116         newType(lhs.getSymbol(), type);
1117         return end(ensureSymbol(type, binaryNode));
1118     }
1119 
1120     private boolean isLocal(FunctionNode function, Symbol symbol) {
1121         final FunctionNode definingFn = lc.getDefiningFunction(symbol);
1122         // Temp symbols are not assigned to a block, so their defining fn is null; those can be assumed local
1123         return definingFn == null || definingFn == function;
1124     }
1125 
1126     @Override
1127     public boolean enterASSIGN(final BinaryNode binaryNode) {
1128         return enterAssignmentNode(binaryNode);
1129     }
1130 
1131     @Override
1132     public Node leaveASSIGN(final BinaryNode binaryNode) {
1133         return leaveAssignmentNode(binaryNode);
1134     }
1135 
1136     @Override
1137     public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
1138         return enterAssignmentNode(binaryNode);
1139     }
1140 
1141     @Override
1142     public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
1143         final Expression lhs = binaryNode.lhs();
1144         final Expression rhs = binaryNode.rhs();
1145 
1146         final Type widest = Type.widest(lhs.getType(), rhs.getType());
1147         //Type.NUMBER if we can't prove that the add doesn't overflow. todo
1148         return leaveSelfModifyingAssignmentNode(binaryNode, widest.isNumeric() ? Type.NUMBER : Type.OBJECT);
1149     }
1150 
1151     @Override
1152     public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
1153         return enterAssignmentNode(binaryNode);
1154     }
1155 
1156     @Override
1157     public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) {
1158         return leaveSelfModifyingAssignmentNode(binaryNode);
1159     }
1160 
1161     @Override
1162     public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
1163         return enterAssignmentNode(binaryNode);
1164     }
1165 
1166     @Override
1167     public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) {
1168         return leaveSelfModifyingAssignmentNode(binaryNode);
1169     }
1170 
1171     @Override
1172     public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
1173         return enterAssignmentNode(binaryNode);
1174     }
1175 
1176     @Override
1177     public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) {
1178         return leaveSelfModifyingAssignmentNode(binaryNode);
1179     }
1180 
1181     @Override
1182     public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
1183         return enterAssignmentNode(binaryNode);
1184     }
1185 
1186     @Override
1187     public Node leaveASSIGN_DIV(final BinaryNode binaryNode) {
1188         return leaveSelfModifyingAssignmentNode(binaryNode);
1189     }
1190 
1191     @Override
1192     public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
1193         return enterAssignmentNode(binaryNode);
1194     }
1195 
1196     @Override
1197     public Node leaveASSIGN_MOD(final BinaryNode binaryNode) {
1198         return leaveSelfModifyingAssignmentNode(binaryNode);
1199     }
1200 
1201     @Override
1202     public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
1203         return enterAssignmentNode(binaryNode);
1204     }
1205 
1206     @Override
1207     public Node leaveASSIGN_MUL(final BinaryNode binaryNode) {
1208         return leaveSelfModifyingAssignmentNode(binaryNode);
1209     }
1210 
1211     @Override
1212     public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
1213         return enterAssignmentNode(binaryNode);
1214     }
1215 
1216     @Override
1217     public Node leaveASSIGN_SAR(final BinaryNode binaryNode) {
1218         return leaveSelfModifyingAssignmentNode(binaryNode);
1219     }
1220 
1221     @Override
1222     public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
1223         return enterAssignmentNode(binaryNode);
1224     }
1225 
1226     @Override
1227     public Node leaveASSIGN_SHL(final BinaryNode binaryNode) {
1228         return leaveSelfModifyingAssignmentNode(binaryNode);
1229     }
1230 
1231     @Override
1232     public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
1233         return enterAssignmentNode(binaryNode);
1234     }
1235 
1236     @Override
1237     public Node leaveASSIGN_SHR(final BinaryNode binaryNode) {
1238         return leaveSelfModifyingAssignmentNode(binaryNode);
1239     }
1240 
1241     @Override
1242     public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
1243         return enterAssignmentNode(binaryNode);
1244     }
1245 
1246     @Override
1247     public Node leaveASSIGN_SUB(final BinaryNode binaryNode) {
1248         return leaveSelfModifyingAssignmentNode(binaryNode);
1249     }
1250 
1251     @Override
1252     public Node leaveBIT_AND(final BinaryNode binaryNode) {
1253         return end(coerce(binaryNode, Type.INT));
1254     }
1255 
1256     @Override
1257     public Node leaveBIT_OR(final BinaryNode binaryNode) {
1258         return end(coerce(binaryNode, Type.INT));
1259     }
1260 
1261     @Override
1262     public Node leaveBIT_XOR(final BinaryNode binaryNode) {
1263         return end(coerce(binaryNode, Type.INT));
1264     }
1265 
1266     @Override
1267     public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
1268         return leaveComma(binaryNode, binaryNode.rhs());
1269     }
1270 
1271     @Override
1272     public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
1273         return leaveComma(binaryNode, binaryNode.lhs());
1274     }
1275 
1276     private Node leaveComma(final BinaryNode commaNode, final Expression effectiveExpr) {
1277         ensureTypeNotUnknown(effectiveExpr);
1278         return end(ensureSymbol(effectiveExpr.getType(), commaNode));
1279     }
1280 
1281     @Override
1282     public Node leaveDIV(final BinaryNode binaryNode) {
1283         return leaveBinaryArithmetic(binaryNode);
1284     }
1285 
1286     private Node leaveCmp(final BinaryNode binaryNode) {
1287         ensureTypeNotUnknown(binaryNode.lhs());
1288         ensureTypeNotUnknown(binaryNode.rhs());
1289         Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
1290         ensureSymbol(widest, binaryNode.lhs());
1291         ensureSymbol(widest, binaryNode.rhs());
1292         return end(ensureSymbol(Type.BOOLEAN, binaryNode));
1293     }
1294 
1295     private Node coerce(final BinaryNode binaryNode, final Type operandType, final Type destType) {
1296         // TODO we currently don't support changing inferred type based on uses, only on
1297         // definitions. we would need some additional logic. We probably want to do that
1298         // in the future, if e.g. a specialized method gets parameter that is only used
1299         // as, say, an int : function(x) { return x & 4711 }, and x is not defined in
1300         // the function. to make this work, uncomment the following two type inferences
1301         // and debug.
1302         //newType(binaryNode.lhs().getSymbol(), operandType);
1303         //newType(binaryNode.rhs().getSymbol(), operandType);
1304         return ensureSymbol(destType, binaryNode);
1305     }
1306 
1307     private Node coerce(final BinaryNode binaryNode, final Type type) {
1308         return coerce(binaryNode, type, type);
1309     }
1310 
1311     //leave a binary node and inherit the widest type of lhs , rhs
1312     private Node leaveBinaryArithmetic(final BinaryNode binaryNode) {
1313         assert !Compiler.shouldUseIntegerArithmetic();
1314         return end(coerce(binaryNode, Type.NUMBER));
1315     }
1316 
1317     @Override
1318     public Node leaveEQ(final BinaryNode binaryNode) {
1319         return leaveCmp(binaryNode);
1320     }
1321 
1322     @Override
1323     public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
1324         return leaveCmp(binaryNode);
1325     }
1326 
1327     @Override
1328     public Node leaveGE(final BinaryNode binaryNode) {
1329         return leaveCmp(binaryNode);
1330     }
1331 
1332     @Override
1333     public Node leaveGT(final BinaryNode binaryNode) {
1334         return leaveCmp(binaryNode);
1335     }
1336 
1337     @Override
1338     public Node leaveIN(final BinaryNode binaryNode) {
1339         return leaveBinaryRuntimeOperator(binaryNode, Request.IN);
1340     }
1341 
1342     @Override
1343     public Node leaveINSTANCEOF(final BinaryNode binaryNode) {
1344         return leaveBinaryRuntimeOperator(binaryNode, Request.INSTANCEOF);
1345     }
1346 
1347     private Node leaveBinaryRuntimeOperator(final BinaryNode binaryNode, final Request request) {
1348         try {
1349             // Don't do a full RuntimeNode.accept, as we don't want to double-visit the binary node operands
1350             return leaveRuntimeNode(new RuntimeNode(binaryNode, request));
1351         } finally {
1352             end(binaryNode);
1353         }
1354     }
1355 
1356     @Override
1357     public Node leaveLE(final BinaryNode binaryNode) {
1358         return leaveCmp(binaryNode);
1359     }
1360 
1361     @Override
1362     public Node leaveLT(final BinaryNode binaryNode) {
1363         return leaveCmp(binaryNode);
1364     }
1365 
1366     @Override
1367     public Node leaveMOD(final BinaryNode binaryNode) {
1368         return leaveBinaryArithmetic(binaryNode);
1369     }
1370 
1371     @Override
1372     public Node leaveMUL(final BinaryNode binaryNode) {
1373         return leaveBinaryArithmetic(binaryNode);
1374     }
1375 
1376     @Override
1377     public Node leaveNE(final BinaryNode binaryNode) {
1378         return leaveCmp(binaryNode);
1379     }
1380 
1381     @Override
1382     public Node leaveNE_STRICT(final BinaryNode binaryNode) {
1383         return leaveCmp(binaryNode);
1384     }
1385 
1386     @Override
1387     public Node leaveOR(final BinaryNode binaryNode) {
1388         return end(ensureSymbol(Type.OBJECT, binaryNode));
1389     }
1390 
1391     @Override
1392     public Node leaveSAR(final BinaryNode binaryNode) {
1393         return end(coerce(binaryNode, Type.INT));
1394     }
1395 
1396     @Override
1397     public Node leaveSHL(final BinaryNode binaryNode) {
1398         return end(coerce(binaryNode, Type.INT));
1399     }
1400 
1401     @Override
1402     public Node leaveSHR(final BinaryNode binaryNode) {
1403         return end(coerce(binaryNode, Type.LONG));
1404     }
1405 
1406     @Override
1407     public Node leaveSUB(final BinaryNode binaryNode) {
1408         return leaveBinaryArithmetic(binaryNode);
1409     }
1410 
1411     @Override
1412     public Node leaveForNode(final ForNode forNode) {
1413         if (forNode.isForIn()) {
1414             forNode.setIterator(newInternal(lc.getCurrentFunction().uniqueName(ITERATOR_PREFIX.symbolName()), Type.typeFor(ITERATOR_PREFIX.type()))); //NASHORN-73
1415             /*
1416              * Iterators return objects, so we need to widen the scope of the
1417              * init variable if it, for example, has been assigned double type
1418              * see NASHORN-50
1419              */
1420             newType(forNode.getInit().getSymbol(), Type.OBJECT);
1421         }
1422 
1423         end(forNode);
1424 
1425         return forNode;
1426     }
1427 
1428     @Override
1429     public Node leaveTernaryNode(final TernaryNode ternaryNode) {
1430         final Expression trueExpr  = ternaryNode.getTrueExpression();
1431         final Expression falseExpr = ternaryNode.getFalseExpression();
1432 
1433         ensureTypeNotUnknown(trueExpr);
1434         ensureTypeNotUnknown(falseExpr);
1435 
1436         final Type type = widestReturnType(trueExpr.getType(), falseExpr.getType());
1437         return end(ensureSymbol(type, ternaryNode));
1438     }
1439 
1440     /**
1441      * When doing widening for return types of a function or a ternary operator, it is not valid to widen a boolean to
1442      * anything other than Object. Also, widening a numeric type to an object type must widen to Object proper and not
1443      * any more specific subclass (e.g. widest of int/long/double and String is Object).
1444      * @param t1 type 1
1445      * @param t2 type 2
1446      * @return wider of t1 and t2, except if one is boolean and the other is neither boolean nor unknown, or if one is
1447      * numeric and the other is neither numeric nor unknown in which case {@code Type.OBJECT} is returned.
1448      */
1449     private static Type widestReturnType(final Type t1, final Type t2) {
1450         if (t1.isUnknown()) {
1451             return t2;
1452         } else if (t2.isUnknown()) {
1453             return t1;
1454         } else if (t1.isBoolean() != t2.isBoolean() || t1.isNumeric() != t2.isNumeric()) {
1455             return Type.OBJECT;
1456         }
1457         return Type.widest(t1, t2);
1458     }
1459 
1460     private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) {
1461         final Class<?> type = cc.type();
1462         // Must not call this method for constants with no explicit types; use the one with (..., Type) signature instead.
1463         assert type != null;
1464         initCompileConstant(cc, block, flags, Type.typeFor(type));
1465     }
1466 
1467     private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags, final Type type) {
1468         final Symbol symbol = defineSymbol(block, cc.symbolName(), flags);
1469         symbol.setTypeOverride(type);
1470         symbol.setNeedsSlot(true);
1471     }
1472 
1473     /**
1474      * Initialize parameters for function node. This may require specializing
1475      * types if a specialization profile is known
1476      *
1477      * @param functionNode the function node
1478      */
1479     private void initParameters(final FunctionNode functionNode, final Block body) {
1480         int pos = 0;
1481         for (final IdentNode param : functionNode.getParameters()) {
1482             addLocalDef(param.getName());
1483 
1484             final Type callSiteParamType = functionNode.getHints().getParameterType(pos);
1485             int flags = IS_PARAM;
1486             if (callSiteParamType != null) {
1487                 LOG.info("Param ", param, " has a callsite type ", callSiteParamType, ". Using that.");
1488                 flags |= Symbol.IS_SPECIALIZED_PARAM;
1489             }
1490 
1491             final Symbol paramSymbol = defineSymbol(body, param.getName(), flags);
1492             assert paramSymbol != null;
1493 
1494             newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
1495 
1496             LOG.info("Initialized param ", pos, "=", paramSymbol);
1497             pos++;
1498         }
1499 
1500     }
1501 
1502     /**
1503      * This has to run before fix assignment types, store any type specializations for
1504      * paramters, then turn then to objects for the generic version of this method
1505      *
1506      * @param functionNode functionNode
1507      */
1508     private FunctionNode finalizeParameters(final FunctionNode functionNode) {
1509         final List<IdentNode> newParams = new ArrayList<>();
1510         final boolean isVarArg = functionNode.isVarArg();
1511         final int nparams = functionNode.getParameters().size();
1512 
1513         int specialize = 0;
1514         int pos = 0;
1515         for (final IdentNode param : functionNode.getParameters()) {
1516             final Symbol paramSymbol = functionNode.getBody().getExistingSymbol(param.getName());
1517             assert paramSymbol != null;
1518             assert paramSymbol.isParam();
1519             newParams.add((IdentNode)param.setSymbol(lc, paramSymbol));
1520 
1521             assert paramSymbol != null;
1522             Type type = functionNode.getHints().getParameterType(pos);
1523             if (type == null) {
1524                 type = Type.OBJECT;
1525             }
1526 
1527             // if we know that a parameter is only used as a certain type throughout
1528             // this function, we can tell the runtime system that no matter what the
1529             // call site is, use this information:
1530             // we also need more than half of the parameters to be specializable
1531             // for the heuristic to be worth it, and we need more than one use of
1532             // the parameter to consider it, i.e. function(x) { call(x); } doens't count
1533             if (paramSymbol.getUseCount() > 1 && !paramSymbol.getSymbolType().isObject()) {
1534                 LOG.finest("Parameter ", param, " could profit from specialization to ", paramSymbol.getSymbolType());
1535                 specialize++;
1536             }
1537 
1538             newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType()));
1539 
1540             // parameters should not be slots for a function that uses variable arity signature
1541             if (isVarArg) {
1542                 paramSymbol.setNeedsSlot(false);
1543             }
1544 
1545             pos++;
1546         }
1547 
1548         FunctionNode newFunctionNode = functionNode;
1549 
1550         if (nparams == 0 || (specialize * 2) < nparams) {
1551             newFunctionNode = newFunctionNode.clearSnapshot(lc);
1552         }
1553 
1554         return newFunctionNode.setParameters(lc, newParams);
1555     }
1556 
1557     /**
1558      * Move any properties from a global map into the scope of this method
1559      * @param block the function node body for which to init scope vars
1560      */
1561     private void initFromPropertyMap(final Block block) {
1562         // For a script, add scope symbols as defined in the property map
1563 
1564         final PropertyMap map = Context.getGlobalMap();
1565 
1566         for (final Property property : map.getProperties()) {
1567             final String key    = property.getKey();
1568             final Symbol symbol = defineSymbol(block, key, IS_GLOBAL);
1569             newType(symbol, Type.OBJECT);
1570             LOG.info("Added global symbol from property map ", symbol);
1571         }
1572     }
1573 
1574     private static void ensureTypeNotUnknown(final Expression node) {
1575 
1576         final Symbol symbol = node.getSymbol();
1577 
1578         LOG.info("Ensure type not unknown for: ", symbol);
1579 
1580         /*
1581          * Note that not just unknowns, but params need to be blown
1582          * up to objects, because we can have something like
1583          *
1584          * function f(a) {
1585          *    var b = ~a; //b and a are inferred to be int
1586          *    return b;
1587          * }
1588          *
1589          * In this case, it would be correct to say that "if you have
1590          * an int at the callsite, just pass it".
1591          *
1592          * However
1593          *
1594          * function f(a) {
1595          *    var b = ~a;      //b and a are inferred to be int
1596          *    return b == 17;  //b is still inferred to be int.
1597          * }
1598          *
1599          * can be called with f("17") and if we assume that b is an
1600          * int and don't blow it up to an object in the comparison, we
1601          * are screwed. I hate JavaScript.
1602          *
1603          * This check has to be done for any operation that might take
1604          * objects as parameters, for example +, but not *, which is known
1605          * to coerce types into doubles
1606          */
1607         if (node.getType().isUnknown() || (symbol.isParam() && !symbol.isSpecializedParam())) {
1608             newType(symbol, Type.OBJECT);
1609             symbol.setCanBeUndefined();
1610          }
1611     }
1612 
1613     private static Symbol pseudoSymbol(final String name) {
1614         return new Symbol(name, 0, Type.OBJECT);
1615     }
1616 
1617     private Symbol exceptionSymbol() {
1618         return newInternal(lc.getCurrentFunction().uniqueName(EXCEPTION_PREFIX.symbolName()), Type.typeFor(EXCEPTION_PREFIX.type()));
1619     }
1620 
1621     /**
1622      * Return the type that arithmetic ops should use. Until we have implemented better type
1623      * analysis (range based) or overflow checks that are fast enough for int arithmetic,
1624      * this is the number type
1625      * @return the arithetic type
1626      */
1627     private static Type arithType() {
1628         return Compiler.shouldUseIntegerArithmetic() ? Type.INT : Type.NUMBER;
1629     }
1630 
1631     /**
1632      * If types have changed, we can have failed to update vars. For example
1633      *
1634      * var x = 17; //x is int
1635      * x = "apa";  //x is object. This will be converted fine
1636      *
1637      * @param functionNode
1638      */
1639     private FunctionNode finalizeTypes(final FunctionNode functionNode) {
1640         final Set<Node> changed = new HashSet<>();
1641         FunctionNode currentFunctionNode = functionNode;
1642         do {
1643             changed.clear();
1644             final FunctionNode newFunctionNode = (FunctionNode)currentFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
1645 
1646                 private Expression widen(final Expression node, final Type to) {
1647                     if (node instanceof LiteralNode) {
1648                         return node;
1649                     }
1650                     Type from = node.getType();
1651                     if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) {
1652                         LOG.fine("Had to post pass widen '", node, "' ", Debug.id(node), " from ", node.getType(), " to ", to);
1653                         Symbol symbol = node.getSymbol();
1654                         if (symbol.isShared() && symbol.wouldChangeType(to)) {
1655                             symbol = temporarySymbols.getTypedTemporarySymbol(to);
1656                         }
1657                         newType(symbol, to);
1658                         final Expression newNode = node.setSymbol(lc, symbol);
1659                         changed.add(newNode);
1660                         return newNode;
1661                     }
1662                     return node;
1663                 }
1664 
1665                 @Override
1666                 public boolean enterFunctionNode(final FunctionNode node) {
1667                     return !node.isLazy();
1668                 }
1669 
1670                 //
1671                 // Eg.
1672                 //
1673                 // var d = 17;
1674                 // var e;
1675                 // e = d; //initially typed as int for node type, should retype as double
1676                 // e = object;
1677                 //
1678                 // var d = 17;
1679                 // var e;
1680                 // e -= d; //initially type number, should number remain with a final conversion supplied by Store. ugly, but the computation result of the sub is numeric
1681                 // e = object;
1682                 //
1683                 @SuppressWarnings("fallthrough")
1684                 @Override
1685                 public Node leaveBinaryNode(final BinaryNode binaryNode) {
1686                     final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
1687                     BinaryNode newBinaryNode = binaryNode;
1688 
1689                     if (isAdd(binaryNode)) {
1690                         newBinaryNode = (BinaryNode)widen(newBinaryNode, widest);
1691                         if (newBinaryNode.getType().isObject() && !isAddString(newBinaryNode)) {
1692                             return new RuntimeNode(newBinaryNode, Request.ADD);
1693                         }
1694                     } else if (binaryNode.isComparison()) {
1695                         final Expression lhs = newBinaryNode.lhs();
1696                         final Expression rhs = newBinaryNode.rhs();
1697 
1698                         Type cmpWidest = Type.widest(lhs.getType(), rhs.getType());
1699 
1700                         boolean newRuntimeNode = false, finalized = false;
1701                         switch (newBinaryNode.tokenType()) {
1702                         case EQ_STRICT:
1703                         case NE_STRICT:
1704                             if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) {
1705                                 newRuntimeNode = true;
1706                                 cmpWidest = Type.OBJECT;
1707                                 finalized = true;
1708                             }
1709                             //fallthru
1710                         default:
1711                             if (newRuntimeNode || cmpWidest.isObject()) {
1712                                 return new RuntimeNode(newBinaryNode, Request.requestFor(binaryNode)).setIsFinal(finalized);
1713                             }
1714                             break;
1715                         }
1716 
1717                         return newBinaryNode;
1718                     } else {
1719                         if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) {
1720                             return newBinaryNode;
1721                         }
1722                         checkThisAssignment(binaryNode);
1723                         newBinaryNode = newBinaryNode.setLHS(widen(newBinaryNode.lhs(), widest));
1724                         newBinaryNode = (BinaryNode)widen(newBinaryNode, widest);
1725                     }
1726 
1727                     return newBinaryNode;
1728 
1729                 }
1730 
1731                 private boolean isAdd(final Node node) {
1732                     return node.isTokenType(TokenType.ADD);
1733                 }
1734 
1735                 /**
1736                  * Determine if the outcome of + operator is a string.
1737                  *
1738                  * @param node  Node to test.
1739                  * @return true if a string result.
1740                  */
1741                 private boolean isAddString(final Node node) {
1742                     if (node instanceof BinaryNode && isAdd(node)) {
1743                         final BinaryNode binaryNode = (BinaryNode)node;
1744                         final Node lhs = binaryNode.lhs();
1745                         final Node rhs = binaryNode.rhs();
1746 
1747                         return isAddString(lhs) || isAddString(rhs);
1748                     }
1749 
1750                     return node instanceof LiteralNode<?> && ((LiteralNode<?>)node).isString();
1751                 }
1752 
1753                 private void checkThisAssignment(final BinaryNode binaryNode) {
1754                     if (binaryNode.isAssignment()) {
1755                         if (binaryNode.lhs() instanceof AccessNode) {
1756                             final AccessNode accessNode = (AccessNode) binaryNode.lhs();
1757 
1758                             if (accessNode.getBase().getSymbol().isThis()) {
1759                                 lc.getCurrentFunction().addThisProperty(accessNode.getProperty().getName());
1760                             }
1761                         }
1762                     }
1763                 }
1764             });
1765             lc.replace(currentFunctionNode, newFunctionNode);
1766             currentFunctionNode = newFunctionNode;
1767         } while (!changed.isEmpty());
1768 
1769         return currentFunctionNode;
1770     }
1771 
1772     private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode) {
1773         return leaveSelfModifyingAssignmentNode(binaryNode, binaryNode.getWidestOperationType());
1774     }
1775 
1776     private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode, final Type destType) {
1777         //e.g. for -=, Number, no wider, destType (binaryNode.getWidestOperationType())  is the coerce type
1778         final Expression lhs = binaryNode.lhs();
1779 
1780         newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType
1781 
1782         return end(ensureSymbol(destType, binaryNode));
1783     }
1784 
1785     private Expression ensureSymbol(final Type type, final Expression expr) {
1786         LOG.info("New TEMPORARY added to ", lc.getCurrentFunction().getName(), " type=", type);
1787         return temporarySymbols.ensureSymbol(lc, type, expr);
1788     }
1789 
1790     private Symbol newInternal(final String name, final Type type) {
1791         final Symbol iter = defineSymbol(lc.getCurrentBlock(), name, IS_VAR | IS_INTERNAL);
1792         iter.setType(type); // NASHORN-73
1793         return iter;
1794     }
1795 
1796     private static void newType(final Symbol symbol, final Type type) {
1797         final Type oldType = symbol.getSymbolType();
1798         symbol.setType(type);
1799 
1800         if (symbol.getSymbolType() != oldType) {
1801             LOG.info("New TYPE ", type, " for ", symbol," (was ", oldType, ")");
1802         }
1803 
1804         if (symbol.isParam()) {
1805             symbol.setType(type);
1806             LOG.info("Param type change ", symbol);
1807         }
1808     }
1809 
1810     private void pushLocalsFunction() {
1811         localDefs.push(new HashSet<String>());
1812         localUses.push(new HashSet<String>());
1813     }
1814 
1815     private void pushLocalsBlock() {
1816         localDefs.push(new HashSet<>(localDefs.peek()));
1817         localUses.push(new HashSet<>(localUses.peek()));
1818     }
1819 
1820     private void popLocals() {
1821         localDefs.pop();
1822         localUses.pop();
1823     }
1824 
1825     private boolean isLocalDef(final String name) {
1826         return localDefs.peek().contains(name);
1827     }
1828 
1829     private void addLocalDef(final String name) {
1830         LOG.info("Adding local def of symbol: '", name, "'");
1831         localDefs.peek().add(name);
1832     }
1833 
1834     private void removeLocalDef(final String name) {
1835         LOG.info("Removing local def of symbol: '", name, "'");
1836         localDefs.peek().remove(name);
1837     }
1838 
1839     private boolean isLocalUse(final String name) {
1840         return localUses.peek().contains(name);
1841     }
1842 
1843     private void addLocalUse(final String name) {
1844         LOG.info("Adding local use of symbol: '", name, "'");
1845         localUses.peek().add(name);
1846     }
1847 
1848     /**
1849      * Pessimistically promote all symbols in current function node to Object types
1850      * This is done when the function contains unevaluated black boxes such as
1851      * lazy sub-function nodes that have not been compiled.
1852      *
1853      * @param body body for the function node we are leaving
1854      */
1855     private static void objectifySymbols(final Block body) {
1856         body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
1857             private void toObject(final Block block) {
1858                 for (final Symbol symbol : block.getSymbols()) {
1859                     if (!symbol.isTemp()) {
1860                         newType(symbol, Type.OBJECT);
1861                     }
1862                 }
1863             }
1864 
1865             @Override
1866             public boolean enterBlock(final Block block) {
1867                 toObject(block);
1868                 return true;
1869             }
1870 
1871             @Override
1872             public boolean enterFunctionNode(final FunctionNode node) {
1873                 return false;
1874             }
1875         });
1876     }
1877 
1878     private static String name(final Node node) {
1879         final String cn = node.getClass().getName();
1880         int lastDot = cn.lastIndexOf('.');
1881         if (lastDot == -1) {
1882             return cn;
1883         }
1884         return cn.substring(lastDot + 1);
1885     }
1886 
1887     private boolean start(final Node node) {
1888         return start(node, true);
1889     }
1890 
1891     private boolean start(final Node node, final boolean printNode) {
1892         if (DEBUG) {
1893             final StringBuilder sb = new StringBuilder();
1894 
1895             sb.append("[ENTER ").
1896                 append(name(node)).
1897                 append("] ").
1898                 append(printNode ? node.toString() : "").
1899                 append(" in '").
1900                 append(lc.getCurrentFunction().getName()).
1901                 append("'");
1902             LOG.info(sb);
1903             LOG.indent();
1904         }
1905 
1906         return true;
1907     }
1908 
1909     private <T extends Node> T end(final T node) {
1910         return end(node, true);
1911     }
1912 
1913     private <T extends Node> T end(final T node, final boolean printNode) {
1914         if(node instanceof Statement) {
1915             // If we're done with a statement, all temporaries can be reused.
1916             temporarySymbols.reuse();
1917         }
1918         if (DEBUG) {
1919             final StringBuilder sb = new StringBuilder();
1920 
1921             sb.append("[LEAVE ").
1922                 append(name(node)).
1923                 append("] ").
1924                 append(printNode ? node.toString() : "").
1925                 append(" in '").
1926                 append(lc.getCurrentFunction().getName()).
1927                 append('\'');
1928 
1929             if (node instanceof Expression) {
1930                 final Symbol symbol = ((Expression)node).getSymbol();
1931                 if (symbol == null) {
1932                     sb.append(" <NO SYMBOL>");
1933                 } else {
1934                     sb.append(" <symbol=").append(symbol).append('>');
1935                 }
1936             }
1937 
1938             LOG.unindent();
1939             LOG.info(sb);
1940         }
1941 
1942         return node;
1943     }
1944 }