src/jdk/nashorn/internal/codegen/Lower.java

changeset 57
59970b70ebb5
parent 44
e62dba3ce52b
child 62
f7825c1a11d3
     1.1 --- a/src/jdk/nashorn/internal/codegen/Lower.java	Tue Jan 29 14:25:39 2013 -0400
     1.2 +++ b/src/jdk/nashorn/internal/codegen/Lower.java	Wed Jan 30 12:26:45 2013 +0100
     1.3 @@ -28,47 +28,19 @@
     1.4  import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
     1.5  import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
     1.6  import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL;
     1.7 -import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
     1.8 -import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
     1.9  import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
    1.10  import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN;
    1.11 -import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
    1.12  import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
    1.13  import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
    1.14 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.ADD;
    1.15 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.DELETE;
    1.16 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.EQ;
    1.17 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.EQ_STRICT;
    1.18 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.FAIL_DELETE;
    1.19 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.GE;
    1.20 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.GT;
    1.21 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.IN;
    1.22 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.INSTANCEOF;
    1.23 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.LE;
    1.24 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.LT;
    1.25 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.NE;
    1.26 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.NE_STRICT;
    1.27 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.TYPEOF;
    1.28 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.VOID;
    1.29 -import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
    1.30 -import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
    1.31 -import static jdk.nashorn.internal.ir.Symbol.IS_LET;
    1.32 -import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
    1.33 -import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
    1.34 -import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
    1.35 -import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
    1.36  
    1.37  import java.util.ArrayDeque;
    1.38  import java.util.ArrayList;
    1.39  import java.util.Arrays;
    1.40  import java.util.Deque;
    1.41 -import java.util.HashSet;
    1.42 -import java.util.LinkedList;
    1.43  import java.util.List;
    1.44 -import java.util.Set;
    1.45 -import jdk.nashorn.internal.codegen.types.Type;
    1.46 +
    1.47  import jdk.nashorn.internal.ir.AccessNode;
    1.48 -import jdk.nashorn.internal.ir.Assignment;
    1.49 +import jdk.nashorn.internal.ir.BaseNode;
    1.50  import jdk.nashorn.internal.ir.BinaryNode;
    1.51  import jdk.nashorn.internal.ir.Block;
    1.52  import jdk.nashorn.internal.ir.BreakNode;
    1.53 @@ -85,19 +57,13 @@
    1.54  import jdk.nashorn.internal.ir.IfNode;
    1.55  import jdk.nashorn.internal.ir.IndexNode;
    1.56  import jdk.nashorn.internal.ir.LabelNode;
    1.57 +import jdk.nashorn.internal.ir.LabeledNode;
    1.58  import jdk.nashorn.internal.ir.LineNumberNode;
    1.59  import jdk.nashorn.internal.ir.LiteralNode;
    1.60 -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
    1.61  import jdk.nashorn.internal.ir.Node;
    1.62 -import jdk.nashorn.internal.ir.ObjectNode;
    1.63 -import jdk.nashorn.internal.ir.PropertyNode;
    1.64 -import jdk.nashorn.internal.ir.ReferenceNode;
    1.65  import jdk.nashorn.internal.ir.ReturnNode;
    1.66 -import jdk.nashorn.internal.ir.RuntimeNode;
    1.67 -import jdk.nashorn.internal.ir.RuntimeNode.Request;
    1.68  import jdk.nashorn.internal.ir.SwitchNode;
    1.69  import jdk.nashorn.internal.ir.Symbol;
    1.70 -import jdk.nashorn.internal.ir.TernaryNode;
    1.71  import jdk.nashorn.internal.ir.ThrowNode;
    1.72  import jdk.nashorn.internal.ir.TryNode;
    1.73  import jdk.nashorn.internal.ir.UnaryNode;
    1.74 @@ -108,54 +74,26 @@
    1.75  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    1.76  import jdk.nashorn.internal.parser.Token;
    1.77  import jdk.nashorn.internal.parser.TokenType;
    1.78 -import jdk.nashorn.internal.runtime.Context;
    1.79  import jdk.nashorn.internal.runtime.DebugLogger;
    1.80 -import jdk.nashorn.internal.runtime.ECMAException;
    1.81 -import jdk.nashorn.internal.runtime.JSType;
    1.82 -import jdk.nashorn.internal.runtime.Property;
    1.83 -import jdk.nashorn.internal.runtime.PropertyMap;
    1.84 -import jdk.nashorn.internal.runtime.ScriptFunction;
    1.85 -import jdk.nashorn.internal.runtime.ScriptObject;
    1.86  import jdk.nashorn.internal.runtime.ScriptRuntime;
    1.87  import jdk.nashorn.internal.runtime.Source;
    1.88 -import jdk.nashorn.internal.runtime.Undefined;
    1.89  
    1.90  /**
    1.91 - * Lower to more primitive operations. After lowering, an AST has symbols and
    1.92 - * types. Lowering may also add specialized versions of methods to the script if
    1.93 - * the optimizer is turned on.
    1.94 + * Lower to more primitive operations. After lowering, an AST still has no symbols
    1.95 + * and types, but several nodes have been turned into more low level constructs
    1.96 + * and control flow termination criteria have been computed.
    1.97   *
    1.98 - * Any expression that requires temporary storage as part of computation will
    1.99 - * also be detected here and give a temporary symbol
   1.100 + * We do things like code copying/inlining of finallies here, as it is much
   1.101 + * harder and context dependent to do any code copying after symbols have been
   1.102 + * finalized.
   1.103   */
   1.104  
   1.105  final class Lower extends NodeOperatorVisitor {
   1.106 -    /** Current compiler. */
   1.107 +
   1.108      private final Compiler compiler;
   1.109  
   1.110 -    /** Current source. */
   1.111      private final Source source;
   1.112  
   1.113 -    /** List of lowered statements */
   1.114 -    private List<Node> statements;
   1.115 -
   1.116 -    /** All symbols that are declared locally in a function node */
   1.117 -    private List<Symbol> declaredSymbolsLocal;
   1.118 -
   1.119 -    /**
   1.120 -     * Local definitions in current block (to discriminate from function
   1.121 -     * declarations always defined in the function scope. This is for
   1.122 -     * "can be undefined" analysis.
   1.123 -     */
   1.124 -    private Set<String> localDefs;
   1.125 -
   1.126 -    /**
   1.127 -     * Local definitions in current block to guard against cases like
   1.128 -     * NASHORN-467 when things can be undefined as they are used before
   1.129 -     * their local var definition. *sigh* JavaScript...
   1.130 -     */
   1.131 -    private Set<String> localUses;
   1.132 -
   1.133      /**
   1.134       * Nesting level stack. Currently just used for loops to avoid the problem
   1.135       * with terminal bodies that end with throw/return but still do continues to
   1.136 @@ -163,8 +101,11 @@
   1.137       */
   1.138      private final Deque<Node> nesting;
   1.139  
   1.140 -    private static final DebugLogger LOG   = new DebugLogger("lower");
   1.141 -    private static final boolean     DEBUG = LOG.isEnabled();
   1.142 +    private static final DebugLogger LOG = new DebugLogger("lower");
   1.143 +
   1.144 +    private Node lastStatement;
   1.145 +
   1.146 +    private List<Node> statements;
   1.147  
   1.148      /**
   1.149       * Constructor.
   1.150 @@ -174,52 +115,17 @@
   1.151      Lower(final Compiler compiler) {
   1.152          this.compiler   = compiler;
   1.153          this.source     = compiler.getSource();
   1.154 +        this.nesting    = new ArrayDeque<>();
   1.155          this.statements = new ArrayList<>();
   1.156 -        this.nesting    = new ArrayDeque<>();
   1.157 -    }
   1.158 -
   1.159 -    private void nest(final Node node) {
   1.160 -        nesting.push(node);
   1.161 -    }
   1.162 -
   1.163 -    private void unnest() {
   1.164 -        nesting.pop();
   1.165 -    }
   1.166 -
   1.167 -    static void debug(final String str) {
   1.168 -        if (DEBUG) {
   1.169 -            LOG.info(str);
   1.170 -        }
   1.171 -    }
   1.172 -
   1.173 -    @Override
   1.174 -    public Node leave(final AccessNode accessNode) {
   1.175 -        //accessNode.setBase(convert(accessNode.getBase(), Type.OBJECT));
   1.176 -        getCurrentFunctionNode().newTemporary(Type.OBJECT, accessNode); //This is not always an object per se, but currently the narrowing logic resides in AccessSpecializer. @see AccessSpecializer!
   1.177 -
   1.178 -        return accessNode;
   1.179      }
   1.180  
   1.181      @Override
   1.182      public Node enter(final Block block) {
   1.183 -        /*
   1.184 -         * Save the statement list from the outer construct we are currently
   1.185 -         * generating and push frame
   1.186 -         */
   1.187 -        final List<Node>  savedStatements = statements;
   1.188 -        final Set<String> savedDefs       = localDefs;
   1.189 -        final Set<String> savedUses       = localUses;
   1.190 -
   1.191 -        block.setFrame(getCurrentFunctionNode().pushFrame());
   1.192 +        final Node       savedLastStatement = lastStatement;
   1.193 +        final List<Node> savedStatements    = statements;
   1.194  
   1.195          try {
   1.196 -            /*
   1.197 -             * Reset the statement instance var, new block
   1.198 -             */
   1.199 -            statements = new ArrayList<>();
   1.200 -            localDefs  = new HashSet<>(savedDefs);
   1.201 -            localUses  = new HashSet<>(savedUses);
   1.202 -
   1.203 +            this.statements = new ArrayList<>();
   1.204              for (final Node statement : block.getStatements()) {
   1.205                  statement.accept(this);
   1.206                  /*
   1.207 @@ -231,28 +137,715 @@
   1.208                   *
   1.209                   * @see NASHORN-285
   1.210                   */
   1.211 -                final Node lastStatement = Node.lastStatement(statements);
   1.212                  if (lastStatement != null && lastStatement.isTerminal()) {
   1.213 -                    block.copyTerminalFlags(lastStatement);
   1.214 +                    copyTerminal(block, lastStatement);
   1.215 +                    break;
   1.216 +                }
   1.217 +            }
   1.218 +            block.setStatements(statements);
   1.219 +
   1.220 +        } finally {
   1.221 +            this.statements = savedStatements;
   1.222 +            this.lastStatement = savedLastStatement;
   1.223 +        }
   1.224 +
   1.225 +        return null;
   1.226 +    }
   1.227 +
   1.228 +    @Override
   1.229 +    public Node enter(final BreakNode breakNode) {
   1.230 +        return enterBreakOrContinue(breakNode);
   1.231 +    }
   1.232 +
   1.233 +    @Override
   1.234 +    public Node enter(final CallNode callNode) {
   1.235 +        final Node function = markerFunction(callNode.getFunction());
   1.236 +        callNode.setFunction(function);
   1.237 +        checkEval(callNode); //check if this is an eval call and store the information
   1.238 +        return callNode;
   1.239 +    }
   1.240 +
   1.241 +    @Override
   1.242 +    public Node leave(final CaseNode caseNode) {
   1.243 +        caseNode.copyTerminalFlags(caseNode.getBody());
   1.244 +        return caseNode;
   1.245 +    }
   1.246 +
   1.247 +    @Override
   1.248 +    public Node leave(final CatchNode catchNode) {
   1.249 +        catchNode.copyTerminalFlags(catchNode.getBody());
   1.250 +        addStatement(catchNode);
   1.251 +        return catchNode;
   1.252 +    }
   1.253 +
   1.254 +    @Override
   1.255 +    public Node enter(final ContinueNode continueNode) {
   1.256 +        return enterBreakOrContinue(continueNode);
   1.257 +    }
   1.258 +
   1.259 +    @Override
   1.260 +    public Node enter(final DoWhileNode doWhileNode) {
   1.261 +        return enter((WhileNode)doWhileNode);
   1.262 +    }
   1.263 +
   1.264 +    @Override
   1.265 +    public Node leave(final DoWhileNode doWhileNode) {
   1.266 +        return leave((WhileNode)doWhileNode);
   1.267 +    }
   1.268 +
   1.269 +    @Override
   1.270 +    public Node enter(final EmptyNode emptyNode) {
   1.271 +        return null;
   1.272 +    }
   1.273 +
   1.274 +    @Override
   1.275 +    public Node leave(final ExecuteNode executeNode) {
   1.276 +        final Node expr = executeNode.getExpression();
   1.277 +
   1.278 +        if (getCurrentFunctionNode().isScript()) {
   1.279 +            if (!(expr instanceof Block)) {
   1.280 +                if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) {
   1.281 +                    executeNode.setExpression(new BinaryNode(source, Token.recast(executeNode.getToken(), TokenType.ASSIGN),
   1.282 +                            getCurrentFunctionNode().getResultNode(),
   1.283 +                            expr));
   1.284 +                }
   1.285 +            }
   1.286 +        }
   1.287 +
   1.288 +        copyTerminal(executeNode, executeNode.getExpression());
   1.289 +        addStatement(executeNode);
   1.290 +
   1.291 +        return executeNode;
   1.292 +    }
   1.293 +
   1.294 +    @Override
   1.295 +    public Node enter(final ForNode forNode) {
   1.296 +        nest(forNode);
   1.297 +        return forNode;
   1.298 +    }
   1.299 +
   1.300 +    @Override
   1.301 +    public Node leave(final ForNode forNode) {
   1.302 +        final Node  test = forNode.getTest();
   1.303 +        final Block body = forNode.getBody();
   1.304 +
   1.305 +        if (!forNode.isForIn() && test == null) {
   1.306 +            setHasGoto(forNode);
   1.307 +        }
   1.308 +
   1.309 +        final boolean escapes = controlFlowEscapes(body);
   1.310 +        if (escapes) {
   1.311 +            setTerminal(body, false);
   1.312 +        }
   1.313 +
   1.314 +        // pop the loop from the loop context
   1.315 +        unnest(forNode);
   1.316 +
   1.317 +        if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
   1.318 +            forNode.setTest(null);
   1.319 +            setTerminal(forNode, !escapes);
   1.320 +        }
   1.321 +
   1.322 +        addStatement(forNode);
   1.323 +
   1.324 +        return forNode;
   1.325 +    }
   1.326 +
   1.327 +    @Override
   1.328 +    public Node enter(final FunctionNode functionNode) {
   1.329 +        LOG.info("START FunctionNode: " + functionNode.getName());
   1.330 +
   1.331 +        initFunctionNode(functionNode);
   1.332 +
   1.333 +        Node initialEvalResult = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED);
   1.334 +
   1.335 +        nest(functionNode);
   1.336 +
   1.337 +        /*
   1.338 +         * As we are evaluating a nested structure, we need to store the
   1.339 +         * statement list for the surrounding block and restore it when the
   1.340 +         * function is done
   1.341 +         */
   1.342 +        final List<Node> savedStatements = statements;
   1.343 +        final Node savedLastStatement = lastStatement;
   1.344 +
   1.345 +        statements    = new ArrayList<>();
   1.346 +        lastStatement = null;
   1.347 +
   1.348 +        // for initial eval result is the last declared function
   1.349 +        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
   1.350 +            final IdentNode ident = nestedFunction.getIdent();
   1.351 +            if (ident != null && nestedFunction.isStatement()) {
   1.352 +                initialEvalResult = new IdentNode(ident);
   1.353 +            }
   1.354 +        }
   1.355 +
   1.356 +        if (functionNode.needsSelfSymbol()) {
   1.357 +            //function needs to start with var funcIdent = __callee_;
   1.358 +            statements.add(functionNode.getSelfSymbolInit().accept(this));
   1.359 +        }
   1.360 +
   1.361 +        try {
   1.362 +            // Every nested function needs a definition in the outer function with its name. Add these.
   1.363 +            for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
   1.364 +                final VarNode varNode = nestedFunction.getFunctionVarNode();
   1.365 +                if (varNode != null) {
   1.366 +                    final LineNumberNode lineNumberNode = nestedFunction.getFunctionVarLineNumberNode();
   1.367 +                    if (lineNumberNode != null) {
   1.368 +                        lineNumberNode.accept(this);
   1.369 +                    }
   1.370 +                    varNode.accept(this);
   1.371 +                    varNode.setIsFunctionVarNode();
   1.372 +                }
   1.373 +            }
   1.374 +
   1.375 +            if (functionNode.isScript()) {
   1.376 +                new ExecuteNode(source, functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this);
   1.377 +            }
   1.378 +
   1.379 +            //do the statements - this fills the block with code
   1.380 +            for (final Node statement : functionNode.getStatements()) {
   1.381 +                statement.accept(this);
   1.382 +                //If there are unused terminated endpoints in the function, we need
   1.383 +                // to add a "return undefined" in those places for correct semantics
   1.384 +                LOG.info("Checking lastStatement="+lastStatement+" for terminal flags");
   1.385 +                if (lastStatement != null && lastStatement.hasTerminalFlags()) {
   1.386 +                    copyTerminal(functionNode, lastStatement);
   1.387                      break;
   1.388                  }
   1.389              }
   1.390  
   1.391 -            block.setStatements(statements);
   1.392 +            functionNode.setStatements(statements);
   1.393 +
   1.394 +            if (!functionNode.isTerminal()) {
   1.395 +                guaranteeReturn(functionNode);
   1.396 +            }
   1.397 +
   1.398 +            //lower all nested functions
   1.399 +            for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
   1.400 +                nestedFunction.accept(this);
   1.401 +            }
   1.402 +
   1.403          } finally {
   1.404 -            /*
   1.405 -             * Restore the saved statements after block ends and pop frame
   1.406 -             */
   1.407 -            statements = savedStatements;
   1.408 -            localDefs  = savedDefs;
   1.409 -            localUses  = savedUses;
   1.410 -
   1.411 -            getCurrentFunctionNode().popFrame();
   1.412 +            statements    = savedStatements;
   1.413 +            lastStatement = savedLastStatement;
   1.414          }
   1.415  
   1.416 +        LOG.info("END FunctionNode: " + functionNode.getName());
   1.417 +        unnest(functionNode);
   1.418 +
   1.419          return null;
   1.420      }
   1.421  
   1.422 +    @Override
   1.423 +    public Node enter(final IfNode ifNode) {
   1.424 +        return nest(ifNode);
   1.425 +    }
   1.426 +
   1.427 +    @Override
   1.428 +    public Node leave(final IfNode ifNode) {
   1.429 +        final Node pass = ifNode.getPass();
   1.430 +        final Node fail = ifNode.getFail();
   1.431 +
   1.432 +        if (pass.isTerminal() && fail != null && fail.isTerminal()) {
   1.433 +            setTerminal(ifNode,  true);
   1.434 +        }
   1.435 +
   1.436 +        addStatement(ifNode);
   1.437 +        unnest(ifNode);
   1.438 +
   1.439 +        return ifNode;
   1.440 +    }
   1.441 +
   1.442 +    @Override
   1.443 +    public Node enter(LabelNode labelNode) {
   1.444 +        final Block body = labelNode.getBody();
   1.445 +        body.accept(this);
   1.446 +        copyTerminal(labelNode, body);
   1.447 +        addStatement(labelNode);
   1.448 +        return null;
   1.449 +    }
   1.450 +
   1.451 +    @Override
   1.452 +    public Node enter(final LineNumberNode lineNumberNode) {
   1.453 +        addStatement(lineNumberNode, false); // don't put it in lastStatement cache
   1.454 +        return null;
   1.455 +    }
   1.456 +
   1.457 +    @Override
   1.458 +    public Node enter(final ReturnNode returnNode) {
   1.459 +        final TryNode tryNode = returnNode.getTryChain();
   1.460 +        final Node    expr    = returnNode.getExpression();
   1.461 +
   1.462 +        if (tryNode != null) {
   1.463 +            //we are inside a try block - we don't necessarily have a result node yet. attr will do that.
   1.464 +            if (expr != null) {
   1.465 +                //we need to evaluate the result of the return in case it is complex while
   1.466 +                //still in the try block, store it in a result value and return it afterwards
   1.467 +                final long token        = returnNode.getToken();
   1.468 +                final Node resultNode   = new IdentNode(getCurrentFunctionNode().getResultNode());
   1.469 +                final Node assignResult = new BinaryNode(source, Token.recast(token, TokenType.ASSIGN), resultNode, expr);
   1.470 +
   1.471 +                //add return_in_try = expr; to try block
   1.472 +                new ExecuteNode(source, token, Token.descPosition(token), assignResult).accept(this);
   1.473 +
   1.474 +                //splice in the finally code, inlining it here
   1.475 +                if (copyFinally(tryNode, null)) {
   1.476 +                    return null;
   1.477 +                }
   1.478 +
   1.479 +                //make sure that the return node now returns 'return_in_try'
   1.480 +                returnNode.setExpression(resultNode);
   1.481 +            } else if (copyFinally(tryNode, null)) {
   1.482 +                return null;
   1.483 +            }
   1.484 +        } else if (expr != null) {
   1.485 +            returnNode.setExpression(expr.accept(this));
   1.486 +        }
   1.487 +
   1.488 +        addStatement(returnNode);
   1.489 +
   1.490 +        return null;
   1.491 +    }
   1.492 +
   1.493 +    @Override
   1.494 +    public Node leave(final ReturnNode returnNode) {
   1.495 +        addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor
   1.496 +        return returnNode;
   1.497 +    }
   1.498 +
   1.499 +    @Override
   1.500 +    public Node enter(final SwitchNode switchNode) {
   1.501 +        nest(switchNode);
   1.502 +        return switchNode;
   1.503 +    }
   1.504 +
   1.505 +    @Override
   1.506 +    public Node leave(final SwitchNode switchNode) {
   1.507 +        unnest(switchNode);
   1.508 +
   1.509 +        final List<CaseNode> cases       = switchNode.getCases();
   1.510 +        final CaseNode       defaultCase = switchNode.getDefaultCase();
   1.511 +
   1.512 +        boolean allTerminal = !cases.isEmpty();
   1.513 +        for (final CaseNode caseNode : switchNode.getCases()) {
   1.514 +            allTerminal &= caseNode.isTerminal();
   1.515 +        }
   1.516 +
   1.517 +        if (allTerminal && defaultCase != null && defaultCase.isTerminal()) {
   1.518 +            setTerminal(switchNode, true);
   1.519 +        }
   1.520 +
   1.521 +        addStatement(switchNode);
   1.522 +
   1.523 +        return switchNode;
   1.524 +    }
   1.525 +
   1.526 +    @Override
   1.527 +    public Node leave(final ThrowNode throwNode) {
   1.528 +        addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor
   1.529 +        return throwNode;
   1.530 +    }
   1.531 +
   1.532 +    @Override
   1.533 +    public Node enter(final TryNode tryNode) {
   1.534 +        final Block  finallyBody = tryNode.getFinallyBody();
   1.535 +        final long   token       = tryNode.getToken();
   1.536 +        final int    finish      = tryNode.getFinish();
   1.537 +
   1.538 +        nest(tryNode);
   1.539 +
   1.540 +        if (finallyBody == null) {
   1.541 +            //do nothing if no finally exists
   1.542 +            return tryNode;
   1.543 +        }
   1.544 +
   1.545 +        /*
   1.546 +         * We have a finally clause.
   1.547 +         *
   1.548 +         * Transform to do finally tail duplication as follows:
   1.549 +         *
   1.550 +         * <pre>
   1.551 +         *  try {
   1.552 +         *    try_body
   1.553 +         *  } catch e1 {
   1.554 +         *    catchbody_1
   1.555 +         *  }
   1.556 +         *  ...
   1.557 +         *  } catch en {
   1.558 +         *    catchbody_n
   1.559 +         *  } finally {
   1.560 +         *    finally_body
   1.561 +         *  }
   1.562 +         *
   1.563 +         *  (where e1 ... en are optional)
   1.564 +         *
   1.565 +         *  turns into
   1.566 +         *
   1.567 +         *  try {
   1.568 +         *    try {
   1.569 +         *      try_body
   1.570 +         *    } catch e1 {
   1.571 +         *      catchbody1
   1.572 +         *      //nothing inlined explicitly here, return, break other
   1.573 +         *      //terminals may inline the finally body
   1.574 +         *      ...
   1.575 +         *    } catch en {
   1.576 +         *      catchbody2
   1.577 +         *      //nothing inlined explicitly here, return, break other
   1.578 +         *      //terminals may inline the finally body
   1.579 +         *    }
   1.580 +         *  } catch all ex {
   1.581 +         *      finally_body_inlined
   1.582 +         *      rethrow ex
   1.583 +         *  }
   1.584 +         *  finally_body_inlined
   1.585 +         * </pre>
   1.586 +         *
   1.587 +         * If tries are catches are terminal, visitors for return, break &
   1.588 +         * continue will handle the tail duplications. Throw needs to be
   1.589 +         * treated specially with the catchall as described in the above
   1.590 +         * ASCII art.
   1.591 +         *
   1.592 +         * If the try isn't terminal we do the finally_body_inlined at the
   1.593 +         * end. If the try is terminated with continue/break/return the
   1.594 +         * existing visitor logic will inline the finally before that
   1.595 +         * operation. if the try is terminated with a throw, the catches e1
   1.596 +         * ... en will have a chance to process the exception. If the
   1.597 +         * appropriate catch e1..en is non terminal we fall through to the
   1.598 +         * last finally_body_inlined. if the catch e1...en IS terminal with
   1.599 +         * continue/break/return existing visitor logic will fix it. If they
   1.600 +         * are terminal with another throw it goes to the catchall and the
   1.601 +         * finally_body_inlined marked (*) will fix it before rethrowing
   1.602 +         * whatever problem there was for identical semantic.
   1.603 +         */
   1.604 +
   1.605 +        // if try node does not contain a catch we can skip creation of a new
   1.606 +        // try node and just append our synthetic catch to the existing try node.
   1.607 +        if (!tryNode.getCatchBlocks().isEmpty()) {
   1.608 +            // insert an intermediate try-catch* node, where we move the body and all catch blocks.
   1.609 +            // the original try node become a try-finally container for the new try-catch* node.
   1.610 +            // because we don't clone (to avoid deep copy), we have to fix the block chain in the end.
   1.611 +            final TryNode innerTryNode;
   1.612 +            innerTryNode = new TryNode(source, token, finish, tryNode.getNext());
   1.613 +            innerTryNode.setBody(tryNode.getBody());
   1.614 +            innerTryNode.setCatchBlocks(tryNode.getCatchBlocks());
   1.615 +
   1.616 +            // set outer tryNode's body to innerTryNode
   1.617 +            final Block outerBody;
   1.618 +            outerBody = new Block(source, token, finish, tryNode.getBody().getParent(), getCurrentFunctionNode());
   1.619 +            outerBody.setStatements(new ArrayList<Node>(Arrays.asList(innerTryNode)));
   1.620 +            tryNode.setBody(outerBody);
   1.621 +            tryNode.setCatchBlocks(null);
   1.622 +
   1.623 +            // now before we go on, we have to fix the block parents
   1.624 +            // (we repair the block tree after the insertion so that all references are intact)
   1.625 +            innerTryNode.getBody().setParent(tryNode.getBody());
   1.626 +            for (final Block block : innerTryNode.getCatchBlocks()) {
   1.627 +                block.setParent(tryNode.getBody());
   1.628 +            }
   1.629 +        }
   1.630 +
   1.631 +        // create a catch-all that inlines finally and rethrows
   1.632 +
   1.633 +        final Block catchBlock      = new Block(source, token, finish, getCurrentBlock(), getCurrentFunctionNode());
   1.634 +        //this catch block should get define symbol
   1.635 +
   1.636 +        final Block catchBody       = new Block(source, token, finish, catchBlock, getCurrentFunctionNode());
   1.637 +        final Node  catchAllFinally = finallyBody.clone();
   1.638 +
   1.639 +        catchBody.addStatement(new ExecuteNode(source, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally));
   1.640 +        setTerminal(catchBody, true);
   1.641 +
   1.642 +        final CatchNode catchAllNode;
   1.643 +        final IdentNode exception;
   1.644 +
   1.645 +        exception    = new IdentNode(source, token, finish, compiler.uniqueName("catch_all"));
   1.646 +        catchAllNode = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody);
   1.647 +        catchAllNode.setIsSyntheticRethrow();
   1.648 +
   1.649 +        catchBlock.addStatement(catchAllNode);
   1.650 +
   1.651 +        // replace all catches of outer tryNode with the catch-all
   1.652 +        tryNode.setCatchBlocks(new ArrayList<>(Arrays.asList(catchBlock)));
   1.653 +
   1.654 +        /*
   1.655 +         * We leave the finally block for the original try in place for now
   1.656 +         * so that children visitations will work. It is removed and placed
   1.657 +         * afterwards in the else case below, after all children are visited
   1.658 +         */
   1.659 +
   1.660 +        return tryNode;
   1.661 +    }
   1.662 +
   1.663 +    @Override
   1.664 +    public Node leave(final TryNode tryNode) {
   1.665 +        final Block finallyBody   = tryNode.getFinallyBody();
   1.666 +
   1.667 +        boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal());
   1.668 +
   1.669 +        for (final Block catchBlock : tryNode.getCatchBlocks()) {
   1.670 +            allTerminal &= catchBlock.isTerminal();
   1.671 +        }
   1.672 +
   1.673 +        tryNode.setIsTerminal(allTerminal);
   1.674 +
   1.675 +        addStatement(tryNode);
   1.676 +        unnest(tryNode);
   1.677 +
   1.678 +        // if finally body is present, place it after the tryNode
   1.679 +        if (finallyBody != null) {
   1.680 +            tryNode.setFinallyBody(null);
   1.681 +            addStatement(finallyBody);
   1.682 +        }
   1.683 +
   1.684 +        return tryNode;
   1.685 +    }
   1.686 +
   1.687 +    @Override
   1.688 +    public Node leave(final VarNode varNode) {
   1.689 +        addStatement(varNode);
   1.690 +        return varNode;
   1.691 +    }
   1.692 +
   1.693 +    @Override
   1.694 +    public Node enter(final WhileNode whileNode) {
   1.695 +        return nest(whileNode);
   1.696 +    }
   1.697 +
   1.698 +    @Override
   1.699 +    public Node leave(final WhileNode whileNode) {
   1.700 +        final Node test = whileNode.getTest();
   1.701 +
   1.702 +        if (test == null) {
   1.703 +            setHasGoto(whileNode);
   1.704 +        }
   1.705 +
   1.706 +        final Block   body    = whileNode.getBody();
   1.707 +        final boolean escapes = controlFlowEscapes(body);
   1.708 +        if (escapes) {
   1.709 +            setTerminal(body, false);
   1.710 +        }
   1.711 +
   1.712 +        Node node = whileNode;
   1.713 +
   1.714 +        if (body.isTerminal()) {
   1.715 +            if (whileNode instanceof DoWhileNode) {
   1.716 +                setTerminal(whileNode, true);
   1.717 +            } else if (conservativeAlwaysTrue(test)) {
   1.718 +                node = new ForNode(source, whileNode.getToken(), whileNode.getFinish());
   1.719 +                ((ForNode)node).setBody(body);
   1.720 +                ((ForNode)node).accept(this);
   1.721 +                setTerminal(node, !escapes);
   1.722 +            }
   1.723 +        }
   1.724 +
   1.725 +        // pop the loop from the loop context
   1.726 +        unnest(whileNode);
   1.727 +        addStatement(node);
   1.728 +
   1.729 +        return node;
   1.730 +    }
   1.731 +
   1.732 +    @Override
   1.733 +    public Node leave(final WithNode withNode) {
   1.734 +        if (withNode.getBody().isTerminal()) {
   1.735 +            setTerminal(withNode,  true);
   1.736 +        }
   1.737 +        addStatement(withNode);
   1.738 +
   1.739 +        return withNode;
   1.740 +    }
   1.741 +
   1.742 +    @Override
   1.743 +    public Node leaveDELETE(final UnaryNode unaryNode) {
   1.744 +        final Node rhs = unaryNode.rhs();
   1.745 +        if (rhs instanceof IdentNode || rhs instanceof BaseNode) {
   1.746 +            return unaryNode;
   1.747 +        }
   1.748 +        addStatement(new ExecuteNode(rhs));
   1.749 +        return LiteralNode.newInstance(unaryNode, true);
   1.750 +    }
   1.751 +
   1.752 +    /**
   1.753 +     * Given a function node that is a callee in a CallNode, replace it with
   1.754 +     * the appropriate marker function. This is used by {@link CodeGenerator}
   1.755 +     * for fast scope calls
   1.756 +     *
   1.757 +     * @param function function called by a CallNode
   1.758 +     * @return transformed node to marker function or identity if not ident/access/indexnode
   1.759 +     */
   1.760 +    private static Node markerFunction(final Node function) {
   1.761 +        if (function instanceof IdentNode) {
   1.762 +            return new IdentNode((IdentNode)function) {
   1.763 +                @Override
   1.764 +                public boolean isFunction() {
   1.765 +                    return true;
   1.766 +                }
   1.767 +            };
   1.768 +        } else if (function instanceof AccessNode) {
   1.769 +            return new AccessNode((AccessNode)function) {
   1.770 +                @Override
   1.771 +                public boolean isFunction() {
   1.772 +                    return true;
   1.773 +                }
   1.774 +            };
   1.775 +        } else if (function instanceof IndexNode) {
   1.776 +            return new IndexNode((IndexNode)function) {
   1.777 +                @Override
   1.778 +                public boolean isFunction() {
   1.779 +                    return true;
   1.780 +                }
   1.781 +            };
   1.782 +        }
   1.783 +
   1.784 +        return function;
   1.785 +    }
   1.786 +
   1.787 +    /**
   1.788 +     * Calculate a synthetic eval location for a node for the stacktrace, for example src#17<eval>
   1.789 +     * @param node a node
   1.790 +     * @return eval location
   1.791 +     */
   1.792 +    private static String evalLocation(final IdentNode node) {
   1.793 +        //final StringBuilder sb = new StringBuilder(node.getSource().getName());
   1.794 +        return new StringBuilder().
   1.795 +            append(node.getSource().getName()).
   1.796 +            append('#').
   1.797 +            append(node.getSource().getLine(node.position())).
   1.798 +            append("<eval>").
   1.799 +            toString();
   1.800 +    }
   1.801 +
   1.802 +    /**
   1.803 +     * Check whether a call node may be a call to eval. In that case we
   1.804 +     * clone the args in order to create the following construct in
   1.805 +     * {@link CodeGenerator}
   1.806 +     *
   1.807 +     * <pre>
   1.808 +     * if (calledFuntion == buildInEval) {
   1.809 +     *    eval(cloned arg);
   1.810 +     * } else {
   1.811 +     *    cloned arg;
   1.812 +     * }
   1.813 +     * </pre>
   1.814 +     *
   1.815 +     * @param callNode call node to check if it's an eval
   1.816 +     */
   1.817 +    private void checkEval(final CallNode callNode) {
   1.818 +        if (callNode.getFunction() instanceof IdentNode) {
   1.819 +
   1.820 +            final List<Node> args   = callNode.getArgs();
   1.821 +            final IdentNode  callee = (IdentNode)callNode.getFunction();
   1.822 +
   1.823 +            // 'eval' call with at least one argument
   1.824 +            if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) {
   1.825 +                final CallNode.EvalArgs evalArgs =
   1.826 +                    new CallNode.EvalArgs(
   1.827 +                        args.get(0).clone().accept(this), //clone as we use this for the "is eval case". original evaluated separately for "is not eval case"
   1.828 +                        getCurrentFunctionNode().getThisNode(),
   1.829 +                        evalLocation(callee),
   1.830 +                        getCurrentFunctionNode().isStrictMode());
   1.831 +                callNode.setEvalArgs(evalArgs);
   1.832 +            }
   1.833 +        }
   1.834 +    }
   1.835 +
   1.836 +    private static boolean conservativeAlwaysTrue(final Node node) {
   1.837 +        return node == null || ((node instanceof LiteralNode) && Boolean.TRUE.equals(((LiteralNode<?>)node).getValue()));
   1.838 +    }
   1.839 +
   1.840 +    /**
   1.841 +     * Helper that given a loop body makes sure that it is not terminal if it
   1.842 +     * has a continue that leads to the loop header or to outer loops' loop
   1.843 +     * headers. This means that, even if the body ends with a terminal
   1.844 +     * statement, we cannot tag it as terminal
   1.845 +     *
   1.846 +     * @param loopBody the loop body to check
   1.847 +     * @return true if control flow may escape the loop
   1.848 +     */
   1.849 +    private boolean controlFlowEscapes(final Node loopBody) {
   1.850 +        final List<Node> escapes = new ArrayList<>();
   1.851 +
   1.852 +        loopBody.accept(new NodeVisitor() {
   1.853 +            @Override
   1.854 +            public Node leave(final BreakNode node) {
   1.855 +                escapes.add(node);
   1.856 +                return node;
   1.857 +            }
   1.858 +
   1.859 +            @Override
   1.860 +            public Node leave(final ContinueNode node) {
   1.861 +                // all inner loops have been popped.
   1.862 +                if (nesting.contains(node.getTargetNode())) {
   1.863 +                    escapes.add(node);
   1.864 +                }
   1.865 +                return node;
   1.866 +            }
   1.867 +        });
   1.868 +
   1.869 +        return !escapes.isEmpty();
   1.870 +    }
   1.871 +
   1.872 +    private void guaranteeReturn(final FunctionNode functionNode) {
   1.873 +        Node resultNode;
   1.874 +
   1.875 +        if (functionNode.isScript()) {
   1.876 +            resultNode = functionNode.getResultNode(); // the eval result, symbol assigned in Attr
   1.877 +        } else {
   1.878 +            if (lastStatement != null && lastStatement.isTerminal() || lastStatement instanceof ReturnNode) {
   1.879 +                return; //already in place or not needed, as it should be for a non-undefined returning function
   1.880 +            }
   1.881 +            resultNode = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED);
   1.882 +        }
   1.883 +
   1.884 +        //create a return statement
   1.885 +        final Node returnNode = new ReturnNode(source, functionNode.getLastToken(), functionNode.getFinish(), resultNode, null);
   1.886 +        returnNode.accept(this);
   1.887 +    }
   1.888 +
   1.889 +
   1.890 +    private Node nest(final Node node) {
   1.891 +        LOG.info("Nesting: " + node);
   1.892 +        LOG.indent();
   1.893 +        nesting.push(node);
   1.894 +        return node;
   1.895 +    }
   1.896 +
   1.897 +    private void unnest(final Node node) {
   1.898 +        LOG.unindent();
   1.899 +        assert nesting.getFirst() == node : "inconsistent nesting order : " + nesting.getFirst() + " != " + node;
   1.900 +        LOG.info("Unnesting: " + nesting);
   1.901 +        nesting.pop();
   1.902 +    }
   1.903 +
   1.904 +    private static void setTerminal(final Node node, final boolean isTerminal) {
   1.905 +        LOG.info("terminal = " + isTerminal + " for " + node);
   1.906 +        node.setIsTerminal(isTerminal);
   1.907 +    }
   1.908 +
   1.909 +    private static void setHasGoto(final Node node) { //, final boolean hasGoto) {
   1.910 +        LOG.info("hasGoto = true for " + node);
   1.911 +        node.setHasGoto();
   1.912 +    }
   1.913 +
   1.914 +    private static void copyTerminal(final Node node, final Node sourceNode) {
   1.915 +        LOG.info("copy terminal flags " + sourceNode + " -> " + node);
   1.916 +        node.copyTerminalFlags(sourceNode);
   1.917 +    }
   1.918 +
   1.919 +    private void addStatement(final Node statement, final boolean storeInLastStatement) {
   1.920 +        LOG.info("add statement = " + statement + " (lastStatement = " + lastStatement + ")");
   1.921 +        statements.add(statement);
   1.922 +        if (storeInLastStatement) {
   1.923 +            lastStatement = statement;
   1.924 +        }
   1.925 +    }
   1.926 +
   1.927 +    private void addStatement(final Node statement) {
   1.928 +        addStatement(statement, true);
   1.929 +    }
   1.930 +
   1.931      /**
   1.932       * Determine if Try block is inside target block.
   1.933       *
   1.934 @@ -316,271 +909,15 @@
   1.935          return false;
   1.936      }
   1.937  
   1.938 -    @Override
   1.939 -    public Node enter(final BreakNode breakNode) {
   1.940 -        final TryNode tryNode = breakNode.getTryChain();
   1.941 -
   1.942 -        if (tryNode != null && copyFinally(tryNode, breakNode.getTargetNode())) {
   1.943 +    private Node enterBreakOrContinue(final LabeledNode labeledNode) {
   1.944 +        final TryNode tryNode = labeledNode.getTryChain();
   1.945 +        if (tryNode != null && copyFinally(tryNode, labeledNode.getTargetNode())) {
   1.946              return null;
   1.947          }
   1.948 -
   1.949 -        statements.add(breakNode);
   1.950 -
   1.951 +        addStatement(labeledNode);
   1.952          return null;
   1.953      }
   1.954  
   1.955 -    /**
   1.956 -     * Blesses nodes with an expectant type, converting if necessary.
   1.957 -     *
   1.958 -     * @param node Node to be blest.
   1.959 -     * @param type Type class expected.
   1.960 -     *
   1.961 -     * @return Converted node or original node if no conversion is needed.
   1.962 -     */
   1.963 -    private Node convert(final Node node, final Type type) {
   1.964 -
   1.965 -        final Symbol       symbol       = node.getSymbol();
   1.966 -        final FunctionNode functionNode = getCurrentFunctionNode();
   1.967 -
   1.968 -        assert !type.isUnknown() : "unknown";
   1.969 -
   1.970 -        /*
   1.971 -         * Conversions are now mandatory, have to be placed at code time and
   1.972 -         * cannot be removed as there might be cases like:
   1.973 -         *
   1.974 -         * var x = 17; if (x + 1) { ... }
   1.975 -         *
   1.976 -         * x = Number(4711); if (x + 1) { ... }
   1.977 -         *
   1.978 -         * In the old world this behaved as follows:
   1.979 -         *
   1.980 -         * Here, x is originally inferred type to a number, then the if does a
   1.981 -         * double add without a cast. However, the next statement turns x into
   1.982 -         * an object, and this messes up the original. If we would now suddenly
   1.983 -         * need an explicit cast for the first addition, given that we don't do
   1.984 -         * fancy stuff like splitting live range.
   1.985 -         *
   1.986 -         * Even worse is the case where we have an eval that modifies local
   1.987 -         * variables in the middle of a function and may widen a type suspected
   1.988 -         * to be at most e.g. NUMBER to e.g. OBJECT.
   1.989 -         *
   1.990 -         * There are a few local optimizations we can do. If we want to do an
   1.991 -         * OBJECT to OBJECT cast, for example, we can skip it as already is
   1.992 -         * maximally wide, except if we are in a method with an eval where
   1.993 -         * everything is possible...
   1.994 -         *
   1.995 -         * @see test/test262/test/suite/ch07/7.2/S7.2_A1.4_T2.js for an example
   1.996 -         * of this.
   1.997 -         */
   1.998 -
   1.999 -        assert symbol != null : "no symbol for " + node;
  1.1000 -
  1.1001 -        /* check object to object cast */
  1.1002 -        if (!functionNode.hasEval() && node.getType().isEquivalentTo(Type.OBJECT) && type.isEquivalentTo(Type.OBJECT)) {
  1.1003 -            return node;
  1.1004 -        }
  1.1005 -
  1.1006 -        Node resultNode = node;
  1.1007 -
  1.1008 -        // Literal nodes may be converted directly
  1.1009 -
  1.1010 -        if (node instanceof LiteralNode) {
  1.1011 -            final LiteralNode<?> convertedLiteral = new LiteralNodeConstantEvaluator((LiteralNode<?>)node, type).eval();
  1.1012 -            if (convertedLiteral != null) {
  1.1013 -                resultNode = newLiteral(convertedLiteral);
  1.1014 -            }
  1.1015 -            // object literals still need the cast
  1.1016 -            if (type.isObject()) {
  1.1017 -                resultNode = new UnaryNode(source, Token.recast(node.getToken(), TokenType.CONVERT), node);
  1.1018 -            }
  1.1019 -        } else {
  1.1020 -            if (resultNode.getSymbol().isParam()) {
  1.1021 -                resultNode.getSymbol().setType(type);
  1.1022 -            }
  1.1023 -             resultNode = new UnaryNode(source, Token.recast(node.getToken(), TokenType.CONVERT), resultNode);
  1.1024 -        }
  1.1025 -
  1.1026 -        functionNode.newTemporary(type, resultNode);
  1.1027 -        resultNode.copyTerminalFlags(node);
  1.1028 -
  1.1029 -        return resultNode;
  1.1030 -    }
  1.1031 -
  1.1032 -    /**
  1.1033 -     * Accept and convert all arguments to type Object. If we have a
  1.1034 -     * specialization profile for this function, we instead try to specialize
  1.1035 -     * the arguments before the casts based on their current types and values.
  1.1036 -     *
  1.1037 -     * @param callNode function call
  1.1038 -     * @return return type for call
  1.1039 -     */
  1.1040 -    private Type acceptArgs(final CallNode callNode) {
  1.1041 -        final List<Node> oldArgs = callNode.getArgs();
  1.1042 -        final List<Node> acceptedArgs = new ArrayList<>(oldArgs.size());
  1.1043 -
  1.1044 -        for (final Node arg : oldArgs) {
  1.1045 -            //acceptedArgs.add(convert(arg.accept(this), OBJECT));
  1.1046 -            acceptedArgs.add(arg.accept(this));
  1.1047 -        }
  1.1048 -        callNode.setArgs(acceptedArgs);
  1.1049 -
  1.1050 -        return Type.OBJECT;
  1.1051 -    }
  1.1052 -
  1.1053 -    private static String evalLocation(final IdentNode node) {
  1.1054 -        final StringBuilder sb = new StringBuilder(node.getSource().getName());
  1.1055 -
  1.1056 -        sb.append('#');
  1.1057 -        sb.append(node.getSource().getLine(node.position()));
  1.1058 -        sb.append("<eval>");
  1.1059 -
  1.1060 -        return sb.toString();
  1.1061 -    }
  1.1062 -
  1.1063 -    private void checkEval(final CallNode callNode) {
  1.1064 -        if (callNode.getFunction() instanceof IdentNode) {
  1.1065 -
  1.1066 -            final List<Node> args   = callNode.getArgs();
  1.1067 -            final IdentNode  callee = (IdentNode)callNode.getFunction();
  1.1068 -
  1.1069 -            // 'eval' call with atleast one argument
  1.1070 -            if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) {
  1.1071 -                final CallNode.EvalArgs evalArgs = new CallNode.EvalArgs();
  1.1072 -                // code that is evaluated
  1.1073 -                evalArgs.code = args.get(0).clone();
  1.1074 -                evalArgs.code.accept(this);
  1.1075 -                // 'this' to be passed to evaluated code
  1.1076 -                evalArgs.evalThis = new IdentNode(getCurrentFunctionNode().getThisNode());
  1.1077 -                // location string of the eval call
  1.1078 -                evalArgs.location = evalLocation(callee);
  1.1079 -                // strict mode context or not?
  1.1080 -                evalArgs.strictMode = getCurrentFunctionNode().isStrictMode();
  1.1081 -                callNode.setEvalArgs(evalArgs);
  1.1082 -            }
  1.1083 -        }
  1.1084 -    }
  1.1085 -
  1.1086 -    private static Node markerFunction(final Node function) {
  1.1087 -        if (function instanceof IdentNode) {
  1.1088 -            return new IdentNode((IdentNode)function) {
  1.1089 -                @Override
  1.1090 -                public boolean isFunction() {
  1.1091 -                    return true;
  1.1092 -                }
  1.1093 -            };
  1.1094 -        } else if (function instanceof AccessNode) {
  1.1095 -            return new AccessNode((AccessNode)function) {
  1.1096 -                @Override
  1.1097 -                public boolean isFunction() {
  1.1098 -                    return true;
  1.1099 -                }
  1.1100 -            };
  1.1101 -        } else if (function instanceof IndexNode) {
  1.1102 -            return new IndexNode((IndexNode)function) {
  1.1103 -                @Override
  1.1104 -                public boolean isFunction() {
  1.1105 -                    return true;
  1.1106 -                }
  1.1107 -            };
  1.1108 -        }
  1.1109 -
  1.1110 -        return function;
  1.1111 -    }
  1.1112 -
  1.1113 -    @Override
  1.1114 -    public Node enter(final CallNode callNode) {
  1.1115 -        final Node function       = callNode.getFunction();
  1.1116 -        final Node markedFunction = markerFunction(function);
  1.1117 -
  1.1118 -        callNode.setFunction(markedFunction.accept(this));
  1.1119 -
  1.1120 -        checkEval(callNode);
  1.1121 -
  1.1122 -        final Type returnType = acceptArgs(callNode);
  1.1123 -        getCurrentFunctionNode().newTemporary(returnType, callNode);
  1.1124 -        callNode.getFunction().getSymbol().setType(returnType);
  1.1125 -
  1.1126 -        return null;
  1.1127 -    }
  1.1128 -
  1.1129 -    @Override
  1.1130 -    public Node leave(final CaseNode caseNode) {
  1.1131 -        caseNode.copyTerminalFlags(caseNode.getBody());
  1.1132 -
  1.1133 -        return caseNode;
  1.1134 -    }
  1.1135 -
  1.1136 -    @Override
  1.1137 -    public Node enter(final CatchNode catchNode) {
  1.1138 -        final IdentNode ident = catchNode.getException();
  1.1139 -        final Block     block = getCurrentBlock();
  1.1140 -
  1.1141 -        // define block-local exception variable
  1.1142 -        block.defineSymbol(ident.getName(), IS_VAR | IS_LET, ident).setType(Type.OBJECT);
  1.1143 -        localDefs.add(ident.getName());
  1.1144 -
  1.1145 -        return catchNode;
  1.1146 -    }
  1.1147 -
  1.1148 -    @Override
  1.1149 -    public Node leave(final CatchNode catchNode) {
  1.1150 -        final Node exceptionCondition = catchNode.getExceptionCondition();
  1.1151 -        if (exceptionCondition != null) {
  1.1152 -            catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
  1.1153 -        }
  1.1154 -
  1.1155 -        catchNode.copyTerminalFlags(catchNode.getBody());
  1.1156 -
  1.1157 -        statements.add(catchNode);
  1.1158 -
  1.1159 -        return catchNode;
  1.1160 -    }
  1.1161 -
  1.1162 -    @Override
  1.1163 -    public Node enter(final ContinueNode continueNode) {
  1.1164 -        final TryNode tryNode = continueNode.getTryChain();
  1.1165 -        final Node target = continueNode.getTargetNode();
  1.1166 -
  1.1167 -        if (tryNode != null && copyFinally(tryNode, target)) {
  1.1168 -            return null;
  1.1169 -        }
  1.1170 -
  1.1171 -        statements.add(continueNode);
  1.1172 -
  1.1173 -        return null;
  1.1174 -    }
  1.1175 -
  1.1176 -    @Override
  1.1177 -    public Node enter(final DoWhileNode whileNode) {
  1.1178 -        return enter((WhileNode)whileNode);
  1.1179 -    }
  1.1180 -
  1.1181 -    @Override
  1.1182 -    public Node leave(final DoWhileNode whileNode) {
  1.1183 -        return leave((WhileNode)whileNode);
  1.1184 -    }
  1.1185 -
  1.1186 -    @Override
  1.1187 -    public Node enter(final EmptyNode emptyNode) {
  1.1188 -        return null;
  1.1189 -    }
  1.1190 -
  1.1191 -    /**
  1.1192 -     * Is this an assignment to the special variable that hosts scripting eval
  1.1193 -     * results?
  1.1194 -     *
  1.1195 -     * @param expression expression to check whether it is $evalresult = X
  1.1196 -     *
  1.1197 -     * @return true if an assignment to eval result, false otherwise
  1.1198 -     */
  1.1199 -    private boolean isEvalResultAssignment(final Node expression) {
  1.1200 -        Node e = expression;
  1.1201 -        if (e.tokenType() == TokenType.DISCARD) {
  1.1202 -            e = ((UnaryNode)expression).rhs();
  1.1203 -        }
  1.1204 -        final Node resultNode = getCurrentFunctionNode().getResultNode();
  1.1205 -        return e instanceof BinaryNode && ((BinaryNode)e).lhs().equals(resultNode);
  1.1206 -    }
  1.1207  
  1.1208      /**
  1.1209       * An internal expression has a symbol that is tagged internal. Check if
  1.1210 @@ -595,2543 +932,41 @@
  1.1211      }
  1.1212  
  1.1213      /**
  1.1214 -     * Discard the result of the expression.
  1.1215 +     * Is this an assignment to the special variable that hosts scripting eval
  1.1216 +     * results?
  1.1217       *
  1.1218 -     * @param expression Expression to discard.
  1.1219 -     *
  1.1220 -     * @return Discard node.
  1.1221 +     * @param expression expression to check whether it is $evalresult = X
  1.1222 +     * @return true if an assignment to eval result, false otherwise
  1.1223       */
  1.1224 -    private Node discard(final Node expression) {
  1.1225 -        expression.setDiscard(true);
  1.1226 -
  1.1227 -        if (expression.getSymbol() != null) {
  1.1228 -            final Node discard = new UnaryNode(source, Token.recast(expression.getToken(), TokenType.DISCARD), expression);
  1.1229 -            discard.copyTerminalFlags(expression);
  1.1230 -
  1.1231 -            return discard;
  1.1232 +    private boolean isEvalResultAssignment(final Node expression) {
  1.1233 +        Node e = expression;
  1.1234 +        if (e.tokenType() == TokenType.DISCARD) {
  1.1235 +            e = ((UnaryNode)expression).rhs();
  1.1236          }
  1.1237 -
  1.1238 -        return expression;
  1.1239 +        final Node resultNode = getCurrentFunctionNode().getResultNode();
  1.1240 +        return e instanceof BinaryNode && ((BinaryNode)e).lhs().equals(resultNode);
  1.1241      }
  1.1242  
  1.1243      /**
  1.1244 -     * ExecuteNodes are needed to actually generate code, with a few exceptions
  1.1245 -     * such as ReturnNodes and ThrowNodes and various control flow that can
  1.1246 -     * standalone. Every other kind of statement needs to be wrapped in an
  1.1247 -     * ExecuteNode in order to become code
  1.1248 -     *
  1.1249 -     * @param executeNode  the execute node to visit
  1.1250 +     * Prepare special function nodes.
  1.1251 +     * TODO : only create those that are needed.
  1.1252 +     * TODO : make sure slot numbering is not hardcoded in {@link CompilerConstants} - now creation order is significant
  1.1253       */
  1.1254 -    @Override
  1.1255 -    public Node leave(final ExecuteNode executeNode) {
  1.1256 -
  1.1257 -        Node expression = executeNode.getExpression();
  1.1258 -
  1.1259 -        /*
  1.1260 -         * Handle the eval result for scripts. Every statement has to write its
  1.1261 -         * return value to a special variable that is the result of the script.
  1.1262 -         */
  1.1263 -        if (getCurrentFunctionNode().isScript() && !(expression instanceof Block) && !isEvalResultAssignment(expression) && !isInternalExpression(expression)) {
  1.1264 -            final Node resultNode = getCurrentFunctionNode().getResultNode();
  1.1265 -            expression = new BinaryNode(source, Token.recast(executeNode.getToken(), TokenType.ASSIGN), resultNode, convert(expression, resultNode.getType()));
  1.1266 -
  1.1267 -            getCurrentFunctionNode().newTemporary(Type.OBJECT, expression);
  1.1268 -        }
  1.1269 -
  1.1270 -        expression = discard(expression);
  1.1271 -        executeNode.setExpression(expression);
  1.1272 -        executeNode.copyTerminalFlags(expression);
  1.1273 -
  1.1274 -        statements.add(executeNode);
  1.1275 -
  1.1276 -        return executeNode;
  1.1277 -    }
  1.1278 -
  1.1279 -    /**
  1.1280 -     * Helper that given a loop body makes sure that it is not terminal if it
  1.1281 -     * has a continue that leads to the loop header or to outer loops' loop
  1.1282 -     * headers. This means that, even if the body ends with a terminal
  1.1283 -     * statement, we cannot tag it as terminal
  1.1284 -     *
  1.1285 -     * @param loopBody the loop body to check
  1.1286 -     */
  1.1287 -    private boolean controlFlowEscapes(final Node loopBody) {
  1.1288 -        final List<Node> escapes = new ArrayList<>();
  1.1289 -
  1.1290 -        loopBody.accept(new NodeVisitor() {
  1.1291 -            @Override
  1.1292 -            public Node leave(final BreakNode node) {
  1.1293 -                escapes.add(node);
  1.1294 -                return node;
  1.1295 -            }
  1.1296 -
  1.1297 -            @Override
  1.1298 -            public Node leave(final ContinueNode node) {
  1.1299 -                // all inner loops have been popped.
  1.1300 -                if (nesting.contains(node.getTargetNode())) {
  1.1301 -                    escapes.add(node);
  1.1302 -                }
  1.1303 -                return node;
  1.1304 -            }
  1.1305 -        });
  1.1306 -
  1.1307 -        return !escapes.isEmpty();
  1.1308 -    }
  1.1309 -
  1.1310 -    @Override
  1.1311 -    public Node enter(final ForNode forNode) {
  1.1312 -        // push the loop to the loop context
  1.1313 -        nest(forNode);
  1.1314 -        return forNode;
  1.1315 -    }
  1.1316 -
  1.1317 -    private static boolean conservativeAlwaysTrue(final Node node) {
  1.1318 -        return node == null || ((node instanceof LiteralNode) && Boolean.TRUE.equals(((LiteralNode<?>)node).getValue()));
  1.1319 -    }
  1.1320 -
  1.1321 -    @Override
  1.1322 -    public Node leave(final ForNode forNode) {
  1.1323 -        final Node init   = forNode.getInit();
  1.1324 -        final Node test   = forNode.getTest();
  1.1325 -        final Node modify = forNode.getModify();
  1.1326 -
  1.1327 -        if (forNode.isForIn()) {
  1.1328 -            final String name = compiler.uniqueName(ITERATOR_PREFIX.tag());
  1.1329 -            // DEFINE SYMBOL: may be any type, not local var
  1.1330 -            final Symbol iter = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null);
  1.1331 -            iter.setType(Type.OBJECT); // NASHORN-73
  1.1332 -            forNode.setIterator(iter);
  1.1333 -            forNode.setModify(convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
  1.1334 -
  1.1335 -            /*
  1.1336 -             * Iterators return objects, so we need to widen the scope of the
  1.1337 -             * init variable if it, for example, has been assigned double type
  1.1338 -             * see NASHORN-50
  1.1339 -             */
  1.1340 -            forNode.getInit().getSymbol().setType(Type.OBJECT);
  1.1341 -        } else {
  1.1342 -            /* Normal for node, not for in */
  1.1343 -
  1.1344 -            if (init != null) {
  1.1345 -                forNode.setInit(discard(init));
  1.1346 -            }
  1.1347 -
  1.1348 -            if (test != null) {
  1.1349 -                forNode.setTest(convert(test, Type.BOOLEAN));
  1.1350 -            } else {
  1.1351 -                forNode.setHasGoto();
  1.1352 -            }
  1.1353 -
  1.1354 -            if (modify != null) {
  1.1355 -                forNode.setModify(discard(modify));
  1.1356 -            }
  1.1357 -        }
  1.1358 -
  1.1359 -        final Block body = forNode.getBody();
  1.1360 -
  1.1361 -        final boolean escapes = controlFlowEscapes(body);
  1.1362 -        if (escapes) {
  1.1363 -            body.setIsTerminal(false);
  1.1364 -        }
  1.1365 -
  1.1366 -        // pop the loop from the loop context
  1.1367 -        unnest();
  1.1368 -
  1.1369 -        if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
  1.1370 -            forNode.setTest(null);
  1.1371 -            forNode.setIsTerminal(!escapes);
  1.1372 -        }
  1.1373 -
  1.1374 -        statements.add(forNode);
  1.1375 -
  1.1376 -        return forNode;
  1.1377 -    }
  1.1378 -
  1.1379 -    /**
  1.1380 -     * Initialize this symbol and variable for function node
  1.1381 -     *
  1.1382 -     * @param functionNode the function node
  1.1383 -     */
  1.1384 -    private void initThis(final FunctionNode functionNode) {
  1.1385 -        final long token = functionNode.getToken();
  1.1386 -        final int finish = functionNode.getFinish();
  1.1387 -
  1.1388 -        final Symbol thisSymbol = functionNode.defineSymbol(THIS.tag(), IS_PARAM | IS_THIS, null);
  1.1389 -        final IdentNode thisNode = new IdentNode(source, token, finish, thisSymbol.getName());
  1.1390 -
  1.1391 -        thisSymbol.setType(Type.OBJECT);
  1.1392 -        thisSymbol.setNeedsSlot(true);
  1.1393 -
  1.1394 -        thisNode.setSymbol(thisSymbol);
  1.1395 -
  1.1396 -        functionNode.setThisNode(thisNode);
  1.1397 -    }
  1.1398 -
  1.1399 -    /**
  1.1400 -     * Initialize scope symbol and variable for function node
  1.1401 -     *
  1.1402 -     * @param functionNode the function node
  1.1403 -     */
  1.1404 -    private void initScope(final FunctionNode functionNode) {
  1.1405 -        final long token = functionNode.getToken();
  1.1406 -        final int finish = functionNode.getFinish();
  1.1407 -
  1.1408 -        final Symbol scopeSymbol = functionNode.defineSymbol(SCOPE.tag(), IS_VAR | IS_INTERNAL, null);
  1.1409 -        final IdentNode scopeNode = new IdentNode(source, token, finish, scopeSymbol.getName());
  1.1410 -
  1.1411 -        scopeSymbol.setType(ScriptObject.class);
  1.1412 -        scopeSymbol.setNeedsSlot(true);
  1.1413 -
  1.1414 -        scopeNode.setSymbol(scopeSymbol);
  1.1415 -
  1.1416 -        functionNode.setScopeNode(scopeNode);
  1.1417 -    }
  1.1418 -
  1.1419 -    /**
  1.1420 -     * Initialize return symbol and variable for function node
  1.1421 -     *
  1.1422 -     * @param functionNode the function node
  1.1423 -     */
  1.1424 -    private void initReturn(final FunctionNode functionNode) {
  1.1425 -        final long token = functionNode.getToken();
  1.1426 -        final int finish = functionNode.getFinish();
  1.1427 -
  1.1428 -        final Symbol returnSymbol = functionNode.defineSymbol(SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null);
  1.1429 -        final IdentNode returnNode = new IdentNode(source, token, finish, returnSymbol.getName());
  1.1430 -
  1.1431 -        returnSymbol.setType(Object.class);
  1.1432 -        returnSymbol.setNeedsSlot(true);
  1.1433 -
  1.1434 -        returnNode.setSymbol(returnSymbol);
  1.1435 -
  1.1436 -        functionNode.setResultNode(returnNode);
  1.1437 -    }
  1.1438 -
  1.1439 -    /**
  1.1440 -     * Initialize varargs for function node, if applicable
  1.1441 -     *
  1.1442 -     * @param functionNode
  1.1443 -     */
  1.1444 -    private void initVarArg(final FunctionNode functionNode) {
  1.1445 +    private void initFunctionNode(final FunctionNode functionNode) {
  1.1446          final long token  = functionNode.getToken();
  1.1447          final int  finish = functionNode.getFinish();
  1.1448  
  1.1449 -        assert functionNode.getCalleeNode() != null;
  1.1450 +        LOG.info("Init this, scope, result, callee, varargs and argument for " + functionNode.getName());
  1.1451  
  1.1452 -        final Symbol    varArgsSymbol = functionNode.defineSymbol(VARARGS.tag(), IS_PARAM | IS_INTERNAL, null);
  1.1453 -        final IdentNode varArgsNode   = new IdentNode(source, token, finish, varArgsSymbol.getName());
  1.1454 -
  1.1455 -        varArgsSymbol.setType(Type.OBJECT_ARRAY);
  1.1456 -        varArgsSymbol.setNeedsSlot(true);
  1.1457 -
  1.1458 -        varArgsNode.setSymbol(varArgsSymbol);
  1.1459 -
  1.1460 -        functionNode.setVarArgsNode(varArgsNode);
  1.1461 -
  1.1462 -        final String    argumentsName    = ARGUMENTS.tag();
  1.1463 -        final String    name             = functionNode.hideArguments() ? functionNode.uniqueName("$" + argumentsName) : argumentsName;
  1.1464 -        final Symbol    argumentsSymbol  = functionNode.defineSymbol(name, IS_PARAM | IS_INTERNAL, null);
  1.1465 -        final IdentNode argumentsNode    = new IdentNode(source, token, finish, argumentsSymbol.getName());
  1.1466 -
  1.1467 -        argumentsSymbol.setType(Type.OBJECT);
  1.1468 -        argumentsSymbol.setNeedsSlot(true);
  1.1469 -
  1.1470 -        argumentsNode.setSymbol(argumentsSymbol);
  1.1471 -
  1.1472 -        functionNode.setArgumentsNode(argumentsNode);
  1.1473 +        functionNode.setThisNode(new IdentNode(source, token, finish, THIS.tag()));
  1.1474 +        functionNode.setScopeNode(new IdentNode(source, token, finish, SCOPE.tag()));
  1.1475 +        functionNode.setResultNode(new IdentNode(source, token, finish, SCRIPT_RETURN.tag()));
  1.1476 +        functionNode.setCalleeNode(new IdentNode(source, token, finish, CALLEE.tag()));
  1.1477 +        functionNode.setVarArgsNode(new IdentNode(source, token, finish, VARARGS.tag()));
  1.1478 +        functionNode.setArgumentsNode(new IdentNode(source, token, finish, functionNode.hideArguments() ? compiler.uniqueName('$' + ARGUMENTS.tag()) : ARGUMENTS.tag()));
  1.1479      }
  1.1480  
  1.1481 -    private void initCallee(final FunctionNode functionNode) {
  1.1482 -        if (functionNode.getCalleeNode() != null) {
  1.1483 -            return;
  1.1484 -        }
  1.1485 +}
  1.1486  
  1.1487 -        final long token = functionNode.getToken();
  1.1488 -        final int finish = functionNode.getFinish();
  1.1489  
  1.1490 -        final Symbol    calleeSymbol = functionNode.defineSymbol(CALLEE.tag(), IS_PARAM | IS_INTERNAL, null);
  1.1491 -        final IdentNode calleeNode   = new IdentNode(source, token, finish, calleeSymbol.getName());
  1.1492  
  1.1493 -        calleeSymbol.setType(ScriptFunction.class);
  1.1494 -        calleeSymbol.setNeedsSlot(true);
  1.1495 -
  1.1496 -        calleeNode.setSymbol(calleeSymbol);
  1.1497 -
  1.1498 -        functionNode.setCalleeNode(calleeNode);
  1.1499 -    }
  1.1500 -
  1.1501 -    /**
  1.1502 -     * Initialize parameters for function node. This may require specializing
  1.1503 -     * types if a specialization profile is known
  1.1504 -     *
  1.1505 -     * @param functionNode the function node
  1.1506 -     */
  1.1507 -    private void initParameters(final FunctionNode functionNode) {
  1.1508 -        /*
  1.1509 -         * If a function is specialized, we don't need to tag either it return
  1.1510 -         * type or its parameters with the widest (OBJECT) type for safety.
  1.1511 -         */
  1.1512 -        functionNode.setReturnType(Type.UNKNOWN);
  1.1513 -
  1.1514 -        for (final IdentNode ident : functionNode.getParameters()) {
  1.1515 -            localDefs.add(ident.getName());
  1.1516 -            final Symbol paramSymbol = functionNode.defineSymbol(ident.getName(), IS_PARAM, ident);
  1.1517 -            if (paramSymbol != null) {
  1.1518 -                paramSymbol.setType(Type.UNKNOWN);
  1.1519 -            }
  1.1520 -        }
  1.1521 -    }
  1.1522 -
  1.1523 -    /**
  1.1524 -     * This has to run before fix assignment types, store any type specializations for
  1.1525 -     * paramters, then turn then to objects for the generic version of this method
  1.1526 -     *
  1.1527 -     * @param functionNode functionNode
  1.1528 -     */
  1.1529 -    private static void fixParameters(final FunctionNode functionNode) {
  1.1530 -        boolean nonObjectParams = false;
  1.1531 -        List<Type> paramSpecializations = new ArrayList<>();
  1.1532 -
  1.1533 -        for (final IdentNode ident : functionNode.getParameters()) {
  1.1534 -            final Symbol paramSymbol = ident.getSymbol();
  1.1535 -            if (paramSymbol != null) {
  1.1536 -                Type type = paramSymbol.getSymbolType();
  1.1537 -                if (type.isUnknown()) {
  1.1538 -                    type = Type.OBJECT;
  1.1539 -                }
  1.1540 -                paramSpecializations.add(type);
  1.1541 -                if (!type.isObject()) {
  1.1542 -                    nonObjectParams = true;
  1.1543 -                }
  1.1544 -                paramSymbol.setType(Type.OBJECT);
  1.1545 -            }
  1.1546 -        }
  1.1547 -
  1.1548 -        if (!nonObjectParams) {
  1.1549 -            paramSpecializations = null;
  1.1550 -            /*
  1.1551 -             * Later, when resolving a call to this method, the linker can say "I have a double, an int and an object" as parameters
  1.1552 -             * here. If the callee has parameter specializations, we can regenerate it with those particular types for speed.
  1.1553 -             */
  1.1554 -        } else {
  1.1555 -            LOG.info("parameter specialization possible: " + functionNode.getName() + " " + paramSpecializations);
  1.1556 -        }
  1.1557 -
  1.1558 -        // parameters should not be slots for a vararg function, make sure this is the case
  1.1559 -        if (functionNode.isVarArg()) {
  1.1560 -            for (final IdentNode param : functionNode.getParameters()) {
  1.1561 -                param.getSymbol().setNeedsSlot(false);
  1.1562 -            }
  1.1563 -        }
  1.1564 -    }
  1.1565 -
  1.1566 -    private LiteralNode<Undefined> undefined() {
  1.1567 -        return LiteralNode.newInstance(source, 0, 0, UNDEFINED);
  1.1568 -    }
  1.1569 -
  1.1570 -    private void guaranteeReturn(final FunctionNode functionNode) {
  1.1571 -        Node resultNode;
  1.1572 -
  1.1573 -        if (functionNode.isScript()) {
  1.1574 -            resultNode = functionNode.getResultNode();
  1.1575 -        } else {
  1.1576 -            final Node lastStatement = Node.lastStatement(functionNode.getStatements());
  1.1577 -            if (lastStatement != null && (lastStatement.isTerminal() || lastStatement instanceof ReturnNode)) {
  1.1578 -                return; // return already in place, as it should be for a non-undefined returning function
  1.1579 -            }
  1.1580 -            resultNode = undefined().accept(this);
  1.1581 -        }
  1.1582 -
  1.1583 -        new ReturnNode(source, functionNode.getLastToken(), functionNode.getFinish(), resultNode, null).accept(this);
  1.1584 -
  1.1585 -        functionNode.setReturnType(Type.OBJECT);
  1.1586 -    }
  1.1587 -
  1.1588 -    /**
  1.1589 -     * Fix return types for a node. The return type is the widest of all known
  1.1590 -     * return expressions in the function, and has to be the same for all return
  1.1591 -     * nodes, thus we need to traverse all of them in case some of them must be
  1.1592 -     * upcast
  1.1593 -     *
  1.1594 -     * @param node function node to scan return types for
  1.1595 -     */
  1.1596 -    private void fixReturnTypes(final FunctionNode node) {
  1.1597 -
  1.1598 -        if (node.getReturnType().isUnknown()) {
  1.1599 -            node.setReturnType(Type.OBJECT); // for example, infinite loops
  1.1600 -        }
  1.1601 -
  1.1602 -        node.accept(new NodeVisitor() {
  1.1603 -            @Override
  1.1604 -            public Node enter(final FunctionNode subFunction) {
  1.1605 -                //leave subfunctions alone. their return values are already specialized and should not
  1.1606 -                //be touched.
  1.1607 -                return null;
  1.1608 -            }
  1.1609 -            @Override
  1.1610 -            public Node leave(final ReturnNode returnNode) {
  1.1611 -                if (returnNode.hasExpression()) {
  1.1612 -                    returnNode.setExpression(convert(returnNode.getExpression(), node.getReturnType()));
  1.1613 -                }
  1.1614 -                return returnNode;
  1.1615 -            }
  1.1616 -        });
  1.1617 -    }
  1.1618 -
  1.1619 -    /**
  1.1620 -     * Augment assignments with casts after traversing a function node.
  1.1621 -     * Assignment nodes are special, as they require entire scope for type
  1.1622 -     * inference.
  1.1623 -     *
  1.1624 -     * Consider e.g.
  1.1625 -     *
  1.1626 -     * var x = 18.5; //double
  1.1627 -     * for (...) {
  1.1628 -     *      var y = x; //initially y is inferred to be double y = func(x) //y changes to object, but above assignment is unaware that it needs to be var y = (object)x instaed
  1.1629 -     */
  1.1630 -
  1.1631 -    private void fixAssignmentTypes(final FunctionNode node, final List<Symbol> declaredSymbols) {
  1.1632 -
  1.1633 -        // Make sure all unknown symbols are OBJECT types now. No UNKNOWN may survive lower.
  1.1634 -        for (final Symbol symbol : declaredSymbols) {
  1.1635 -            if (symbol.getSymbolType().isUnknown()) {
  1.1636 -                LOG.finest("fixAssignmentTypes: widening " + symbol + " to object " + symbol + " in " + getCurrentFunctionNode());
  1.1637 -                symbol.setType(Type.OBJECT);
  1.1638 -                symbol.setCanBeUndefined();
  1.1639 -            }
  1.1640 -
  1.1641 -            if (symbol.canBeUndefined()) {
  1.1642 -                /*
  1.1643 -                 * Ideally we'd like to only widen to the narrowest
  1.1644 -                 * possible type that supports local variables, i.e. doubles for
  1.1645 -                 * numerics, but
  1.1646 -                 *
  1.1647 -                 * var x;
  1.1648 -                 *
  1.1649 -                 * if (x !== undefined) do something
  1.1650 -                 *
  1.1651 -                 * x++;
  1.1652 -                 *
  1.1653 -                 * Screws this up, as x is determined to be a double even though
  1.1654 -                 * the use is undefined This can be fixed with better use
  1.1655 -                 * analysis in the IdentNode visitor.
  1.1656 -                 *
  1.1657 -                 * This actually seems to be superseded with calls to ensureTypeNotUnknown
  1.1658 -                 */
  1.1659 -                if (!Type.areEquivalent(symbol.getSymbolType(), Type.OBJECT)) {
  1.1660 -                    debug(symbol + " in " + getCurrentFunctionNode() + " can be undefined. Widening to object");
  1.1661 -                    symbol.setType(Type.OBJECT);
  1.1662 -                }
  1.1663 -            }
  1.1664 -        }
  1.1665 -
  1.1666 -        node.accept(new NodeVisitor() {
  1.1667 -
  1.1668 -            private void fix(final Assignment<? extends Node> assignment) {
  1.1669 -                final Node src  = assignment.getAssignmentSource();
  1.1670 -                final Node dest = assignment.getAssignmentDest();
  1.1671 -
  1.1672 -                if (src == null) {
  1.1673 -                    return;
  1.1674 -                }
  1.1675 -
  1.1676 -                //we don't know about scope yet so this is too conservative. AccessSpecialized will remove casts that are not required
  1.1677 -
  1.1678 -                final Type srcType  = src.getType();
  1.1679 -                Type destType = dest.getType();
  1.1680 -
  1.1681 -                //we can only narrow an operation type if the variable doesn't have a slot
  1.1682 -                if (!dest.getSymbol().hasSlot() && !node.hasDeepWithOrEval()) {
  1.1683 -                    destType = Type.narrowest(((Node)assignment).getWidestOperationType(), destType);
  1.1684 -                }
  1.1685 -
  1.1686 -                if (!Type.areEquivalent(destType, srcType)) {
  1.1687 -                    LOG.finest("fixAssignment " + assignment + " type = " + src.getType() + "=>" + dest.getType());
  1.1688 -                    assignment.setAssignmentSource(convert(src, destType));
  1.1689 -                }
  1.1690 -            }
  1.1691 -
  1.1692 -            private Node checkAssignment(final Node assignNode) {
  1.1693 -                if (!assignNode.isAssignment()) {
  1.1694 -                    return assignNode;
  1.1695 -                }
  1.1696 -                fix((Assignment<?>)assignNode);
  1.1697 -                return assignNode;
  1.1698 -            }
  1.1699 -
  1.1700 -
  1.1701 -            @Override
  1.1702 -            public Node leave(final UnaryNode unaryNode) {
  1.1703 -                return checkAssignment(unaryNode);
  1.1704 -            }
  1.1705 -
  1.1706 -            @Override
  1.1707 -            public Node leave(final BinaryNode binaryNode) {
  1.1708 -                return checkAssignment(binaryNode);
  1.1709 -            }
  1.1710 -
  1.1711 -            @Override
  1.1712 -            public Node leave(final VarNode varNode) {
  1.1713 -                return checkAssignment(varNode);
  1.1714 -            }
  1.1715 -        });
  1.1716 -    }
  1.1717 -
  1.1718 -    /*
  1.1719 -     * For a script, add scope symbols as defined in the property map
  1.1720 -     */
  1.1721 -    private static void initFromPropertyMap(final Context context, final FunctionNode functionNode) {
  1.1722 -        assert functionNode.isScript();
  1.1723 -
  1.1724 -        final PropertyMap map = context.getGlobalMap();
  1.1725 -
  1.1726 -        for (final Property property : map.getProperties()) {
  1.1727 -            final String key = property.getKey();
  1.1728 -            functionNode.defineSymbol(key, IS_GLOBAL, null).setType(Type.OBJECT);
  1.1729 -        }
  1.1730 -    }
  1.1731 -
  1.1732 -    @Override
  1.1733 -    public Node enter(final FunctionNode functionNode) {
  1.1734 -        LOG.info("START FunctionNode: " + functionNode.getName());
  1.1735 -
  1.1736 -        Node initialEvalResult = undefined();
  1.1737 -
  1.1738 -        nest(functionNode);
  1.1739 -
  1.1740 -        localDefs = new HashSet<>();
  1.1741 -        localUses = new HashSet<>();
  1.1742 -
  1.1743 -        functionNode.setFrame(functionNode.pushFrame());
  1.1744 -
  1.1745 -        initThis(functionNode);
  1.1746 -        initCallee(functionNode);
  1.1747 -        if (functionNode.isVarArg()) {
  1.1748 -            initVarArg(functionNode);
  1.1749 -        }
  1.1750 -
  1.1751 -        initParameters(functionNode);
  1.1752 -        initScope(functionNode);
  1.1753 -        initReturn(functionNode);
  1.1754 -
  1.1755 -        /*
  1.1756 -         * Add all nested functions as symbols in this function
  1.1757 -         */
  1.1758 -        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
  1.1759 -            final IdentNode ident = nestedFunction.getIdent();
  1.1760 -
  1.1761 -            // for initial eval result is the last declared function
  1.1762 -            if (ident != null && nestedFunction.isStatement()) {
  1.1763 -                final Symbol functionSymbol = functionNode.defineSymbol(ident.getName(), IS_VAR, nestedFunction);
  1.1764 -
  1.1765 -                functionSymbol.setType(ScriptFunction.class);
  1.1766 -                initialEvalResult = new IdentNode(ident);
  1.1767 -            }
  1.1768 -        }
  1.1769 -
  1.1770 -        if (functionNode.isScript()) {
  1.1771 -            initFromPropertyMap(compiler.getContext(), functionNode);
  1.1772 -        }
  1.1773 -
  1.1774 -        // Add function name as local symbol
  1.1775 -        if (!functionNode.isStatement() && !functionNode.isAnonymous() && !functionNode.isScript()) {
  1.1776 -            final Symbol selfSymbol = functionNode.defineSymbol(functionNode.getIdent().getName(), IS_VAR, functionNode);
  1.1777 -            selfSymbol.setType(Type.OBJECT);
  1.1778 -            selfSymbol.setNode(functionNode);
  1.1779 -        }
  1.1780 -
  1.1781 -        /*
  1.1782 -         * As we are evaluating a nested structure, we need to store the
  1.1783 -         * statement list for the surrounding block and restore it when the
  1.1784 -         * function is done
  1.1785 -         */
  1.1786 -        final List<Node> savedStatements = statements;
  1.1787 -        statements = new ArrayList<>();
  1.1788 -
  1.1789 -        /*
  1.1790 -         * This pushes all declarations (except for non-statements, i.e. for
  1.1791 -         * node temporaries) to the top of the function scope. This way we can
  1.1792 -         * get around problems like
  1.1793 -         *
  1.1794 -         * while (true) {
  1.1795 -         *   break;
  1.1796 -         *   if (true) {
  1.1797 -         *     var s;
  1.1798 -         *   }
  1.1799 -         * }
  1.1800 -         *
  1.1801 -         * to an arbitrary nesting depth.
  1.1802 -         *
  1.1803 -         * @see NASHORN-73
  1.1804 -         */
  1.1805 -
  1.1806 -        final List<Symbol> declaredSymbols = new ArrayList<>();
  1.1807 -        for (final VarNode decl : functionNode.getDeclarations()) {
  1.1808 -            final IdentNode ident = decl.getName();
  1.1809 -            // any declared symbols that aren't visited need to be typed as
  1.1810 -            // well, hence the list
  1.1811 -            declaredSymbols.add(functionNode.defineSymbol(ident.getName(), IS_VAR, new IdentNode(ident)));
  1.1812 -        }
  1.1813 -
  1.1814 -        declaredSymbolsLocal = new ArrayList<>();
  1.1815 -
  1.1816 -        /*
  1.1817 -         * Main function code lowering
  1.1818 -         */
  1.1819 -        try {
  1.1820 -            /*
  1.1821 -             * Every nested function needs a definition in the outer function
  1.1822 -             * with its name. Add these.
  1.1823 -             */
  1.1824 -            for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
  1.1825 -                final VarNode varNode = nestedFunction.getFunctionVarNode();
  1.1826 -                if (varNode != null) {
  1.1827 -                    final LineNumberNode lineNumberNode = nestedFunction.getFunctionVarLineNumberNode();
  1.1828 -                    if (lineNumberNode != null) {
  1.1829 -                        lineNumberNode.accept(this);
  1.1830 -                    }
  1.1831 -                    varNode.accept(this);
  1.1832 -                    varNode.setIsFunctionVarNode();
  1.1833 -                }
  1.1834 -            }
  1.1835 -
  1.1836 -            if (functionNode.isScript()) {
  1.1837 -                new ExecuteNode(source, functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this);
  1.1838 -            }
  1.1839 -
  1.1840 -            /*
  1.1841 -             * Now we do the statements. This fills the block with code
  1.1842 -             */
  1.1843 -            for (final Node statement : functionNode.getStatements()) {
  1.1844 -                statement.accept(this);
  1.1845 -
  1.1846 -                final Node lastStatement = Node.lastStatement(statements);
  1.1847 -                if (lastStatement != null && lastStatement.hasTerminalFlags()) {
  1.1848 -                    functionNode.copyTerminalFlags(lastStatement);
  1.1849 -                    break;
  1.1850 -                }
  1.1851 -            }
  1.1852 -
  1.1853 -            functionNode.setStatements(statements);
  1.1854 -
  1.1855 -            /*
  1.1856 -             * If there are unusedterminated enpoints in the function, we need
  1.1857 -             * to add a "return undefined" in those places for correct semantics
  1.1858 -             */
  1.1859 -            if (!functionNode.isTerminal()) {
  1.1860 -                guaranteeReturn(functionNode);
  1.1861 -            }
  1.1862 -
  1.1863 -            /*
  1.1864 -             * Emit all nested functions
  1.1865 -             */
  1.1866 -            for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
  1.1867 -                nestedFunction.accept(this);
  1.1868 -            }
  1.1869 -
  1.1870 -            fixReturnTypes(functionNode);
  1.1871 -
  1.1872 -            if (functionNode.needsSelfSymbol()) {
  1.1873 -                final IdentNode selfSlotNode = new IdentNode(functionNode.getIdent());
  1.1874 -                selfSlotNode.setSymbol(functionNode.findSymbol(functionNode.getIdent().getName()));
  1.1875 -                final Node functionRef = new IdentNode(functionNode.getCalleeNode());
  1.1876 -                statements.add(0, new VarNode(source, functionNode.getToken(), functionNode.getFinish(), selfSlotNode, functionRef, false).accept(this));
  1.1877 -            }
  1.1878 -        } finally {
  1.1879 -            /*
  1.1880 -             * Restore statement for outer block that we were working on
  1.1881 -             */
  1.1882 -            statements = savedStatements;
  1.1883 -        }
  1.1884 -
  1.1885 -        unnest();
  1.1886 -
  1.1887 -        fixParameters(functionNode);
  1.1888 -
  1.1889 -        /*
  1.1890 -         * Fix assignment issues. assignments are troublesome as they can be on
  1.1891 -         * the form "dest = dest op source" where the type is different for the
  1.1892 -         * two dest:s: right now this is a post pass that handles putting casts
  1.1893 -         * in the correct places, but it can be implemented as a bottom up AST
  1.1894 -         * traversal on the fly. TODO.
  1.1895 -         */
  1.1896 -        fixAssignmentTypes(functionNode, declaredSymbols);
  1.1897 -
  1.1898 -        /*
  1.1899 -         * Set state of function to lowered and pop the frame
  1.1900 -         */
  1.1901 -        functionNode.setIsLowered(true);
  1.1902 -        functionNode.popFrame();
  1.1903 -
  1.1904 -        LOG.info("END FunctionNode: " + functionNode.getName());
  1.1905 -
  1.1906 -        return null;
  1.1907 -    }
  1.1908 -
  1.1909 -    @Override
  1.1910 -    public Node enter(final IdentNode identNode) {
  1.1911 -        final String name = identNode.getName();
  1.1912 -
  1.1913 -        if (identNode.isPropertyName()) {
  1.1914 -            // assign a pseudo symbol to property name
  1.1915 -            identNode.setSymbol(new Symbol(name, 0, Type.OBJECT));
  1.1916 -            return null;
  1.1917 -        }
  1.1918 -
  1.1919 -        final Block block = getCurrentBlock();
  1.1920 -        final Symbol oldSymbol = identNode.getSymbol();
  1.1921 -        Symbol symbol = block.findSymbol(name);
  1.1922 -
  1.1923 -        /*
  1.1924 -         * If an existing symbol with the name is found, use that otherwise,
  1.1925 -         * declare a new one
  1.1926 -         */
  1.1927 -
  1.1928 -        if (symbol != null) {
  1.1929 -            if (isFunctionExpressionSelfReference(symbol)) {
  1.1930 -                ((FunctionNode) symbol.getNode()).setNeedsSelfSymbol();
  1.1931 -            }
  1.1932 -
  1.1933 -            if (!identNode.isInitializedHere()) { // NASHORN-448
  1.1934 -                // here is a use outside the local def scope
  1.1935 -                if (!localDefs.contains(name)) {
  1.1936 -                    symbol.setType(Type.OBJECT);
  1.1937 -                    symbol.setCanBeUndefined();
  1.1938 -                }
  1.1939 -            }
  1.1940 -
  1.1941 -            identNode.setSymbol(symbol);
  1.1942 -            if (!getCurrentFunctionNode().isLocal(symbol)) {
  1.1943 -                // non-local: we need to put symbol in scope (if it isn't already)
  1.1944 -                if (!symbol.isScope()) {
  1.1945 -                    final List<Block> lookupBlocks = findLookupBlocksHelper(getCurrentFunctionNode(), symbol.findFunction());
  1.1946 -                    for (final Block lookupBlock : lookupBlocks) {
  1.1947 -                        final Symbol refSymbol = lookupBlock.findSymbol(name);
  1.1948 -                        if (refSymbol != null) { // See NASHORN-837, function declaration in lexical scope: try {} catch (x){ function f() { use(x) } } f()
  1.1949 -                            LOG.finest("Found a ref symbol that must be scope " + refSymbol);
  1.1950 -                            refSymbol.setIsScope();
  1.1951 -                        }
  1.1952 -                    }
  1.1953 -                }
  1.1954 -            }
  1.1955 -        } else {
  1.1956 -            symbol = block.useSymbol(name, identNode);
  1.1957 -            // we have never seen this before, it can be undefined
  1.1958 -            symbol.setType(Type.OBJECT); // TODO unknown -we have explicit casts anyway?
  1.1959 -            symbol.setCanBeUndefined();
  1.1960 -        }
  1.1961 -
  1.1962 -        if (symbol != oldSymbol && !identNode.isInitializedHere()) {
  1.1963 -            symbol.increaseUseCount();
  1.1964 -        }
  1.1965 -        localUses.add(identNode.getName());
  1.1966 -
  1.1967 -        return null;
  1.1968 -    }
  1.1969 -
  1.1970 -    private static List<Block> findLookupBlocksHelper(final FunctionNode currentFunction, final FunctionNode topFunction) {
  1.1971 -        if (currentFunction.findParentFunction() == topFunction) {
  1.1972 -            final List<Block> blocks = new LinkedList<>();
  1.1973 -
  1.1974 -            blocks.add(currentFunction.getParent());
  1.1975 -            blocks.addAll(currentFunction.getReferencingParentBlocks());
  1.1976 -            return blocks;
  1.1977 -        }
  1.1978 -        /**
  1.1979 -         * assumption: all parent blocks of an inner function will always be in the same outer function;
  1.1980 -         * therefore we can simply skip through intermediate functions.
  1.1981 -         * @see FunctionNode#addReferencingParentBlock(Block)
  1.1982 -         */
  1.1983 -        return findLookupBlocksHelper(currentFunction.findParentFunction(), topFunction);
  1.1984 -    }
  1.1985 -
  1.1986 -    private static boolean isFunctionExpressionSelfReference(final Symbol symbol) {
  1.1987 -        if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) {
  1.1988 -            return ((FunctionNode) symbol.getNode()).getIdent().getName().equals(symbol.getName());
  1.1989 -        }
  1.1990 -        return false;
  1.1991 -    }
  1.1992 -
  1.1993 -    @Override
  1.1994 -    public Node enter(final IfNode ifNode) {
  1.1995 -        nest(ifNode);
  1.1996 -        return ifNode;
  1.1997 -    }
  1.1998 -
  1.1999 -    @Override
  1.2000 -    public Node leave(final IfNode ifNode) {
  1.2001 -
  1.2002 -        final Node test = convert(ifNode.getTest(), Type.BOOLEAN);
  1.2003 -
  1.2004 -        /*
  1.2005 -         * constant if checks should short circuit into just one of the
  1.2006 -         * pass/fail blocks
  1.2007 -         */
  1.2008 -        if (test.getSymbol().isConstant()) {
  1.2009 -            assert test instanceof LiteralNode<?>;
  1.2010 -            final Block shortCut = ((LiteralNode<?>)test).isTrue() ? ifNode.getPass() : ifNode.getFail();
  1.2011 -            if (shortCut != null) {
  1.2012 -                for (final Node statement : shortCut.getStatements()) {
  1.2013 -                    statements.add(statement);
  1.2014 -                }
  1.2015 -            }
  1.2016 -            return null;
  1.2017 -        }
  1.2018 -
  1.2019 -        final Node pass = ifNode.getPass();
  1.2020 -        final Node fail = ifNode.getFail();
  1.2021 -
  1.2022 -        if (pass.isTerminal() && fail != null && fail.isTerminal()) {
  1.2023 -            ifNode.setIsTerminal(true);
  1.2024 -        }
  1.2025 -
  1.2026 -        ifNode.setTest(test);
  1.2027 -        statements.add(ifNode);
  1.2028 -
  1.2029 -        unnest();
  1.2030 -
  1.2031 -        return ifNode;
  1.2032 -    }
  1.2033 -
  1.2034 -    @Override
  1.2035 -    public Node leave(final IndexNode indexNode) {
  1.2036 -        getCurrentFunctionNode().newTemporary(Type.OBJECT, indexNode);
  1.2037 -
  1.2038 -        return indexNode;
  1.2039 -    }
  1.2040 -
  1.2041 -    @Override
  1.2042 -    public Node enter(final LabelNode labeledNode) {
  1.2043 -        // Don't want a symbol for label.
  1.2044 -        final Block body = labeledNode.getBody();
  1.2045 -        body.accept(this);
  1.2046 -        labeledNode.copyTerminalFlags(body);
  1.2047 -        statements.add(labeledNode);
  1.2048 -
  1.2049 -        return null;
  1.2050 -    }
  1.2051 -
  1.2052 -    @Override
  1.2053 -    public Node enter(final LineNumberNode lineNumberNode) {
  1.2054 -        statements.add(lineNumberNode);
  1.2055 -        return null;
  1.2056 -    }
  1.2057 -
  1.2058 -    /**
  1.2059 -     * Generate a new literal symbol.
  1.2060 -     *
  1.2061 -     * @param literalNode LiteralNode needing symbol.
  1.2062 -     * @return The literal node augmented with the symbol
  1.2063 -     */
  1.2064 -    private LiteralNode<?> newLiteral(final LiteralNode<?> literalNode) {
  1.2065 -        return newLiteral(getCurrentFunctionNode(), literalNode);
  1.2066 -    }
  1.2067 -
  1.2068 -    private static LiteralNode<?> newLiteral(final FunctionNode functionNode, final LiteralNode<?> literalNode) {
  1.2069 -        functionNode.newLiteral(literalNode);
  1.2070 -        return literalNode;
  1.2071 -    }
  1.2072 -
  1.2073 -    @SuppressWarnings("rawtypes")
  1.2074 -    @Override
  1.2075 -    public Node enter(final LiteralNode literalNode) {
  1.2076 -        if (literalNode.isTokenType(TokenType.THIS)) {
  1.2077 -            literalNode.setSymbol(getCurrentFunctionNode().getThisNode().getSymbol());
  1.2078 -            return null;
  1.2079 -        }
  1.2080 -
  1.2081 -        if (literalNode instanceof ArrayLiteralNode) {
  1.2082 -            final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
  1.2083 -            final Node[] array = arrayLiteralNode.getValue();
  1.2084 -
  1.2085 -            for (int i = 0; i < array.length; i++) {
  1.2086 -                final Node element = array[i];
  1.2087 -                if (element != null) {
  1.2088 -                    array[i] = array[i].accept(this);
  1.2089 -                }
  1.2090 -            }
  1.2091 -
  1.2092 -            arrayLiteralNode.analyze();
  1.2093 -            final Type elementType = arrayLiteralNode.getElementType();
  1.2094 -
  1.2095 -            // we only have types after all elements are accepted
  1.2096 -            for (int i = 0; i < array.length; i++) {
  1.2097 -                final Node element = array[i];
  1.2098 -                if (element != null) {
  1.2099 -                    array[i] = convert(element, elementType);
  1.2100 -                }
  1.2101 -            }
  1.2102 -        } else {
  1.2103 -            final Object value = literalNode.getValue();
  1.2104 -
  1.2105 -            if (value instanceof Node) {
  1.2106 -                final Node node = ((Node)value).accept(this);
  1.2107 -                if (node != null) {
  1.2108 -                    return newLiteral(LiteralNode.newInstance(source, node.getToken(), node.getFinish(), node));
  1.2109 -                }
  1.2110 -            }
  1.2111 -        }
  1.2112 -
  1.2113 -        newLiteral(literalNode);
  1.2114 -
  1.2115 -        return null;
  1.2116 -    }
  1.2117 -
  1.2118 -    @Override
  1.2119 -    public Node leave(final ObjectNode objectNode) {
  1.2120 -        getCurrentFunctionNode().newTemporary(Type.OBJECT, objectNode);
  1.2121 -        return objectNode;
  1.2122 -    }
  1.2123 -
  1.2124 -    @Override
  1.2125 -    public Node enter(final PropertyNode propertyNode) {
  1.2126 -        // assign a pseudo symbol to property name, see NASHORN-710
  1.2127 -        propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
  1.2128 -        return propertyNode;
  1.2129 -    }
  1.2130 -
  1.2131 -    @Override
  1.2132 -    public Node enter(final ReferenceNode referenceNode) {
  1.2133 -        final FunctionNode functionNode = referenceNode.getReference();
  1.2134 -        if (functionNode != null) {
  1.2135 -            functionNode.addReferencingParentBlock(getCurrentBlock());
  1.2136 -        }
  1.2137 -        return referenceNode;
  1.2138 -    }
  1.2139 -
  1.2140 -    @Override
  1.2141 -    public Node leave(final ReferenceNode referenceNode) {
  1.2142 -        getCurrentFunctionNode().newTemporary(Type.OBJECT, referenceNode);
  1.2143 -        return referenceNode;
  1.2144 -    }
  1.2145 -
  1.2146 -    /**
  1.2147 -     * For each return node we need to set a return expression of the correct
  1.2148 -     * type. In the non-specializing world, we can always upcast to object and
  1.2149 -     * ignore the rest, but in the specializing world we have to keep track of
  1.2150 -     * the widest return type so far, which is the common one for this method.
  1.2151 -     *
  1.2152 -     * @param returnNode  the return node we are generating
  1.2153 -     * @param expression  the expression to return from this code
  1.2154 -     */
  1.2155 -    private void setReturnExpression(final ReturnNode returnNode, final Node expression) {
  1.2156 -        final FunctionNode functionNode = getCurrentFunctionNode();
  1.2157 -
  1.2158 -        returnNode.setExpression(expression);
  1.2159 -
  1.2160 -        final Symbol symbol = expression.getSymbol();
  1.2161 -        if (expression.getType().isUnknown() && symbol.isParam()) {
  1.2162 -            symbol.setType(Type.OBJECT);
  1.2163 -        }
  1.2164 -        functionNode.setReturnType(Type.widest(expression.getType(), functionNode.getReturnType()));
  1.2165 -    }
  1.2166 -
  1.2167 -    @Override
  1.2168 -    public Node enter(final ReturnNode returnNode) {
  1.2169 -        final TryNode tryNode = returnNode.getTryChain();
  1.2170 -        final Node expression = returnNode.getExpression();
  1.2171 -
  1.2172 -        if (tryNode != null) {
  1.2173 -            if (expression != null) {
  1.2174 -                final long token = returnNode.getToken();
  1.2175 -                final Node resultNode = getCurrentFunctionNode().getResultNode();
  1.2176 -                new ExecuteNode(source, token, Token.descPosition(token), new BinaryNode(source, Token.recast(token, TokenType.ASSIGN), resultNode, expression)).accept(this);
  1.2177 -
  1.2178 -                if (copyFinally(tryNode, null)) {
  1.2179 -                    return null;
  1.2180 -                }
  1.2181 -
  1.2182 -                setReturnExpression(returnNode, resultNode);
  1.2183 -            } else if (copyFinally(tryNode, null)) {
  1.2184 -                return null;
  1.2185 -            }
  1.2186 -        } else if (expression != null) {
  1.2187 -            setReturnExpression(returnNode, expression.accept(this));
  1.2188 -        }
  1.2189 -
  1.2190 -        statements.add(returnNode);
  1.2191 -
  1.2192 -        return null;
  1.2193 -    }
  1.2194 -
  1.2195 -    @Override
  1.2196 -    public Node leave(final RuntimeNode runtimeNode) {
  1.2197 -        for (final Node arg : runtimeNode.getArgs()) {
  1.2198 -            ensureTypeNotUnknown(arg);
  1.2199 -        }
  1.2200 -
  1.2201 -        getCurrentFunctionNode().newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode);
  1.2202 -
  1.2203 -        return runtimeNode;
  1.2204 -    }
  1.2205 -
  1.2206 -    @Override
  1.2207 -    public Node enter(final SwitchNode switchNode) {
  1.2208 -        nest(switchNode);
  1.2209 -        return switchNode;
  1.2210 -    }
  1.2211 -
  1.2212 -    @Override
  1.2213 -    public Node leave(final SwitchNode switchNode) {
  1.2214 -        unnest();
  1.2215 -
  1.2216 -        final Node           expression  = switchNode.getExpression();
  1.2217 -        final List<CaseNode> cases       = switchNode.getCases();
  1.2218 -        final CaseNode       defaultCase = switchNode.getDefaultCase();
  1.2219 -        final boolean        hasDefault  = defaultCase != null;
  1.2220 -        final int            n           =  cases.size() + (hasDefault ? -1 : 0);
  1.2221 -
  1.2222 -        boolean allTerminal = !cases.isEmpty();
  1.2223 -        boolean allInteger  = n > 1;
  1.2224 -
  1.2225 -        for (final CaseNode caseNode : cases) {
  1.2226 -            allTerminal &= caseNode.isTerminal();
  1.2227 -            final Node test = caseNode.getTest();
  1.2228 -
  1.2229 -            // Skip default.
  1.2230 -            if (test == null) {
  1.2231 -                continue;
  1.2232 -            }
  1.2233 -
  1.2234 -            // If not a number.
  1.2235 -            if (!(test instanceof LiteralNode) || !test.getType().isNumeric()) {
  1.2236 -                allInteger = false;
  1.2237 -                continue;
  1.2238 -            }
  1.2239 -
  1.2240 -            final LiteralNode<?> testLiteral = (LiteralNode<?>)test;
  1.2241 -
  1.2242 -            // If a potential integer.
  1.2243 -            if (!(testLiteral.getValue() instanceof Integer)) {
  1.2244 -                // If not an integer value.
  1.2245 -                if (!JSType.isRepresentableAsInt(testLiteral.getNumber())) {
  1.2246 -                    allInteger = false;
  1.2247 -                    continue;
  1.2248 -                }
  1.2249 -
  1.2250 -                // Guarantee all case literals are Integers (simplifies sorting in code gen.)
  1.2251 -                caseNode.setTest(newLiteral(LiteralNode.newInstance(source, testLiteral.getToken(), testLiteral.getFinish(), testLiteral.getInt32())));
  1.2252 -            }
  1.2253 -        }
  1.2254 -
  1.2255 -        if (allTerminal && defaultCase != null && defaultCase.isTerminal()) {
  1.2256 -            switchNode.setIsTerminal(true);
  1.2257 -        }
  1.2258 -
  1.2259 -        if (!allInteger) {
  1.2260 -            switchNode.setExpression(convert(expression, Type.OBJECT));
  1.2261 -
  1.2262 -            for (final CaseNode caseNode : cases) {
  1.2263 -                final Node test = caseNode.getTest();
  1.2264 -
  1.2265 -                if (test != null) {
  1.2266 -                    caseNode.setTest(convert(test, Type.OBJECT));
  1.2267 -                }
  1.2268 -            }
  1.2269 -        }
  1.2270 -
  1.2271 -        final String name = compiler.uniqueName(SWITCH_TAG_PREFIX.tag());
  1.2272 -        final Symbol tag  = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null);
  1.2273 -
  1.2274 -        tag.setType(allInteger ? Type.INT : Type.OBJECT);
  1.2275 -        switchNode.setTag(tag);
  1.2276 -
  1.2277 -        statements.add(switchNode);
  1.2278 -
  1.2279 -        return switchNode;
  1.2280 -    }
  1.2281 -
  1.2282 -    @Override
  1.2283 -    public Node leave(final ThrowNode throwNode) {
  1.2284 -        throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
  1.2285 -        statements.add(throwNode);
  1.2286 -
  1.2287 -        return throwNode;
  1.2288 -    }
  1.2289 -
  1.2290 -    @Override
  1.2291 -    public Node enter(final TryNode tryNode) {
  1.2292 -        final Block  finallyBody   = tryNode.getFinallyBody();
  1.2293 -        final Source tryNodeSource = tryNode.getSource();
  1.2294 -        final long   token         = tryNode.getToken();
  1.2295 -        final int    finish        = tryNode.getFinish();
  1.2296 -
  1.2297 -        nest(tryNode);
  1.2298 -
  1.2299 -        if (finallyBody == null) {
  1.2300 -            return tryNode;
  1.2301 -        }
  1.2302 -
  1.2303 -        /**
  1.2304 -         * We have a finally clause.
  1.2305 -         *
  1.2306 -         * Transform to do finally tail duplication as follows:
  1.2307 -         *
  1.2308 -         * <pre>
  1.2309 -         *  try {
  1.2310 -         *    try_body
  1.2311 -         *  } catch e1 {
  1.2312 -         *    catchbody_1
  1.2313 -         *  }
  1.2314 -         *  ...
  1.2315 -         *  } catch en {
  1.2316 -         *    catchbody_n
  1.2317 -         *  } finally {
  1.2318 -         *    finally_body
  1.2319 -         *  }
  1.2320 -         *
  1.2321 -         *  (where e1 ... en are optional)
  1.2322 -         *
  1.2323 -         *  turns into
  1.2324 -         *
  1.2325 -         *  try {
  1.2326 -         *    try {
  1.2327 -         *      try_body
  1.2328 -         *    } catch e1 {
  1.2329 -         *      catchbody1
  1.2330 -         *      //nothing inlined explicitly here, return, break other
  1.2331 -         *      //terminals may inline the finally body
  1.2332 -         *      ...
  1.2333 -         *    } catch en {
  1.2334 -         *      catchbody2
  1.2335 -         *      //nothing inlined explicitly here, return, break other
  1.2336 -         *      //terminals may inline the finally body
  1.2337 -         *    }
  1.2338 -         *  } catch all ex {
  1.2339 -         *      finally_body_inlined
  1.2340 -         *      rethrow ex
  1.2341 -         *  }
  1.2342 -         *  finally_body_inlined
  1.2343 -         * </pre>
  1.2344 -         *
  1.2345 -         * If tries are catches are terminal, visitors for return, break &
  1.2346 -         * continue will handle the tail duplications. Throw needs to be
  1.2347 -         * treated specially with the catchall as described in the above
  1.2348 -         * ASCII art.
  1.2349 -         *
  1.2350 -         * If the try isn't terminal we do the finally_body_inlined at the
  1.2351 -         * end. If the try is terminated with continue/break/return the
  1.2352 -         * existing visitor logic will inline the finally before that
  1.2353 -         * operation. if the try is terminated with a throw, the catches e1
  1.2354 -         * ... en will have a chance to process the exception. If the
  1.2355 -         * appropriate catch e1..en is non terminal we fall through to the
  1.2356 -         * last finally_body_inlined. if the catch e1...en IS terminal with
  1.2357 -         * continue/break/return existing visitor logic will fix it. If they
  1.2358 -         * are terminal with another throw it goes to the catchall and the
  1.2359 -         * finally_body_inlined marked (*) will fix it before rethrowing
  1.2360 -         * whatever problem there was for identical semantic.
  1.2361 -         */
  1.2362 -
  1.2363 -        // if try node does not contain a catch we can skip creation of a new
  1.2364 -        // try node and just append our synthetic catch to the existing try node.
  1.2365 -        if (!tryNode.getCatchBlocks().isEmpty()) {
  1.2366 -            // insert an intermediate try-catch* node, where we move the body and all catch blocks.
  1.2367 -            // the original try node become a try-finally container for the new try-catch* node.
  1.2368 -            // because we don't clone (to avoid deep copy), we have to fix the block chain in the end.
  1.2369 -            final TryNode innerTryNode = new TryNode(tryNodeSource, token, finish, tryNode.getNext());
  1.2370 -            innerTryNode.setBody(tryNode.getBody());
  1.2371 -            innerTryNode.setCatchBlocks(tryNode.getCatchBlocks());
  1.2372 -
  1.2373 -            // set outer tryNode's body to innerTryNode
  1.2374 -            final Block outerBody = new Block(tryNodeSource, token, finish, tryNode.getBody().getParent(), getCurrentFunctionNode());
  1.2375 -            outerBody.setStatements(new ArrayList<Node>(Arrays.asList(innerTryNode)));
  1.2376 -            tryNode.setBody(outerBody);
  1.2377 -            tryNode.setCatchBlocks(null);
  1.2378 -
  1.2379 -            // now before we go on, we have to fix the block parents
  1.2380 -            // (we repair the block tree after the insertion so that all references are intact)
  1.2381 -            innerTryNode.getBody().setParent(tryNode.getBody());
  1.2382 -            for (final Block block : innerTryNode.getCatchBlocks()) {
  1.2383 -                block.setParent(tryNode.getBody());
  1.2384 -            }
  1.2385 -        }
  1.2386 -
  1.2387 -        // create a catch-all that inlines finally and rethrows
  1.2388 -        final String name = compiler.uniqueName(EXCEPTION_PREFIX.tag());
  1.2389 -
  1.2390 -        final IdentNode exception = new IdentNode(tryNodeSource, token, finish, name);
  1.2391 -        exception.accept(this);
  1.2392 -
  1.2393 -        final Block catchBlock      = new Block(tryNodeSource, token, finish, getCurrentBlock(), getCurrentFunctionNode());
  1.2394 -        final Block catchBody       = new Block(tryNodeSource, token, finish, catchBlock, getCurrentFunctionNode());
  1.2395 -        final Node  catchAllFinally = finallyBody.clone();
  1.2396 -
  1.2397 -        catchBody.addStatement(new ExecuteNode(tryNodeSource, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally));
  1.2398 -        catchBody.setIsTerminal(true);
  1.2399 -
  1.2400 -        final CatchNode catchNode = new CatchNode(tryNodeSource, token, finish, new IdentNode(exception), null, catchBody);
  1.2401 -        catchNode.setIsSyntheticRethrow();
  1.2402 -        catchBlock.addStatement(catchNode);
  1.2403 -
  1.2404 -        // replace all catches of outer tryNode with the catch-all
  1.2405 -        tryNode.setCatchBlocks(new ArrayList<>(Arrays.asList(catchBlock)));
  1.2406 -
  1.2407 -        /*
  1.2408 -         * We leave the finally block for the original try in place for now
  1.2409 -         * so that children visitations will work. It is removed and placed
  1.2410 -         * afterwards in the else case below, after all children are visited
  1.2411 -         */
  1.2412 -
  1.2413 -        return tryNode;
  1.2414 -    }
  1.2415 -
  1.2416 -    @Override
  1.2417 -    public Node leave(final TryNode tryNode) {
  1.2418 -        final Block finallyBody   = tryNode.getFinallyBody();
  1.2419 -
  1.2420 -        boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal());
  1.2421 -
  1.2422 -        for (final Block catchBlock : tryNode.getCatchBlocks()) {
  1.2423 -            allTerminal &= catchBlock.isTerminal();
  1.2424 -        }
  1.2425 -
  1.2426 -        tryNode.setIsTerminal(allTerminal);
  1.2427 -
  1.2428 -        final String name      = compiler.uniqueName(EXCEPTION_PREFIX.tag());
  1.2429 -        final Symbol exception = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null);
  1.2430 -
  1.2431 -        exception.setType(ECMAException.class);
  1.2432 -        tryNode.setException(exception);
  1.2433 -
  1.2434 -        statements.add(tryNode);
  1.2435 -
  1.2436 -        unnest();
  1.2437 -
  1.2438 -        // if finally body is present, place it after the tryNode
  1.2439 -        if (finallyBody != null) {
  1.2440 -            tryNode.setFinallyBody(null);
  1.2441 -            statements.add(finallyBody);
  1.2442 -        }
  1.2443 -
  1.2444 -        return tryNode;
  1.2445 -    }
  1.2446 -
  1.2447 -    @Override
  1.2448 -    public Node enter(final VarNode varNode) {
  1.2449 -        final IdentNode ident = varNode.getName();
  1.2450 -        final String    name  = ident.getName();
  1.2451 -
  1.2452 -        final Symbol symbol = getCurrentBlock().defineSymbol(name, IS_VAR, ident);
  1.2453 -        assert symbol != null;
  1.2454 -
  1.2455 -        varNode.setSymbol(symbol);
  1.2456 -        declaredSymbolsLocal.add(symbol);
  1.2457 -
  1.2458 -        // NASHORN-467 - use before definition of vars - conservative
  1.2459 -        if (localUses.contains(ident.getName())) {
  1.2460 -            symbol.setType(Type.OBJECT);
  1.2461 -            symbol.setCanBeUndefined();
  1.2462 -        }
  1.2463 -
  1.2464 -        return varNode;
  1.2465 -    }
  1.2466 -
  1.2467 -    private static boolean isScopeOrGlobal(final Symbol symbol) {
  1.2468 -        return symbol.getBlock().getFunction().isScript(); //we can't do explicit isScope checks as early as lower, which is why AccessSpecializer is afterwards as well.
  1.2469 -    }
  1.2470 -
  1.2471 -    @Override
  1.2472 -    public Node leave(final VarNode varNode) {
  1.2473 -        final Node      init  = varNode.getInit();
  1.2474 -        final IdentNode ident = varNode.getName();
  1.2475 -        final String    name  = ident.getName();
  1.2476 -
  1.2477 -        if (init != null) {
  1.2478 -            localDefs.add(name);
  1.2479 -        }
  1.2480 -
  1.2481 -        if (varNode.shouldAppend()) {
  1.2482 -            statements.add(varNode);
  1.2483 -        }
  1.2484 -
  1.2485 -        if (init == null) {
  1.2486 -            // var x; with no init will be treated like a use of x by
  1.2487 -            // visit(IdentNode) unless we remove the name
  1.2488 -            // from the localdef list.
  1.2489 -            localDefs.remove(name);
  1.2490 -            return varNode;
  1.2491 -        }
  1.2492 -
  1.2493 -        final Symbol symbol = varNode.getSymbol();
  1.2494 -        if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScopeOrGlobal(symbol)) { //see NASHORN-56
  1.2495 -            // Forbid integers as local vars for now as we have no way to treat them as undefined
  1.2496 -            final Type type = Compiler.shouldUseIntegers() ? init.getType() : Type.NUMBER;
  1.2497 -            varNode.setInit(convert(init, type));
  1.2498 -            symbol.setType(type);
  1.2499 -        } else {
  1.2500 -            symbol.setType(Type.OBJECT); // var x = obj;, x is an "object" //TODO too conservative now?
  1.2501 -        }
  1.2502 -
  1.2503 -        return varNode;
  1.2504 -    }
  1.2505 -
  1.2506 -    @Override
  1.2507 -    public Node enter(final WhileNode whileNode) {
  1.2508 -        // push the loop to the loop context
  1.2509 -        nest(whileNode);
  1.2510 -
  1.2511 -        return whileNode;
  1.2512 -    }
  1.2513 -
  1.2514 -    @Override
  1.2515 -    public Node leave(final WhileNode whileNode) {
  1.2516 -        final Node test = whileNode.getTest();
  1.2517 -
  1.2518 -        if (test != null) {
  1.2519 -            whileNode.setTest(convert(test, Type.BOOLEAN));
  1.2520 -        } else {
  1.2521 -            whileNode.setHasGoto();
  1.2522 -        }
  1.2523 -
  1.2524 -        Node returnNode = whileNode;
  1.2525 -
  1.2526 -        final Block   body    = whileNode.getBody();
  1.2527 -        final boolean escapes = controlFlowEscapes(body);
  1.2528 -        if (escapes) {
  1.2529 -            body.setIsTerminal(false);
  1.2530 -        }
  1.2531 -
  1.2532 -        if (body.isTerminal()) {
  1.2533 -            if (whileNode instanceof DoWhileNode) {
  1.2534 -                whileNode.setIsTerminal(true);
  1.2535 -            } else if (conservativeAlwaysTrue(test)) {
  1.2536 -                returnNode = new ForNode(source, whileNode.getToken(), whileNode.getFinish());
  1.2537 -                ((ForNode)returnNode).setBody(body);
  1.2538 -                returnNode.setIsTerminal(!escapes);
  1.2539 -            }
  1.2540 -        }
  1.2541 -
  1.2542 -        // pop the loop from the loop context
  1.2543 -        unnest();
  1.2544 -        statements.add(returnNode);
  1.2545 -
  1.2546 -        return returnNode;
  1.2547 -    }
  1.2548 -
  1.2549 -    @Override
  1.2550 -    public Node leave(final WithNode withNode) {
  1.2551 -        withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT));
  1.2552 -
  1.2553 -        if (withNode.getBody().isTerminal()) {
  1.2554 -            withNode.setIsTerminal(true);
  1.2555 -        }
  1.2556 -
  1.2557 -        statements.add(withNode);
  1.2558 -
  1.2559 -        return withNode;
  1.2560 -    }
  1.2561 -
  1.2562 -    /*
  1.2563 -     * Unary visits.
  1.2564 -     */
  1.2565 -
  1.2566 -    /**
  1.2567 -     * Visit unary node and set symbols and inferred type
  1.2568 -     *
  1.2569 -     * @param unaryNode  unary node to visit
  1.2570 -     * @param type       The narrowest allowed type for this node
  1.2571 -     *
  1.2572 -     * @return the final node, or replacement thereof
  1.2573 -     */
  1.2574 -    public Node leaveUnary(final UnaryNode unaryNode, final Type type) {
  1.2575 -        /* Try to eliminate the unary node if the expression is constant */
  1.2576 -        final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval();
  1.2577 -        if (literalNode != null) {
  1.2578 -            return newLiteral(literalNode);
  1.2579 -        }
  1.2580 -
  1.2581 -        /* Add explicit conversion */
  1.2582 -        unaryNode.setRHS(convert(unaryNode.rhs(), type));
  1.2583 -        getCurrentFunctionNode().newTemporary(type, unaryNode);
  1.2584 -
  1.2585 -        return unaryNode;
  1.2586 -    }
  1.2587 -
  1.2588 -    @Override
  1.2589 -    public Node leaveADD(final UnaryNode unaryNode) {
  1.2590 -        return leaveUnary(unaryNode, Type.NUMBER);
  1.2591 -    }
  1.2592 -
  1.2593 -    @Override
  1.2594 -    public Node leaveBIT_NOT(final UnaryNode unaryNode) {
  1.2595 -        return leaveUnary(unaryNode, Type.INT);
  1.2596 -    }
  1.2597 -
  1.2598 -    @Override
  1.2599 -    public Node leaveCONVERT(final UnaryNode unaryNode) {
  1.2600 -        final Node rhs = unaryNode.rhs();
  1.2601 -
  1.2602 -        if (rhs.isTokenType(TokenType.CONVERT)) {
  1.2603 -            rhs.setSymbol(unaryNode.getSymbol());
  1.2604 -            return rhs;
  1.2605 -        }
  1.2606 -
  1.2607 -        return unaryNode;
  1.2608 -    }
  1.2609 -
  1.2610 -    /**
  1.2611 -     * In an assignment, recursively make sure that there are slots for
  1.2612 -     * everything that has to be laid out as temporary storage, which is the
  1.2613 -     * case if we are assign-op:ing a BaseNode subclass. This has to be
  1.2614 -     * recursive to handle things like multi dimensional arrays as lhs
  1.2615 -     *
  1.2616 -     * see NASHORN-258
  1.2617 -     *
  1.2618 -     * @param functionNode   the current function node (has to be passed as it changes in the visitor below)
  1.2619 -     * @param assignmentDest the destination node of the assignment, e.g. lhs for binary nodes
  1.2620 -     */
  1.2621 -    private static void ensureAssignmentSlots(final FunctionNode functionNode, final Node assignmentDest) {
  1.2622 -
  1.2623 -        assignmentDest.accept(new NodeVisitor() {
  1.2624 -
  1.2625 -            @Override
  1.2626 -            public Node leave(final AccessNode accessNode) {
  1.2627 -                final Node base = accessNode.getBase();
  1.2628 -                if (base.getSymbol().isThis()) {
  1.2629 -                    functionNode.addThisProperty(accessNode.getProperty().getName(), accessNode);
  1.2630 -                }
  1.2631 -
  1.2632 -                return accessNode;
  1.2633 -            }
  1.2634 -
  1.2635 -            @Override
  1.2636 -            public Node leave(final IndexNode indexNode) {
  1.2637 -                final Node index = indexNode.getIndex();
  1.2638 -                index.getSymbol().setNeedsSlot(!index.getSymbol().isConstant());
  1.2639 -
  1.2640 -                return indexNode;
  1.2641 -            }
  1.2642 -
  1.2643 -        });
  1.2644 -    }
  1.2645 -
  1.2646 -    @Override
  1.2647 -    public Node leaveDECINC(final UnaryNode unaryNode) {
  1.2648 -        // @see assignOffset
  1.2649 -        ensureAssignmentSlots(getCurrentFunctionNode(), unaryNode.rhs());
  1.2650 -        final Type type = Compiler.shouldUseIntegerArithmetic() ? Type.INT : Type.NUMBER;
  1.2651 -
  1.2652 -        unaryNode.rhs().getSymbol().setType(type);
  1.2653 -        getCurrentFunctionNode().newTemporary(type, unaryNode);
  1.2654 -
  1.2655 -        return unaryNode;
  1.2656 -    }
  1.2657 -
  1.2658 -    /**
  1.2659 -     * Helper class for wrapping a result
  1.2660 -     */
  1.2661 -    private abstract static class ResultNodeVisitor extends NodeVisitor {
  1.2662 -
  1.2663 -        private Node resultNode;
  1.2664 -
  1.2665 -        ResultNodeVisitor(final Node resultNode) {
  1.2666 -            this.resultNode = resultNode;
  1.2667 -        }
  1.2668 -
  1.2669 -        Node getResultNode() {
  1.2670 -            return resultNode;
  1.2671 -        }
  1.2672 -
  1.2673 -        void setResultNode(final Node resultNode) {
  1.2674 -            this.resultNode = resultNode;
  1.2675 -        }
  1.2676 -    }
  1.2677 -
  1.2678 -    @Override
  1.2679 -    public Node leaveDELETE(final UnaryNode unaryNode) {
  1.2680 -        final boolean              strictMode     = getCurrentFunctionNode().isStrictMode();
  1.2681 -        final Node                 rhs            = unaryNode.rhs();
  1.2682 -        final long                 token          = unaryNode.getToken();
  1.2683 -        final int                  finish         = unaryNode.getFinish();
  1.2684 -        final LiteralNode<Boolean> trueNode       = LiteralNode.newInstance(source, token, finish, true);
  1.2685 -        final LiteralNode<Boolean> strictFlagNode = LiteralNode.newInstance(source, token, finish, strictMode);
  1.2686 -
  1.2687 -        newLiteral(trueNode);
  1.2688 -        newLiteral(strictFlagNode);
  1.2689 -
  1.2690 -        final NodeVisitor  lower = this;
  1.2691 -        final FunctionNode currentFunctionNode = getCurrentFunctionNode();
  1.2692 -
  1.2693 -        final ResultNodeVisitor visitor = new ResultNodeVisitor(trueNode) {
  1.2694 -
  1.2695 -            private void initRuntimeNode(final RuntimeNode node) {
  1.2696 -                node.accept(lower);
  1.2697 -                currentFunctionNode.newTemporary(Type.BOOLEAN, unaryNode);
  1.2698 -                node.setSymbol(unaryNode.getSymbol());
  1.2699 -
  1.2700 -                setResultNode(node);
  1.2701 -            }
  1.2702 -
  1.2703 -            @Override
  1.2704 -            public Node enter(final IdentNode node) {
  1.2705 -                // If this is a declared variable or a function parameter, delete always fails (except for globals).
  1.2706 -                final boolean failDelete =
  1.2707 -                        strictMode ||
  1.2708 -                        node.getSymbol().isParam() ||
  1.2709 -                        (node.getSymbol().isVar() && !node.getSymbol().isTopLevel());
  1.2710 -
  1.2711 -                final Node literalNode =
  1.2712 -                    newLiteral(
  1.2713 -                        currentFunctionNode,
  1.2714 -                        LiteralNode.newInstance(
  1.2715 -                            source,
  1.2716 -                            node.getToken(),
  1.2717 -                            node.getFinish(),
  1.2718 -                            node.getName()));
  1.2719 -
  1.2720 -                if (failDelete) {
  1.2721 -                    if (THIS.tag().equals(node.getName())) {
  1.2722 -                        statements.add(new ExecuteNode(source, token, finish, discard(node)));
  1.2723 -                        currentFunctionNode.newTemporary(Type.BOOLEAN, trueNode);
  1.2724 -                        return null; //trueNode it is
  1.2725 -                    }
  1.2726 -                }
  1.2727 -
  1.2728 -                final List<Node> args = new ArrayList<>();
  1.2729 -                args.add(convert(currentFunctionNode.getScopeNode(), Type.OBJECT));
  1.2730 -                args.add(convert(literalNode, Type.OBJECT));
  1.2731 -                args.add(strictFlagNode);
  1.2732 -
  1.2733 -                initRuntimeNode(
  1.2734 -                    new RuntimeNode(
  1.2735 -                        source,
  1.2736 -                        token,
  1.2737 -                        finish,
  1.2738 -                        failDelete ? FAIL_DELETE : DELETE,
  1.2739 -                        args));
  1.2740 -
  1.2741 -                return null;
  1.2742 -            }
  1.2743 -
  1.2744 -            @Override
  1.2745 -            public Node enter(final AccessNode node) {
  1.2746 -                final IdentNode property = node.getProperty();
  1.2747 -
  1.2748 -                initRuntimeNode(
  1.2749 -                    new RuntimeNode(
  1.2750 -                        source,
  1.2751 -                        token,
  1.2752 -                        finish,
  1.2753 -                        DELETE,
  1.2754 -                        convert(node.getBase(), Type.OBJECT),
  1.2755 -                        convert(
  1.2756 -                            newLiteral(
  1.2757 -                                currentFunctionNode,
  1.2758 -                                LiteralNode.newInstance(
  1.2759 -                                    source,
  1.2760 -                                    property.getToken(),
  1.2761 -                                    property.getFinish(),
  1.2762 -                                    property.getName())),
  1.2763 -                            Type.OBJECT),
  1.2764 -                        strictFlagNode));
  1.2765 -
  1.2766 -                return null;
  1.2767 -            }
  1.2768 -
  1.2769 -            @Override
  1.2770 -            public Node enter(final IndexNode node) {
  1.2771 -                initRuntimeNode(
  1.2772 -                    new RuntimeNode(
  1.2773 -                        source,
  1.2774 -                        token,
  1.2775 -                        finish,
  1.2776 -                        DELETE,
  1.2777 -                        convert(node.getBase(), Type.OBJECT),
  1.2778 -                        convert(node.getIndex(), Type.OBJECT),
  1.2779 -                        strictFlagNode));
  1.2780 -
  1.2781 -                return null;
  1.2782 -            }
  1.2783 -
  1.2784 -            @Override
  1.2785 -            public Node enterDefault(final Node node) {
  1.2786 -                statements.add(new ExecuteNode(source, token, finish, discard(node)));
  1.2787 -                currentFunctionNode.newTemporary(Type.BOOLEAN, trueNode);
  1.2788 -                //just return trueNode which is the default
  1.2789 -                return null;
  1.2790 -            }
  1.2791 -        };
  1.2792 -
  1.2793 -        rhs.accept(visitor);
  1.2794 -
  1.2795 -        return visitor.getResultNode();
  1.2796 -    }
  1.2797 -
  1.2798 -    @Override
  1.2799 -    public Node enterNEW(final UnaryNode unaryNode) {
  1.2800 -        final CallNode callNode = (CallNode)unaryNode.rhs();
  1.2801 -
  1.2802 -        callNode.setIsNew();
  1.2803 -
  1.2804 -        final Node function = callNode.getFunction();
  1.2805 -        final Node markedFunction = markerFunction(function);
  1.2806 -
  1.2807 -        callNode.setFunction(markedFunction.accept(this));
  1.2808 -
  1.2809 -        acceptArgs(callNode);
  1.2810 -
  1.2811 -        getCurrentFunctionNode().newTemporary(Type.OBJECT, unaryNode);
  1.2812 -
  1.2813 -        return null;
  1.2814 -    }
  1.2815 -
  1.2816 -    @Override
  1.2817 -    public Node leaveNOT(final UnaryNode unaryNode) {
  1.2818 -        return leaveUnary(unaryNode, Type.BOOLEAN);
  1.2819 -    }
  1.2820 -
  1.2821 -    /**
  1.2822 -     * Create a null literal
  1.2823 -     *
  1.2824 -     * @param parent node to inherit token from.
  1.2825 -     * @return the new null literal
  1.2826 -     */
  1.2827 -    private LiteralNode<?> newNullLiteral(final Node parent) {
  1.2828 -        return newLiteral(LiteralNode.newInstance(parent.getSource(), parent.getToken(), parent.getFinish()));
  1.2829 -    }
  1.2830 -
  1.2831 -    @Override
  1.2832 -    public Node leaveTYPEOF(final UnaryNode unaryNode) {
  1.2833 -        final Node rhs    = unaryNode.rhs();
  1.2834 -        final long token  = unaryNode.getToken();
  1.2835 -        final int  finish = unaryNode.getFinish();
  1.2836 -
  1.2837 -        RuntimeNode runtimeNode;
  1.2838 -
  1.2839 -        if (rhs instanceof IdentNode) {
  1.2840 -            final IdentNode ident = (IdentNode)rhs;
  1.2841 -
  1.2842 -            if (ident.getSymbol().isParam() || ident.getSymbol().isVar()) {
  1.2843 -                runtimeNode = new RuntimeNode(
  1.2844 -                    source,
  1.2845 -                    token,
  1.2846 -                    finish,
  1.2847 -                    TYPEOF,
  1.2848 -                    convert(
  1.2849 -                        rhs,
  1.2850 -                        Type.OBJECT),
  1.2851 -                    newNullLiteral(unaryNode));
  1.2852 -            } else {
  1.2853 -                runtimeNode = new RuntimeNode(
  1.2854 -                    source,
  1.2855 -                    token,
  1.2856 -                    finish,
  1.2857 -                    TYPEOF,
  1.2858 -                    convert(
  1.2859 -                        getCurrentFunctionNode().getScopeNode(),
  1.2860 -                        Type.OBJECT),
  1.2861 -                    convert(
  1.2862 -                        newLiteral(
  1.2863 -                            LiteralNode.newInstance(
  1.2864 -                                source,
  1.2865 -                                ident.getToken(),
  1.2866 -                                ident.getFinish(),
  1.2867 -                                ident.getName())),
  1.2868 -                    Type.OBJECT));
  1.2869 -            }
  1.2870 -        }  else {
  1.2871 -            runtimeNode = new RuntimeNode(
  1.2872 -                source,
  1.2873 -                token,
  1.2874 -                finish,
  1.2875 -                TYPEOF,
  1.2876 -                convert(
  1.2877 -                    rhs,
  1.2878 -                    Type.OBJECT),
  1.2879 -                newNullLiteral(unaryNode));
  1.2880 -        }
  1.2881 -
  1.2882 -        runtimeNode.accept(this);
  1.2883 -
  1.2884 -        getCurrentFunctionNode().newTemporary(Type.OBJECT, unaryNode);
  1.2885 -        runtimeNode.setSymbol(unaryNode.getSymbol());
  1.2886 -
  1.2887 -        return runtimeNode;
  1.2888 -    }
  1.2889 -
  1.2890 -    @Override
  1.2891 -    public Node leaveSUB(final UnaryNode unaryNode) {
  1.2892 -        return leaveUnary(unaryNode, Type.NUMBER);
  1.2893 -    }
  1.2894 -
  1.2895 -    @Override
  1.2896 -    public Node leaveVOID(final UnaryNode unaryNode) {
  1.2897 -        final Node rhs = unaryNode.rhs();
  1.2898 -        getCurrentFunctionNode().newTemporary(Type.OBJECT, unaryNode);
  1.2899 -
  1.2900 -        // Set up request.
  1.2901 -        final RuntimeNode runtimeNode = new RuntimeNode(source, unaryNode.getToken(), unaryNode.getFinish(), VOID, convert(rhs, Type.OBJECT));
  1.2902 -
  1.2903 -        // Use same symbol as unary node.
  1.2904 -        runtimeNode.setSymbol(unaryNode.getSymbol());
  1.2905 -
  1.2906 -        return runtimeNode;
  1.2907 -    }
  1.2908 -
  1.2909 -    /**
  1.2910 -     * Compute the narrowest possible type for a binaryNode, based on its
  1.2911 -     * contents.
  1.2912 -     *
  1.2913 -     * @param binaryNode the binary node
  1.2914 -     * @return the narrowest possible type
  1.2915 -     */
  1.2916 -    private static Type binaryType(final BinaryNode binaryNode) {
  1.2917 -        final Node lhs = binaryNode.lhs();
  1.2918 -        assert lhs.hasType();
  1.2919 -
  1.2920 -        final Node rhs = binaryNode.rhs();
  1.2921 -        assert rhs.hasType();
  1.2922 -
  1.2923 -        // actually bitwise assignments are ok for ints. TODO
  1.2924 -        switch (binaryNode.tokenType()) {
  1.2925 -        case ASSIGN_BIT_AND:
  1.2926 -        case ASSIGN_BIT_OR:
  1.2927 -        case ASSIGN_BIT_XOR:
  1.2928 -        case ASSIGN_SHL:
  1.2929 -        case ASSIGN_SAR:
  1.2930 -            return Compiler.shouldUseIntegers() ? Type.widest(lhs.getType(), rhs.getType(), Type.INT) : Type.INT;
  1.2931 -        case ASSIGN_SHR:
  1.2932 -            return Type.LONG;
  1.2933 -        case ASSIGN:
  1.2934 -            return Compiler.shouldUseIntegers() ? Type.widest(lhs.getType(), rhs.getType(), Type.NUMBER) : Type.NUMBER;
  1.2935 -        default:
  1.2936 -            return binaryArithType(lhs.getType(), rhs.getType());
  1.2937 -        }
  1.2938 -    }
  1.2939 -
  1.2940 -    private static Type binaryArithType(final Type lhsType, final Type rhsType) {
  1.2941 -         if (!Compiler.shouldUseIntegerArithmetic()) {
  1.2942 -             return Type.NUMBER;
  1.2943 -         }
  1.2944 -         return Type.widest(lhsType, rhsType, Type.NUMBER);
  1.2945 -     }
  1.2946 -
  1.2947 -    /**
  1.2948 -     * Visit binary node and set symbols and inferred type
  1.2949 -     *
  1.2950 -     * @param binaryNode unary node to visit
  1.2951 -     * @param type       The narrowest allowed type for this node
  1.2952 -     *
  1.2953 -     * @return the final node, or replacement thereof
  1.2954 -     */
  1.2955 -    private Node leaveBinary(final BinaryNode binaryNode, final Type type) {
  1.2956 -        return leaveBinary(binaryNode, type, type, type);
  1.2957 -    }
  1.2958 -
  1.2959 -    /**
  1.2960 -     * Visit a binary node, specifying types for rhs, rhs and destType.
  1.2961 -     *
  1.2962 -     * @param binaryNode the binary node
  1.2963 -     * @param destType destination type
  1.2964 -     * @param lhsType type for left hand side
  1.2965 -     * @param rhsType type for right hand side
  1.2966 -     *
  1.2967 -     * @return resulting binary node
  1.2968 -     */
  1.2969 -    private Node leaveBinary(final BinaryNode binaryNode, final Type destType, final Type lhsType, final Type rhsType) {
  1.2970 -        /* Attempt to turn this binary expression into a constant */
  1.2971 -        final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval();
  1.2972 -        if (literalNode != null) {
  1.2973 -            return newLiteral(literalNode);
  1.2974 -        }
  1.2975 -
  1.2976 -        if (lhsType != null) {
  1.2977 -            binaryNode.setLHS(convert(binaryNode.lhs(), lhsType));
  1.2978 -        }
  1.2979 -
  1.2980 -        if (rhsType != null) {
  1.2981 -            binaryNode.setRHS(convert(binaryNode.rhs(), rhsType));
  1.2982 -        }
  1.2983 -
  1.2984 -        getCurrentFunctionNode().newTemporary(destType, binaryNode);
  1.2985 -
  1.2986 -        return binaryNode;
  1.2987 -    }
  1.2988 -
  1.2989 -    /**
  1.2990 -     * Determine if the outcome of + operator is a string.
  1.2991 -     *
  1.2992 -     * @param node
  1.2993 -     *            Node to test.
  1.2994 -     * @return true if a string result.
  1.2995 -     */
  1.2996 -    private boolean isAddString(final Node node) {
  1.2997 -        if (node instanceof BinaryNode && node.isTokenType(TokenType.ADD)) {
  1.2998 -            final BinaryNode binaryNode = (BinaryNode)node;
  1.2999 -            final Node lhs = binaryNode.lhs();
  1.3000 -            final Node rhs = binaryNode.rhs();
  1.3001 -
  1.3002 -            return isAddString(lhs) || isAddString(rhs);
  1.3003 -        }
  1.3004 -
  1.3005 -        return node instanceof LiteralNode<?> && ((LiteralNode<?>)node).getObject() instanceof String;
  1.3006 -    }
  1.3007 -
  1.3008 -    /**
  1.3009 -     * Helper for creating a new runtime node from a parent node, inheriting its
  1.3010 -     * types and tokens
  1.3011 -     *
  1.3012 -     * @param parent   Parent node.
  1.3013 -     * @param args     Runtime request arguments.
  1.3014 -     * @param request  Runtime request type.
  1.3015 -     * @return New {@link RuntimeNode}.
  1.3016 -     */
  1.3017 -
  1.3018 -    private RuntimeNode newRuntime(final Node parent, final List<Node> args, final Request request) {
  1.3019 -        final RuntimeNode runtimeNode = new RuntimeNode(source, parent.getToken(), parent.getFinish(), request, args);
  1.3020 -        runtimeNode.setStart(parent.getStart());
  1.3021 -        runtimeNode.setFinish(parent.getFinish());
  1.3022 -
  1.3023 -        // Use same symbol as parent node.
  1.3024 -        runtimeNode.accept(this);
  1.3025 -        runtimeNode.setSymbol(parent.getSymbol());
  1.3026 -
  1.3027 -        return runtimeNode;
  1.3028 -    }
  1.3029 -
  1.3030 -    /**
  1.3031 -     * Helper for creating a new runtime node from a binary node
  1.3032 -     *
  1.3033 -     * @param binaryNode {@link RuntimeNode} expression.
  1.3034 -     * @param request    Runtime request type.
  1.3035 -     * @return New {@link RuntimeNode}.
  1.3036 -     */
  1.3037 -    private RuntimeNode newRuntime(final BinaryNode binaryNode, final Request request) {
  1.3038 -        return newRuntime(binaryNode, Arrays.asList(new Node[] { binaryNode.lhs(), binaryNode.rhs() }), request);
  1.3039 -    }
  1.3040 -
  1.3041 -    /**
  1.3042 -     * Add is a special binary, as it works not only on arithmetic, but for
  1.3043 -     * strings etc as well.
  1.3044 -     */
  1.3045 -    @Override
  1.3046 -    public Node leaveADD(final BinaryNode binaryNode) {
  1.3047 -        final Node lhs = binaryNode.lhs();
  1.3048 -        final Node rhs = binaryNode.rhs();
  1.3049 -
  1.3050 -        //parameters must be blown up to objects
  1.3051 -        ensureTypeNotUnknown(lhs);
  1.3052 -        ensureTypeNotUnknown(rhs);
  1.3053 -
  1.3054 -        if ((lhs.getType().isNumeric() || lhs.getType().isBoolean()) && (rhs.getType().isNumeric() || rhs.getType().isBoolean())) {
  1.3055 -            return leaveBinary(binaryNode, binaryType(binaryNode));
  1.3056 -        } else if (isAddString(binaryNode)) {
  1.3057 -            binaryNode.setLHS(convert(lhs, Type.OBJECT));
  1.3058 -            binaryNode.setRHS(convert(rhs, Type.OBJECT));
  1.3059 -            getCurrentFunctionNode().newTemporary(Type.OBJECT, binaryNode);
  1.3060 -        } else {
  1.3061 -            getCurrentFunctionNode().newTemporary(Type.OBJECT, binaryNode);
  1.3062 -            return newRuntime(binaryNode, ADD);
  1.3063 -        }
  1.3064 -
  1.3065 -        return binaryNode;
  1.3066 -    }
  1.3067 -
  1.3068 -    @Override
  1.3069 -    public Node leaveAND(final BinaryNode binaryNode) {
  1.3070 -        return leaveBinary(binaryNode, Type.OBJECT, null, null);
  1.3071 -    }
  1.3072 -
  1.3073 -    /**
  1.3074 -     * This is a helper called before an assignment.
  1.3075 -     * @param binaryNode assignment node
  1.3076 -     */
  1.3077 -    private Node enterAssign(final BinaryNode binaryNode) {
  1.3078 -        final Node lhs = binaryNode.lhs();
  1.3079 -
  1.3080 -        if (!(lhs instanceof IdentNode)) {
  1.3081 -            return binaryNode;
  1.3082 -        }
  1.3083 -
  1.3084 -        final Block     block = getCurrentBlock();
  1.3085 -        final IdentNode ident = (IdentNode)lhs;
  1.3086 -        final String    name  = ident.getName();
  1.3087 -
  1.3088 -        Symbol symbol = getCurrentBlock().findSymbol(name);
  1.3089 -
  1.3090 -        if (symbol == null) {
  1.3091 -            symbol = block.defineSymbol(name, IS_GLOBAL, ident);
  1.3092 -            binaryNode.setSymbol(symbol);
  1.3093 -        } else if (!getCurrentFunctionNode().isLocal(symbol)) {
  1.3094 -            symbol.setIsScope();
  1.3095 -        }
  1.3096 -
  1.3097 -        localDefs.add(name);
  1.3098 -
  1.3099 -        return binaryNode;
  1.3100 -    }
  1.3101 -
  1.3102 -    /**
  1.3103 -     * This assign helper is called after an assignment, when all children of
  1.3104 -     * the assign has been processed. It fixes the types and recursively makes
  1.3105 -     * sure that everyhing has slots that should have them in the chain.
  1.3106 -     *
  1.3107 -     * @param binaryNode assignment node
  1.3108 -     * @param destType   destination type of assignment
  1.3109 -     */
  1.3110 -    private Node leaveAssign(final BinaryNode binaryNode, final Type destType) {
  1.3111 -        binaryNode.lhs().getSymbol().setType(destType); // lhs inherits dest type
  1.3112 -        getCurrentFunctionNode().newTemporary(destType, binaryNode); // as does destination
  1.3113 -
  1.3114 -        ensureAssignmentSlots(getCurrentFunctionNode(), binaryNode.lhs());
  1.3115 -        return binaryNode;
  1.3116 -    }
  1.3117 -
  1.3118 -    @Override
  1.3119 -    public Node enterASSIGN(final BinaryNode binaryNode) {
  1.3120 -        return enterAssign(binaryNode);
  1.3121 -    }
  1.3122 -
  1.3123 -    @Override
  1.3124 -    public Node leaveASSIGN(final BinaryNode binaryNode) {
  1.3125 -        final Node lhs = binaryNode.lhs();
  1.3126 -        final Node rhs = binaryNode.rhs();
  1.3127 -
  1.3128 -        if (rhs.getType().isNumeric()) {
  1.3129 -            final Symbol lhsSymbol = lhs.getSymbol();
  1.3130 -            final Type lhsType = Type.widest(lhs.getType(), binaryType(binaryNode));
  1.3131 -
  1.3132 -            // for index nodes, we can set anything
  1.3133 -            lhsSymbol.setType(lhsType);
  1.3134 -            getCurrentFunctionNode().newTemporary(lhs.getType(), binaryNode);
  1.3135 -            if (!(lhs instanceof IndexNode)) {
  1.3136 -                binaryNode.setRHS(convert(rhs, lhsType));
  1.3137 -            }
  1.3138 -        } else {
  1.3139 -            // Force symbol to be object if not numeric assignment.
  1.3140 -            binaryNode.setRHS(convert(rhs, Type.OBJECT));
  1.3141 -            getCurrentFunctionNode().newTemporary(Type.OBJECT, binaryNode);
  1.3142 -
  1.3143 -            if (lhs instanceof IdentNode) {
  1.3144 -                lhs.getSymbol().setType(Type.OBJECT);
  1.3145 -            }
  1.3146 -        }
  1.3147 -
  1.3148 -        if (lhs instanceof AccessNode) {
  1.3149 -            final Node baseNode = ((AccessNode)lhs).getBase();
  1.3150 -
  1.3151 -            if (baseNode.getSymbol().isThis()) {
  1.3152 -                final IdentNode property = ((AccessNode)lhs).getProperty();
  1.3153 -                getCurrentFunctionNode().addThisProperty(property.getName(), property);
  1.3154 -            }
  1.3155 -        }
  1.3156 -
  1.3157 -        return binaryNode;
  1.3158 -    }
  1.3159 -
  1.3160 -    @Override
  1.3161 -    public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
  1.3162 -        return enterAssign(binaryNode);
  1.3163 -    }
  1.3164 -
  1.3165 -    @Override
  1.3166 -    public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
  1.3167 -        final Node lhs = binaryNode.lhs();
  1.3168 -        final Node rhs = binaryNode.rhs();
  1.3169 -        final boolean bothNumeric = lhs.getType().isNumeric() && rhs.getType().isNumeric();
  1.3170 -
  1.3171 -        /*
  1.3172 -         * In the case of bothNumeric,
  1.3173 -         * compute type for lhs += rhs. Assign type to lhs. Assign type to
  1.3174 -         * temporary for this node. Convert rhs from whatever it was to this
  1.3175 -         * type. Legacy wise dest has always been narrowed. It should
  1.3176 -         * actually only be the lhs that is the double, or other narrower
  1.3177 -         * type than OBJECT
  1.3178 -         */
  1.3179 -        return leaveAssign(binaryNode, bothNumeric ? binaryType(binaryNode) : Type.OBJECT);
  1.3180 -    }
  1.3181 -
  1.3182 -    @Override
  1.3183 -    public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
  1.3184 -        return enterAssign(binaryNode);
  1.3185 -    }
  1.3186 -
  1.3187 -    @Override
  1.3188 -    public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) {
  1.3189 -        return leaveAssign(binaryNode, binaryType(binaryNode));
  1.3190 -    }
  1.3191 -
  1.3192 -    @Override
  1.3193 -    public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
  1.3194 -        return enterAssign(binaryNode);
  1.3195 -    }
  1.3196 -
  1.3197 -    @Override
  1.3198 -    public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) {
  1.3199 -        return leaveAssign(binaryNode, binaryType(binaryNode));
  1.3200 -    }
  1.3201 -
  1.3202 -    @Override
  1.3203 -    public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
  1.3204 -        return enterAssign(binaryNode);
  1.3205 -    }
  1.3206 -
  1.3207 -    @Override
  1.3208 -    public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) {
  1.3209 -        return leaveAssign(binaryNode, binaryType(binaryNode));
  1.3210 -    }
  1.3211 -
  1.3212 -    @Override
  1.3213 -    public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
  1.3214 -        return enterAssign(binaryNode);
  1.3215 -    }
  1.3216 -
  1.3217 -    @Override
  1.3218 -    public Node leaveASSIGN_DIV(final BinaryNode binaryNode) {
  1.3219 -        return leaveAssign(binaryNode, binaryType(binaryNode));
  1.3220 -    }
  1.3221 -
  1.3222 -    @Override
  1.3223 -    public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
  1.3224 -        return enterAssign(binaryNode);
  1.3225 -    }
  1.3226 -
  1.3227 -    @Override
  1.3228 -    public Node leaveASSIGN_MOD(final BinaryNode binaryNode) {
  1.3229 -        return leaveAssign(binaryNode, binaryType(binaryNode));
  1.3230 -    }
  1.3231 -
  1.3232 -    @Override
  1.3233 -    public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
  1.3234 -        return enterAssign(binaryNode);
  1.3235 -    }
  1.3236 -
  1.3237 -    @Override
  1.3238 -    public Node leaveASSIGN_MUL(final BinaryNode binaryNode) {
  1.3239 -        return leaveAssign(binaryNode, binaryType(binaryNode));
  1.3240 -    }
  1.3241 -
  1.3242 -    @Override
  1.3243 -    public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
  1.3244 -        return enterAssign(binaryNode);
  1.3245 -    }
  1.3246 -
  1.3247 -    @Override
  1.3248 -    public Node leaveASSIGN_SAR(final BinaryNode binaryNode) {
  1.3249 -        return leaveAssign(binaryNode, binaryType(binaryNode));
  1.3250 -    }
  1.3251 -
  1.3252 -    @Override
  1.3253 -    public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
  1.3254 -        return enterAssign(binaryNode);
  1.3255 -    }
  1.3256 -
  1.3257 -    @Override
  1.3258 -    public Node leaveASSIGN_SHL(final BinaryNode binaryNode) {
  1.3259 -        return leaveAssign(binaryNode, binaryType(binaryNode));
  1.3260 -    }
  1.3261 -
  1.3262 -    @Override
  1.3263 -    public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
  1.3264 -        return enterAssign(binaryNode);
  1.3265 -    }
  1.3266 -
  1.3267 -    @Override
  1.3268 -    public Node leaveASSIGN_SHR(final BinaryNode binaryNode) {
  1.3269 -        return leaveAssign(binaryNode, binaryType(binaryNode));
  1.3270 -    }
  1.3271 -
  1.3272 -    @Override
  1.3273 -    public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
  1.3274 -        return enterAssign(binaryNode);
  1.3275 -    }
  1.3276 -
  1.3277 -    @Override
  1.3278 -    public Node leaveASSIGN_SUB(final BinaryNode binaryNode) {
  1.3279 -        return leaveAssign(binaryNode, binaryType(binaryNode));
  1.3280 -    }
  1.3281 -
  1.3282 -    @Override
  1.3283 -    public Node leaveBIT_AND(final BinaryNode binaryNode) {
  1.3284 -        return leaveBinary(binaryNode, Type.INT);
  1.3285 -    }
  1.3286 -
  1.3287 -    @Override
  1.3288 -    public Node leaveBIT_OR(final BinaryNode binaryNode) {
  1.3289 -        return leaveBinary(binaryNode, Type.INT);
  1.3290 -    }
  1.3291 -
  1.3292 -    @Override
  1.3293 -    public Node leaveBIT_XOR(final BinaryNode binaryNode) {
  1.3294 -        return leaveBinary(binaryNode, Type.INT);
  1.3295 -    }
  1.3296 -
  1.3297 -    @Override
  1.3298 -    public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
  1.3299 -        binaryNode.setLHS(discard(binaryNode.lhs()));
  1.3300 -        getCurrentFunctionNode().newTemporary(binaryNode.rhs().getType(), binaryNode);
  1.3301 -
  1.3302 -        return binaryNode;
  1.3303 -    }
  1.3304 -
  1.3305 -    @Override
  1.3306 -    public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
  1.3307 -        binaryNode.setRHS(discard(binaryNode.rhs()));
  1.3308 -        getCurrentFunctionNode().newTemporary(binaryNode.lhs().getType(), binaryNode);
  1.3309 -
  1.3310 -        return binaryNode;
  1.3311 -    }
  1.3312 -
  1.3313 -    @Override
  1.3314 -    public Node leaveDIV(final BinaryNode binaryNode) {
  1.3315 -        return leaveBinary(binaryNode, binaryType(binaryNode));
  1.3316 -    }
  1.3317 -
  1.3318 -    @Override
  1.3319 -    public Node leaveEQ(final BinaryNode binaryNode) {
  1.3320 -        return leaveCmp(binaryNode, EQ);
  1.3321 -    }
  1.3322 -
  1.3323 -    @Override
  1.3324 -    public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
  1.3325 -        return leaveCmp(binaryNode, EQ_STRICT);
  1.3326 -    }
  1.3327 -
  1.3328 -    private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) {
  1.3329 -        final Node lhs = binaryNode.lhs();
  1.3330 -        final Node rhs = binaryNode.rhs();
  1.3331 -
  1.3332 -        /* Attempt to turn this comparison into a constant and collapse it */
  1.3333 -        final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval();
  1.3334 -        if (literalNode != null) {
  1.3335 -            return newLiteral(literalNode);
  1.3336 -        }
  1.3337 -
  1.3338 -        // another case where dest != source operand types always a boolean
  1.3339 -        getCurrentFunctionNode().newTemporary(request.getReturnType(), binaryNode);
  1.3340 -
  1.3341 -        ensureTypeNotUnknown(lhs);
  1.3342 -        ensureTypeNotUnknown(rhs);
  1.3343 -        final Type type = Type.widest(lhs.getType(), rhs.getType());
  1.3344 -
  1.3345 -        if (type.isObject()) {
  1.3346 -            return newRuntime(binaryNode, request);
  1.3347 -        }
  1.3348 -
  1.3349 -        if ((request.equals(EQ_STRICT) || request.equals(NE_STRICT)) && lhs.getType().isBoolean() != rhs.getType().isBoolean()) {
  1.3350 -            // special case: number compared against boolean => never equal. must not convert!
  1.3351 -            final boolean              result     = request.equals(NE_STRICT);
  1.3352 -            final LiteralNode<Boolean> resultNode = LiteralNode.newInstance(source, 0, 0, result);
  1.3353 -            final boolean              canSkipLHS = (lhs instanceof LiteralNode);
  1.3354 -            final boolean              canSkipRHS = (rhs instanceof LiteralNode);
  1.3355 -
  1.3356 -            if (canSkipLHS && canSkipRHS) {
  1.3357 -                return resultNode.accept(this);
  1.3358 -            }
  1.3359 -
  1.3360 -            final Node argNode;
  1.3361 -
  1.3362 -            if (!canSkipLHS && !canSkipRHS) {
  1.3363 -                argNode = new BinaryNode(source, Token.recast(binaryNode.getToken(), TokenType.COMMARIGHT), lhs, rhs);
  1.3364 -            } else {
  1.3365 -                argNode = !canSkipLHS ? lhs : rhs;
  1.3366 -            }
  1.3367 -
  1.3368 -            return new BinaryNode(source, Token.recast(binaryNode.getToken(), TokenType.COMMARIGHT), argNode, resultNode).accept(this);
  1.3369 -        }
  1.3370 -
  1.3371 -        binaryNode.setLHS(convert(lhs, type));
  1.3372 -        binaryNode.setRHS(convert(rhs, type));
  1.3373 -
  1.3374 -        return binaryNode;
  1.3375 -    }
  1.3376 -
  1.3377 -    @Override
  1.3378 -    public Node leaveGE(final BinaryNode binaryNode) {
  1.3379 -        return leaveCmp(binaryNode, GE);
  1.3380 -    }
  1.3381 -
  1.3382 -    @Override
  1.3383 -    public Node leaveGT(final BinaryNode binaryNode) {
  1.3384 -        return leaveCmp(binaryNode, GT);
  1.3385 -    }
  1.3386 -
  1.3387 -    private Node exitIN_INSTANCEOF(final BinaryNode binaryNode, final Request request) {
  1.3388 -        getCurrentFunctionNode().newTemporary(request.getReturnType(), binaryNode);
  1.3389 -        return newRuntime(binaryNode, request);
  1.3390 -    }
  1.3391 -
  1.3392 -    @Override
  1.3393 -    public Node leaveIN(final BinaryNode binaryNode) {
  1.3394 -        return exitIN_INSTANCEOF(binaryNode, IN);
  1.3395 -    }
  1.3396 -
  1.3397 -    @Override
  1.3398 -    public Node leaveINSTANCEOF(final BinaryNode binaryNode) {
  1.3399 -        return exitIN_INSTANCEOF(binaryNode, INSTANCEOF);
  1.3400 -    }
  1.3401 -
  1.3402 -    @Override
  1.3403 -    public Node leaveLE(final BinaryNode binaryNode) {
  1.3404 -        return leaveCmp(binaryNode, LE);
  1.3405 -    }
  1.3406 -
  1.3407 -    @Override
  1.3408 -    public Node leaveLT(final BinaryNode binaryNode) {
  1.3409 -        return leaveCmp(binaryNode, LT);
  1.3410 -    }
  1.3411 -
  1.3412 -    @Override
  1.3413 -    public Node leaveMOD(final BinaryNode binaryNode) {
  1.3414 -        return leaveBinary(binaryNode, binaryType(binaryNode));
  1.3415 -    }
  1.3416 -
  1.3417 -    @Override
  1.3418 -    public Node leaveMUL(final BinaryNode binaryNode) {
  1.3419 -        return leaveBinary(binaryNode,  binaryType(binaryNode));
  1.3420 -    }
  1.3421 -
  1.3422 -    @Override
  1.3423 -    public Node leaveNE(final BinaryNode binaryNode) {
  1.3424 -        return leaveCmp(binaryNode, NE);
  1.3425 -    }
  1.3426 -
  1.3427 -    @Override
  1.3428 -    public Node leaveNE_STRICT(final BinaryNode binaryNode) {
  1.3429 -        return leaveCmp(binaryNode, NE_STRICT);
  1.3430 -    }
  1.3431 -
  1.3432 -    @Override
  1.3433 -    public Node leaveOR(final BinaryNode binaryNode) {
  1.3434 -        return leaveBinary(binaryNode, Type.OBJECT, null, null);
  1.3435 -    }
  1.3436 -
  1.3437 -    @Override
  1.3438 -    public Node leaveSAR(final BinaryNode binaryNode) {
  1.3439 -        return leaveBinary(binaryNode, Type.INT);
  1.3440 -    }
  1.3441 -
  1.3442 -    @Override
  1.3443 -    public Node leaveSHL(final BinaryNode binaryNode) {
  1.3444 -        return leaveBinary(binaryNode, Type.INT);
  1.3445 -    }
  1.3446 -
  1.3447 -    @Override
  1.3448 -    public Node leaveSHR(final BinaryNode binaryNode) {
  1.3449 -        return leaveBinary(binaryNode, Type.LONG, Type.INT, Type.INT);
  1.3450 -    }
  1.3451 -
  1.3452 -    @Override
  1.3453 -    public Node leaveSUB(final BinaryNode binaryNode) {
  1.3454 -        return leaveBinary(binaryNode, binaryType(binaryNode));
  1.3455 -    }
  1.3456 -
  1.3457 -    @Override
  1.3458 -    public Node leave(final TernaryNode ternaryNode) {
  1.3459 -        final Node test = ternaryNode.lhs();
  1.3460 -        final Node lhs  = ternaryNode.rhs();
  1.3461 -        final Node rhs  = ternaryNode.third();
  1.3462 -
  1.3463 -        ensureTypeNotUnknown(lhs);
  1.3464 -        ensureTypeNotUnknown(rhs);
  1.3465 -        final Type type = Type.widest(lhs.getType(), rhs.getType());
  1.3466 -
  1.3467 -        ternaryNode.setRHS(convert(lhs, type));
  1.3468 -        ternaryNode.setThird(convert(rhs, type));
  1.3469 -
  1.3470 -        // optimize away the ternary if the test is constant.
  1.3471 -        if (test.getSymbol().isConstant()) {
  1.3472 -            return ((LiteralNode<?>)test).isTrue() ? lhs : rhs;
  1.3473 -        }
  1.3474 -
  1.3475 -        ternaryNode.setLHS(convert(test, Type.BOOLEAN));
  1.3476 -
  1.3477 -        getCurrentFunctionNode().newTemporary(type, ternaryNode);
  1.3478 -
  1.3479 -        return ternaryNode;
  1.3480 -    }
  1.3481 -
  1.3482 -    private static void ensureTypeNotUnknown(final Node node) {
  1.3483 -        final Symbol symbol = node.getSymbol();
  1.3484 -
  1.3485 -        /*
  1.3486 -         * Note that not just unknowns, but params need to be blown
  1.3487 -         * up to objects, because we can have something like
  1.3488 -         *
  1.3489 -         * function f(a) {
  1.3490 -         *    var b = ~a; //b and a are inferred to be int
  1.3491 -         *    return b;
  1.3492 -         * }
  1.3493 -         *
  1.3494 -         * In this case, it would be correct to say that "if you have
  1.3495 -         * an int at the callsite, just pass it".
  1.3496 -         *
  1.3497 -         * However
  1.3498 -         *
  1.3499 -         * function f(a) {
  1.3500 -         *    var b = ~a;      //b and a are inferred to be int
  1.3501 -         *    return b == 17;  //b is still inferred to be int.
  1.3502 -         * }
  1.3503 -         *
  1.3504 -         * can be called with f("17") and if we assume that b is an
  1.3505 -         * int and don't blow it up to an object in the comparison, we
  1.3506 -         * are screwed. I hate JavaScript.
  1.3507 -         *
  1.3508 -         * This check has to be done for any operation that might take
  1.3509 -         * objects as parameters, for example +, but not *, which is known
  1.3510 -         * to coerce types into doubles
  1.3511 -         */
  1.3512 -        if (node.getType().isUnknown() || symbol.isParam()) {
  1.3513 -            symbol.setType(Type.OBJECT);
  1.3514 -            symbol.setCanBeUndefined();
  1.3515 -         }
  1.3516 -    }
  1.3517 -
  1.3518 -    /**
  1.3519 -     * A simple node visitor that ensure that scope and slot information is correct.
  1.3520 -     * This is run as a post pass after we know all scope changing information about
  1.3521 -     * the Lowering. This is also called after potential mutations like splitting
  1.3522 -     * have taken place, as splitting forces scope.
  1.3523 -     *
  1.3524 -     * This was previously done on a per functionNode basis in {@link CodeGenerator},
  1.3525 -     * but this is too late for type information to be used in {@link AccessSpecializer}
  1.3526 -     */
  1.3527 -    static class FinalizeSymbols extends NodeVisitor {
  1.3528 -        @Override
  1.3529 -        public Node leave(final Block block) {
  1.3530 -            return updateSymbols(block);
  1.3531 -        }
  1.3532 -
  1.3533 -        @Override
  1.3534 -        public Node leave(final FunctionNode function) {
  1.3535 -            return updateSymbols(function);
  1.3536 -        }
  1.3537 -
  1.3538 -        private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) {
  1.3539 -            if (!symbol.isScope()) {
  1.3540 -                LOG.finest("updateSymbols: " + symbol + " => scope, because all vars in " + functionNode.getName() + " are in scope");
  1.3541 -            }
  1.3542 -            if (loseSlot && symbol.hasSlot()) {
  1.3543 -                LOG.finest("updateSymbols: " + symbol + " => no slot, because all vars in " + functionNode.getName() + " are in scope");
  1.3544 -            }
  1.3545 -        }
  1.3546 -
  1.3547 -        // called after a block or function node (subclass of block) is finished
  1.3548 -        // to correct scope and slot assignment for variables
  1.3549 -        private static Block updateSymbols(final Block block) {
  1.3550 -
  1.3551 -            if (!block.needsScope()) {
  1.3552 -                return block; // nothing to do
  1.3553 -            }
  1.3554 -
  1.3555 -            assert !(block instanceof FunctionNode) || block.getFunction() == block;
  1.3556 -
  1.3557 -            final FunctionNode functionNode   = block.getFunction();
  1.3558 -            final List<Symbol> symbols        = block.getFrame().getSymbols();
  1.3559 -            final boolean      allVarsInScope = functionNode.varsInScope();
  1.3560 -            final boolean      isVarArg       = functionNode.isVarArg();
  1.3561 -
  1.3562 -            for (final Symbol symbol : symbols) {
  1.3563 -                if (symbol.isInternal() || symbol.isThis()) {
  1.3564 -                    continue;
  1.3565 -                }
  1.3566 -
  1.3567 -                if (symbol.isVar()) {
  1.3568 -                    if (allVarsInScope || symbol.isScope()) {
  1.3569 -                        updateSymbolsLog(functionNode, symbol, true);
  1.3570 -                        symbol.setIsScope();
  1.3571 -                        symbol.setNeedsSlot(false);
  1.3572 -                    } else {
  1.3573 -                        assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
  1.3574 -                    }
  1.3575 -                } else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) {
  1.3576 -                    updateSymbolsLog(functionNode, symbol, isVarArg);
  1.3577 -                    symbol.setIsScope();
  1.3578 -                    symbol.setNeedsSlot(!isVarArg);
  1.3579 -                }
  1.3580 -            }
  1.3581 -
  1.3582 -            return block;
  1.3583 -        }
  1.3584 -    }
  1.3585 -
  1.3586 -    /**
  1.3587 -     * Helper class to evaluate constant expressions at compile time This is
  1.3588 -     * also a simplifier used by BinaryNode visits, UnaryNode visits and
  1.3589 -     * conversions.
  1.3590 -     */
  1.3591 -    private abstract static class ConstantEvaluator<T extends Node> {
  1.3592 -        protected T            parent;
  1.3593 -        protected final Source source;
  1.3594 -        protected final long   token;
  1.3595 -        protected final int    finish;
  1.3596 -
  1.3597 -        protected ConstantEvaluator(final T parent) {
  1.3598 -            this.parent  = parent;
  1.3599 -            this.source  = parent.getSource();
  1.3600 -            this.token   = parent.getToken();
  1.3601 -            this.finish  = parent.getFinish();
  1.3602 -        }
  1.3603 -
  1.3604 -        /**
  1.3605 -         * Returns a literal node that replaces the given parent node, or null if replacement
  1.3606 -         * is impossible
  1.3607 -         * @return the literal node
  1.3608 -         */
  1.3609 -        protected abstract LiteralNode<?> eval();
  1.3610 -    }
  1.3611 -
  1.3612 -    static class LiteralNodeConstantEvaluator extends ConstantEvaluator<LiteralNode<?>> {
  1.3613 -        private final Type type;
  1.3614 -
  1.3615 -        LiteralNodeConstantEvaluator(final LiteralNode<?> parent, final Type type) {
  1.3616 -            super(parent);
  1.3617 -            this.type = type;
  1.3618 -        }
  1.3619 -
  1.3620 -        @Override
  1.3621 -        protected LiteralNode<?> eval() {
  1.3622 -            final Object value = ((LiteralNode<?>)parent).getValue();
  1.3623 -
  1.3624 -            LiteralNode<?> literalNode = null;
  1.3625 -
  1.3626 -            if (type.isString()) {
  1.3627 -                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toString(value));
  1.3628 -            } else if (type.isBoolean()) {
  1.3629 -                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toBoolean(value));
  1.3630 -            } else if (type.isInteger()) {
  1.3631 -                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toInt32(value));
  1.3632 -            } else if (type.isLong()) {
  1.3633 -                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toLong(value));
  1.3634 -            } else if (type.isNumber() || parent.getType().isNumeric() && !parent.getType().isNumber()) {
  1.3635 -                literalNode = LiteralNode.newInstance(source, token, finish, JSType.toNumber(value));
  1.3636 -            }
  1.3637 -
  1.3638 -            return literalNode;
  1.3639 -        }
  1.3640 -    }
  1.3641 -
  1.3642 -    private static class UnaryNodeConstantEvaluator extends ConstantEvaluator<UnaryNode> {
  1.3643 -        UnaryNodeConstantEvaluator(final UnaryNode parent) {
  1.3644 -            super(parent);
  1.3645 -        }
  1.3646 -
  1.3647 -        @Override
  1.3648 -        protected LiteralNode<?> eval() {
  1.3649 -            final Node rhsNode = parent.rhs();
  1.3650 -
  1.3651 -            if (!rhsNode.getSymbol().isConstant()) {
  1.3652 -                return null;
  1.3653 -            }
  1.3654 -
  1.3655 -            final LiteralNode<?> rhs = (LiteralNode<?>)rhsNode;
  1.3656 -            final boolean rhsInteger = rhs.getType().isInteger();
  1.3657 -
  1.3658 -            LiteralNode<?> literalNode;
  1.3659 -
  1.3660 -            switch (parent.tokenType()) {
  1.3661 -            case ADD:
  1.3662 -                if (rhsInteger) {
  1.3663 -                    literalNode = LiteralNode.newInstance(source, token, finish, rhs.getInt32());
  1.3664 -                } else {
  1.3665 -                    literalNode = LiteralNode.newInstance(source, token, finish, rhs.getNumber());
  1.3666 -                }
  1.3667 -                break;
  1.3668 -            case SUB:
  1.3669 -                if (rhsInteger && rhs.getInt32() != 0) { // @see test/script/basic/minuszero.js
  1.3670 -                    literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getInt32());
  1.3671 -                } else {
  1.3672 -                    literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getNumber());
  1.3673 -                }
  1.3674 -                break;
  1.3675 -            case NOT:
  1.3676 -                literalNode = LiteralNode.newInstance(source, token, finish, !rhs.getBoolean());
  1.3677 -                break;
  1.3678 -            case BIT_NOT:
  1.3679 -                literalNode = LiteralNode.newInstance(source, token, finish, ~rhs.getInt32());
  1.3680 -                break;
  1.3681 -            default:
  1.3682 -                return null;
  1.3683 -            }
  1.3684 -
  1.3685 -            return literalNode;
  1.3686 -        }
  1.3687 -    }
  1.3688 -
  1.3689 -    private static class BinaryNodeConstantEvaluator extends ConstantEvaluator<BinaryNode> {
  1.3690 -        BinaryNodeConstantEvaluator(final BinaryNode parent) {
  1.3691 -            super(parent);
  1.3692 -        }
  1.3693 -
  1.3694 -        @Override
  1.3695 -        protected LiteralNode<?> eval() {
  1.3696 -
  1.3697 -            if (!parent.lhs().getSymbol().isConstant() || !parent.rhs().getSymbol().isConstant()) {
  1.3698 -                return null;
  1.3699 -            }
  1.3700 -
  1.3701 -            final LiteralNode<?> lhs = (LiteralNode<?>)parent.lhs();
  1.3702 -            final LiteralNode<?> rhs = (LiteralNode<?>)parent.rhs();
  1.3703 -
  1.3704 -            final Type widest = Type.widest(lhs.getType(), rhs.getType());
  1.3705 -
  1.3706 -            boolean isInteger = widest.isInteger();
  1.3707 -            boolean isLong    = widest.isLong();
  1.3708 -
  1.3709 -            double value;
  1.3710 -
  1.3711 -            switch (parent.tokenType()) {
  1.3712 -            case AND:
  1.3713 -                return JSType.toBoolean(lhs.getObject()) ? rhs : lhs;
  1.3714 -            case OR:
  1.3715 -                return JSType.toBoolean(lhs.getObject()) ? lhs : rhs;
  1.3716 -            case DIV:
  1.3717 -                value = lhs.getNumber() / rhs.getNumber();
  1.3718 -                break;
  1.3719 -            case ADD:
  1.3720 -                value = lhs.getNumber() + rhs.getNumber();
  1.3721 -                break;
  1.3722 -            case MUL:
  1.3723 -                value = lhs.getNumber() * rhs.getNumber();
  1.3724 -                break;
  1.3725 -            case MOD:
  1.3726 -                value = lhs.getNumber() % rhs.getNumber();
  1.3727 -                break;
  1.3728 -            case SUB:
  1.3729 -                value = lhs.getNumber() - rhs.getNumber();
  1.3730 -                break;
  1.3731 -            case SHR:
  1.3732 -                return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & 0xffff_ffffL);
  1.3733 -            case SAR:
  1.3734 -                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() >> rhs.getInt32());
  1.3735 -            case SHL:
  1.3736 -                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() << rhs.getInt32());
  1.3737 -            case BIT_XOR:
  1.3738 -                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() ^ rhs.getInt32());
  1.3739 -            case BIT_AND:
  1.3740 -                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() & rhs.getInt32());
  1.3741 -            case BIT_OR:
  1.3742 -                return LiteralNode.newInstance(source, token, finish, lhs.getInt32() | rhs.getInt32());
  1.3743 -            case GE:
  1.3744 -                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GE(lhs.getObject(), rhs.getObject()));
  1.3745 -            case LE:
  1.3746 -                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LE(lhs.getObject(), rhs.getObject()));
  1.3747 -            case GT:
  1.3748 -                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GT(lhs.getObject(), rhs.getObject()));
  1.3749 -            case LT:
  1.3750 -                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LT(lhs.getObject(), rhs.getObject()));
  1.3751 -            case NE:
  1.3752 -                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE(lhs.getObject(), rhs.getObject()));
  1.3753 -            case NE_STRICT:
  1.3754 -                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE_STRICT(lhs.getObject(), rhs.getObject()));
  1.3755 -            case EQ:
  1.3756 -                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ(lhs.getObject(), rhs.getObject()));
  1.3757 -            case EQ_STRICT:
  1.3758 -                return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ_STRICT(lhs.getObject(), rhs.getObject()));
  1.3759 -            default:
  1.3760 -                return null;
  1.3761 -            }
  1.3762 -
  1.3763 -            isInteger &= value != 0.0 && JSType.isRepresentableAsInt(value);
  1.3764 -            isLong    &= value != 0.0 && JSType.isRepresentableAsLong(value);
  1.3765 -
  1.3766 -            if (isInteger) {
  1.3767 -                return LiteralNode.newInstance(source, token, finish, JSType.toInt32(value));
  1.3768 -            } else if (isLong) {
  1.3769 -                return LiteralNode.newInstance(source, token, finish, JSType.toLong(value));
  1.3770 -            }
  1.3771 -
  1.3772 -            return LiteralNode.newInstance(source, token, finish, value);
  1.3773 -        }
  1.3774 -    }
  1.3775 -}

mercurial