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