1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.codegen;
  27 
  28 import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE;
  29 import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC;
  30 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
  31 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
  32 import static jdk.nashorn.internal.codegen.CompilerConstants.LEAF;
  33 import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX;
  34 import static jdk.nashorn.internal.codegen.CompilerConstants.REGEX_PREFIX;
  35 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
  36 import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_ARRAY_ARG;
  37 import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
  38 import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup;
  39 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
  40 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
  41 import static jdk.nashorn.internal.codegen.CompilerConstants.staticField;
  42 import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
  43 import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
  44 import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
  45 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FAST_SCOPE;
  46 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FUNCTION_DECLARATION;
  47 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_SCOPE;
  48 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
  49 
  50 import java.io.PrintWriter;
  51 import java.util.ArrayList;
  52 import java.util.Arrays;
  53 import java.util.EnumSet;
  54 import java.util.HashMap;
  55 import java.util.Iterator;
  56 import java.util.LinkedList;
  57 import java.util.List;
  58 import java.util.Map;
  59 import java.util.Map.Entry;
  60 import java.util.TreeMap;
  61 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
  62 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
  63 import jdk.nashorn.internal.codegen.MethodEmitter.Condition;
  64 import jdk.nashorn.internal.codegen.MethodEmitter.Label;
  65 import jdk.nashorn.internal.codegen.RuntimeCallSite.SpecializedRuntimeNode;
  66 import jdk.nashorn.internal.codegen.objects.FieldObjectCreator;
  67 import jdk.nashorn.internal.codegen.objects.FunctionObjectCreator;
  68 import jdk.nashorn.internal.codegen.objects.MapCreator;
  69 import jdk.nashorn.internal.codegen.objects.ObjectMapCreator;
  70 import jdk.nashorn.internal.codegen.types.ArrayType;
  71 import jdk.nashorn.internal.codegen.types.Type;
  72 import jdk.nashorn.internal.ir.AccessNode;
  73 import jdk.nashorn.internal.ir.BaseNode;
  74 import jdk.nashorn.internal.ir.BinaryNode;
  75 import jdk.nashorn.internal.ir.Block;
  76 import jdk.nashorn.internal.ir.BreakNode;
  77 import jdk.nashorn.internal.ir.CallNode;
  78 import jdk.nashorn.internal.ir.CaseNode;
  79 import jdk.nashorn.internal.ir.CatchNode;
  80 import jdk.nashorn.internal.ir.ContinueNode;
  81 import jdk.nashorn.internal.ir.DoWhileNode;
  82 import jdk.nashorn.internal.ir.EmptyNode;
  83 import jdk.nashorn.internal.ir.ExecuteNode;
  84 import jdk.nashorn.internal.ir.ForNode;
  85 import jdk.nashorn.internal.ir.FunctionNode;
  86 import jdk.nashorn.internal.ir.IdentNode;
  87 import jdk.nashorn.internal.ir.IfNode;
  88 import jdk.nashorn.internal.ir.IndexNode;
  89 import jdk.nashorn.internal.ir.LineNumberNode;
  90 import jdk.nashorn.internal.ir.LiteralNode;
  91 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
  92 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
  93 import jdk.nashorn.internal.ir.Node;
  94 import jdk.nashorn.internal.ir.ObjectNode;
  95 import jdk.nashorn.internal.ir.PropertyNode;
  96 import jdk.nashorn.internal.ir.ReferenceNode;
  97 import jdk.nashorn.internal.ir.ReturnNode;
  98 import jdk.nashorn.internal.ir.RuntimeNode;
  99 import jdk.nashorn.internal.ir.RuntimeNode.Request;
 100 import jdk.nashorn.internal.ir.SplitNode;
 101 import jdk.nashorn.internal.ir.SwitchNode;
 102 import jdk.nashorn.internal.ir.Symbol;
 103 import jdk.nashorn.internal.ir.TernaryNode;
 104 import jdk.nashorn.internal.ir.ThrowNode;
 105 import jdk.nashorn.internal.ir.TryNode;
 106 import jdk.nashorn.internal.ir.UnaryNode;
 107 import jdk.nashorn.internal.ir.VarNode;
 108 import jdk.nashorn.internal.ir.WhileNode;
 109 import jdk.nashorn.internal.ir.WithNode;
 110 import jdk.nashorn.internal.ir.debug.ASTWriter;
 111 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
 112 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 113 import jdk.nashorn.internal.parser.Lexer.RegexToken;
 114 import jdk.nashorn.internal.parser.TokenType;
 115 import jdk.nashorn.internal.runtime.Context;
 116 import jdk.nashorn.internal.runtime.ECMAException;
 117 import jdk.nashorn.internal.runtime.PropertyMap;
 118 import jdk.nashorn.internal.runtime.Scope;
 119 import jdk.nashorn.internal.runtime.ScriptFunction;
 120 import jdk.nashorn.internal.runtime.ScriptObject;
 121 import jdk.nashorn.internal.runtime.ScriptRuntime;
 122 import jdk.nashorn.internal.runtime.Source;
 123 import jdk.nashorn.internal.runtime.Undefined;
 124 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
 125 
 126 /**
 127  * This is the lowest tier of the code generator. It takes lowered ASTs emitted
 128  * from Lower and emits Java byte code. The byte code emission logic is broken
 129  * out into MethodEmitter. MethodEmitter works internally with a type stack, and
 130  * keeps track of the contents of the byte code stack. This way we avoid a large
 131  * number of special cases on the form
 132  * <pre>
 133  * if (type == INT) {
 134  *     visitInsn(ILOAD, slot);
 135  * } else if (type == DOUBLE) {
 136  *     visitInsn(DOUBLE, slot);
 137  * }
 138  * </pre>
 139  * This quickly became apparent when the code generator was generalized to work
 140  * with all types, and not just numbers or objects.
 141  * <p>
 142  * The CodeGenerator visits nodes only once, tags them as resolved and emits
 143  * bytecode for them.
 144  */
 145 public final class CodeGenerator extends NodeOperatorVisitor {
 146 
 147     /** Current compiler */
 148     private final Compiler compiler;
 149 
 150     /** Compiler context */
 151     private final Context context;
 152 
 153     /** Call site flags given to the code generator to be used for all generated call sites */
 154     private final int callSiteFlags;
 155 
 156     /** How many regexp fields have been emitted */
 157     private int regexFieldCount;
 158 
 159     /** Map of shared scope call sites */
 160     private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>();
 161 
 162     /** When should we stop caching regexp expressions in fields to limit bytecode size? */
 163     private static final int MAX_REGEX_FIELDS = 2 * 1024;
 164 
 165     /**
 166      * Constructor.
 167      *
 168      * @param compiler
 169      */
 170     CodeGenerator(final Compiler compiler) {
 171         this.compiler      = compiler;
 172         this.context       = compiler.getContext();
 173         this.callSiteFlags = context._callsite_flags;
 174     }
 175 
 176     /**
 177      * Get the compiler
 178      *
 179      * @return the compiler used
 180      */
 181     public Compiler getCompiler() {
 182         return compiler;
 183     }
 184 
 185     /**
 186      * Gets the call site flags, adding the strict flag if the current function
 187      * being generated is in strict mode
 188      *
 189      * @return the correct flags for a call site in the current function
 190      */
 191     public int getCallSiteFlags() {
 192         return getCurrentFunctionNode().isStrictMode() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
 193     }
 194 
 195     /**
 196      * Load an identity node
 197      *
 198      * @param identNode an identity node to load
 199      * @return the method generator used
 200      */
 201     private MethodEmitter loadIdent(final IdentNode identNode) {
 202         final Symbol symbol = identNode.getSymbol();
 203 
 204         if (!symbol.isScope()) {
 205             assert symbol.hasSlot() && symbol.getSlot() != 0 || symbol.isThis();
 206             return method.load(symbol);
 207         }
 208 
 209         final String name = symbol.getName();
 210 
 211         if (CompilerConstants.__FILE__.name().equals(name)) {
 212             return method.load(identNode.getSource().getName());
 213         } else if (CompilerConstants.__DIR__.name().equals(name)) {
 214             return method.load(identNode.getSource().getBase());
 215         } else if (CompilerConstants.__LINE__.name().equals(name)) {
 216             return method.load(identNode.getSource().getLine(identNode.position())).convert(Type.OBJECT);
 217         } else {
 218             assert identNode.getSymbol().isScope() : identNode + " is not in scope!";
 219 
 220             final int flags = CALLSITE_SCOPE | getCallSiteFlags();
 221             method.loadScope();
 222 
 223             if (symbol.isFastScope(getCurrentFunctionNode())) {
 224                 // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
 225                 if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD) {
 226                     return loadSharedScopeVar(identNode.getType(), symbol, flags);
 227                 }
 228                 return loadFastScopeVar(identNode.getType(), symbol, flags, identNode.isFunction());
 229             }
 230             return method.dynamicGet(identNode.getType(), identNode.getName(), flags, identNode.isFunction());
 231         }
 232     }
 233 
 234     private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) {
 235         method.load(symbol.isFastScope(getCurrentFunctionNode()) ? getScopeProtoDepth(getCurrentBlock(), symbol) : -1);
 236         final SharedScopeCall scopeCall = getScopeGet(valueType, symbol, flags | CALLSITE_FAST_SCOPE);
 237         scopeCall.generateInvoke(method);
 238         return method;
 239     }
 240 
 241     private MethodEmitter loadFastScopeVar(final Type valueType, final Symbol symbol, final int flags, final boolean isMethod) {
 242         loadFastScopeProto(symbol, false);
 243         method.dynamicGet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE, isMethod);
 244         return method;
 245     }
 246 
 247     private MethodEmitter storeFastScopeVar(final Type valueType, final Symbol symbol, final int flags) {
 248         loadFastScopeProto(symbol, true);
 249         method.dynamicSet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE);
 250         return method;
 251     }
 252 
 253     private static int getScopeProtoDepth(final Block currentBlock, final Symbol symbol) {
 254         if (currentBlock == symbol.getBlock()) {
 255             return 0;
 256         }
 257 
 258         final int   delta       = currentBlock.needsScope() ? 1 : 0;
 259         final Block parentBlock = currentBlock.getParent();
 260 
 261         if (parentBlock != null) {
 262             final int result = getScopeProtoDepth(parentBlock, symbol);
 263             if (result != -1) {
 264                 return delta + result;
 265             }
 266         }
 267 
 268         if (currentBlock instanceof FunctionNode) {
 269             for (final Block lookupBlock : ((FunctionNode)currentBlock).getReferencingParentBlocks()) {
 270                 final int result = getScopeProtoDepth(lookupBlock, symbol);
 271                 if (result != -1) {
 272                     return delta + result;
 273                 }
 274             }
 275         }
 276 
 277         return -1;
 278     }
 279 
 280     private void loadFastScopeProto(final Symbol symbol, final boolean swap) {
 281         final int depth = getScopeProtoDepth(getCurrentBlock(), symbol);
 282         assert depth != -1;
 283         if(depth > 0) {
 284             if (swap) {
 285                 method.swap();
 286             }
 287             for (int i = 0; i < depth; i++) {
 288                 method.invoke(ScriptObject.GET_PROTO);
 289             }
 290             if (swap) {
 291                 method.swap();
 292             }
 293         }
 294     }
 295 
 296     /**
 297      * Generate code that loads this node to the stack. This method is only
 298      * public to be accessible from the maps sub package. Do not call externally
 299      *
 300      * @param node node to load
 301      *
 302      * @return the method emitter used
 303      */
 304     public MethodEmitter load(final Node node) {
 305         return load(node, false);
 306     }
 307 
 308     private MethodEmitter load(final Node node, final boolean baseAlreadyOnStack) {
 309         final Symbol symbol = node.getSymbol();
 310 
 311         // If we lack symbols, we just generate what we see.
 312         if (symbol == null) {
 313             node.accept(this);
 314             return method;
 315         }
 316 
 317         /*
 318          * The load may be of type IdentNode, e.g. "x", AccessNode, e.g. "x.y"
 319          * or IndexNode e.g. "x[y]". Both AccessNodes and IndexNodes are
 320          * BaseNodes and the logic for loading the base object is reused
 321          */
 322         final CodeGenerator codegen = this;
 323 
 324         node.accept(new NodeVisitor(compileUnit, method) {
 325             @Override
 326             public Node enter(final IdentNode identNode) {
 327                 loadIdent(identNode);
 328                 return null;
 329             }
 330 
 331             @Override
 332             public Node enter(final AccessNode accessNode) {
 333                 if (!baseAlreadyOnStack) {
 334                     load(accessNode.getBase()).convert(Type.OBJECT);
 335                 }
 336                 assert method.peekType().isObject();
 337                 method.dynamicGet(node.getType(), accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction());
 338                 return null;
 339             }
 340 
 341             @Override
 342             public Node enter(final IndexNode indexNode) {
 343                 if (!baseAlreadyOnStack) {
 344                     load(indexNode.getBase());
 345                     load(indexNode.getIndex());
 346                 }
 347                 method.dynamicGetIndex(node.getType(), getCallSiteFlags(), indexNode.isFunction());
 348                 return null;
 349             }
 350 
 351             @Override
 352             public Node enterDefault(final Node otherNode) {
 353                 otherNode.accept(codegen); // generate code for whatever we are looking at.
 354                 method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there)
 355                 return null;
 356             }
 357         });
 358 
 359         return method;
 360     }
 361 
 362     @Override
 363     public Node enter(final AccessNode accessNode) {
 364         if (accessNode.testResolved()) {
 365             return null;
 366         }
 367 
 368         load(accessNode);
 369 
 370         return null;
 371     }
 372 
 373     /**
 374      * Initialize a specific set of vars to undefined. This has to be done at
 375      * the start of each method for local variables that aren't passed as
 376      * parameters.
 377      *
 378      * @param symbols list of symbols.
 379      */
 380     private void initSymbols(final Iterable<Symbol> symbols) {
 381         final LinkedList<Symbol> numbers = new LinkedList<>();
 382         final LinkedList<Symbol> objects = new LinkedList<>();
 383 
 384         for (final Symbol symbol : symbols) {
 385             /*
 386              * The following symbols are guaranteed to be defined and thus safe
 387              * from having unsigned written to them: parameters internals this
 388              *
 389              * Otherwise we must, unless we perform control/escape analysis,
 390              * assign them undefined.
 391              */
 392             final boolean isInternal = symbol.isParam() || symbol.isInternal() || symbol.isThis() || !symbol.canBeUndefined();
 393 
 394             if (symbol.hasSlot() && !isInternal) {
 395                 assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getCurrentFunctionNode();
 396                 if (symbol.getSymbolType().isNumber()) {
 397                     numbers.add(symbol);
 398                 } else if (symbol.getSymbolType().isObject()) {
 399                     objects.add(symbol);
 400                 }
 401             }
 402         }
 403 
 404         initSymbols(numbers, Type.NUMBER);
 405         initSymbols(objects, Type.OBJECT);
 406     }
 407 
 408     private void initSymbols(final LinkedList<Symbol> symbols, final Type type) {
 409         if (symbols.isEmpty()) {
 410             return;
 411         }
 412 
 413         method.loadUndefined(type);
 414         while (!symbols.isEmpty()) {
 415             final Symbol symbol = symbols.removeFirst();
 416             if (!symbols.isEmpty()) {
 417                 method.dup();
 418             }
 419             method.store(symbol);
 420         }
 421     }
 422 
 423     /**
 424      * Create symbol debug information.
 425      *
 426      * @param block block containing symbols.
 427      */
 428     private void symbolInfo(final Block block) {
 429         for (final Symbol symbol : block.getFrame().getSymbols()) {
 430             method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel());
 431         }
 432     }
 433 
 434     @Override
 435     public Node enter(final Block block) {
 436         if (block.testResolved()) {
 437             return null;
 438         }
 439 
 440         method.label(block.getEntryLabel());
 441         initLocals(block);
 442 
 443         return block;
 444     }
 445 
 446     @Override
 447     public Node leave(final Block block) {
 448         method.label(block.getBreakLabel());
 449         symbolInfo(block);
 450 
 451         if (block.needsScope()) {
 452             popBlockScope(block);
 453         }
 454 
 455         return block;
 456     }
 457 
 458     private void popBlockScope(final Block block) {
 459         final Label exitLabel     = new Label("block_exit");
 460         final Label recoveryLabel = new Label("block_catch");
 461         final Label skipLabel     = new Label("skip_catch");
 462 
 463         /* pop scope a la try-finally */
 464         method.loadScope();
 465         method.invoke(ScriptObject.GET_PROTO);
 466         method.storeScope();
 467         method._goto(skipLabel);
 468         method.label(exitLabel);
 469 
 470         method._catch(recoveryLabel);
 471         method.loadScope();
 472         method.invoke(ScriptObject.GET_PROTO);
 473         method.storeScope();
 474         method.athrow();
 475         method.label(skipLabel);
 476         method._try(block.getEntryLabel(), exitLabel, recoveryLabel, Throwable.class);
 477     }
 478 
 479     @Override
 480     public Node enter(final BreakNode breakNode) {
 481         if (breakNode.testResolved()) {
 482             return null;
 483         }
 484 
 485         for (int i = 0; i < breakNode.getScopeNestingLevel(); i++) {
 486             closeWith();
 487         }
 488 
 489         method.splitAwareGoto(breakNode.getTargetLabel());
 490 
 491         return null;
 492     }
 493 
 494     private MethodEmitter loadArgs(final List<Node> args) {
 495         return loadArgs(args, null, false, args.size());
 496     }
 497 
 498     private MethodEmitter loadArgs(final List<Node> args, final String signature, final boolean isVarArg, final int argCount) {
 499         // arg have already been converted to objects here.
 500         if (isVarArg || argCount > LinkerCallSite.ARGLIMIT) {
 501             loadArgsArray(args);
 502             return method;
 503         }
 504 
 505         // pad with undefined if size is too short. argCount is the real number of args
 506         int n = 0;
 507         final Type[] params = signature == null ? null : Type.getMethodArguments(signature);
 508         for (final Node arg : args) {
 509             assert arg != null;
 510             load(arg);
 511             if (n >= argCount) {
 512                 method.pop(); // we had to load the arg for its side effects
 513             } else if (params != null) {
 514                 method.convert(params[n]);
 515             }
 516             n++;
 517         }
 518 
 519         while (n < argCount) {
 520             method.loadUndefined(Type.OBJECT);
 521             n++;
 522         }
 523 
 524         return method;
 525     }
 526 
 527     /**
 528      * Create a new function object, including generating its stub code.
 529      *
 530      * @param functionNode  FunctionNode to utilize.
 531      */
 532     private void newFunctionObject(final FunctionNode functionNode) {
 533          // Turn thisProperties into keys and symbols for the FunctionAnalyzer
 534         final Map<String, Node> thisProperties = functionNode.getThisProperties();
 535 
 536         final List<String> keys    = new ArrayList<>();
 537         final List<Symbol> symbols = new ArrayList<>();
 538 
 539         /* TODO - Predefine known properties.
 540         for (final Entry<String, Node> entry : thisProperties.entrySet()) {
 541             keys.add(entry.getKey());
 542             symbols.add(entry.getValue().getSymbol());
 543         }
 544         */
 545 
 546         new FunctionObjectCreator(this, functionNode, keys, symbols).makeObject(method);
 547     }
 548 
 549     @Override
 550     public Node enter(final CallNode callNode) {
 551         if (callNode.testResolved()) {
 552             return null;
 553         }
 554 
 555         final List<Node>   args            = callNode.getArgs();
 556         final Node         function        = callNode.getFunction();
 557         final FunctionNode currentFunction = getCurrentFunctionNode();
 558         final Block        currentBlock    = getCurrentBlock();
 559 
 560         function.accept(new NodeVisitor(compileUnit, method) {
 561 
 562             private void sharedScopeCall(final IdentNode identNode, final int flags) {
 563                 final Symbol symbol = identNode.getSymbol();
 564                 int    scopeCallFlags = flags;
 565                 method.loadScope();
 566                 if (symbol.isFastScope(currentFunction)) {
 567                     method.load(getScopeProtoDepth(currentBlock, symbol));
 568                     scopeCallFlags |= CALLSITE_FAST_SCOPE;
 569                 } else {
 570                     method.load(-1); // Bypass fast-scope code in shared callsite
 571                 }
 572                 loadArgs(args);
 573                 final Type[] paramTypes = method.getTypesFromStack(args.size());
 574                 final SharedScopeCall scopeCall = getScopeCall(symbol, identNode.getType(), callNode.getType(), paramTypes, scopeCallFlags);
 575                 scopeCall.generateInvoke(method);
 576             }
 577 
 578             private void scopeCall(final IdentNode node, final int flags) {
 579                 load(node);
 580                 method.convert(Type.OBJECT); // foo() makes no sense if foo == 3
 581                 // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
 582                 method.loadNull();
 583                 loadArgs(args);
 584                 method.dynamicCall(callNode.getType(), args.size(), flags);
 585             }
 586 
 587             private void evalCall(final IdentNode node, final int flags) {
 588                 load(node);
 589                 method.convert(Type.OBJECT); // foo() makes no sense if foo == 3
 590 
 591                 final Label not_eval  = new Label("not_eval");
 592                 final Label eval_done = new Label("eval_done");
 593 
 594                 // check if this is the real built-in eval
 595                 method.dup();
 596                 globalIsEval();
 597 
 598                 method.ifeq(not_eval);
 599                 // We don't need ScriptFunction object for 'eval'
 600                 method.pop();
 601 
 602                 method.loadScope(); // Load up self (scope).
 603 
 604                 final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
 605                 // load evaluated code
 606                 load(evalArgs.code);
 607                 method.convert(Type.OBJECT);
 608                 // special/extra 'eval' arguments
 609                 load(evalArgs.evalThis);
 610                 method.load(evalArgs.location);
 611                 method.load(evalArgs.strictMode);
 612                 method.convert(Type.OBJECT);
 613 
 614                 // direct call to Global.directEval
 615                 globalDirectEval();
 616                 method.convert(callNode.getType());
 617                 method._goto(eval_done);
 618 
 619                 method.label(not_eval);
 620                 // This is some scope 'eval' or global eval replaced by user
 621                 // but not the built-in ECMAScript 'eval' function call
 622                 method.loadNull();
 623                 loadArgs(args);
 624                 method.dynamicCall(callNode.getType(), args.size(), flags);
 625 
 626                 method.label(eval_done);
 627             }
 628 
 629             @Override
 630             public Node enter(final IdentNode node) {
 631                 final Symbol symbol = node.getSymbol();
 632 
 633                 if (symbol.isScope()) {
 634                     final int flags = getCallSiteFlags() | CALLSITE_SCOPE;
 635                     final int useCount = symbol.getUseCount();
 636 
 637                     // Threshold for generating shared scope callsite is lower for fast scope symbols because we know
 638                     // we can dial in the correct scope. However, we als need to enable it for non-fast scopes to
 639                     // support huge scripts like mandreel.js.
 640                     if (callNode.isEval()) {
 641                         evalCall(node, flags);
 642                     } else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD
 643                             || (!symbol.isFastScope(currentFunction) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD)
 644                             || callNode.inWithBlock()) {
 645                         scopeCall(node, flags);
 646                     } else {
 647                         sharedScopeCall(node, flags);
 648                     }
 649                     assert method.peekType().equals(callNode.getType());
 650                 } else {
 651                     enterDefault(node);
 652                 }
 653 
 654                 return null;
 655             }
 656 
 657             @Override
 658             public Node enter(final AccessNode node) {
 659                 load(node.getBase());
 660                 method.convert(Type.OBJECT);
 661                 method.dup();
 662                 method.dynamicGet(node.getType(), node.getProperty().getName(), getCallSiteFlags(), true);
 663                 method.swap();
 664                 loadArgs(args);
 665                 method.dynamicCall(callNode.getType(), args.size(), getCallSiteFlags());
 666                 assert method.peekType().equals(callNode.getType());
 667 
 668                 return null;
 669             }
 670 
 671             @Override
 672             public Node enter(final ReferenceNode node) {
 673                 final FunctionNode callee   = node.getReference();
 674                 final boolean      isVarArg = callee.isVarArg();
 675                 final int          argCount = isVarArg ? -1 : callee.getParameters().size();
 676 
 677                 final String signature = new FunctionSignature(true, callee.needsCallee(), callee.getReturnType(), isVarArg ? null : callee.getParameters()).toString();
 678 
 679                 if (callee.isStrictMode()) { // self is undefined
 680                     method.loadUndefined(Type.OBJECT);
 681                 } else { // get global from scope (which is the self)
 682                     globalInstance();
 683                 }
 684 
 685                 if (callee.needsCallee()) { // TODO: always true
 686                     newFunctionObject(callee); // TODO: if callee not needed, function object is used only to pass scope (could be optimized). if neither the scope nor the function object is needed by the callee, we can pass null instead.
 687                 }
 688 
 689                 loadArgs(args, signature, isVarArg, argCount);
 690                 method.invokeStatic(callee.getCompileUnit().getUnitClassName(), callee.getName(), signature);
 691                 assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType();
 692 
 693                 return null;
 694             }
 695 
 696             @Override
 697             public Node enter(final IndexNode node) {
 698                 load(node.getBase());
 699                 method.convert(Type.OBJECT);
 700                 method.dup();
 701                 load(node.getIndex());
 702                 final Type indexType = node.getIndex().getType();
 703                 if (indexType.isObject() || indexType.isBoolean()) {
 704                     method.convert(Type.OBJECT); //TODO
 705                 }
 706                 method.dynamicGetIndex(node.getType(), getCallSiteFlags(), true);
 707                 method.swap();
 708                 loadArgs(args);
 709                 method.dynamicCall(callNode.getType(), args.size(), getCallSiteFlags());
 710                 assert method.peekType().equals(callNode.getType());
 711 
 712                 return null;
 713             }
 714 
 715             @Override
 716             protected Node enterDefault(final Node node) {
 717                 // Load up function.
 718                 load(function);
 719                 method.convert(Type.OBJECT); //TODO, e.g. booleans can be used as functions
 720                 method.loadNull(); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE
 721 
 722                 loadArgs(args);
 723                 method.dynamicCall(callNode.getType(), args.size(), getCallSiteFlags() | CALLSITE_SCOPE);
 724                 assert method.peekType().equals(callNode.getType());
 725 
 726                 return null;
 727             }
 728         });
 729 
 730         method.store(callNode.getSymbol());
 731 
 732         return null;
 733     }
 734 
 735     @Override
 736     public Node enter(final ContinueNode continueNode) {
 737         if (continueNode.testResolved()) {
 738             return null;
 739         }
 740 
 741         for (int i = 0; i < continueNode.getScopeNestingLevel(); i++) {
 742             closeWith();
 743         }
 744 
 745         method.splitAwareGoto(continueNode.getTargetLabel());
 746 
 747         return null;
 748     }
 749 
 750     @Override
 751     public Node enter(final DoWhileNode doWhileNode) {
 752         return enter((WhileNode)doWhileNode);
 753     }
 754 
 755     @Override
 756     public Node enter(final EmptyNode emptyNode) {
 757         return null;
 758     }
 759 
 760     @Override
 761     public Node enter(final ExecuteNode executeNode) {
 762         if (executeNode.testResolved()) {
 763             return null;
 764         }
 765 
 766         final Node expression = executeNode.getExpression();
 767         expression.accept(this);
 768 
 769         return null;
 770     }
 771 
 772     @Override
 773     public Node enter(final ForNode forNode) {
 774         if (forNode.testResolved()) {
 775             return null;
 776         }
 777 
 778         final Node  test   = forNode.getTest();
 779         final Block body   = forNode.getBody();
 780         final Node  modify = forNode.getModify();
 781 
 782         final Label breakLabel    = forNode.getBreakLabel();
 783         final Label continueLabel = forNode.getContinueLabel();
 784         final Label loopLabel     = new Label("loop");
 785 
 786         Node init = forNode.getInit();
 787 
 788         if (forNode.isForIn()) {
 789             final Symbol iter = forNode.getIterator();
 790 
 791             // We have to evaluate the optional initializer expression
 792             // of the iterator variable of the for-in statement.
 793             if (init instanceof VarNode) {
 794                 init.accept(this);
 795                 init = ((VarNode)init).getName();
 796             }
 797 
 798             load(modify);
 799             assert modify.getType().isObject();
 800             method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR);
 801             method.store(iter);
 802             method._goto(continueLabel);
 803             method.label(loopLabel);
 804 
 805             new Store<Node>(init) {
 806                 @Override
 807                 protected void evaluate() {
 808                     method.load(iter);
 809                     method.invoke(interfaceCallNoLookup(Iterator.class, "next", Object.class));
 810                 }
 811             }.store();
 812 
 813             body.accept(this);
 814 
 815             method.label(continueLabel);
 816             method.load(iter);
 817             method.invoke(interfaceCallNoLookup(Iterator.class, "hasNext", boolean.class));
 818             method.ifne(loopLabel);
 819             method.label(breakLabel);
 820         } else {
 821             if (init != null) {
 822                 init.accept(this);
 823             }
 824 
 825             final Label testLabel = new Label("test");
 826 
 827             method._goto(testLabel);
 828             method.label(loopLabel);
 829             body.accept(this);
 830             method.label(continueLabel);
 831 
 832             if (!body.isTerminal() && modify != null) {
 833                 load(modify);
 834             }
 835 
 836             method.label(testLabel);
 837             if (test != null) {
 838                 new BranchOptimizer(this, method).execute(test, loopLabel, true);
 839             } else {
 840                 method._goto(loopLabel);
 841             }
 842 
 843             method.label(breakLabel);
 844         }
 845 
 846         return null;
 847     }
 848 
 849     /**
 850      * Initialize the slots in a frame to undefined.
 851      *
 852      * @param block block with local vars.
 853      */
 854     private void initLocals(final Block block) {
 855         final FunctionNode function       = block.getFunction();
 856         final boolean      isFunctionNode = block == function;
 857 
 858         /*
 859          * Get the symbols from the frame and realign the frame so that all
 860          * slots get correct numbers. The slot numbering is not fixed until
 861          * after initLocals has been run
 862          */
 863         final Frame        frame   = block.getFrame();
 864         final List<Symbol> symbols = frame.getSymbols();
 865 
 866         /* Fix the predefined slots so they have numbers >= 0, like varargs. */
 867         frame.realign();
 868 
 869         /*
 870          * Determine if block needs scope, if not, just do initSymbols for this block.
 871          */
 872         if (block.needsScope()) {
 873             /*
 874              * Determine if function is varargs and consequently variables have to
 875              * be in the scope.
 876              */
 877             final boolean isVarArg    = function.isVarArg();
 878             final boolean varsInScope = function.varsInScope();
 879 
 880             // TODO for LET we can do better: if *block* does not contain any eval/with, we don't need its vars in scope.
 881 
 882             final List<String> nameList = new ArrayList<>();
 883             final List<Symbol> locals   = new ArrayList<>();
 884 
 885             // If there are variable arguments, we need to load them (functions only).
 886             if (isFunctionNode && isVarArg) {
 887                 method.loadVarArgs();
 888                 method.loadCallee();
 889                 method.load(function.getParameters().size());
 890                 globalAllocateArguments();
 891                 method.storeArguments();
 892             }
 893 
 894             // Initalize symbols and values
 895             final List<Symbol> newSymbols = new ArrayList<>();
 896             final List<Symbol> values     = new ArrayList<>();
 897 
 898             for (final Symbol symbol : symbols) {
 899                 if (symbol.isInternal() || symbol.isThis()) {
 900                     continue;
 901                 }
 902 
 903                 if (symbol.isVar() && (varsInScope || symbol.isScope())) {
 904                     nameList.add(symbol.getName());
 905                     newSymbols.add(symbol);
 906                     values.add(null);
 907                     assert symbol.isScope()   : "scope for " + symbol + " should have been set in Lower already " + function.getName();
 908                     assert !symbol.hasSlot()  : "slot for " + symbol + " should have been removed in Lower already" + function.getName();
 909                 } else if (symbol.isVar()) {
 910                     assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
 911                     locals.add(symbol);
 912                 } else if (symbol.isParam() && (varsInScope || isVarArg || symbol.isScope())) {
 913                     nameList.add(symbol.getName());
 914                     newSymbols.add(symbol);
 915                     values.add(isVarArg ? null : symbol);
 916                     assert symbol.isScope()   : "scope for " + symbol + " should have been set in Lower already " + function.getName() + " varsInScope="+varsInScope+" isVarArg="+isVarArg+" symbol.isScope()=" + symbol.isScope();
 917                     assert !(isVarArg && symbol.hasSlot())  : "slot for " + symbol + " should have been removed in Lower already " + function.getName();
 918                 }
 919             }
 920 
 921             /* Correct slot numbering again */
 922             frame.realign();
 923 
 924             // we may have locals that need to be initialized
 925             initSymbols(locals);
 926 
 927             if (isFunctionNode) {
 928                 initScope();
 929             }
 930 
 931             /*
 932              * Create a new object based on the symbols and values, generate
 933              * bootstrap code for object
 934              */
 935             final FieldObjectCreator<Symbol> foc = new FieldObjectCreator<Symbol>(this, nameList, newSymbols, values, true, isVarArg) {
 936                 @Override
 937                 protected Type getValueType(final Symbol value) {
 938                     return value.getSymbolType();
 939                 }
 940 
 941                 @Override
 942                 protected void loadValue(final Symbol value) {
 943                     method.load(value);
 944                 }
 945             };
 946             foc.makeObject(method);
 947 
 948             // runScript(): merge scope into global
 949             if (isFunctionNode && function.isScript()) {
 950                 method.invoke(ScriptRuntime.MERGE_SCOPE);
 951             }
 952 
 953             method.storeScope();
 954         } else {
 955             initSymbols(symbols);
 956 
 957             if (isFunctionNode) {
 958                 initScope();
 959             }
 960         }
 961 
 962         // Debugging: print symbols? @see --print-symbols flag
 963         printSymbols(block, (isFunctionNode ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName()));
 964     }
 965 
 966     private void initScope() {
 967         method.loadCallee();
 968         method.invoke(ScriptFunction.GET_SCOPE);
 969         method.storeScope();
 970     }
 971 
 972     @Override
 973     public Node enter(final FunctionNode functionNode) {
 974         if (functionNode.testResolved()) {
 975             return null;
 976         }
 977 
 978         compileUnit = functionNode.getCompileUnit();
 979         assert compileUnit != null;
 980 
 981         method = compileUnit.getClassEmitter().method(functionNode);
 982         functionNode.setMethodEmitter(method);
 983         // Mark end for variable tables.
 984         method.begin();
 985         method.label(functionNode.getEntryLabel());
 986 
 987         initLocals(functionNode);
 988 
 989         return functionNode;
 990     }
 991 
 992     @Override
 993     public Node leave(final FunctionNode functionNode) {
 994         // Mark end for variable tables.
 995         method.label(functionNode.getBreakLabel());
 996 
 997         if (!functionNode.needsScope()) {
 998             method.markerVariable(LEAF.tag(), functionNode.getEntryLabel(), functionNode.getBreakLabel());
 999         }
1000 
1001         symbolInfo(functionNode);
1002         try {
1003             method.end(); // wrap up this method
1004         } catch (final Throwable t) {
1005             Context.printStackTrace(t);
1006             final VerifyError e = new VerifyError("Code generation bug in \"" + functionNode.getName() + "\": likely stack misaligned: " + t + " " + functionNode.getSource().getName());
1007             e.initCause(t);
1008             throw e;
1009         }
1010 
1011         return functionNode;
1012     }
1013 
1014     @Override
1015     public Node enter(final IdentNode identNode) {
1016         return null;
1017     }
1018 
1019     @Override
1020     public Node enter(final IfNode ifNode) {
1021         if (ifNode.testResolved()) {
1022             return null;
1023         }
1024 
1025         final Node  test = ifNode.getTest();
1026         final Block pass = ifNode.getPass();
1027         final Block fail = ifNode.getFail();
1028 
1029         final Label failLabel  = new Label("if_fail");
1030         final Label afterLabel = fail == null ? failLabel : new Label("if_done");
1031 
1032         new BranchOptimizer(this, method).execute(test, failLabel, false);
1033 
1034         boolean passTerminal = false;
1035         boolean failTerminal = false;
1036 
1037         pass.accept(this);
1038         if (!pass.hasTerminalFlags()) {
1039             method._goto(afterLabel); //don't fallthru to fail block
1040         } else {
1041             passTerminal = pass.isTerminal();
1042         }
1043 
1044         if (fail != null) {
1045             method.label(failLabel);
1046             fail.accept(this);
1047             failTerminal = fail.isTerminal();
1048         }
1049 
1050         //if if terminates, put the after label there
1051         if (!passTerminal || !failTerminal) {
1052             method.label(afterLabel);
1053         }
1054 
1055         return null;
1056     }
1057 
1058     @Override
1059     public Node enter(final IndexNode indexNode) {
1060         if (indexNode.testResolved()) {
1061             return null;
1062         }
1063 
1064         load(indexNode);
1065 
1066         return null;
1067     }
1068 
1069     @Override
1070     public Node enter(final LineNumberNode lineNumberNode) {
1071         if (lineNumberNode.testResolved()) {
1072             return null;
1073         }
1074 
1075         final Label label = new Label("line:" + lineNumberNode.getLineNumber() + " (" + getCurrentFunctionNode().getName() + ")");
1076         method.label(label);
1077         method.lineNumber(lineNumberNode.getLineNumber(), label);
1078 
1079         return null;
1080     }
1081 
1082     /**
1083      * Load a list of nodes as an array of a specific type
1084      * The array will contain the visited nodes.
1085      *
1086      * @param arrayLiteralNode the array of contents
1087      * @param arrayType        the type of the array, e.g. ARRAY_NUMBER or ARRAY_OBJECT
1088      *
1089      * @return the method generator that was used
1090      */
1091     private MethodEmitter loadArray(final ArrayLiteralNode arrayLiteralNode, final ArrayType arrayType) {
1092         assert arrayType == Type.INT_ARRAY || arrayType == Type.NUMBER_ARRAY || arrayType == Type.OBJECT_ARRAY;
1093 
1094         final Node[]          nodes    = arrayLiteralNode.getValue();
1095         final Object          presets  = arrayLiteralNode.getPresets();
1096         final int[]           postsets = arrayLiteralNode.getPostsets();
1097         final Class<?>        type     = arrayType.getTypeClass();
1098         final List<ArrayUnit> units    = arrayLiteralNode.getUnits();
1099 
1100         loadConstant(presets);
1101 
1102         final Type elementType = arrayType.getElementType();
1103 
1104         if (units != null) {
1105             final CompileUnit   savedCompileUnit = compileUnit;
1106             final MethodEmitter savedMethod      = method;
1107 
1108             try {
1109                 for (final ArrayUnit unit : units) {
1110                     compileUnit = unit.getCompileUnit();
1111 
1112                     final String className = compileUnit.getUnitClassName();
1113                     final String name      = compiler.uniqueName(SPLIT_PREFIX.tag());
1114                     final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
1115 
1116                     method = compileUnit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
1117                     method.setFunctionNode(getCurrentFunctionNode());
1118                     method.begin();
1119 
1120                     fixScopeSlot();
1121 
1122                     method.load(arrayType, SPLIT_ARRAY_ARG.slot());
1123 
1124                     for (int i = unit.getLo(); i < unit.getHi(); i++) {
1125                         storeElement(nodes, elementType, postsets[i]);
1126                     }
1127 
1128                     method._return();
1129                     method.end();
1130 
1131                     savedMethod.loadThis();
1132                     savedMethod.swap();
1133                     savedMethod.loadCallee();
1134                     savedMethod.swap();
1135                     savedMethod.loadScope();
1136                     savedMethod.swap();
1137                     savedMethod.invokeStatic(className, name, signature);
1138                 }
1139             } finally {
1140                 compileUnit = savedCompileUnit;
1141                 method      = savedMethod;
1142             }
1143 
1144             return method;
1145         }
1146 
1147         for (final int postset : postsets) {
1148             storeElement(nodes, elementType, postset);
1149         }
1150 
1151         return method;
1152     }
1153 
1154     private void storeElement(final Node[] nodes, final Type elementType, final int index) {
1155         method.dup();
1156         method.load(index);
1157 
1158         final Node element = nodes[index];
1159 
1160         if (element == null) {
1161             method.loadEmpty(elementType);
1162         } else {
1163             assert elementType.isEquivalentTo(element.getType()) : "array element type doesn't match array type";
1164             load(element);
1165         }
1166 
1167         method.arraystore();
1168     }
1169 
1170     private MethodEmitter loadArgsArray(final List<Node> args) {
1171         final Object[] array = new Object[args.size()];
1172         loadConstant(array);
1173 
1174         for (int i = 0; i < args.size(); i++) {
1175             method.dup();
1176             method.load(i);
1177             load(args.get(i)).convert(Type.OBJECT); //has to be upcast to object or we fail
1178             method.arraystore();
1179         }
1180 
1181         return method;
1182     }
1183 
1184     /**
1185      * Load a constant from the constant array. This is only public to be callable from the objects
1186      * subpackage. Do not call directly.
1187      *
1188      * @param string string to load
1189      */
1190     public void loadConstant(final String string) {
1191         final String       unitClassName = compileUnit.getUnitClassName();
1192         final ClassEmitter classEmitter  = compileUnit.getClassEmitter();
1193         final int          index         = compiler.getConstantData().add(string);
1194 
1195         method.load(index);
1196         method.invokeStatic(unitClassName, GET_STRING.tag(), methodDescriptor(String.class, int.class));
1197         classEmitter.needGetConstantMethod(String.class);
1198     }
1199 
1200     /**
1201      * Load a constant from the constant array. This is only public to be callable from the objects
1202      * subpackage. Do not call directly.
1203      *
1204      * @param object object to load
1205      */
1206     public void loadConstant(final Object object) {
1207         final String       unitClassName = compileUnit.getUnitClassName();
1208         final ClassEmitter classEmitter  = compileUnit.getClassEmitter();
1209         final int          index         = compiler.getConstantData().add(object);
1210         final Class<?>     cls           = object.getClass();
1211 
1212         if (cls == PropertyMap.class) {
1213             method.load(index);
1214             method.invokeStatic(unitClassName, GET_MAP.tag(), methodDescriptor(PropertyMap.class, int.class));
1215             classEmitter.needGetConstantMethod(PropertyMap.class);
1216         } else if (cls.isArray()) {
1217             method.load(index);
1218             final String methodName = ClassEmitter.getArrayMethodName(cls);
1219             method.invokeStatic(unitClassName, methodName, methodDescriptor(cls, int.class));
1220             classEmitter.needGetConstantMethod(cls);
1221         } else {
1222             method.loadConstants(unitClassName).load(index).arrayload();
1223             if (cls != Object.class) {
1224                 method.checkcast(cls);
1225             }
1226         }
1227     }
1228 
1229     // literal values
1230     private MethodEmitter load(final LiteralNode<?> node) {
1231         final Object value = node.getValue();
1232 
1233         if (value == null) {
1234             method.loadNull();
1235         } else if (value instanceof Undefined) {
1236             method.loadUndefined(Type.OBJECT);
1237         } else if (value instanceof String) {
1238             final String string = (String)value;
1239 
1240             if (string.length() > (MethodEmitter.LARGE_STRING_THRESHOLD / 3)) { // 3 == max bytes per encoded char
1241                 loadConstant(string);
1242             } else {
1243                 method.load(string);
1244             }
1245         } else if (value instanceof RegexToken) {
1246             loadRegex((RegexToken)value);
1247         } else if (value instanceof Boolean) {
1248             method.load((Boolean)value);
1249         } else if (value instanceof Integer) {
1250             method.load((Integer)value);
1251         } else if (value instanceof Long) {
1252             method.load((Long)value);
1253         } else if (value instanceof Double) {
1254             method.load((Double)value);
1255         } else if (node instanceof ArrayLiteralNode) {
1256             final ArrayType type = (ArrayType)node.getType();
1257             loadArray((ArrayLiteralNode)node, type);
1258             globalAllocateArray(type);
1259         } else {
1260             assert false : "Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value;
1261         }
1262 
1263         return method;
1264     }
1265 
1266     private MethodEmitter loadRegexToken(final RegexToken value) {
1267         method.load(value.getExpression());
1268         method.load(value.getOptions());
1269         return globalNewRegExp();
1270     }
1271 
1272     private MethodEmitter loadRegex(final RegexToken regexToken) {
1273         if (regexFieldCount > MAX_REGEX_FIELDS) {
1274             return loadRegexToken(regexToken);
1275         }
1276         // emit field
1277         final String       regexName    = compiler.uniqueName(REGEX_PREFIX.tag());
1278         final ClassEmitter classEmitter = compileUnit.getClassEmitter();
1279 
1280         classEmitter.field(EnumSet.of(PRIVATE, STATIC), regexName, Object.class);
1281         regexFieldCount++;
1282 
1283         // get field, if null create new regex, finally clone regex object
1284         method.getStatic(compileUnit.getUnitClassName(), regexName, typeDescriptor(Object.class));
1285         method.dup();
1286         final Label cachedLabel = new Label("cached");
1287         method.ifnonnull(cachedLabel);
1288 
1289         method.pop();
1290         loadRegexToken(regexToken);
1291         method.dup();
1292         method.putStatic(compileUnit.getUnitClassName(), regexName, typeDescriptor(Object.class));
1293 
1294         method.label(cachedLabel);
1295         globalRegExpCopy();
1296 
1297         return method;
1298     }
1299 
1300     @SuppressWarnings("rawtypes")
1301     @Override
1302     public Node enter(final LiteralNode literalNode) {
1303         load(literalNode).store(literalNode.getSymbol());
1304         return null;
1305     }
1306 
1307     @Override
1308     public Node enter(final ObjectNode objectNode) {
1309         if (objectNode.testResolved()) {
1310             return null;
1311         }
1312 
1313         final List<Node> elements = objectNode.getElements();
1314         final int        size     = elements.size();
1315 
1316         final List<String> keys    = new ArrayList<>();
1317         final List<Symbol> symbols = new ArrayList<>();
1318         final List<Node>   values  = new ArrayList<>();
1319 
1320         boolean hasGettersSetters = false;
1321 
1322         for (int i = 0; i < size; i++) {
1323             final PropertyNode propertyNode = (PropertyNode)elements.get(i);
1324             final Node         value        = propertyNode.getValue();
1325             final String       key          = propertyNode.getKeyName();
1326             final Symbol       symbol       = value == null ? null : propertyNode.getSymbol();
1327 
1328             if (value == null) {
1329                 hasGettersSetters = true;
1330             }
1331 
1332             keys.add(key);
1333             symbols.add(symbol);
1334             values.add(value);
1335         }
1336 
1337         new FieldObjectCreator<Node>(this, keys, symbols, values) {
1338             @Override
1339             protected Type getValueType(final Node node) {
1340                 return node.getType();
1341             }
1342 
1343             @Override
1344             protected void loadValue(final Node node) {
1345                 load(node);
1346             }
1347 
1348             /**
1349              * Ensure that the properties start out as object types so that
1350              * we can do putfield initializations instead of dynamicSetIndex
1351              * which would be the case to determine initial property type
1352              * otherwise.
1353              *
1354              * Use case, it's very expensive to do a million var x = {a:obj, b:obj}
1355              * just to have to invalidate them immediately on initialization
1356              *
1357              * see NASHORN-594
1358              */
1359             @Override
1360             protected MapCreator newMapCreator(final Class<?> fieldObjectClass) {
1361                 return new ObjectMapCreator(fieldObjectClass, keys, symbols);
1362             }
1363 
1364         }.makeObject(method);
1365 
1366         method.dup();
1367         globalObjectPrototype();
1368         method.invoke(ScriptObject.SET_PROTO);
1369 
1370         if (!hasGettersSetters) {
1371             method.store(objectNode.getSymbol());
1372             return null;
1373         }
1374 
1375         for (final Node element : elements) {
1376             final PropertyNode  propertyNode = (PropertyNode)element;
1377             final Object        key          = propertyNode.getKey();
1378             final ReferenceNode getter       = (ReferenceNode)propertyNode.getGetter();
1379             final ReferenceNode setter       = (ReferenceNode)propertyNode.getSetter();
1380 
1381             if (getter == null && setter == null) {
1382                 continue;
1383             }
1384 
1385             method.dup().loadKey(key);
1386 
1387             if (getter == null) {
1388                 method.loadNull();
1389             } else {
1390                 getter.accept(this);
1391             }
1392 
1393             if (setter == null) {
1394                 method.loadNull();
1395             } else {
1396                 setter.accept(this);
1397             }
1398 
1399             method.invoke(ScriptObject.SET_USER_ACCESSORS);
1400         }
1401 
1402         method.store(objectNode.getSymbol());
1403 
1404         return null;
1405     }
1406 
1407     @Override
1408     public Node enter(final ReferenceNode referenceNode) {
1409         if (referenceNode.testResolved()) {
1410             return null;
1411         }
1412 
1413         newFunctionObject(referenceNode.getReference());
1414 
1415         return null;
1416     }
1417 
1418     @Override
1419     public Node enter(final ReturnNode returnNode) {
1420         if (returnNode.testResolved()) {
1421             return null;
1422         }
1423 
1424         // Set the split return flag in the scope if this is a split method fragment.
1425         if (method.getSplitNode() != null) {
1426             assert method.getSplitNode().hasReturn() : "unexpected return in split node";
1427 
1428             method.loadScope();
1429             method.checkcast(Scope.class);
1430             method.load(0);
1431             method.invoke(Scope.SET_SPLIT_STATE);
1432         }
1433 
1434         final Node expression = returnNode.getExpression();
1435         if (expression != null) {
1436             load(expression);
1437         } else {
1438             method.loadUndefined(getCurrentFunctionNode().getReturnType());
1439         }
1440 
1441         method._return(getCurrentFunctionNode().getReturnType());
1442 
1443         return null;
1444     }
1445 
1446     private static boolean isNullLiteral(final Node node) {
1447         return node instanceof LiteralNode<?> && ((LiteralNode<?>) node).isNull();
1448     }
1449 
1450     private boolean nullCheck(final RuntimeNode runtimeNode, final List<Node> args, final String signature) {
1451         final Request request = runtimeNode.getRequest();
1452 
1453         if (!Request.isEQ(request) && !Request.isNE(request)) {
1454             return false;
1455         }
1456 
1457         assert args.size() == 2 : "EQ or NE or TYPEOF need two args";
1458 
1459         Node lhs = args.get(0);
1460         Node rhs = args.get(1);
1461 
1462         if (isNullLiteral(lhs)) {
1463             final Node tmp = lhs;
1464             lhs = rhs;
1465             rhs = tmp;
1466         }
1467 
1468         if (isNullLiteral(rhs)) {
1469             final Label trueLabel  = new Label("trueLabel");
1470             final Label falseLabel = new Label("falseLabel");
1471             final Label endLabel   = new Label("end");
1472 
1473             load(lhs);
1474             method.dup();
1475             if (Request.isEQ(request)) {
1476                 method.ifnull(trueLabel);
1477             } else if (Request.isNE(request)) {
1478                 method.ifnonnull(trueLabel);
1479             } else {
1480                 assert false : "Invalid request " + request;
1481             }
1482 
1483             method.label(falseLabel);
1484             load(rhs);
1485             method.invokeStatic(CompilerConstants.className(ScriptRuntime.class), request.toString(), signature);
1486             method._goto(endLabel);
1487 
1488             method.label(trueLabel);
1489             // if NE (not strict) this can be "undefined != null" which is supposed to be false
1490             if (request == Request.NE) {
1491                 method.loadUndefined(Type.OBJECT);
1492                 final Label isUndefined = new Label("isUndefined");
1493                 final Label afterUndefinedCheck = new Label("afterUndefinedCheck");
1494                 method.if_acmpeq(isUndefined);
1495                 // not undefined
1496                 method.load(true);
1497                 method._goto(afterUndefinedCheck);
1498                 method.label(isUndefined);
1499                 method.load(false);
1500                 method.label(afterUndefinedCheck);
1501             } else {
1502                 method.pop();
1503                 method.load(true);
1504             }
1505             method.label(endLabel);
1506             method.convert(runtimeNode.getType());
1507             method.store(runtimeNode.getSymbol());
1508 
1509             return true;
1510         }
1511 
1512         return false;
1513     }
1514 
1515     private boolean specializationCheck(final RuntimeNode.Request request, final Node node, final List<Node> args) {
1516         if (!request.canSpecialize()) {
1517             return false;
1518         }
1519 
1520         assert args.size() == 2;
1521         final Node lhs = args.get(0);
1522         final Node rhs = args.get(1);
1523 
1524         final Type returnType = node.getType();
1525         load(lhs);
1526         load(rhs);
1527 
1528         Request finalRequest = request;
1529 
1530         final Request reverse = Request.reverse(request);
1531         if (method.peekType().isObject() && reverse != null) {
1532             if (!method.peekType(1).isObject()) {
1533                 method.swap();
1534                 finalRequest = reverse;
1535             }
1536         }
1537 
1538         method.dynamicRuntimeCall(
1539                 new SpecializedRuntimeNode(
1540                     finalRequest,
1541                     new Type[] {
1542                         method.peekType(1),
1543                         method.peekType()
1544                     },
1545                     returnType).getInitialName(),
1546                 returnType,
1547                 finalRequest);
1548 
1549         method.convert(node.getType());
1550         method.store(node.getSymbol());
1551 
1552         return true;
1553     }
1554 
1555     @Override
1556     public Node enter(final RuntimeNode runtimeNode) {
1557         if (runtimeNode.testResolved()) {
1558             return null;
1559         }
1560 
1561         /*
1562          * First check if this should be something other than a runtime node
1563          * AccessSpecializer might have changed the type
1564          */
1565         if (runtimeNode.isPrimitive()) {
1566 
1567             final Node lhs = runtimeNode.getArgs().get(0);
1568             Node rhs = null;
1569 
1570             if (runtimeNode.getArgs().size() > 1) {
1571                 rhs = runtimeNode.getArgs().get(1);
1572             }
1573 
1574             final Type   type   = runtimeNode.getType();
1575             final Symbol symbol = runtimeNode.getSymbol();
1576 
1577             switch (runtimeNode.getRequest()) {
1578             case EQ:
1579             case EQ_STRICT:
1580                 return enterCmp(lhs, rhs, Condition.EQ, type, symbol);
1581             case NE:
1582             case NE_STRICT:
1583                 return enterCmp(lhs, rhs, Condition.NE, type, symbol);
1584             case LE:
1585                 return enterCmp(lhs, rhs, Condition.LE, type, symbol);
1586             case LT:
1587                 return enterCmp(lhs, rhs, Condition.LT, type, symbol);
1588             case GE:
1589                 return enterCmp(lhs, rhs, Condition.GE, type, symbol);
1590             case GT:
1591                 return enterCmp(lhs, rhs, Condition.GT, type, symbol);
1592             case ADD:
1593                 return enterNumericAdd(lhs, rhs, type, symbol);
1594             default:
1595                 // it's ok to send this one on with only primitive arguments, maybe INSTANCEOF(true, true) or similar
1596                 // assert false : runtimeNode + " has all primitive arguments. This is an inconsistent state";
1597                 break;
1598             }
1599         }
1600 
1601         // Get the request arguments.
1602         final List<Node> args = runtimeNode.getArgs();
1603 
1604         if (nullCheck(runtimeNode, args, new FunctionSignature(false, runtimeNode.getType(), args).toString())) {
1605             return null;
1606         }
1607 
1608         if (specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) {
1609             return null;
1610         }
1611 
1612         for (final Node arg : runtimeNode.getArgs()) {
1613             load(arg).convert(Type.OBJECT); //TODO this should not be necessary below Lower
1614         }
1615 
1616         method.invokeStatic(
1617             CompilerConstants.className(ScriptRuntime.class),
1618             runtimeNode.getRequest().toString(),
1619             new FunctionSignature(
1620                 false,
1621                 runtimeNode.getType(),
1622                 runtimeNode.getArgs().size()).toString());
1623         method.convert(runtimeNode.getType());
1624         method.store(runtimeNode.getSymbol());
1625 
1626         return null;
1627     }
1628 
1629     @Override
1630     public Node enter(final SplitNode splitNode) {
1631         if (splitNode.testResolved()) {
1632             return null;
1633         }
1634 
1635         final CompileUnit splitCompileUnit = splitNode.getCompileUnit();
1636 
1637         final FunctionNode fn   = getCurrentFunctionNode();
1638         final String className  = splitCompileUnit.getUnitClassName();
1639         final String name       = splitNode.getName();
1640 
1641         final Class<?>   rtype  = fn.getReturnType().getTypeClass();
1642         final Class<?>[] ptypes = fn.isVarArg() ?
1643                 new Class<?>[] {Object.class, ScriptFunction.class, ScriptObject.class, Object.class} :
1644                 new Class<?>[] {Object.class, ScriptFunction.class, ScriptObject.class};
1645 
1646         setCurrentCompileUnit(splitCompileUnit);
1647         splitNode.setCompileUnit(splitCompileUnit);
1648 
1649         final Call splitCall = staticCallNoLookup(
1650             className,
1651             name,
1652             methodDescriptor(rtype, ptypes));
1653 
1654         setCurrentMethodEmitter(
1655             splitCompileUnit.getClassEmitter().method(
1656                 EnumSet.of(Flag.PUBLIC, Flag.STATIC),
1657                 name,
1658                 rtype,
1659                 ptypes));
1660 
1661         method.setFunctionNode(fn);
1662         method.setSplitNode(splitNode);
1663         splitNode.setMethodEmitter(method);
1664 
1665         final MethodEmitter caller = splitNode.getCaller();
1666         caller.loadThis();
1667         caller.loadCallee();
1668         caller.loadScope();
1669         if (fn.isVarArg()) {
1670             caller.loadArguments();
1671         }
1672         caller.invoke(splitCall);
1673         caller.storeResult();
1674 
1675         method.begin();
1676 
1677         method.loadUndefined(fn.getReturnType());
1678         method.storeResult();
1679 
1680         fixScopeSlot();
1681 
1682         return splitNode;
1683     }
1684 
1685     private void fixScopeSlot() {
1686         if (getCurrentFunctionNode().getScopeNode().getSymbol().getSlot() != SCOPE.slot()) {
1687             // TODO hack to move the scope to the expected slot (that's needed because split methods reuse the same slots as the root method)
1688             method.load(Type.typeFor(ScriptObject.class), SCOPE.slot());
1689             method.storeScope();
1690         }
1691     }
1692 
1693     @Override
1694     public Node leave(final SplitNode splitNode) {
1695         try {
1696             // Wrap up this method.
1697             method.loadResult();
1698             method._return(getCurrentFunctionNode().getReturnType());
1699             method.end();
1700         } catch (final Throwable t) {
1701             Context.printStackTrace(t);
1702             final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + compiler.getSource().getName());
1703             e.initCause(t);
1704             throw e;
1705         }
1706 
1707         // Handle return from split method if there was one.
1708         final MethodEmitter caller      = splitNode.getCaller();
1709         final List<Label>   targets     = splitNode.getExternalTargets();
1710         final int           targetCount = targets.size();
1711 
1712         if (splitNode.hasReturn() || targetCount > 0) {
1713 
1714             caller.loadScope();
1715             caller.checkcast(Scope.class);
1716             caller.invoke(Scope.GET_SPLIT_STATE);
1717 
1718             // Split state is -1 for no split state, 0 for return, 1..n+1 for break/continue
1719             final Label   breakLabel = new Label("no_split_state");
1720             final int     low        = splitNode.hasReturn() ? 0 : 1;
1721             final int     labelCount = targetCount + 1 - low;
1722             final Label[] labels     = new Label[labelCount];
1723 
1724             for (int i = 0; i < labelCount; i++) {
1725                 labels[i] = new Label("split_state_" + i);
1726             }
1727 
1728             caller.tableSwitch(low, targetCount, breakLabel, labels);
1729             for (int i = low; i <= targetCount; i++) {
1730                 caller.label(labels[i - low]);
1731                 if (i == 0) {
1732                     caller.loadResult();
1733                     caller._return(getCurrentFunctionNode().getReturnType());
1734                 } else {
1735                     // Clear split state.
1736                     caller.loadScope();
1737                     caller.checkcast(Scope.class);
1738                     caller.load(-1);
1739                     caller.invoke(Scope.SET_SPLIT_STATE);
1740                     caller.splitAwareGoto(targets.get(i - 1));
1741                 }
1742             }
1743 
1744             caller.label(breakLabel);
1745         }
1746 
1747         return splitNode;
1748     }
1749 
1750     @Override
1751     public Node enter(final SwitchNode switchNode) {
1752         if (switchNode.testResolved()) {
1753             return null;
1754         }
1755 
1756         final Node           expression  = switchNode.getExpression();
1757         final Symbol         tag         = switchNode.getTag();
1758         final boolean        allInteger  = tag.getSymbolType().isInteger();
1759         final List<CaseNode> cases       = switchNode.getCases();
1760         final CaseNode       defaultCase = switchNode.getDefaultCase();
1761         final Label          breakLabel  = switchNode.getBreakLabel();
1762 
1763         Label defaultLabel = breakLabel;
1764         boolean hasDefault = false;
1765 
1766         if (defaultCase != null) {
1767             defaultLabel = defaultCase.getEntry();
1768             hasDefault = true;
1769         }
1770 
1771         if (cases.isEmpty()) {
1772             method.label(breakLabel);
1773             return null;
1774         }
1775 
1776         if (allInteger) {
1777             // Tree for sorting values.
1778             final TreeMap<Integer, Label> tree = new TreeMap<>();
1779 
1780             // Build up sorted tree.
1781             for (final CaseNode caseNode : cases) {
1782                 final Node test = caseNode.getTest();
1783 
1784                 if (test != null) {
1785                     final Integer value = (Integer)((LiteralNode<?>) test).getValue();
1786                     final Label   entry = caseNode.getEntry();
1787 
1788                     // Take first duplicate.
1789                     if (!(tree.containsKey(value))) {
1790                         tree.put(value, entry);
1791                     }
1792                 }
1793             }
1794 
1795             // Copy values and labels to arrays.
1796             final int       size   = tree.size();
1797             final Integer[] values = tree.keySet().toArray(new Integer[size]);
1798             final Label[]   labels = tree.values().toArray(new Label[size]);
1799 
1800             // Discern low, high and range.
1801             final int lo    = values[0];
1802             final int hi    = values[size - 1];
1803             final int range = hi - lo + 1;
1804 
1805             // Find an unused value for default.
1806             int deflt = Integer.MIN_VALUE;
1807             for (final int value : values) {
1808                 if (deflt == value) {
1809                     deflt++;
1810                 } else if (deflt < value) {
1811                     break;
1812                 }
1813             }
1814 
1815             // Load switch expression.
1816             load(expression);
1817             final Type type = expression.getType();
1818 
1819             // If expression not int see if we can convert, if not use deflt to trigger default.
1820             if (!type.isInteger()) {
1821                 method.load(deflt);
1822                 method.invoke(staticCallNoLookup(ScriptRuntime.class, "switchTagAsInt", int.class, type.getTypeClass(), int.class));
1823             }
1824 
1825             // If reasonable size and not too sparse (80%), use table otherwise use lookup.
1826             if (range > 0 && range < 4096 && range < (size * 5 / 4)) {
1827                 final Label[] table = new Label[range];
1828                 Arrays.fill(table, defaultLabel);
1829 
1830                 for (int i = 0; i < size; i++) {
1831                     final int value = values[i];
1832                     table[value - lo] = labels[i];
1833                 }
1834 
1835                 method.tableSwitch(lo, hi, defaultLabel, table);
1836             } else {
1837                 final int[] ints = new int[size];
1838 
1839                 for (int i = 0; i < size; i++) {
1840                     ints[i] = values[i];
1841                 }
1842 
1843                 method.lookupSwitch(defaultLabel, ints, labels);
1844             }
1845         } else {
1846             load(expression);
1847 
1848             if (expression.getType().isInteger()) {
1849                 method.convert(Type.NUMBER).dup();
1850                 method.store(tag);
1851                 method.conditionalJump(MethodEmitter.Condition.NE, true, defaultLabel);
1852             } else {
1853                 method.store(tag);
1854             }
1855 
1856             for (final CaseNode caseNode : cases) {
1857                 final Node test = caseNode.getTest();
1858 
1859                 if (test != null) {
1860                     method.load(tag);
1861                     load(test);
1862                     method.invoke(ScriptRuntime.EQ_STRICT);
1863                     method.ifne(caseNode.getEntry());
1864                 }
1865             }
1866 
1867             method._goto(hasDefault ? defaultLabel : breakLabel);
1868         }
1869 
1870         for (final CaseNode caseNode : cases) {
1871             method.label(caseNode.getEntry());
1872             caseNode.getBody().accept(this);
1873         }
1874 
1875         if (!switchNode.isTerminal()) {
1876             method.label(breakLabel);
1877         }
1878 
1879         return null;
1880     }
1881 
1882     @Override
1883     public Node enter(final ThrowNode throwNode) {
1884         if (throwNode.testResolved()) {
1885             return null;
1886         }
1887 
1888         method._new(ECMAException.class).dup();
1889 
1890         final Node   expression = throwNode.getExpression();
1891         final Source source     = compiler.getSource();
1892         final int    position   = throwNode.position();
1893         final int    line       = source.getLine(position);
1894         final int    column     = source.getColumn(position);
1895 
1896         load(expression);
1897         assert expression.getType().isObject();
1898 
1899         method.load(source.getName());
1900         method.load(line);
1901         method.load(column);
1902         method.invoke(ECMAException.THROW_INIT);
1903 
1904         method.athrow();
1905 
1906         return null;
1907     }
1908 
1909     @Override
1910     public Node enter(final TryNode tryNode) {
1911         if (tryNode.testResolved()) {
1912             return null;
1913         }
1914 
1915         final Block       body        = tryNode.getBody();
1916         final List<Block> catchBlocks = tryNode.getCatchBlocks();
1917         final Symbol      symbol      = tryNode.getException();
1918         final Label       entry       = new Label("try");
1919         final Label       recovery    = new Label("catch");
1920         final Label       exit        = tryNode.getExit();
1921         final Label       skip        = new Label("skip");
1922 
1923         method.label(entry);
1924 
1925         body.accept(this);
1926 
1927         if (!body.hasTerminalFlags()) {
1928             method._goto(skip);
1929         }
1930 
1931         method.label(exit);
1932 
1933         method._catch(recovery);
1934         method.store(symbol);
1935 
1936         for (int i = 0; i < catchBlocks.size(); i++) {
1937             final Block saveBlock = getCurrentBlock();
1938             final Block catchBlock = catchBlocks.get(i);
1939 
1940             setCurrentBlock(catchBlock);
1941 
1942             try {
1943                 enter(catchBlock);
1944 
1945                 final CatchNode catchNode          = (CatchNode)catchBlocks.get(i).getStatements().get(0);
1946                 final IdentNode exception          = catchNode.getException();
1947                 final Node      exceptionCondition = catchNode.getExceptionCondition();
1948                 final Block     catchBody          = catchNode.getBody();
1949 
1950                 if (catchNode.isSyntheticRethrow()) {
1951                     // Generate catch body (inlined finally) and rethrow exception
1952                     catchBody.accept(this);
1953                     method.load(symbol).athrow();
1954                     continue;
1955                 }
1956 
1957                 new Store<IdentNode>(exception) {
1958                     @Override
1959                     protected void evaluate() {
1960                         /*
1961                          * If caught object is an instance of ECMAException, then
1962                          * bind obj.thrown to the script catch var. Or else bind the
1963                          * caught object itself to the script catch var.
1964                          */
1965                         final Label notEcmaException = new Label("no_ecma_exception");
1966 
1967                         method.load(symbol).dup()._instanceof(ECMAException.class).ifeq(notEcmaException);
1968                         method.checkcast(ECMAException.class); //TODO is this necessary?
1969                         method.getField(ECMAException.THROWN);
1970                         method.label(notEcmaException);
1971                     }
1972                 }.store();
1973 
1974                 final Label next;
1975 
1976                 if (exceptionCondition != null) {
1977                     next = new Label("next");
1978                     load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next);
1979                 } else {
1980                     next = null;
1981                 }
1982 
1983                 catchBody.accept(this);
1984 
1985                 if (i + 1 != catchBlocks.size() && !catchBody.hasTerminalFlags()) {
1986                     method._goto(skip);
1987                 }
1988 
1989                 if (next != null) {
1990                     if (i + 1 == catchBlocks.size()) {
1991                         // no next catch block - rethrow if condition failed
1992                         method._goto(skip);
1993                         method.label(next);
1994                         method.load(symbol).athrow();
1995                     } else {
1996                         method.label(next);
1997                     }
1998                 }
1999 
2000                 leave(catchBlock);
2001             } finally {
2002                 setCurrentBlock(saveBlock);
2003             }
2004         }
2005 
2006         method.label(skip);
2007         method._try(entry, exit, recovery, Throwable.class);
2008 
2009         // Finally body is always inlined elsewhere so it doesn't need to be emitted
2010 
2011         return null;
2012     }
2013 
2014     @Override
2015     public Node enter(final VarNode varNode) {
2016         final Node init = varNode.getInit();
2017 
2018         if (varNode.testResolved() || init == null) {
2019             return null;
2020         }
2021 
2022         final Symbol varSymbol = varNode.getSymbol();
2023 
2024         assert varSymbol != null : "variable node " + varNode + " requires a symbol";
2025 
2026         assert method != null;
2027 
2028         final boolean needsScope = varSymbol.isScope();
2029         if (needsScope) {
2030             method.loadScope();
2031         }
2032 
2033         load(init);
2034 
2035         if (needsScope) {
2036             int flags = CALLSITE_SCOPE | getCallSiteFlags();
2037             if (varNode.isFunctionVarNode()) {
2038                 flags |= CALLSITE_FUNCTION_DECLARATION;
2039             }
2040             final IdentNode identNode = varNode.getName();
2041             final Type type = identNode.getType();
2042             if(varSymbol.isFastScope(getCurrentFunctionNode())) {
2043                 storeFastScopeVar(type, varSymbol, flags);
2044             } else {
2045                 method.dynamicSet(type, identNode.getName(), flags);
2046             }
2047         } else {
2048             assert varNode.getType() == varNode.getName().getType() : "varNode type=" + varNode.getType() + " nametype=" + varNode.getName().getType() + " inittype=" + init.getType();
2049 
2050             method.convert(varNode.getType()); // aw: convert moved here
2051             method.store(varSymbol);
2052         }
2053 
2054         return null;
2055     }
2056 
2057     @Override
2058     public Node enter(final WhileNode whileNode) {
2059         if (whileNode.testResolved()) {
2060             return null;
2061         }
2062 
2063         final Node  test          = whileNode.getTest();
2064         final Block body          = whileNode.getBody();
2065         final Label breakLabel    = whileNode.getBreakLabel();
2066         final Label continueLabel = whileNode.getContinueLabel();
2067         final Label loopLabel     = new Label("loop");
2068 
2069         if (!(whileNode instanceof DoWhileNode)) {
2070             method._goto(continueLabel);
2071         }
2072 
2073         method.label(loopLabel);
2074         body.accept(this);
2075         if (!whileNode.isTerminal()) {
2076             method.label(continueLabel);
2077             new BranchOptimizer(this, method).execute(test, loopLabel, true);
2078             method.label(breakLabel);
2079         }
2080 
2081         return null;
2082     }
2083 
2084     private void closeWith() {
2085         method.loadScope();
2086         method.invoke(ScriptRuntime.CLOSE_WITH);
2087         method.storeScope();
2088     }
2089 
2090     @Override
2091     public Node enter(final WithNode withNode) {
2092         if (withNode.testResolved()) {
2093             return null;
2094         }
2095 
2096         final Node expression = withNode.getExpression();
2097         final Node body       = withNode.getBody();
2098 
2099         final Label tryLabel   = new Label("with_try");
2100         final Label endLabel   = new Label("with_end");
2101         final Label catchLabel = new Label("with_catch");
2102         final Label exitLabel  = new Label("with_exit");
2103 
2104         method.label(tryLabel);
2105 
2106         method.loadScope();
2107         load(expression);
2108 
2109         assert expression.getType().isObject() : "with expression needs to be object: " + expression;
2110 
2111         method.invoke(ScriptRuntime.OPEN_WITH);
2112         method.storeScope();
2113 
2114         body.accept(this);
2115 
2116         if (!body.isTerminal()) {
2117             closeWith();
2118             method._goto(exitLabel);
2119         }
2120 
2121         method.label(endLabel);
2122 
2123         method._catch(catchLabel);
2124         closeWith();
2125         method.athrow();
2126 
2127         method.label(exitLabel);
2128 
2129         method._try(tryLabel, endLabel, catchLabel);
2130 
2131         return null;
2132     }
2133 
2134     @Override
2135     public Node enterADD(final UnaryNode unaryNode) {
2136         if (unaryNode.testResolved()) {
2137             return null;
2138         }
2139 
2140         load(unaryNode.rhs());
2141         assert unaryNode.rhs().getType().isNumber();
2142         method.store(unaryNode.getSymbol());
2143 
2144         return null;
2145     }
2146 
2147     @Override
2148     public Node enterBIT_NOT(final UnaryNode unaryNode) {
2149         if (unaryNode.testResolved()) {
2150             return null;
2151         }
2152 
2153         load(unaryNode.rhs()).convert(Type.INT).load(-1).xor().store(unaryNode.getSymbol());
2154 
2155         return null;
2156     }
2157 
2158     // do this better with convert calls to method. TODO
2159     @Override
2160     public Node enterCONVERT(final UnaryNode unaryNode) {
2161         if (unaryNode.testResolved()) {
2162             return null;
2163         }
2164 
2165         final Node rhs = unaryNode.rhs();
2166         final Type to  = unaryNode.getType();
2167 
2168         if (to.isObject() && rhs instanceof LiteralNode) {
2169             final LiteralNode<?> literalNode = (LiteralNode<?>)rhs;
2170             final Object value = literalNode.getValue();
2171 
2172             if (value instanceof Number) {
2173                 assert !to.isArray() : "type hygiene - cannot convert number to array: (" + to.getTypeClass().getSimpleName() + ')' + value;
2174                 if (value instanceof Integer) {
2175                     method.load((Integer)value);
2176                 } else if (value instanceof Long) {
2177                     method.load((Long)value);
2178                 } else if (value instanceof Double) {
2179                     method.load((Double)value);
2180                 } else {
2181                     assert false;
2182                 }
2183                 method.convert(Type.OBJECT);
2184             } else if (value instanceof Boolean) {
2185                 method.getField(staticField(Boolean.class, value.toString().toUpperCase(), Boolean.class));
2186             } else {
2187                 load(rhs);
2188                 method.convert(unaryNode.getType());
2189             }
2190         } else {
2191             load(rhs);
2192             method.convert(unaryNode.getType());
2193         }
2194 
2195         method.store(unaryNode.getSymbol());
2196 
2197         return null;
2198     }
2199 
2200     @Override
2201     public Node enterDECINC(final UnaryNode unaryNode) {
2202         if (unaryNode.testResolved()) {
2203             return null;
2204         }
2205 
2206         final Node      rhs         = unaryNode.rhs();
2207         final Type      type        = unaryNode.getType();
2208         final TokenType tokenType   = unaryNode.tokenType();
2209         final boolean   isPostfix   = tokenType == TokenType.DECPOSTFIX || tokenType == TokenType.INCPOSTFIX;
2210         final boolean   isIncrement = tokenType == TokenType.INCPREFIX || tokenType == TokenType.INCPOSTFIX;
2211 
2212         assert !type.isObject();
2213 
2214         new SelfModifyingStore<UnaryNode>(unaryNode, rhs) {
2215 
2216             @Override
2217             protected void evaluate() {
2218                 load(rhs, true);
2219 
2220                 method.convert(type);
2221                 if (!isPostfix) {
2222                     if (type.isInteger()) {
2223                         method.load(isIncrement ? 1 : -1);
2224                     } else if (type.isLong()) {
2225                         method.load(isIncrement ? 1L : -1L);
2226                     } else {
2227                         method.load(isIncrement ? 1.0 : -1.0);
2228                     }
2229                     method.add();
2230                 }
2231             }
2232 
2233             @Override
2234             protected void storeNonDiscard() {
2235                 super.storeNonDiscard();
2236                 if (isPostfix) {
2237                     if (type.isInteger()) {
2238                         method.load(isIncrement ? 1 : -1);
2239                     } else if (type.isLong()) {
2240                         method.load(isIncrement ? 1L : 1L);
2241                     } else {
2242                         method.load(isIncrement ? 1.0 : -1.0);
2243                     }
2244                     method.add();
2245                 }
2246             }
2247         }.store();
2248 
2249         return null;
2250     }
2251 
2252     @Override
2253     public Node enterDISCARD(final UnaryNode unaryNode) {
2254         if (unaryNode.testResolved()) {
2255             return null;
2256         }
2257 
2258         final Node rhs = unaryNode.rhs();
2259 
2260         load(rhs);
2261 
2262         if (rhs.shouldDiscard()) {
2263             method.pop();
2264         }
2265 
2266         return null;
2267     }
2268 
2269     @Override
2270     public Node enterNEW(final UnaryNode unaryNode) {
2271         if (unaryNode.testResolved()) {
2272             return null;
2273         }
2274 
2275         final CallNode callNode = (CallNode)unaryNode.rhs();
2276         final List<Node> args   = callNode.getArgs();
2277 
2278         // Load function reference.
2279         load(callNode.getFunction()).convert(Type.OBJECT); // must detect type error
2280 
2281         // Load arguments.
2282         loadArgs(args);
2283 
2284         method.dynamicNew(args.size(), getCallSiteFlags());
2285         method.store(unaryNode.getSymbol());
2286 
2287         return null;
2288     }
2289 
2290     @Override
2291     public Node enterNOT(final UnaryNode unaryNode) {
2292         if (unaryNode.testResolved()) {
2293             return null;
2294         }
2295 
2296         final Node rhs = unaryNode.rhs();
2297 
2298         load(rhs);
2299 
2300         final Label trueLabel  = new Label("true");
2301         final Label afterLabel = new Label("after");
2302 
2303         method.convert(Type.BOOLEAN);
2304         method.ifne(trueLabel);
2305         method.load(true);
2306         method._goto(afterLabel);
2307         method.label(trueLabel);
2308         method.load(false);
2309         method.label(afterLabel);
2310         method.store(unaryNode.getSymbol());
2311 
2312         return null;
2313     }
2314 
2315     @Override
2316     public Node enterSUB(final UnaryNode unaryNode) {
2317         if (unaryNode.testResolved()) {
2318             return null;
2319         }
2320 
2321         load(unaryNode.rhs()).neg().store(unaryNode.getSymbol());
2322 
2323         return null;
2324     }
2325 
2326     private Node enterNumericAdd(final Node lhs, final Node rhs, final Type type, final Symbol symbol) {
2327         assert lhs.getType().equals(rhs.getType()) && lhs.getType().equals(type);
2328         load(lhs);
2329         load(rhs);
2330         method.add();
2331         method.store(symbol);
2332 
2333         return null;
2334     }
2335 
2336     @Override
2337     public Node enterADD(final BinaryNode binaryNode) {
2338         if (binaryNode.testResolved()) {
2339             return null;
2340         }
2341 
2342         final Node lhs = binaryNode.lhs();
2343         final Node rhs = binaryNode.rhs();
2344 
2345         final Type type = binaryNode.getType();
2346         if (type.isNumeric()) {
2347             enterNumericAdd(lhs, rhs, type, binaryNode.getSymbol());
2348         } else {
2349             load(lhs).convert(Type.OBJECT);
2350             load(rhs).convert(Type.OBJECT);
2351             method.add();
2352             method.store(binaryNode.getSymbol());
2353         }
2354 
2355         return null;
2356     }
2357 
2358     private Node enterAND_OR(final BinaryNode binaryNode) {
2359         if (binaryNode.testResolved()) {
2360             return null;
2361         }
2362 
2363         final Node lhs = binaryNode.lhs();
2364         final Node rhs = binaryNode.rhs();
2365 
2366         final Label skip = new Label("skip");
2367 
2368         load(lhs).convert(Type.OBJECT).dup().convert(Type.BOOLEAN);
2369 
2370         if (binaryNode.tokenType() == TokenType.AND) {
2371             method.ifeq(skip);
2372         } else {
2373             method.ifne(skip);
2374         }
2375 
2376         method.pop();
2377         load(rhs).convert(Type.OBJECT);
2378         method.label(skip);
2379         method.store(binaryNode.getSymbol());
2380 
2381         return null;
2382     }
2383 
2384     @Override
2385     public Node enterAND(final BinaryNode binaryNode) {
2386         return enterAND_OR(binaryNode);
2387     }
2388 
2389     @Override
2390     public Node enterASSIGN(final BinaryNode binaryNode) {
2391         if (binaryNode.testResolved()) {
2392             return null;
2393         }
2394 
2395         final Node lhs = binaryNode.lhs();
2396         final Node rhs = binaryNode.rhs();
2397 
2398         final Type lhsType = lhs.getType();
2399         final Type rhsType = rhs.getType();
2400 
2401         if (!lhsType.isEquivalentTo(rhsType)) {
2402             //this is OK if scoped, only locals are wrong
2403             assert !(lhs instanceof IdentNode) || lhs.getSymbol().isScope() : new ASTWriter(binaryNode);
2404         }
2405 
2406         new Store<BinaryNode>(binaryNode, lhs) {
2407             @Override
2408             protected void evaluate() {
2409                 load(rhs);
2410             }
2411         }.store();
2412 
2413         return null;
2414     }
2415 
2416     /**
2417      * Helper class for assignment ops, e.g. *=, += and so on..
2418      */
2419     private abstract class AssignOp extends SelfModifyingStore<BinaryNode> {
2420 
2421         /** The type of the resulting operation */
2422         private final Type opType;
2423 
2424         /**
2425          * Constructor
2426          *
2427          * @param node the assign op node
2428          */
2429         AssignOp(final BinaryNode node) {
2430             this(node.getType(), node);
2431         }
2432 
2433         /**
2434          * Constructor
2435          *
2436          * @param opType type of the computation - overriding the type of the node
2437          * @param node the assign op node
2438          */
2439         AssignOp(final Type opType, final BinaryNode node) {
2440             super(node, node.lhs());
2441             this.opType = opType;
2442         }
2443 
2444         @Override
2445         public void store() {
2446             if (assignNode.testResolved()) {
2447                 return;
2448             }
2449             super.store();
2450         }
2451 
2452         protected abstract void op();
2453 
2454         @Override
2455         protected void evaluate() {
2456             load(assignNode.lhs(), true).convert(opType);
2457             load(assignNode.rhs()).convert(opType);
2458             op();
2459             method.convert(assignNode.getType());
2460         }
2461     }
2462 
2463     @Override
2464     public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
2465         assert RuntimeNode.Request.ADD.canSpecialize();
2466         final boolean specialize = binaryNode.getType() == Type.OBJECT;
2467 
2468         new AssignOp(binaryNode) {
2469             @Override
2470             protected boolean isSelfModifying() {
2471                 return !specialize;
2472             }
2473 
2474             @Override
2475             protected void op() {
2476                 method.add();
2477             }
2478 
2479             @Override
2480             protected void evaluate() {
2481                 if (specialize && specializationCheck(Request.ADD, assignNode, Arrays.asList(assignNode.lhs(), assignNode.rhs()))) {
2482                     return;
2483                 }
2484                 super.evaluate();
2485             }
2486         }.store();
2487 
2488         return null;
2489     }
2490 
2491     @Override
2492     public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
2493         new AssignOp(Type.INT, binaryNode) {
2494             @Override
2495             protected void op() {
2496                 method.and();
2497             }
2498         }.store();
2499 
2500         return null;
2501     }
2502 
2503     @Override
2504     public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
2505         new AssignOp(Type.INT, binaryNode) {
2506             @Override
2507             protected void op() {
2508                 method.or();
2509             }
2510         }.store();
2511 
2512         return null;
2513     }
2514 
2515     @Override
2516     public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
2517         new AssignOp(Type.INT, binaryNode) {
2518             @Override
2519             protected void op() {
2520                 method.xor();
2521             }
2522         }.store();
2523 
2524         return null;
2525     }
2526 
2527     @Override
2528     public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
2529         new AssignOp(binaryNode) {
2530             @Override
2531             protected void op() {
2532                 method.div();
2533             }
2534         }.store();
2535 
2536         return null;
2537     }
2538 
2539     @Override
2540     public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
2541         new AssignOp(binaryNode) {
2542             @Override
2543             protected void op() {
2544                 method.rem();
2545             }
2546         }.store();
2547 
2548         return null;
2549     }
2550 
2551     @Override
2552     public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
2553         new AssignOp(binaryNode) {
2554             @Override
2555             protected void op() {
2556                 method.mul();
2557             }
2558         }.store();
2559 
2560         return null;
2561     }
2562 
2563     @Override
2564     public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
2565         new AssignOp(Type.INT, binaryNode) {
2566             @Override
2567             protected void op() {
2568                 method.sar();
2569             }
2570         }.store();
2571 
2572         return null;
2573     }
2574 
2575     @Override
2576     public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
2577         new AssignOp(Type.INT, binaryNode) {
2578             @Override
2579             protected void op() {
2580                 method.shl();
2581             }
2582         }.store();
2583 
2584         return null;
2585     }
2586 
2587     @Override
2588     public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
2589         new AssignOp(Type.INT, binaryNode) {
2590             @Override
2591             protected void op() {
2592                 method.shr();
2593                 method.convert(Type.LONG).load(0xffff_ffffL).and();
2594             }
2595         }.store();
2596 
2597         return null;
2598     }
2599 
2600     @Override
2601     public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
2602         new AssignOp(binaryNode) {
2603             @Override
2604             protected void op() {
2605                 method.sub();
2606             }
2607         }.store();
2608 
2609         return null;
2610     }
2611 
2612     /**
2613      * Helper class for binary arithmetic ops
2614      */
2615     private abstract class BinaryArith {
2616 
2617         protected abstract void op();
2618 
2619         protected void evaluate(final BinaryNode node) {
2620             if (node.testResolved()) {
2621                 return;
2622             }
2623             load(node.lhs());
2624             load(node.rhs());
2625             op();
2626             method.store(node.getSymbol());
2627         }
2628     }
2629 
2630     @Override
2631     public Node enterBIT_AND(final BinaryNode binaryNode) {
2632         new BinaryArith() {
2633             @Override
2634             protected void op() {
2635                 method.and();
2636             }
2637         }.evaluate(binaryNode);
2638 
2639         return null;
2640     }
2641 
2642     @Override
2643     public Node enterBIT_OR(final BinaryNode binaryNode) {
2644         new BinaryArith() {
2645             @Override
2646             protected void op() {
2647                 method.or();
2648             }
2649         }.evaluate(binaryNode);
2650 
2651         return null;
2652     }
2653 
2654     @Override
2655     public Node enterBIT_XOR(final BinaryNode binaryNode) {
2656         new BinaryArith() {
2657             @Override
2658             protected void op() {
2659                 method.xor();
2660             }
2661         }.evaluate(binaryNode);
2662 
2663         return null;
2664     }
2665 
2666     private Node enterComma(final BinaryNode binaryNode) {
2667         if (binaryNode.testResolved()) {
2668             return null;
2669         }
2670 
2671         final Node lhs = binaryNode.lhs();
2672         final Node rhs = binaryNode.rhs();
2673 
2674         load(lhs);
2675         load(rhs);
2676         method.store(binaryNode.getSymbol());
2677 
2678         return null;
2679     }
2680 
2681     @Override
2682     public Node enterCOMMARIGHT(final BinaryNode binaryNode) {
2683         return enterComma(binaryNode);
2684     }
2685 
2686     @Override
2687     public Node enterCOMMALEFT(final BinaryNode binaryNode) {
2688         return enterComma(binaryNode);
2689     }
2690 
2691     @Override
2692     public Node enterDIV(final BinaryNode binaryNode) {
2693         new BinaryArith() {
2694             @Override
2695             protected void op() {
2696                 method.div();
2697             }
2698         }.evaluate(binaryNode);
2699 
2700         return null;
2701     }
2702 
2703     private Node enterCmp(final Node lhs, final Node rhs, final Condition cond, final Type type, final Symbol symbol) {
2704         final Type lhsType = lhs.getType();
2705         final Type rhsType = rhs.getType();
2706 
2707         final Type widest = Type.widest(lhsType, rhsType);
2708         assert widest.isNumeric() || widest.isBoolean() : widest;
2709 
2710         load(lhs);
2711         method.convert(widest);
2712         load(rhs);
2713         method.convert(widest);
2714 
2715         final Label trueLabel  = new Label("trueLabel");
2716         final Label afterLabel = new Label("skip");
2717 
2718         method.conditionalJump(cond, trueLabel);
2719 
2720         method.load(Boolean.FALSE);
2721         method._goto(afterLabel);
2722         method.label(trueLabel);
2723         method.load(Boolean.TRUE);
2724         method.label(afterLabel);
2725 
2726         method.convert(type);
2727         method.store(symbol);
2728 
2729         return null;
2730     }
2731 
2732     private Node enterCmp(final BinaryNode binaryNode, final Condition cond) {
2733         if (binaryNode.testResolved()) {
2734             return null;
2735         }
2736         return enterCmp(binaryNode.lhs(), binaryNode.rhs(), cond, binaryNode.getType(), binaryNode.getSymbol());
2737     }
2738 
2739     @Override
2740     public Node enterEQ(final BinaryNode binaryNode) {
2741         return enterCmp(binaryNode, Condition.EQ);
2742     }
2743 
2744     @Override
2745     public Node enterEQ_STRICT(final BinaryNode binaryNode) {
2746         return enterCmp(binaryNode, Condition.EQ);
2747     }
2748 
2749     @Override
2750     public Node enterGE(final BinaryNode binaryNode) {
2751         return enterCmp(binaryNode, Condition.GE);
2752     }
2753 
2754     @Override
2755     public Node enterGT(final BinaryNode binaryNode) {
2756         return enterCmp(binaryNode, Condition.GT);
2757     }
2758 
2759     @Override
2760     public Node enterLE(final BinaryNode binaryNode) {
2761         return enterCmp(binaryNode, Condition.LE);
2762     }
2763 
2764     @Override
2765     public Node enterLT(final BinaryNode binaryNode) {
2766         return enterCmp(binaryNode, Condition.LT);
2767     }
2768 
2769     @Override
2770     public Node enterMOD(final BinaryNode binaryNode) {
2771         new BinaryArith() {
2772             @Override
2773             protected void op() {
2774                 method.rem();
2775             }
2776         }.evaluate(binaryNode);
2777 
2778         return null;
2779     }
2780 
2781     @Override
2782     public Node enterMUL(final BinaryNode binaryNode) {
2783         new BinaryArith() {
2784             @Override
2785             protected void op() {
2786                 method.mul();
2787             }
2788         }.evaluate(binaryNode);
2789 
2790         return null;
2791     }
2792 
2793     @Override
2794     public Node enterNE(final BinaryNode binaryNode) {
2795         return enterCmp(binaryNode, Condition.NE);
2796     }
2797 
2798     @Override
2799     public Node enterNE_STRICT(final BinaryNode binaryNode) {
2800         return enterCmp(binaryNode, Condition.NE);
2801     }
2802 
2803     @Override
2804     public Node enterOR(final BinaryNode binaryNode) {
2805         return enterAND_OR(binaryNode);
2806     }
2807 
2808     @Override
2809     public Node enterSAR(final BinaryNode binaryNode) {
2810         new BinaryArith() {
2811             @Override
2812             protected void op() {
2813                 method.sar();
2814             }
2815         }.evaluate(binaryNode);
2816 
2817         return null;
2818     }
2819 
2820     @Override
2821     public Node enterSHL(final BinaryNode binaryNode) {
2822         new BinaryArith() {
2823             @Override
2824             protected void op() {
2825                 method.shl();
2826             }
2827         }.evaluate(binaryNode);
2828 
2829         return null;
2830     }
2831 
2832     @Override
2833     public Node enterSHR(final BinaryNode binaryNode) {
2834         new BinaryArith() {
2835             @Override
2836             protected void op() {
2837                 method.shr();
2838                 method.convert(Type.LONG).load(0xffff_ffffL).and();
2839             }
2840         }.evaluate(binaryNode);
2841 
2842         return null;
2843     }
2844 
2845     @Override
2846     public Node enterSUB(final BinaryNode binaryNode) {
2847         new BinaryArith() {
2848             @Override
2849             protected void op() {
2850                 method.sub();
2851             }
2852         }.evaluate(binaryNode);
2853 
2854         return null;
2855     }
2856 
2857     /*
2858      * Ternary visits.
2859      */
2860     @Override
2861     public Node enter(final TernaryNode ternaryNode) {
2862         if (ternaryNode.testResolved()) {
2863             return null;
2864         }
2865 
2866         final Node lhs   = ternaryNode.lhs();
2867         final Node rhs   = ternaryNode.rhs();
2868         final Node third = ternaryNode.third();
2869 
2870         final Symbol symbol     = ternaryNode.getSymbol();
2871         final Label  falseLabel = new Label("ternary_false");
2872         final Label  exitLabel  = new Label("ternary_exit");
2873 
2874         final Type widest = Type.widest(rhs.getType(), third.getType());
2875 
2876         load(lhs);
2877         assert lhs.getType().isBoolean() : "lhs in ternary must be boolean";
2878 
2879         // we still keep the conversion here as the AccessSpecializer can have separated the types, e.g. var y = x ? x=55 : 17
2880         // will left as (Object)x=55 : (Object)17 by Lower. Then the first term can be {I}x=55 of type int, which breaks the
2881         // symmetry for the temporary slot for this TernaryNode. This is evidence that we assign types and explicit conversions
2882         // to early, or Apply the AccessSpecializer too late. We are mostly probably looking for a separate type pass to
2883         // do this property. Then we never need any conversions in CodeGenerator
2884         method.ifeq(falseLabel);
2885         load(rhs);
2886         method.convert(widest);
2887         method._goto(exitLabel);
2888         method.label(falseLabel);
2889         load(third);
2890         method.convert(widest);
2891         method.label(exitLabel);
2892         method.store(symbol);
2893 
2894         return null;
2895     }
2896 
2897     /**
2898      * Generate all shared scope calls generated during codegen.
2899      */
2900     protected void generateScopeCalls() {
2901         for (final SharedScopeCall scopeAccess : scopeCalls.values()) {
2902             scopeAccess.generateScopeCall();
2903         }
2904     }
2905 
2906     /**
2907      * Get a shared static method representing a dynamic scope callsite.
2908      *
2909      * @param symbol the symbol
2910      * @param valueType the value type of the symbol
2911      * @param returnType the return type
2912      * @param paramTypes the parameter types
2913      * @param flags the callsite flags
2914      * @return an object representing a shared scope call
2915      */
2916     private SharedScopeCall getScopeCall(final Symbol symbol, final Type valueType, final Type returnType,
2917                                          final Type[] paramTypes, final int flags) {
2918 
2919         final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags);
2920         if (scopeCalls.containsKey(scopeCall)) {
2921             return scopeCalls.get(scopeCall);
2922         }
2923         scopeCall.setClassAndName(compileUnit, compiler);
2924         scopeCalls.put(scopeCall, scopeCall);
2925         return scopeCall;
2926     }
2927 
2928     /**
2929      * Get a shared static method representing a dynamic scope get access.
2930      *
2931      * @param type the type of the variable
2932      * @param symbol the symbol
2933      * @param flags the callsite flags
2934      * @return an object representing a shared scope call
2935      */
2936     private SharedScopeCall getScopeGet(final Type type, final Symbol symbol, final int flags) {
2937 
2938         final SharedScopeCall scopeCall = new SharedScopeCall(symbol, type, type, null, flags);
2939         if (scopeCalls.containsKey(scopeCall)) {
2940             return scopeCalls.get(scopeCall);
2941         }
2942         scopeCall.setClassAndName(compileUnit, compiler);
2943         scopeCalls.put(scopeCall, scopeCall);
2944         return scopeCall;
2945     }
2946 
2947     /**
2948      * Debug code used to print symbols
2949      *
2950      * @param block the block we are in
2951      * @param ident identifier for block or function where applicable
2952      */
2953     private void printSymbols(final Block block, final String ident) {
2954         if (!context._print_symbols) {
2955             return;
2956         }
2957 
2958         @SuppressWarnings("resource")
2959         final PrintWriter out = context.getErr();
2960         out.println("[BLOCK in '" + ident + "']");
2961         if (!block.printSymbols(out)) {
2962             out.println("<no symbols>");
2963         }
2964         out.println();
2965     }
2966 
2967 
2968     /**
2969      * The difference between a store and a self modifying store is that
2970      * the latter may load part of the target on the stack, e.g. the base
2971      * of an AccessNode or the base and index of an IndexNode. These are used
2972      * both as target and as an extra source. Previously it was problematic
2973      * for self modifying stores if the target/lhs didn't belong to one
2974      * of three trivial categories: IdentNode, AcessNodes, IndexNodes. In that
2975      * case it was evaluated and tagged as "resolved", which meant at the second
2976      * time the lhs of this store was read (e.g. in a = a (second) + b for a += b,
2977      * it would be evaluated to a nop in the scope and cause stack underflow
2978      *
2979      * see NASHORN-703
2980      *
2981      * @param <T>
2982      */
2983     private abstract class SelfModifyingStore<T extends Node> extends Store<T> {
2984         protected SelfModifyingStore(final T assignNode, final Node target) {
2985             super(assignNode, target);
2986         }
2987 
2988         @Override
2989         protected boolean isSelfModifying() {
2990             return true;
2991         }
2992     }
2993 
2994     /**
2995      * Helper class to generate stores
2996      */
2997     private abstract class Store<T extends Node> {
2998 
2999         /** An assignment node, e.g. x += y */
3000         protected final T assignNode;
3001 
3002         /** The target node to store to, e.g. x */
3003         private final Node target;
3004 
3005         /** Should the result always be discarded, no matter what? */
3006         private final boolean alwaysDiscard;
3007 
3008         /** How deep on the stack do the arguments go if this generates an indy call */
3009         private int depth;
3010 
3011         /** If we have too many arguments, we need temporary storage, this is stored in 'quick' */
3012         private Symbol quick;
3013 
3014         /**
3015          * Constructor
3016          *
3017          * @param assignNode the node representing the whole assignment
3018          * @param target     the target node of the assignment (destination)
3019          */
3020         protected Store(final T assignNode, final Node target) {
3021             this.assignNode = assignNode;
3022             this.target = target;
3023             this.alwaysDiscard = assignNode == target;
3024         }
3025 
3026         /**
3027          * Constructor
3028          *
3029          * @param assignNode the node representing the whole assignment
3030          */
3031         protected Store(final T assignNode) {
3032             this(assignNode, assignNode);
3033         }
3034 
3035         /**
3036          * Is this a self modifying store operation, e.g. *= or ++
3037          * @return true if self modifying store
3038          */
3039         protected boolean isSelfModifying() {
3040             return false;
3041         }
3042 
3043         private void prologue() {
3044             final Symbol targetSymbol = target.getSymbol();
3045             final Symbol scopeSymbol  = getCurrentFunctionNode().getScopeNode().getSymbol();
3046 
3047             /**
3048              * This loads the parts of the target, e.g base and index. they are kept
3049              * on the stack throughout the store and used at the end to execute it
3050              */
3051 
3052             target.accept(new NodeVisitor(compileUnit, method) {
3053                 @Override
3054                 public Node enter(final IdentNode node) {
3055                     if (targetSymbol.isScope()) {
3056                         method.load(scopeSymbol);
3057                         depth++;
3058                     }
3059                     return null;
3060                 }
3061 
3062                 private void enterBaseNode() {
3063                     assert target instanceof BaseNode : "error - base node " + target + " must be instanceof BaseNode";
3064                     final BaseNode baseNode = (BaseNode)target;
3065                     final Node     base     = baseNode.getBase();
3066 
3067                     load(base);
3068                     method.convert(Type.OBJECT);
3069                     depth += Type.OBJECT.getSlots();
3070 
3071                     if (isSelfModifying()) {
3072                         method.dup();
3073                     }
3074                 }
3075 
3076                 @Override
3077                 public Node enter(final AccessNode node) {
3078                     enterBaseNode();
3079                     return null;
3080                 }
3081 
3082                 @Override
3083                 public Node enter(final IndexNode node) {
3084                     enterBaseNode();
3085 
3086                     final Node index = node.getIndex();
3087                     // could be boolean here as well
3088                     load(index);
3089                     if (!index.getType().isNumeric()) {
3090                         method.convert(Type.OBJECT);
3091                     }
3092                     depth += index.getType().getSlots();
3093 
3094                     if (isSelfModifying()) {
3095                         //convert "base base index" to "base index base index"
3096                         method.dup(1);
3097                     }
3098 
3099                     return null;
3100                 }
3101 
3102             });
3103         }
3104 
3105         private Symbol quickSymbol(final Type type) {
3106             return quickSymbol(type, QUICK_PREFIX.tag());
3107         }
3108 
3109         /**
3110          * Quick symbol generates an extra local variable, always using the same
3111          * slot, one that is available after the end of the frame.
3112          *
3113          * @param type the type of the symbol
3114          * @param prefix the prefix for the variable name for the symbol
3115          *
3116          * @return the quick symbol
3117          */
3118         private Symbol quickSymbol(final Type type, final String prefix) {
3119             final String name = compiler.uniqueName(prefix);
3120             final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL, null, null);
3121 
3122             symbol.setType(type);
3123             symbol.setSlot(getCurrentBlock().getFrame().getSlotCount());
3124 
3125             return symbol;
3126         }
3127 
3128         // store the result that "lives on" after the op, e.g. "i" in i++ postfix.
3129         protected void storeNonDiscard() {
3130             if (assignNode.shouldDiscard() || alwaysDiscard) {
3131                 assignNode.setDiscard(false);
3132                 return;
3133             }
3134 
3135             final Symbol symbol = assignNode.getSymbol();
3136             if (symbol.hasSlot()) {
3137                 method.dup().store(symbol);
3138                 return;
3139             }
3140 
3141             if (method.dup(depth) == null) {
3142                 method.dup();
3143                 this.quick = quickSymbol(method.peekType());
3144                 method.store(quick);
3145             }
3146         }
3147 
3148         private void epilogue() {
3149             final Symbol targetSymbol = target.getSymbol();
3150             final FunctionNode currentFunction = getCurrentFunctionNode();
3151 
3152             /**
3153              * Take the original target args from the stack and use them
3154              * together with the value to be stored to emit the store code
3155              */
3156             if (targetSymbol.hasSlot()) {
3157                 method.convert(target.getType());
3158             }
3159 
3160             target.accept(new NodeVisitor(compileUnit, method) {
3161                 @Override
3162                 public Node enter(final IdentNode node) {
3163                     final Symbol symbol = target.getSymbol();
3164                     if (symbol.isScope()) {
3165                         if(symbol.isFastScope(currentFunction)) {
3166                             storeFastScopeVar(target.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags());
3167                         } else {
3168                             method.dynamicSet(target.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags());
3169                         }
3170                     } else {
3171                         assert targetSymbol != null;
3172                         method.store(targetSymbol);
3173                     }
3174                     return null;
3175 
3176                 }
3177 
3178                 @Override
3179                 public Node enter(final AccessNode node) {
3180                     method.dynamicSet(node.getProperty().getType(), node.getProperty().getName(), getCallSiteFlags());
3181                     return null;
3182                 }
3183 
3184                 @Override
3185                 public Node enter(final IndexNode node) {
3186                     method.dynamicSetIndex(getCallSiteFlags());
3187                     return null;
3188                 }
3189             });
3190 
3191 
3192             // whatever is on the stack now is the final answer
3193         }
3194 
3195         protected abstract void evaluate();
3196 
3197         void store() {
3198             prologue();
3199             evaluate(); // leaves an operation of whatever the operationType was on the stack
3200             storeNonDiscard();
3201             epilogue();
3202             if (quick != null) {
3203                 method.load(quick);
3204             }
3205         }
3206 
3207     }
3208 
3209     /*
3210      * Globals are special. We cannot refer to any Global (or NativeObject) class by .class, as they are different
3211      * for different contexts. As far as I can tell, the only NativeObject that we need to deal with like this
3212      * is from the code pipeline is Global
3213      */
3214     private MethodEmitter globalInstance() {
3215         return method.invokeStatic(Compiler.GLOBAL_OBJECT, "instance", "()L" + Compiler.GLOBAL_OBJECT + ';');
3216     }
3217 
3218     private MethodEmitter globalObjectPrototype() {
3219         return method.invokeStatic(Compiler.GLOBAL_OBJECT, "objectPrototype", methodDescriptor(ScriptObject.class));
3220     }
3221 
3222     private MethodEmitter globalAllocateArguments() {
3223         return method.invokeStatic(Compiler.GLOBAL_OBJECT, "allocateArguments", methodDescriptor(Object.class, Object[].class, Object.class, int.class));
3224     }
3225 
3226     private MethodEmitter globalNewRegExp() {
3227         return method.invokeStatic(Compiler.GLOBAL_OBJECT, "newRegExp", methodDescriptor(Object.class, String.class, String.class));
3228     }
3229 
3230     private MethodEmitter globalRegExpCopy() {
3231         return method.invokeStatic(Compiler.GLOBAL_OBJECT, "regExpCopy", methodDescriptor(Object.class, Object.class));
3232     }
3233 
3234     private MethodEmitter globalAllocateArray(final ArrayType type) {
3235         //make sure the native array is treated as an array type
3236         return method.invokeStatic(Compiler.GLOBAL_OBJECT, "allocate", methodDescriptor(Object.class, type.getTypeClass()), type);
3237     }
3238 
3239     private MethodEmitter globalIsEval() {
3240         return method.invokeStatic(Compiler.GLOBAL_OBJECT, "isEval", methodDescriptor(boolean.class, Object.class));
3241     }
3242 
3243     private MethodEmitter globalDirectEval() {
3244         return method.invokeStatic(Compiler.GLOBAL_OBJECT, "directEval",
3245                 methodDescriptor(Object.class, Object.class, Object.class, Object.class, Object.class, Object.class));
3246     }
3247 }