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.RETURN;
  29 import static jdk.nashorn.internal.ir.Expression.isAlwaysFalse;
  30 import static jdk.nashorn.internal.ir.Expression.isAlwaysTrue;
  31 
  32 import java.util.ArrayDeque;
  33 import java.util.ArrayList;
  34 import java.util.Collections;
  35 import java.util.Deque;
  36 import java.util.HashSet;
  37 import java.util.IdentityHashMap;
  38 import java.util.Iterator;
  39 import java.util.LinkedList;
  40 import java.util.List;
  41 import java.util.Map;
  42 import java.util.Set;
  43 import jdk.nashorn.internal.codegen.types.Type;
  44 import jdk.nashorn.internal.ir.AccessNode;
  45 import jdk.nashorn.internal.ir.BinaryNode;
  46 import jdk.nashorn.internal.ir.Block;
  47 import jdk.nashorn.internal.ir.BreakNode;
  48 import jdk.nashorn.internal.ir.BreakableNode;
  49 import jdk.nashorn.internal.ir.CallNode;
  50 import jdk.nashorn.internal.ir.CaseNode;
  51 import jdk.nashorn.internal.ir.CatchNode;
  52 import jdk.nashorn.internal.ir.ContinueNode;
  53 import jdk.nashorn.internal.ir.Expression;
  54 import jdk.nashorn.internal.ir.ExpressionStatement;
  55 import jdk.nashorn.internal.ir.ForNode;
  56 import jdk.nashorn.internal.ir.FunctionNode;
  57 import jdk.nashorn.internal.ir.GetSplitState;
  58 import jdk.nashorn.internal.ir.IdentNode;
  59 import jdk.nashorn.internal.ir.IfNode;
  60 import jdk.nashorn.internal.ir.IndexNode;
  61 import jdk.nashorn.internal.ir.JoinPredecessor;
  62 import jdk.nashorn.internal.ir.JoinPredecessorExpression;
  63 import jdk.nashorn.internal.ir.JumpStatement;
  64 import jdk.nashorn.internal.ir.JumpToInlinedFinally;
  65 import jdk.nashorn.internal.ir.LabelNode;
  66 import jdk.nashorn.internal.ir.LexicalContext;
  67 import jdk.nashorn.internal.ir.LexicalContextNode;
  68 import jdk.nashorn.internal.ir.LiteralNode;
  69 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
  70 import jdk.nashorn.internal.ir.LocalVariableConversion;
  71 import jdk.nashorn.internal.ir.LoopNode;
  72 import jdk.nashorn.internal.ir.Node;
  73 import jdk.nashorn.internal.ir.ObjectNode;
  74 import jdk.nashorn.internal.ir.PropertyNode;
  75 import jdk.nashorn.internal.ir.ReturnNode;
  76 import jdk.nashorn.internal.ir.RuntimeNode;
  77 import jdk.nashorn.internal.ir.RuntimeNode.Request;
  78 import jdk.nashorn.internal.ir.SplitReturn;
  79 import jdk.nashorn.internal.ir.Statement;
  80 import jdk.nashorn.internal.ir.SwitchNode;
  81 import jdk.nashorn.internal.ir.Symbol;
  82 import jdk.nashorn.internal.ir.TernaryNode;
  83 import jdk.nashorn.internal.ir.ThrowNode;
  84 import jdk.nashorn.internal.ir.TryNode;
  85 import jdk.nashorn.internal.ir.UnaryNode;
  86 import jdk.nashorn.internal.ir.VarNode;
  87 import jdk.nashorn.internal.ir.WhileNode;
  88 import jdk.nashorn.internal.ir.WithNode;
  89 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
  90 import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor;
  91 import jdk.nashorn.internal.parser.TokenType;
  92 
  93 /**
  94  * Calculates types for local variables. For purposes of local variable type calculation, the only types used are
  95  * Undefined, boolean, int, long, double, and Object. The calculation eagerly widens types of local variable to their
  96  * widest at control flow join points.
  97  * TODO: investigate a more sophisticated solution that uses use/def information to only widens the type of a local
  98  * variable to its widest used type after the join point. That would eliminate some widenings of undefined variables to
  99  * object, most notably those used only in loops. We need a full liveness analysis for that. Currently, we can establish
 100  * per-type liveness, which eliminates most of unwanted dead widenings.
 101  * NOTE: the way this class is implemented, it actually processes the AST in two passes. The first pass is top-down and
 102  * implemented in {@code enterXxx} methods. This pass does not mutate the AST (except for one occurrence, noted below),
 103  * as being able to find relevant labels for control flow joins is sensitive to their reference identity, and mutated
 104  * label-carrying nodes will create copies of their labels. A second bottom-up pass applying the changes is implemented
 105  * in the separate visitor sitting in {@link #leaveFunctionNode(FunctionNode)}. This visitor will also instantiate new
 106  * instances of the calculator to be run on nested functions (when not lazy compiling).
 107  *
 108  */
 109 final class LocalVariableTypesCalculator extends SimpleNodeVisitor {
 110 
 111     private static class JumpOrigin {
 112         final JoinPredecessor node;
 113         final Map<Symbol, LvarType> types;
 114 
 115         JumpOrigin(final JoinPredecessor node, final Map<Symbol, LvarType> types) {
 116             this.node = node;
 117             this.types = types;
 118         }
 119     }
 120 
 121     private static class JumpTarget {
 122         private final List<JumpOrigin> origins = new LinkedList<>();
 123         private Map<Symbol, LvarType> types = Collections.emptyMap();
 124 
 125         void addOrigin(final JoinPredecessor originNode, final Map<Symbol, LvarType> originTypes) {
 126             origins.add(new JumpOrigin(originNode, originTypes));
 127             this.types = getUnionTypes(this.types, originTypes);
 128         }
 129     }
 130     private enum LvarType {
 131         UNDEFINED(Type.UNDEFINED),
 132         BOOLEAN(Type.BOOLEAN),
 133         INT(Type.INT),
 134         DOUBLE(Type.NUMBER),
 135         OBJECT(Type.OBJECT);
 136 
 137         private final Type type;
 138         private final TypeHolderExpression typeExpression;
 139 
 140         private LvarType(final Type type) {
 141             this.type = type;
 142             this.typeExpression = new TypeHolderExpression(type);
 143         }
 144     }
 145 
 146     /**
 147      * A bogus Expression subclass that only reports its type. Used to interrogate BinaryNode and UnaryNode about their
 148      * types by creating temporary copies of them and replacing their operands with instances of these. An alternative
 149      * solution would be to add BinaryNode.getType(Type lhsType, Type rhsType) and UnaryNode.getType(Type exprType)
 150      * methods. For the time being though, this is easier to implement and is in fact fairly clean. It does result in
 151      * generation of higher number of temporary short lived nodes, though.
 152      */
 153     private static class TypeHolderExpression extends Expression {
 154         private static final long serialVersionUID = 1L;
 155 
 156         private final Type type;
 157 
 158         TypeHolderExpression(final Type type) {
 159             super(0L, 0, 0);
 160             this.type = type;
 161         }
 162 
 163         @Override
 164         public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
 165             throw new AssertionError();
 166         }
 167 
 168         @Override
 169         public Type getType() {
 170             return type;
 171         }
 172 
 173         @Override
 174         public void toString(final StringBuilder sb, final boolean printType) {
 175             throw new AssertionError();
 176         }
 177     }
 178 
 179     private static final Map<Type, LvarType> TO_LVAR_TYPE = new IdentityHashMap<>();
 180 
 181     static {
 182         for(final LvarType lvarType: LvarType.values()) {
 183             TO_LVAR_TYPE.put(lvarType.type, lvarType);
 184         }
 185     }
 186 
 187     @SuppressWarnings("unchecked")
 188     private static IdentityHashMap<Symbol, LvarType> cloneMap(final Map<Symbol, LvarType> map) {
 189         return (IdentityHashMap<Symbol, LvarType>)((IdentityHashMap<?,?>)map).clone();
 190     }
 191 
 192     private LocalVariableConversion createConversion(final Symbol symbol, final LvarType branchLvarType,
 193             final Map<Symbol, LvarType> joinLvarTypes, final LocalVariableConversion next) {
 194         final LvarType targetType = joinLvarTypes.get(symbol);
 195         assert targetType != null;
 196         if(targetType == branchLvarType) {
 197             return next;
 198         }
 199         // NOTE: we could naively just use symbolIsUsed(symbol, branchLvarType) here, but that'd be wrong. While
 200         // technically a conversion will read the value of the symbol with that type, but it will also write it to a new
 201         // type, and that type might be dead (we can't know yet). For this reason, we don't treat conversion reads as
 202         // real uses until we know their target type is live. If we didn't do this, and just did a symbolIsUsed here,
 203         // we'd introduce false live variables which could nevertheless turn into dead ones in a subsequent
 204         // deoptimization, causing a shift in the list of live locals that'd cause erroneous restoration of
 205         // continuations (since RewriteException's byteCodeSlots carries an array and not a name-value map).
 206 
 207         symbolIsConverted(symbol, branchLvarType, targetType);
 208         return new LocalVariableConversion(symbol, branchLvarType.type, targetType.type, next);
 209     }
 210 
 211     private static Map<Symbol, LvarType> getUnionTypes(final Map<Symbol, LvarType> types1, final Map<Symbol, LvarType> types2) {
 212         if(types1 == types2 || types1.isEmpty()) {
 213             return types2;
 214         } else if(types2.isEmpty()) {
 215             return types1;
 216         }
 217         final Set<Symbol> commonSymbols = new HashSet<>(types1.keySet());
 218         commonSymbols.retainAll(types2.keySet());
 219         // We have a chance of returning an unmodified set if both sets have the same keys and one is strictly wider
 220         // than the other.
 221         final int commonSize = commonSymbols.size();
 222         final int types1Size = types1.size();
 223         final int types2Size = types2.size();
 224         if(commonSize == types1Size && commonSize == types2Size) {
 225             boolean matches1 = true, matches2 = true;
 226             Map<Symbol, LvarType> union = null;
 227             for(final Symbol symbol: commonSymbols) {
 228                 final LvarType type1 = types1.get(symbol);
 229                 final LvarType type2 = types2.get(symbol);
 230                 final LvarType widest = widestLvarType(type1, type2);
 231                 if(widest != type1 && matches1) {
 232                     matches1 = false;
 233                     if(!matches2) {
 234                         union = cloneMap(types1);
 235                     }
 236                 }
 237                 if (widest != type2 && matches2) {
 238                     matches2 = false;
 239                     if(!matches1) {
 240                         union = cloneMap(types2);
 241                     }
 242                 }
 243                 if(!(matches1 || matches2)) {
 244                     assert union != null;
 245                     union.put(symbol, widest);
 246                 }
 247             }
 248             return matches1 ? types1 : matches2 ? types2 : union;
 249         }
 250         // General case
 251         final Map<Symbol, LvarType> union;
 252         if(types1Size > types2Size) {
 253             union = cloneMap(types1);
 254             union.putAll(types2);
 255         } else {
 256             union = cloneMap(types2);
 257             union.putAll(types1);
 258         }
 259         for(final Symbol symbol: commonSymbols) {
 260             final LvarType type1 = types1.get(symbol);
 261             final LvarType type2 = types2.get(symbol);
 262             union.put(symbol, widestLvarType(type1,  type2));
 263         }
 264         return union;
 265     }
 266 
 267     private static void symbolIsUsed(final Symbol symbol, final LvarType type) {
 268         if(type != LvarType.UNDEFINED) {
 269             symbol.setHasSlotFor(type.type);
 270         }
 271     }
 272 
 273     private static class SymbolConversions {
 274         private static final byte I2D = 1 << 0;
 275         private static final byte I2O = 1 << 1;
 276         private static final byte D2O = 1 << 2;
 277 
 278         private byte conversions;
 279 
 280         void recordConversion(final LvarType from, final LvarType to) {
 281             switch (from) {
 282             case UNDEFINED:
 283                 return;
 284             case INT:
 285             case BOOLEAN:
 286                 switch (to) {
 287                 case DOUBLE:
 288                     recordConversion(I2D);
 289                     return;
 290                 case OBJECT:
 291                     recordConversion(I2O);
 292                     return;
 293                 default:
 294                     illegalConversion(from, to);
 295                     return;
 296                 }
 297             case DOUBLE:
 298                 if(to == LvarType.OBJECT) {
 299                     recordConversion(D2O);
 300                 }
 301                 return;
 302             default:
 303                 illegalConversion(from, to);
 304             }
 305         }
 306 
 307         private static void illegalConversion(final LvarType from, final LvarType to) {
 308             throw new AssertionError("Invalid conversion from " + from + " to " + to);
 309         }
 310 
 311         void recordConversion(final byte convFlag) {
 312             conversions = (byte)(conversions | convFlag);
 313         }
 314 
 315         boolean hasConversion(final byte convFlag) {
 316             return (conversions & convFlag) != 0;
 317         }
 318 
 319         void calculateTypeLiveness(final Symbol symbol) {
 320             if(symbol.hasSlotFor(Type.OBJECT)) {
 321                 if(hasConversion(D2O)) {
 322                     symbol.setHasSlotFor(Type.NUMBER);
 323                 }
 324                 if(hasConversion(I2O)) {
 325                     symbol.setHasSlotFor(Type.INT);
 326                 }
 327             }
 328             if(symbol.hasSlotFor(Type.NUMBER)) {
 329                 if(hasConversion(I2D)) {
 330                     symbol.setHasSlotFor(Type.INT);
 331                 }
 332             }
 333         }
 334     }
 335 
 336     private void symbolIsConverted(final Symbol symbol, final LvarType from, final LvarType to) {
 337         SymbolConversions conversions = symbolConversions.get(symbol);
 338         if(conversions == null) {
 339             conversions = new SymbolConversions();
 340             symbolConversions.put(symbol, conversions);
 341         }
 342         conversions.recordConversion(from, to);
 343     }
 344 
 345     private static LvarType toLvarType(final Type type) {
 346         assert type != null;
 347         final LvarType lvarType = TO_LVAR_TYPE.get(type);
 348         if(lvarType != null) {
 349             return lvarType;
 350         }
 351         assert type.isObject() : "Unsupported primitive type: " + type;
 352         return LvarType.OBJECT;
 353     }
 354     private static LvarType widestLvarType(final LvarType t1, final LvarType t2) {
 355         if(t1 == t2) {
 356             return t1;
 357         }
 358         // Undefined or boolean to anything always widens to object.
 359         if(t1.ordinal() < LvarType.INT.ordinal() || t2.ordinal() < LvarType.INT.ordinal()) {
 360             return LvarType.OBJECT;
 361         }
 362         // NOTE: we allow "widening" of long to double even though it can lose precision. ECMAScript doesn't have an
 363         // Int64 type anyway, so this loss of precision is actually more conformant to the specification...
 364         return LvarType.values()[Math.max(t1.ordinal(), t2.ordinal())];
 365     }
 366     private final Compiler compiler;
 367     private final Map<Label, JumpTarget> jumpTargets = new IdentityHashMap<>();
 368     // Local variable type mapping at the currently evaluated point. No map instance is ever modified; setLvarType() always
 369     // allocates a new map. Immutability of maps allows for cheap snapshots by just keeping the reference to the current
 370     // value.
 371     private Map<Symbol, LvarType> localVariableTypes = new IdentityHashMap<>();
 372     // Stack for evaluated expression types.
 373     private final Deque<LvarType> typeStack = new ArrayDeque<>();
 374 
 375     // Whether the current point in the AST is reachable code
 376     private boolean reachable = true;
 377     // Return type of the function
 378     private Type returnType = Type.UNKNOWN;
 379     // Synthetic return node that we must insert at the end of the function if it's end is reachable.
 380     private ReturnNode syntheticReturn;
 381 
 382     private boolean alreadyEnteredTopLevelFunction;
 383 
 384     // LvarType and conversion information gathered during the top-down pass; applied to nodes in the bottom-up pass.
 385     private final Map<JoinPredecessor, LocalVariableConversion> localVariableConversions = new IdentityHashMap<>();
 386 
 387     private final Map<IdentNode, LvarType> identifierLvarTypes = new IdentityHashMap<>();
 388     private final Map<Symbol, SymbolConversions> symbolConversions = new IdentityHashMap<>();
 389 
 390     // Stack of open labels for starts of catch blocks, one for every currently traversed try block; for inserting
 391     // control flow edges to them. Note that we currently don't insert actual control flow edges, but instead edges that
 392     // help us with type calculations. This means that some operations that can result in an exception being thrown
 393     // aren't considered (function calls, side effecting property getters and setters etc.), while some operations that
 394     // don't result in control flow transfers do originate an edge to the catch blocks (namely, assignments to local
 395     // variables).
 396     private final Deque<Label> catchLabels = new ArrayDeque<>();
 397 
 398     LocalVariableTypesCalculator(final Compiler compiler) {
 399         this.compiler = compiler;
 400     }
 401 
 402     private JumpTarget createJumpTarget(final Label label) {
 403         assert !jumpTargets.containsKey(label);
 404         final JumpTarget jumpTarget = new JumpTarget();
 405         jumpTargets.put(label, jumpTarget);
 406         return jumpTarget;
 407     }
 408 
 409     private void doesNotContinueSequentially() {
 410         reachable = false;
 411         localVariableTypes = Collections.emptyMap();
 412         assertTypeStackIsEmpty();
 413     }
 414 
 415     private boolean pushExpressionType(final Expression expr) {
 416         typeStack.push(toLvarType(expr.getType()));
 417         return false;
 418     }
 419 
 420     @Override
 421     public boolean enterAccessNode(final AccessNode accessNode) {
 422         visitExpression(accessNode.getBase());
 423         return pushExpressionType(accessNode);
 424     }
 425 
 426     @Override
 427     public boolean enterBinaryNode(final BinaryNode binaryNode) {
 428         // NOTE: regardless of operator's lexical associativity, lhs is always evaluated first.
 429         final Expression lhs = binaryNode.lhs();
 430         final LvarType lhsType;
 431         if (!(lhs instanceof IdentNode && binaryNode.isTokenType(TokenType.ASSIGN))) {
 432             lhsType = visitExpression(lhs);
 433         } else {
 434             // Can't visit IdentNode on LHS of a simple assignment, as visits imply use, and this is def.
 435             // The type is irrelevant, as only RHS is used to determine the type anyway.
 436             lhsType = LvarType.UNDEFINED;
 437         }
 438 
 439         final boolean isLogical = binaryNode.isLogical();
 440         final Label joinLabel = isLogical ? new Label("") : null;
 441         if(isLogical) {
 442             jumpToLabel((JoinPredecessor)lhs, joinLabel);
 443         }
 444 
 445         final Expression rhs = binaryNode.rhs();
 446         final LvarType rhsType = visitExpression(rhs);
 447         if(isLogical) {
 448             jumpToLabel((JoinPredecessor)rhs, joinLabel);
 449         }
 450         joinOnLabel(joinLabel);
 451 
 452         final LvarType type = toLvarType(binaryNode.setOperands(lhsType.typeExpression, rhsType.typeExpression).getType());
 453 
 454         if(binaryNode.isAssignment() && lhs instanceof IdentNode) {
 455             if(binaryNode.isSelfModifying()) {
 456                 onSelfAssignment((IdentNode)lhs, type);
 457             } else {
 458                 onAssignment((IdentNode)lhs, type);
 459             }
 460         }
 461         typeStack.push(type);
 462         return false;
 463     }
 464 
 465     @Override
 466     public boolean enterBlock(final Block block) {
 467         for(final Symbol symbol: block.getSymbols()) {
 468             if(symbol.isBytecodeLocal() && getLocalVariableTypeOrNull(symbol) == null) {
 469                 setType(symbol, LvarType.UNDEFINED);
 470             }
 471         }
 472         return true;
 473     }
 474 
 475     @Override
 476     public boolean enterBreakNode(final BreakNode breakNode) {
 477         return enterJumpStatement(breakNode);
 478     }
 479 
 480     @Override
 481     public boolean enterCallNode(final CallNode callNode) {
 482         visitExpression(callNode.getFunction());
 483         visitExpressions(callNode.getArgs());
 484         final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
 485         if (evalArgs != null) {
 486             visitExpressions(evalArgs.getArgs());
 487         }
 488         return pushExpressionType(callNode);
 489     }
 490 
 491     @Override
 492     public boolean enterContinueNode(final ContinueNode continueNode) {
 493         return enterJumpStatement(continueNode);
 494     }
 495 
 496     private boolean enterJumpStatement(final JumpStatement jump) {
 497         if(!reachable) {
 498             return false;
 499         }
 500         assertTypeStackIsEmpty();
 501         jumpToLabel(jump, jump.getTargetLabel(lc), getBreakTargetTypes(jump.getPopScopeLimit(lc)));
 502         doesNotContinueSequentially();
 503         return false;
 504     }
 505 
 506     @Override
 507     protected boolean enterDefault(final Node node) {
 508         return reachable;
 509     }
 510 
 511     private void enterDoWhileLoop(final WhileNode loopNode) {
 512         assertTypeStackIsEmpty();
 513         final JoinPredecessorExpression test = loopNode.getTest();
 514         final Block body = loopNode.getBody();
 515         final Label continueLabel = loopNode.getContinueLabel();
 516         final Label breakLabel = loopNode.getBreakLabel();
 517         final Map<Symbol, LvarType> beforeLoopTypes = localVariableTypes;
 518         final Label repeatLabel = new Label("");
 519         for(;;) {
 520             jumpToLabel(loopNode, repeatLabel, beforeLoopTypes);
 521             final Map<Symbol, LvarType> beforeRepeatTypes = localVariableTypes;
 522             body.accept(this);
 523             if(reachable) {
 524                 jumpToLabel(body, continueLabel);
 525             }
 526             joinOnLabel(continueLabel);
 527             if(!reachable) {
 528                 break;
 529             }
 530             visitExpressionOnEmptyStack(test);
 531             jumpToLabel(test, breakLabel);
 532             if(isAlwaysFalse(test)) {
 533                 break;
 534             }
 535             jumpToLabel(test, repeatLabel);
 536             joinOnLabel(repeatLabel);
 537             if(localVariableTypes.equals(beforeRepeatTypes)) {
 538                 break;
 539             }
 540             resetJoinPoint(continueLabel);
 541             resetJoinPoint(breakLabel);
 542             resetJoinPoint(repeatLabel);
 543         }
 544 
 545         if(isAlwaysTrue(test)) {
 546             doesNotContinueSequentially();
 547         }
 548 
 549         leaveBreakable(loopNode);
 550     }
 551 
 552     @Override
 553     public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
 554         if (reachable) {
 555             visitExpressionOnEmptyStack(expressionStatement.getExpression());
 556         }
 557         return false;
 558     }
 559 
 560     private void assertTypeStackIsEmpty() {
 561         assert typeStack.isEmpty();
 562     }
 563 
 564     @Override
 565     protected Node leaveDefault(final Node node) {
 566         assert !(node instanceof Expression); // All expressions were handled
 567         assert !(node instanceof Statement) || typeStack.isEmpty(); // No statements leave with a non-empty stack
 568         return node;
 569     }
 570 
 571     private LvarType visitExpressionOnEmptyStack(final Expression expr) {
 572         assertTypeStackIsEmpty();
 573         return visitExpression(expr);
 574     }
 575 
 576     private LvarType visitExpression(final Expression expr) {
 577         final int stackSize = typeStack.size();
 578         expr.accept(this);
 579         assert typeStack.size() == stackSize + 1;
 580         return typeStack.pop();
 581     }
 582 
 583     private void visitExpressions(final List<Expression> exprs) {
 584         for(final Expression expr: exprs) {
 585             if (expr != null) {
 586                 visitExpression(expr);
 587             }
 588         }
 589     }
 590 
 591     @Override
 592     public boolean enterForNode(final ForNode forNode) {
 593         if(!reachable) {
 594             return false;
 595         }
 596 
 597         final Expression init = forNode.getInit();
 598         if(forNode.isForInOrOf()) {
 599             final JoinPredecessorExpression iterable = forNode.getModify();
 600             visitExpression(iterable);
 601             enterTestFirstLoop(forNode, null, init,
 602                     // If we're iterating over property names, and we can discern from the runtime environment
 603                     // of the compilation that the object being iterated over must use strings for property
 604                     // names (e.g., it is a native JS object or array), then we'll not bother trying to treat
 605                     // the property names optimistically.
 606                     !compiler.useOptimisticTypes() || (!forNode.isForEach() && compiler.hasStringPropertyIterator(iterable.getExpression())));
 607         } else {
 608             if(init != null) {
 609                 visitExpressionOnEmptyStack(init);
 610             }
 611             enterTestFirstLoop(forNode, forNode.getModify(), null, false);
 612         }
 613         assertTypeStackIsEmpty();
 614         return false;
 615     }
 616 
 617     @Override
 618     public boolean enterFunctionNode(final FunctionNode functionNode) {
 619         if(alreadyEnteredTopLevelFunction) {
 620             typeStack.push(LvarType.OBJECT);
 621             return false;
 622         }
 623         int pos = 0;
 624         if(!functionNode.isVarArg()) {
 625             for (final IdentNode param : functionNode.getParameters()) {
 626                 final Symbol symbol = param.getSymbol();
 627                 // Parameter is not necessarily bytecode local as it can be scoped due to nested context use, but it
 628                 // must have a slot if we aren't in a function with vararg signature.
 629                 assert symbol.hasSlot();
 630                 final Type callSiteParamType = compiler.getParamType(functionNode, pos);
 631                 final LvarType paramType = callSiteParamType == null ? LvarType.OBJECT : toLvarType(callSiteParamType);
 632                 setType(symbol, paramType);
 633                 // Make sure parameter slot for its incoming value is not marked dead. NOTE: this is a heuristic. Right
 634                 // now, CodeGenerator.expandParameters() relies on the fact that every parameter's final slot width will
 635                 // be at least the same as incoming width, therefore even if a parameter is never read, we'll still keep
 636                 // its slot.
 637                 symbolIsUsed(symbol);
 638                 setIdentifierLvarType(param, paramType);
 639                 pos++;
 640             }
 641         }
 642         setCompilerConstantAsObject(functionNode, CompilerConstants.THIS);
 643 
 644         // TODO: coarse-grained. If we wanted to solve it completely precisely,
 645         // we'd also need to push/pop its type when handling WithNode (so that
 646         // it can go back to undefined after a 'with' block.
 647         if(functionNode.hasScopeBlock() || functionNode.needsParentScope()) {
 648             setCompilerConstantAsObject(functionNode, CompilerConstants.SCOPE);
 649         }
 650         if(functionNode.needsCallee()) {
 651             setCompilerConstantAsObject(functionNode, CompilerConstants.CALLEE);
 652         }
 653         if(functionNode.needsArguments()) {
 654             setCompilerConstantAsObject(functionNode, CompilerConstants.ARGUMENTS);
 655         }
 656 
 657         alreadyEnteredTopLevelFunction = true;
 658         return true;
 659     }
 660 
 661     @Override
 662     public boolean enterGetSplitState(final GetSplitState getSplitState) {
 663         return pushExpressionType(getSplitState);
 664     }
 665 
 666     @Override
 667     public boolean enterIdentNode(final IdentNode identNode) {
 668         final Symbol symbol = identNode.getSymbol();
 669         if(symbol.isBytecodeLocal()) {
 670             symbolIsUsed(symbol);
 671             final LvarType type = getLocalVariableType(symbol);
 672             setIdentifierLvarType(identNode, type);
 673             typeStack.push(type);
 674         } else {
 675             pushExpressionType(identNode);
 676         }
 677         return false;
 678     }
 679 
 680     @Override
 681     public boolean enterIfNode(final IfNode ifNode) {
 682         processIfNode(ifNode);
 683         return false;
 684     }
 685 
 686     private void processIfNode(final IfNode ifNode) {
 687         if(!reachable) {
 688             return;
 689         }
 690 
 691         final Expression test = ifNode.getTest();
 692         final Block pass = ifNode.getPass();
 693         final Block fail = ifNode.getFail();
 694 
 695         visitExpressionOnEmptyStack(test);
 696 
 697         final Map<Symbol, LvarType> passLvarTypes;
 698         final boolean reachableFromPass;
 699         final boolean isTestAlwaysTrue = isAlwaysTrue(test);
 700         if(isAlwaysFalse(test)) {
 701             passLvarTypes = null;
 702             reachableFromPass = false;
 703         } else {
 704             final Map<Symbol, LvarType> afterTestLvarTypes = localVariableTypes;
 705             pass.accept(this);
 706             assertTypeStackIsEmpty();
 707             if (isTestAlwaysTrue) {
 708                 return;
 709             }
 710             passLvarTypes = localVariableTypes;
 711             reachableFromPass = reachable;
 712             localVariableTypes = afterTestLvarTypes;
 713             reachable = true;
 714         }
 715 
 716         // If we get here, then we need to consider the case where pass block is not executed
 717         assert !isTestAlwaysTrue;
 718 
 719         if (fail != null) {
 720             fail.accept(this);
 721             assertTypeStackIsEmpty();
 722         }
 723 
 724         if(reachable) {
 725             if(reachableFromPass) {
 726                 final Map<Symbol, LvarType> failLvarTypes = localVariableTypes;
 727                 localVariableTypes = getUnionTypes(passLvarTypes, failLvarTypes);
 728                 setConversion(pass, passLvarTypes, localVariableTypes);
 729                 // IfNode itself is associated with conversions that might need to be performed after the test if
 730                 // there's no else branch. E.g.
 731                 // if(x = 1, cond) { x = 1.0 } must widen "x = 1" to a double.
 732                 setConversion(fail != null ? fail : ifNode, failLvarTypes, localVariableTypes);
 733             }
 734         } else if (reachableFromPass) {
 735             assert passLvarTypes != null;
 736             localVariableTypes = passLvarTypes;
 737             reachable = true;
 738         }
 739     }
 740 
 741     @Override
 742     public boolean enterIndexNode(final IndexNode indexNode) {
 743         visitExpression(indexNode.getBase());
 744         visitExpression(indexNode.getIndex());
 745         return pushExpressionType(indexNode);
 746     }
 747 
 748     @Override
 749     public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinExpr) {
 750         final Expression expr = joinExpr.getExpression();
 751         if (expr != null) {
 752             expr.accept(this);
 753         } else {
 754             typeStack.push(LvarType.UNDEFINED);
 755         }
 756         return false;
 757     }
 758 
 759     @Override
 760     public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) {
 761         return enterJumpStatement(jumpToInlinedFinally);
 762     }
 763 
 764     @Override
 765     public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
 766         if (literalNode instanceof ArrayLiteralNode) {
 767             final List<Expression> expressions = ((ArrayLiteralNode)literalNode).getElementExpressions();
 768             if (expressions != null) {
 769                 visitExpressions(expressions);
 770             }
 771         }
 772         pushExpressionType(literalNode);
 773         return false;
 774     }
 775 
 776     @Override
 777     public boolean enterObjectNode(final ObjectNode objectNode) {
 778         for(final PropertyNode propertyNode: objectNode.getElements()) {
 779             // Avoid falsely adding property keys to the control flow graph
 780             final Expression value = propertyNode.getValue();
 781             if (value != null) {
 782                 visitExpression(value);
 783             }
 784         }
 785         return pushExpressionType(objectNode);
 786     }
 787 
 788     @Override
 789     public boolean enterPropertyNode(final PropertyNode propertyNode) {
 790         // Property nodes are only accessible through object literals, and we handled that case above
 791         throw new AssertionError();
 792     }
 793 
 794     @Override
 795     public boolean enterReturnNode(final ReturnNode returnNode) {
 796         if(!reachable) {
 797             return false;
 798         }
 799 
 800         final Expression returnExpr = returnNode.getExpression();
 801         final Type returnExprType;
 802         if(returnExpr != null) {
 803             returnExprType = visitExpressionOnEmptyStack(returnExpr).type;
 804         } else {
 805             assertTypeStackIsEmpty();
 806             returnExprType = Type.UNDEFINED;
 807         }
 808         returnType = Type.widestReturnType(returnType, returnExprType);
 809         doesNotContinueSequentially();
 810         return false;
 811     }
 812 
 813     @Override
 814     public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
 815         visitExpressions(runtimeNode.getArgs());
 816         return pushExpressionType(runtimeNode);
 817     }
 818 
 819     @Override
 820     public boolean enterSplitReturn(final SplitReturn splitReturn) {
 821         doesNotContinueSequentially();
 822         return false;
 823     }
 824 
 825     @Override
 826     public boolean enterSwitchNode(final SwitchNode switchNode) {
 827         if(!reachable) {
 828             return false;
 829         }
 830 
 831         visitExpressionOnEmptyStack(switchNode.getExpression());
 832 
 833         final List<CaseNode> cases = switchNode.getCases();
 834         if(cases.isEmpty()) {
 835             return false;
 836         }
 837 
 838         // Control flow is different for all-integer cases where we dispatch by switch table, and for all other cases
 839         // where we do sequential comparison. Note that CaseNode objects act as join points.
 840         final boolean isInteger = switchNode.isUniqueInteger();
 841         final Label breakLabel = switchNode.getBreakLabel();
 842         final boolean hasDefault = switchNode.getDefaultCase() != null;
 843 
 844         boolean tagUsed = false;
 845         for(final CaseNode caseNode: cases) {
 846             final Expression test = caseNode.getTest();
 847             if(!isInteger && test != null) {
 848                 visitExpressionOnEmptyStack(test);
 849                 if(!tagUsed) {
 850                     symbolIsUsed(switchNode.getTag(), LvarType.OBJECT);
 851                     tagUsed = true;
 852                 }
 853             }
 854             // CaseNode carries the conversions that need to be performed on its entry from the test.
 855             // CodeGenerator ensures these are only emitted when arriving on the branch and not through a
 856             // fallthrough.
 857             jumpToLabel(caseNode, caseNode.getBody().getEntryLabel());
 858         }
 859         if(!hasDefault) {
 860             // No default case means we can arrive at the break label without entering any cases. In that case
 861             // SwitchNode will carry the conversions that need to be performed before it does that jump.
 862             jumpToLabel(switchNode, breakLabel);
 863         }
 864 
 865         // All cases are arrived at through jumps
 866         doesNotContinueSequentially();
 867 
 868         Block previousBlock = null;
 869         for(final CaseNode caseNode: cases) {
 870             final Block body = caseNode.getBody();
 871             final Label entryLabel = body.getEntryLabel();
 872             if(previousBlock != null && reachable) {
 873                 jumpToLabel(previousBlock, entryLabel);
 874             }
 875             joinOnLabel(entryLabel);
 876             assert reachable == true;
 877             body.accept(this);
 878             previousBlock = body;
 879         }
 880         if(previousBlock != null && reachable) {
 881             jumpToLabel(previousBlock, breakLabel);
 882         }
 883         leaveBreakable(switchNode);
 884         return false;
 885     }
 886 
 887     @Override
 888     public boolean enterTernaryNode(final TernaryNode ternaryNode) {
 889         final Expression test = ternaryNode.getTest();
 890         final Expression trueExpr = ternaryNode.getTrueExpression();
 891         final Expression falseExpr = ternaryNode.getFalseExpression();
 892 
 893         visitExpression(test);
 894 
 895         final Map<Symbol, LvarType> testExitLvarTypes = localVariableTypes;
 896         final LvarType trueType;
 897         if(!isAlwaysFalse(test)) {
 898             trueType = visitExpression(trueExpr);
 899         } else {
 900             trueType = null;
 901         }
 902         final Map<Symbol, LvarType> trueExitLvarTypes = localVariableTypes;
 903         localVariableTypes = testExitLvarTypes;
 904         final LvarType falseType;
 905         if(!isAlwaysTrue(test)) {
 906             falseType = visitExpression(falseExpr);
 907         } else {
 908             falseType = null;
 909         }
 910         final Map<Symbol, LvarType> falseExitLvarTypes = localVariableTypes;
 911         localVariableTypes = getUnionTypes(trueExitLvarTypes, falseExitLvarTypes);
 912         setConversion((JoinPredecessor)trueExpr, trueExitLvarTypes, localVariableTypes);
 913         setConversion((JoinPredecessor)falseExpr, falseExitLvarTypes, localVariableTypes);
 914 
 915         typeStack.push(trueType != null ? falseType != null ? widestLvarType(trueType, falseType) : trueType : assertNotNull(falseType));
 916         return false;
 917     }
 918 
 919     private static <T> T assertNotNull(final T t) {
 920         assert t != null;
 921         return t;
 922     }
 923 
 924     private void enterTestFirstLoop(final LoopNode loopNode, final JoinPredecessorExpression modify,
 925             final Expression iteratorValues, final boolean iteratorValuesAreObject) {
 926         final JoinPredecessorExpression test = loopNode.getTest();
 927         if(isAlwaysFalse(test)) {
 928             visitExpressionOnEmptyStack(test);
 929             return;
 930         }
 931 
 932         final Label continueLabel = loopNode.getContinueLabel();
 933         final Label breakLabel = loopNode.getBreakLabel();
 934 
 935         final Label repeatLabel = modify == null ? continueLabel : new Label("");
 936         final Map<Symbol, LvarType> beforeLoopTypes = localVariableTypes;
 937         for(;;) {
 938             jumpToLabel(loopNode, repeatLabel, beforeLoopTypes);
 939             final Map<Symbol, LvarType> beforeRepeatTypes = localVariableTypes;
 940             if(test != null) {
 941                 visitExpressionOnEmptyStack(test);
 942             }
 943             if(!isAlwaysTrue(test)) {
 944                 jumpToLabel(test, breakLabel);
 945             }
 946             if(iteratorValues instanceof IdentNode) {
 947                 final IdentNode ident = (IdentNode)iteratorValues;
 948                 // Receives iterator values; the optimistic type of the iterator values is tracked on the
 949                 // identifier, but we override optimism if it's known that the object being iterated over will
 950                 // never have primitive property names.
 951                 onAssignment(ident, iteratorValuesAreObject ? LvarType.OBJECT :
 952                     toLvarType(compiler.getOptimisticType(ident)));
 953             }
 954             final Block body = loopNode.getBody();
 955             body.accept(this);
 956             if(reachable) {
 957                 jumpToLabel(body, continueLabel);
 958             }
 959             joinOnLabel(continueLabel);
 960             if(!reachable) {
 961                 break;
 962             }
 963             if(modify != null) {
 964                 visitExpressionOnEmptyStack(modify);
 965                 jumpToLabel(modify, repeatLabel);
 966                 joinOnLabel(repeatLabel);
 967             }
 968             if(localVariableTypes.equals(beforeRepeatTypes)) {
 969                 break;
 970             }
 971             // Reset the join points and repeat the analysis
 972             resetJoinPoint(continueLabel);
 973             resetJoinPoint(breakLabel);
 974             resetJoinPoint(repeatLabel);
 975         }
 976 
 977         if(isAlwaysTrue(test) && iteratorValues == null) {
 978             doesNotContinueSequentially();
 979         }
 980 
 981         leaveBreakable(loopNode);
 982     }
 983 
 984     @Override
 985     public boolean enterThrowNode(final ThrowNode throwNode) {
 986         if(!reachable) {
 987             return false;
 988         }
 989 
 990         visitExpressionOnEmptyStack(throwNode.getExpression());
 991         jumpToCatchBlock(throwNode);
 992         doesNotContinueSequentially();
 993         return false;
 994     }
 995 
 996     @Override
 997     public boolean enterTryNode(final TryNode tryNode) {
 998         if(!reachable) {
 999             return false;
1000         }
1001 
1002         // This is the label for the join point at the entry of the catch blocks.
1003         final Label catchLabel = new Label("");
1004         catchLabels.push(catchLabel);
1005 
1006         // Presume that even the start of the try block can immediately go to the catch
1007         jumpToLabel(tryNode, catchLabel);
1008 
1009         final Block body = tryNode.getBody();
1010         body.accept(this);
1011         catchLabels.pop();
1012 
1013         // Final exit label for the whole try/catch construct (after the try block and after all catches).
1014         final Label endLabel = new Label("");
1015 
1016         boolean canExit = false;
1017         if(reachable) {
1018             jumpToLabel(body, endLabel);
1019             canExit = true;
1020         }
1021         doesNotContinueSequentially();
1022 
1023         for (final Block inlinedFinally : tryNode.getInlinedFinallies()) {
1024             final Block finallyBody = TryNode.getLabelledInlinedFinallyBlock(inlinedFinally);
1025             joinOnLabel(finallyBody.getEntryLabel());
1026             // NOTE: the jump to inlined finally can end up in dead code, so it is not necessarily reachable.
1027             if (reachable) {
1028                 finallyBody.accept(this);
1029                 // All inlined finallies end with a jump or a return
1030                 assert !reachable;
1031             }
1032         }
1033 
1034         joinOnLabel(catchLabel);
1035         for(final CatchNode catchNode: tryNode.getCatches()) {
1036             final IdentNode exception = catchNode.getException();
1037             onAssignment(exception, LvarType.OBJECT);
1038             final Expression condition = catchNode.getExceptionCondition();
1039             if(condition != null) {
1040                 visitExpression(condition);
1041             }
1042             final Map<Symbol, LvarType> afterConditionTypes = localVariableTypes;
1043             final Block catchBody = catchNode.getBody();
1044             // TODO: currently, we consider that the catch blocks are always reachable from the try block as currently
1045             // we lack enough analysis to prove that no statement before a break/continue/return in the try block can
1046             // throw an exception.
1047             reachable = true;
1048             catchBody.accept(this);
1049             final Symbol exceptionSymbol = exception.getSymbol();
1050             if(reachable) {
1051                 localVariableTypes = cloneMap(localVariableTypes);
1052                 localVariableTypes.remove(exceptionSymbol);
1053                 jumpToLabel(catchBody, endLabel);
1054                 canExit = true;
1055             }
1056             localVariableTypes = cloneMap(afterConditionTypes);
1057             localVariableTypes.remove(exceptionSymbol);
1058         }
1059         // NOTE: if we had one or more conditional catch blocks with no unconditional catch block following them, then
1060         // there will be an unconditional rethrow, so the join point can never be reached from the last
1061         // conditionExpression.
1062         doesNotContinueSequentially();
1063 
1064         if(canExit) {
1065             joinOnLabel(endLabel);
1066         }
1067 
1068         return false;
1069     }
1070 
1071 
1072     @Override
1073     public boolean enterUnaryNode(final UnaryNode unaryNode) {
1074         final Expression expr = unaryNode.getExpression();
1075         final LvarType unaryType = toLvarType(unaryNode.setExpression(visitExpression(expr).typeExpression).getType());
1076         if(unaryNode.isSelfModifying() && expr instanceof IdentNode) {
1077             onSelfAssignment((IdentNode)expr, unaryType);
1078         }
1079         typeStack.push(unaryType);
1080         return false;
1081     }
1082 
1083     @Override
1084     public boolean enterVarNode(final VarNode varNode) {
1085         if (!reachable) {
1086             return false;
1087         }
1088         final Expression init = varNode.getInit();
1089         if(init != null) {
1090             onAssignment(varNode.getName(), visitExpression(init));
1091         }
1092         return false;
1093     }
1094 
1095     @Override
1096     public boolean enterWhileNode(final WhileNode whileNode) {
1097         if(!reachable) {
1098             return false;
1099         }
1100         if(whileNode.isDoWhile()) {
1101             enterDoWhileLoop(whileNode);
1102         } else {
1103             enterTestFirstLoop(whileNode, null, null, false);
1104         }
1105         return false;
1106     }
1107 
1108     @Override
1109     public boolean enterWithNode(final WithNode withNode) {
1110         if (reachable) {
1111             visitExpression(withNode.getExpression());
1112             withNode.getBody().accept(this);
1113         }
1114         return false;
1115     };
1116 
1117     private Map<Symbol, LvarType> getBreakTargetTypes(final LexicalContextNode target) {
1118         // Remove symbols defined in the the blocks that are being broken out of.
1119         Map<Symbol, LvarType> types = localVariableTypes;
1120         for(final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
1121             final LexicalContextNode node = it.next();
1122             if(node instanceof Block) {
1123                 for(final Symbol symbol: ((Block)node).getSymbols()) {
1124                     if(localVariableTypes.containsKey(symbol)) {
1125                         if(types == localVariableTypes) {
1126                             types = cloneMap(localVariableTypes);
1127                         }
1128                         types.remove(symbol);
1129                     }
1130                 }
1131             }
1132             if(node == target) {
1133                 break;
1134             }
1135         }
1136         return types;
1137     }
1138 
1139     /**
1140      * Returns the current type of the local variable represented by the symbol. This is the most strict of all
1141      * {@code getLocalVariableType*} methods, as it will throw an assertion if the type is null. Therefore, it is only
1142      * safe to be invoked on symbols known to be bytecode locals, and only after they have been initialized.
1143      * Regardless, it is recommended to use this method in majority of cases, as because of its strictness it is the
1144      * best suited for catching missing type calculation bugs early.
1145      * @param symbol a symbol representing a bytecode local variable.
1146      * @return the current type of the local variable represented by the symbol
1147      */
1148     private LvarType getLocalVariableType(final Symbol symbol) {
1149         final LvarType type = getLocalVariableTypeOrNull(symbol);
1150         assert type != null;
1151         return type;
1152     }
1153 
1154     /**
1155      * Gets the type for a variable represented by a symbol, or null if the type is not know. This is the least strict
1156      * of all local variable type getters, and as such its use is discouraged except in initialization scenarios (where
1157      * a just-defined symbol might still be null).
1158      * @param symbol the symbol
1159      * @return the current type for the symbol, or null if the type is not known either because the symbol has not been
1160      * initialized, or because the symbol does not represent a bytecode local variable.
1161      */
1162     private LvarType getLocalVariableTypeOrNull(final Symbol symbol) {
1163         return localVariableTypes.get(symbol);
1164     }
1165 
1166     private JumpTarget getOrCreateJumpTarget(final Label label) {
1167         JumpTarget jumpTarget = jumpTargets.get(label);
1168         if(jumpTarget == null) {
1169             jumpTarget = createJumpTarget(label);
1170         }
1171         return jumpTarget;
1172     }
1173 
1174 
1175     /**
1176      * If there's a join point associated with a label, insert the join point into the flow.
1177      * @param label the label to insert a join point for.
1178      */
1179     private void joinOnLabel(final Label label) {
1180         final JumpTarget jumpTarget = jumpTargets.remove(label);
1181         if(jumpTarget == null) {
1182             return;
1183         }
1184         assert !jumpTarget.origins.isEmpty();
1185         reachable = true;
1186         localVariableTypes = getUnionTypes(jumpTarget.types, localVariableTypes);
1187         for(final JumpOrigin jumpOrigin: jumpTarget.origins) {
1188             setConversion(jumpOrigin.node, jumpOrigin.types, localVariableTypes);
1189         }
1190     }
1191 
1192     /**
1193      * If we're in a try/catch block, add an edge from the specified node to the try node's pre-catch label.
1194      */
1195     private void jumpToCatchBlock(final JoinPredecessor jumpOrigin) {
1196         final Label currentCatchLabel = catchLabels.peek();
1197         if(currentCatchLabel != null) {
1198             jumpToLabel(jumpOrigin, currentCatchLabel);
1199         }
1200     }
1201 
1202     private void jumpToLabel(final JoinPredecessor jumpOrigin, final Label label) {
1203         jumpToLabel(jumpOrigin, label, localVariableTypes);
1204     }
1205 
1206     private void jumpToLabel(final JoinPredecessor jumpOrigin, final Label label, final Map<Symbol, LvarType> types) {
1207         getOrCreateJumpTarget(label).addOrigin(jumpOrigin, types);
1208     }
1209 
1210     @Override
1211     public Node leaveBlock(final Block block) {
1212         if(lc.isFunctionBody()) {
1213             if(reachable) {
1214                 // reachable==true means we can reach the end of the function without an explicit return statement. We
1215                 // need to insert a synthetic one then. This logic used to be in Lower.leaveBlock(), but Lower's
1216                 // reachability analysis (through Terminal.isTerminal() flags) is not precise enough so
1217                 // Lower$BlockLexicalContext.afterSetStatements will sometimes think the control flow terminates even
1218                 // when it didn't. Example: function() { switch((z)) { default: {break; } throw x; } }.
1219                 createSyntheticReturn(block);
1220                 assert !reachable;
1221             }
1222             // We must calculate the return type here (and not in leaveFunctionNode) as it can affect the liveness of
1223             // the :return symbol and thus affect conversion type liveness calculations for it.
1224             calculateReturnType();
1225         }
1226 
1227         boolean cloned = false;
1228         for(final Symbol symbol: block.getSymbols()) {
1229             // Undefine the symbol outside the block
1230             if(localVariableTypes.containsKey(symbol)) {
1231                 if(!cloned) {
1232                     localVariableTypes = cloneMap(localVariableTypes);
1233                     cloned = true;
1234                 }
1235                 localVariableTypes.remove(symbol);
1236             }
1237 
1238             if(symbol.hasSlot()) {
1239                 final SymbolConversions conversions = symbolConversions.get(symbol);
1240                 if(conversions != null) {
1241                     // Potentially make some currently dead types live if they're needed as a source of a type
1242                     // conversion at a join.
1243                     conversions.calculateTypeLiveness(symbol);
1244                 }
1245                 if(symbol.slotCount() == 0) {
1246                     // This is a local variable that is never read. It won't need a slot.
1247                     symbol.setNeedsSlot(false);
1248                 }
1249             }
1250         }
1251 
1252         if(reachable) {
1253             // TODO: this is totally backwards. Block should not be breakable, LabelNode should be breakable.
1254             final LabelNode labelNode = lc.getCurrentBlockLabelNode();
1255             if(labelNode != null) {
1256                 jumpToLabel(labelNode, block.getBreakLabel());
1257             }
1258         }
1259         leaveBreakable(block);
1260         return block;
1261     }
1262 
1263     private void calculateReturnType() {
1264         // NOTE: if return type is unknown, then the function does not explicitly return a value. Such a function under
1265         // ECMAScript rules returns Undefined, which has Type.OBJECT. We might consider an optimization in the future
1266         // where we can return void functions.
1267         if(returnType.isUnknown()) {
1268             returnType = Type.OBJECT;
1269         }
1270     }
1271 
1272     private void createSyntheticReturn(final Block body) {
1273         final FunctionNode functionNode = lc.getCurrentFunction();
1274         final long token = functionNode.getToken();
1275         final int finish = functionNode.getFinish();
1276         final List<Statement> statements = body.getStatements();
1277         final int lineNumber = statements.isEmpty() ? functionNode.getLineNumber() : statements.get(statements.size() - 1).getLineNumber();
1278         final IdentNode returnExpr;
1279         if(functionNode.isProgram()) {
1280             returnExpr = new IdentNode(token, finish, RETURN.symbolName()).setSymbol(getCompilerConstantSymbol(functionNode, RETURN));
1281         } else {
1282             returnExpr = null;
1283         }
1284         syntheticReturn = new ReturnNode(lineNumber, token, finish, returnExpr);
1285         syntheticReturn.accept(this);
1286     }
1287 
1288     /**
1289      * Leave a breakable node. If there's a join point associated with its break label (meaning there was at least one
1290      * break statement to the end of the node), insert the join point into the flow.
1291      * @param breakable the breakable node being left.
1292      */
1293     private void leaveBreakable(final BreakableNode breakable) {
1294         joinOnLabel(breakable.getBreakLabel());
1295         assertTypeStackIsEmpty();
1296     }
1297 
1298     @Override
1299     public Node leaveFunctionNode(final FunctionNode functionNode) {
1300         // Sets the return type of the function and also performs the bottom-up pass of applying type and conversion
1301         // information to nodes as well as doing the calculation on nested functions as required.
1302         FunctionNode newFunction = functionNode;
1303         final SimpleNodeVisitor applyChangesVisitor = new SimpleNodeVisitor() {
1304             private boolean inOuterFunction = true;
1305             private final Deque<JoinPredecessor> joinPredecessors = new ArrayDeque<>();
1306 
1307             @Override
1308             protected boolean enterDefault(final Node node) {
1309                 if(!inOuterFunction) {
1310                     return false;
1311                 }
1312                 if(node instanceof JoinPredecessor) {
1313                     joinPredecessors.push((JoinPredecessor)node);
1314                 }
1315                 return inOuterFunction;
1316             }
1317 
1318             @Override
1319             public boolean enterFunctionNode(final FunctionNode fn) {
1320                 if(compiler.isOnDemandCompilation()) {
1321                     // Only calculate nested function local variable types if we're doing eager compilation
1322                     return false;
1323                 }
1324                 inOuterFunction = false;
1325                 return true;
1326             }
1327 
1328             @SuppressWarnings("fallthrough")
1329             @Override
1330             public Node leaveBinaryNode(final BinaryNode binaryNode) {
1331                 if(binaryNode.isComparison()) {
1332                     final Expression lhs = binaryNode.lhs();
1333                     final Expression rhs = binaryNode.rhs();
1334 
1335                     final TokenType tt = binaryNode.tokenType();
1336                     switch (tt) {
1337                     case EQ_STRICT:
1338                     case NE_STRICT:
1339                         // Specialize comparison with undefined
1340                         final Expression undefinedNode = createIsUndefined(binaryNode, lhs, rhs,
1341                                 tt == TokenType.EQ_STRICT ? Request.IS_UNDEFINED : Request.IS_NOT_UNDEFINED);
1342                         if(undefinedNode != binaryNode) {
1343                             return undefinedNode;
1344                         }
1345                         // Specialize comparison of boolean with non-boolean
1346                         if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) {
1347                             return new RuntimeNode(binaryNode);
1348                         }
1349                         // fallthrough
1350                     default:
1351                         if (lhs.getType().isObject() && rhs.getType().isObject()) {
1352                             return new RuntimeNode(binaryNode);
1353                         }
1354                     }
1355                 } else if(binaryNode.isOptimisticUndecidedType()) {
1356                     // At this point, we can assign a static type to the optimistic binary ADD operator as now we know
1357                     // the types of its operands.
1358                     return binaryNode.decideType();
1359                 }
1360                 return binaryNode;
1361             }
1362 
1363             @Override
1364             protected Node leaveDefault(final Node node) {
1365                 if(node instanceof JoinPredecessor) {
1366                     final JoinPredecessor original = joinPredecessors.pop();
1367                     assert original.getClass() == node.getClass() : original.getClass().getName() + "!=" + node.getClass().getName();
1368                     final JoinPredecessor newNode = setLocalVariableConversion(original, (JoinPredecessor)node);
1369                     if (newNode instanceof LexicalContextNode) {
1370                         lc.replace((LexicalContextNode)node, (LexicalContextNode)newNode);
1371                     }
1372                     return (Node)newNode;
1373                 }
1374                 return node;
1375             }
1376 
1377             @Override
1378             public Node leaveBlock(final Block block) {
1379                 if(inOuterFunction && syntheticReturn != null && lc.isFunctionBody()) {
1380                     final ArrayList<Statement> stmts = new ArrayList<>(block.getStatements());
1381                     stmts.add((ReturnNode)syntheticReturn.accept(this));
1382                     return block.setStatements(lc, stmts);
1383                 }
1384                 return super.leaveBlock(block);
1385             }
1386 
1387             @Override
1388             public Node leaveFunctionNode(final FunctionNode nestedFunctionNode) {
1389                 inOuterFunction = true;
1390                 final FunctionNode newNestedFunction = (FunctionNode)nestedFunctionNode.accept(
1391                         new LocalVariableTypesCalculator(compiler));
1392                 lc.replace(nestedFunctionNode, newNestedFunction);
1393                 return newNestedFunction;
1394             }
1395 
1396             @Override
1397             public Node leaveIdentNode(final IdentNode identNode) {
1398                 final IdentNode original = (IdentNode)joinPredecessors.pop();
1399                 final Symbol symbol = identNode.getSymbol();
1400                 if(symbol == null) {
1401                     assert identNode.isPropertyName();
1402                     return identNode;
1403                 } else if(symbol.hasSlot()) {
1404                     assert !symbol.isScope() || symbol.isParam(); // Only params can be slotted and scoped.
1405                     assert original.getName().equals(identNode.getName());
1406                     final LvarType lvarType = identifierLvarTypes.remove(original);
1407                     if(lvarType != null) {
1408                         return setLocalVariableConversion(original, identNode.setType(lvarType.type));
1409                     }
1410                     // If there's no type, then the identifier must've been in unreachable code. In that case, it can't
1411                     // have assigned conversions either.
1412                     assert localVariableConversions.get(original) == null;
1413                 } else {
1414                     assert identIsDeadAndHasNoLiveConversions(original);
1415                 }
1416                 return identNode;
1417             }
1418 
1419             @Override
1420             public Node leaveLiteralNode(final LiteralNode<?> literalNode) {
1421                 //for e.g. ArrayLiteralNodes the initial types may have been narrowed due to the
1422                 //introduction of optimistic behavior - hence ensure that all literal nodes are
1423                 //reinitialized
1424                 return literalNode.initialize(lc);
1425             }
1426 
1427             @Override
1428             public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
1429                 final Request request = runtimeNode.getRequest();
1430                 final boolean isEqStrict = request == Request.EQ_STRICT;
1431                 if(isEqStrict || request == Request.NE_STRICT) {
1432                     return createIsUndefined(runtimeNode, runtimeNode.getArgs().get(0), runtimeNode.getArgs().get(1),
1433                             isEqStrict ? Request.IS_UNDEFINED : Request.IS_NOT_UNDEFINED);
1434                 }
1435                 return runtimeNode;
1436             }
1437 
1438             @SuppressWarnings("unchecked")
1439             private <T extends JoinPredecessor> T setLocalVariableConversion(final JoinPredecessor original, final T jp) {
1440                 // NOTE: can't use Map.remove() as our copy-on-write AST semantics means some nodes appear twice (in
1441                 // finally blocks), so we need to be able to access conversions for them multiple times.
1442                 return (T)jp.setLocalVariableConversion(lc, localVariableConversions.get(original));
1443             }
1444         };
1445 
1446         newFunction = newFunction.setBody(lc, (Block)newFunction.getBody().accept(applyChangesVisitor));
1447         newFunction = newFunction.setReturnType(lc, returnType);
1448 
1449 
1450         newFunction = newFunction.setParameters(lc, newFunction.visitParameters(applyChangesVisitor));
1451         return newFunction;
1452     }
1453 
1454     private static Expression createIsUndefined(final Expression parent, final Expression lhs, final Expression rhs, final Request request) {
1455         if (isUndefinedIdent(lhs) || isUndefinedIdent(rhs)) {
1456             return new RuntimeNode(parent, request, lhs, rhs);
1457         }
1458         return parent;
1459     }
1460 
1461     private static boolean isUndefinedIdent(final Expression expr) {
1462         return expr instanceof IdentNode && "undefined".equals(((IdentNode)expr).getName());
1463     }
1464 
1465     private boolean identIsDeadAndHasNoLiveConversions(final IdentNode identNode) {
1466         final LocalVariableConversion conv = localVariableConversions.get(identNode);
1467         return conv == null || !conv.isLive();
1468     }
1469 
1470     private void onAssignment(final IdentNode identNode, final LvarType type) {
1471         final Symbol symbol = identNode.getSymbol();
1472         assert symbol != null : identNode.getName();
1473         if(!symbol.isBytecodeLocal()) {
1474             return;
1475         }
1476         assert type != null;
1477         final LvarType finalType;
1478         if(type == LvarType.UNDEFINED && getLocalVariableType(symbol) != LvarType.UNDEFINED) {
1479             // Explicit assignment of a known undefined local variable to a local variable that is not undefined will
1480             // materialize that undefined in the assignment target. Note that assigning known undefined to known
1481             // undefined will *not* initialize the variable, e.g. "var x; var y = x;" compiles to no-op.
1482             finalType = LvarType.OBJECT;
1483             symbol.setFlag(Symbol.HAS_OBJECT_VALUE);
1484         } else {
1485             finalType = type;
1486         }
1487         setType(symbol, finalType);
1488         // Explicit assignment of an undefined value. Make sure the variable can store an object
1489         // TODO: if we communicated the fact to codegen with a flag on the IdentNode that the value was already
1490         // undefined before the assignment, we could just ignore it. In general, we could ignore an assignment if we
1491         // know that the value assigned is the same as the current value of the variable, but we'd need constant
1492         // propagation for that.
1493         setIdentifierLvarType(identNode, finalType);
1494         // For purposes of type calculation, we consider an assignment to a local variable to be followed by
1495         // the catch nodes of the current (if any) try block. This will effectively enforce that narrower
1496         // assignments to a local variable in a try block will also have to store a widened value as well. Code
1497         // within the try block will be able to keep loading the narrower value, but after the try block only
1498         // the widest value will remain live.
1499         // Rationale for this is that if there's an use for that variable in any of the catch blocks, or
1500         // following the catch blocks, they must use the widest type.
1501         // Example:
1502         /*
1503             Originally:
1504             ===========
1505             var x;
1506             try {
1507               x = 1; <-- stores into int slot for x
1508               f(x); <-- loads the int slot for x
1509               x = 3.14 <-- stores into the double slot for x
1510               f(x); <-- loads the double slot for x
1511               x = 1; <-- stores into int slot for x
1512               f(x); <-- loads the int slot for x
1513             } finally {
1514               f(x); <-- loads the double slot for x, but can be reached by a path where x is int, so we need
1515                            to go back and ensure that double values are also always stored along with int
1516                            values.
1517             }
1518 
1519             After correction:
1520             =================
1521 
1522             var x;
1523             try {
1524               x = 1; <-- stores into both int and double slots for x
1525               f(x); <-- loads the int slot for x
1526               x = 3.14 <-- stores into the double slot for x
1527               f(x); <-- loads the double slot for x
1528               x = 1; <-- stores into both int and double slots for x
1529               f(x); <-- loads the int slot for x
1530             } finally {
1531               f(x); <-- loads the double slot for x
1532             }
1533          */
1534         jumpToCatchBlock(identNode);
1535     }
1536 
1537     private void onSelfAssignment(final IdentNode identNode, final LvarType type) {
1538         final Symbol symbol = identNode.getSymbol();
1539         assert symbol != null : identNode.getName();
1540         if(!symbol.isBytecodeLocal()) {
1541             return;
1542         }
1543         // Self-assignment never produce either a boolean or undefined
1544         assert type != null && type != LvarType.UNDEFINED && type != LvarType.BOOLEAN;
1545         setType(symbol, type);
1546         jumpToCatchBlock(identNode);
1547     }
1548 
1549     private void resetJoinPoint(final Label label) {
1550         jumpTargets.remove(label);
1551     }
1552 
1553     private void setCompilerConstantAsObject(final FunctionNode functionNode, final CompilerConstants cc) {
1554         final Symbol symbol = getCompilerConstantSymbol(functionNode, cc);
1555         setType(symbol, LvarType.OBJECT);
1556         // never mark compiler constants as dead
1557         symbolIsUsed(symbol);
1558     }
1559 
1560     private static Symbol getCompilerConstantSymbol(final FunctionNode functionNode, final CompilerConstants cc) {
1561         return functionNode.getBody().getExistingSymbol(cc.symbolName());
1562     }
1563 
1564     private void setConversion(final JoinPredecessor node, final Map<Symbol, LvarType> branchLvarTypes, final Map<Symbol, LvarType> joinLvarTypes) {
1565         if(node == null) {
1566             return;
1567         }
1568         if(branchLvarTypes.isEmpty() || joinLvarTypes.isEmpty()) {
1569             localVariableConversions.remove(node);
1570         }
1571 
1572         LocalVariableConversion conversion = null;
1573         if(node instanceof IdentNode) {
1574             // conversions on variable assignment in try block are special cases, as they only apply to the variable
1575             // being assigned and all other conversions should be ignored.
1576             final Symbol symbol = ((IdentNode)node).getSymbol();
1577             conversion = createConversion(symbol, branchLvarTypes.get(symbol), joinLvarTypes, null);
1578         } else {
1579             for(final Map.Entry<Symbol, LvarType> entry: branchLvarTypes.entrySet()) {
1580                 final Symbol symbol = entry.getKey();
1581                 final LvarType branchLvarType = entry.getValue();
1582                 conversion = createConversion(symbol, branchLvarType, joinLvarTypes, conversion);
1583             }
1584         }
1585         if(conversion != null) {
1586             localVariableConversions.put(node, conversion);
1587         } else {
1588             localVariableConversions.remove(node);
1589         }
1590     }
1591 
1592     private void setIdentifierLvarType(final IdentNode identNode, final LvarType type) {
1593         assert type != null;
1594         identifierLvarTypes.put(identNode, type);
1595     }
1596 
1597     /**
1598      * Marks a local variable as having a specific type from this point onward. Invoked by stores to local variables.
1599      * @param symbol the symbol representing the variable
1600      * @param type the type
1601      */
1602     private void setType(final Symbol symbol, final LvarType type) {
1603         if(getLocalVariableTypeOrNull(symbol) == type) {
1604             return;
1605         }
1606         assert symbol.hasSlot();
1607         assert !symbol.isGlobal();
1608         localVariableTypes = localVariableTypes.isEmpty() ? new IdentityHashMap<Symbol, LvarType>() : cloneMap(localVariableTypes);
1609         localVariableTypes.put(symbol, type);
1610     }
1611 
1612     /**
1613      * Set a flag in the symbol marking it as needing to be able to store a value of a particular type. Every symbol for
1614      * a local variable will be assigned between 1 and 6 local variable slots for storing all types it is known to need
1615      * to store.
1616      * @param symbol the symbol
1617      */
1618     private void symbolIsUsed(final Symbol symbol) {
1619         symbolIsUsed(symbol, getLocalVariableType(symbol));
1620     }
1621 }