CodeGenerator.java revision 1262:ee849fe4b32d
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
26package jdk.nashorn.internal.codegen;
27
28import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE;
29import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC;
30import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
31import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
32import static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION;
33import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
34import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
35import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX;
36import static jdk.nashorn.internal.codegen.CompilerConstants.REGEX_PREFIX;
37import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
38import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
39import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
40import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
41import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup;
42import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
43import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
44import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
45import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
46import static jdk.nashorn.internal.ir.Symbol.HAS_SLOT;
47import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
48import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
49import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
50import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_APPLY_TO_CALL;
51import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_DECLARE;
52import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FAST_SCOPE;
53import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
54import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT;
55import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_SCOPE;
56
57import java.io.PrintWriter;
58import java.util.ArrayDeque;
59import java.util.ArrayList;
60import java.util.Arrays;
61import java.util.BitSet;
62import java.util.Collection;
63import java.util.Collections;
64import java.util.Deque;
65import java.util.EnumSet;
66import java.util.HashMap;
67import java.util.HashSet;
68import java.util.Iterator;
69import java.util.LinkedList;
70import java.util.List;
71import java.util.Map;
72import java.util.Set;
73import java.util.TreeMap;
74import java.util.function.Supplier;
75import jdk.nashorn.internal.AssertsEnabled;
76import jdk.nashorn.internal.IntDeque;
77import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
78import jdk.nashorn.internal.codegen.CompilerConstants.Call;
79import jdk.nashorn.internal.codegen.types.ArrayType;
80import jdk.nashorn.internal.codegen.types.Type;
81import jdk.nashorn.internal.ir.AccessNode;
82import jdk.nashorn.internal.ir.BaseNode;
83import jdk.nashorn.internal.ir.BinaryNode;
84import jdk.nashorn.internal.ir.Block;
85import jdk.nashorn.internal.ir.BlockStatement;
86import jdk.nashorn.internal.ir.BreakNode;
87import jdk.nashorn.internal.ir.CallNode;
88import jdk.nashorn.internal.ir.CaseNode;
89import jdk.nashorn.internal.ir.CatchNode;
90import jdk.nashorn.internal.ir.ContinueNode;
91import jdk.nashorn.internal.ir.EmptyNode;
92import jdk.nashorn.internal.ir.Expression;
93import jdk.nashorn.internal.ir.ExpressionStatement;
94import jdk.nashorn.internal.ir.ForNode;
95import jdk.nashorn.internal.ir.FunctionNode;
96import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
97import jdk.nashorn.internal.ir.GetSplitState;
98import jdk.nashorn.internal.ir.IdentNode;
99import jdk.nashorn.internal.ir.IfNode;
100import jdk.nashorn.internal.ir.IndexNode;
101import jdk.nashorn.internal.ir.JoinPredecessorExpression;
102import jdk.nashorn.internal.ir.JumpStatement;
103import jdk.nashorn.internal.ir.JumpToInlinedFinally;
104import jdk.nashorn.internal.ir.LabelNode;
105import jdk.nashorn.internal.ir.LexicalContext;
106import jdk.nashorn.internal.ir.LexicalContextNode;
107import jdk.nashorn.internal.ir.LiteralNode;
108import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
109import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
110import jdk.nashorn.internal.ir.LiteralNode.PrimitiveLiteralNode;
111import jdk.nashorn.internal.ir.LocalVariableConversion;
112import jdk.nashorn.internal.ir.LoopNode;
113import jdk.nashorn.internal.ir.Node;
114import jdk.nashorn.internal.ir.ObjectNode;
115import jdk.nashorn.internal.ir.Optimistic;
116import jdk.nashorn.internal.ir.PropertyNode;
117import jdk.nashorn.internal.ir.ReturnNode;
118import jdk.nashorn.internal.ir.RuntimeNode;
119import jdk.nashorn.internal.ir.RuntimeNode.Request;
120import jdk.nashorn.internal.ir.SetSplitState;
121import jdk.nashorn.internal.ir.SplitReturn;
122import jdk.nashorn.internal.ir.Statement;
123import jdk.nashorn.internal.ir.SwitchNode;
124import jdk.nashorn.internal.ir.Symbol;
125import jdk.nashorn.internal.ir.TernaryNode;
126import jdk.nashorn.internal.ir.ThrowNode;
127import jdk.nashorn.internal.ir.TryNode;
128import jdk.nashorn.internal.ir.UnaryNode;
129import jdk.nashorn.internal.ir.VarNode;
130import jdk.nashorn.internal.ir.WhileNode;
131import jdk.nashorn.internal.ir.WithNode;
132import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
133import jdk.nashorn.internal.ir.visitor.NodeVisitor;
134import jdk.nashorn.internal.objects.Global;
135import jdk.nashorn.internal.objects.ScriptFunctionImpl;
136import jdk.nashorn.internal.parser.Lexer.RegexToken;
137import jdk.nashorn.internal.parser.TokenType;
138import jdk.nashorn.internal.runtime.Context;
139import jdk.nashorn.internal.runtime.Debug;
140import jdk.nashorn.internal.runtime.ECMAException;
141import jdk.nashorn.internal.runtime.JSType;
142import jdk.nashorn.internal.runtime.OptimisticReturnFilters;
143import jdk.nashorn.internal.runtime.PropertyMap;
144import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
145import jdk.nashorn.internal.runtime.RewriteException;
146import jdk.nashorn.internal.runtime.Scope;
147import jdk.nashorn.internal.runtime.ScriptEnvironment;
148import jdk.nashorn.internal.runtime.ScriptFunction;
149import jdk.nashorn.internal.runtime.ScriptObject;
150import jdk.nashorn.internal.runtime.ScriptRuntime;
151import jdk.nashorn.internal.runtime.Source;
152import jdk.nashorn.internal.runtime.Undefined;
153import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
154import jdk.nashorn.internal.runtime.arrays.ArrayData;
155import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
156import jdk.nashorn.internal.runtime.logging.DebugLogger;
157import jdk.nashorn.internal.runtime.logging.Loggable;
158import jdk.nashorn.internal.runtime.logging.Logger;
159import jdk.nashorn.internal.runtime.options.Options;
160
161/**
162 * This is the lowest tier of the code generator. It takes lowered ASTs emitted
163 * from Lower and emits Java byte code. The byte code emission logic is broken
164 * out into MethodEmitter. MethodEmitter works internally with a type stack, and
165 * keeps track of the contents of the byte code stack. This way we avoid a large
166 * number of special cases on the form
167 * <pre>
168 * if (type == INT) {
169 *     visitInsn(ILOAD, slot);
170 * } else if (type == DOUBLE) {
171 *     visitInsn(DOUBLE, slot);
172 * }
173 * </pre>
174 * This quickly became apparent when the code generator was generalized to work
175 * with all types, and not just numbers or objects.
176 * <p>
177 * The CodeGenerator visits nodes only once and emits bytecode for them.
178 */
179@Logger(name="codegen")
180final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContext> implements Loggable {
181
182    private static final Type SCOPE_TYPE = Type.typeFor(ScriptObject.class);
183
184    private static final String GLOBAL_OBJECT = Type.getInternalName(Global.class);
185
186    private static final Call CREATE_REWRITE_EXCEPTION = CompilerConstants.staticCallNoLookup(RewriteException.class,
187            "create", RewriteException.class, UnwarrantedOptimismException.class, Object[].class, String[].class);
188    private static final Call CREATE_REWRITE_EXCEPTION_REST_OF = CompilerConstants.staticCallNoLookup(RewriteException.class,
189            "create", RewriteException.class, UnwarrantedOptimismException.class, Object[].class, String[].class, int[].class);
190
191    private static final Call ENSURE_INT = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
192            "ensureInt", int.class, Object.class, int.class);
193    private static final Call ENSURE_LONG = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
194            "ensureLong", long.class, Object.class, int.class);
195    private static final Call ENSURE_NUMBER = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
196            "ensureNumber", double.class, Object.class, int.class);
197
198    private static final Call CREATE_FUNCTION_OBJECT = CompilerConstants.staticCallNoLookup(ScriptFunctionImpl.class,
199            "create", ScriptFunction.class, Object[].class, int.class, ScriptObject.class);
200    private static final Call CREATE_FUNCTION_OBJECT_NO_SCOPE = CompilerConstants.staticCallNoLookup(ScriptFunctionImpl.class,
201            "create", ScriptFunction.class, Object[].class, int.class);
202
203    private static final Call TO_NUMBER_FOR_EQ = CompilerConstants.staticCallNoLookup(JSType.class,
204            "toNumberForEq", double.class, Object.class);
205    private static final Call TO_NUMBER_FOR_STRICT_EQ = CompilerConstants.staticCallNoLookup(JSType.class,
206            "toNumberForStrictEq", double.class, Object.class);
207
208
209    private static final Class<?> ITERATOR_CLASS = Iterator.class;
210    static {
211        assert ITERATOR_CLASS == CompilerConstants.ITERATOR_PREFIX.type();
212    }
213    private static final Type ITERATOR_TYPE = Type.typeFor(ITERATOR_CLASS);
214    private static final Type EXCEPTION_TYPE = Type.typeFor(CompilerConstants.EXCEPTION_PREFIX.type());
215
216    private static final Integer INT_ZERO = Integer.valueOf(0);
217
218    /** Constant data & installation. The only reason the compiler keeps this is because it is assigned
219     *  by reflection in class installation */
220    private final Compiler compiler;
221
222    /** Is the current code submitted by 'eval' call? */
223    private final boolean evalCode;
224
225    /** Call site flags given to the code generator to be used for all generated call sites */
226    private final int callSiteFlags;
227
228    /** How many regexp fields have been emitted */
229    private int regexFieldCount;
230
231    /** Line number for last statement. If we encounter a new line number, line number bytecode information
232     *  needs to be generated */
233    private int lastLineNumber = -1;
234
235    /** When should we stop caching regexp expressions in fields to limit bytecode size? */
236    private static final int MAX_REGEX_FIELDS = 2 * 1024;
237
238    /** Current method emitter */
239    private MethodEmitter method;
240
241    /** Current compile unit */
242    private CompileUnit unit;
243
244    private final DebugLogger log;
245
246    /** From what size should we use spill instead of fields for JavaScript objects? */
247    private static final int OBJECT_SPILL_THRESHOLD = Options.getIntProperty("nashorn.spill.threshold", 256);
248
249    private final Set<String> emittedMethods = new HashSet<>();
250
251    // Function Id -> ContinuationInfo. Used by compilation of rest-of function only.
252    private final Map<Integer, ContinuationInfo> fnIdToContinuationInfo = new HashMap<>();
253
254    private final Deque<Label> scopeEntryLabels = new ArrayDeque<>();
255
256    private static final Label METHOD_BOUNDARY = new Label("");
257    private final Deque<Label> catchLabels = new ArrayDeque<>();
258    // Number of live locals on entry to (and thus also break from) labeled blocks.
259    private final IntDeque labeledBlockBreakLiveLocals = new IntDeque();
260
261    //is this a rest of compilation
262    private final int[] continuationEntryPoints;
263
264    /**
265     * Constructor.
266     *
267     * @param compiler
268     */
269    CodeGenerator(final Compiler compiler, final int[] continuationEntryPoints) {
270        super(new CodeGeneratorLexicalContext());
271        this.compiler                = compiler;
272        this.evalCode                = compiler.getSource().isEvalCode();
273        this.continuationEntryPoints = continuationEntryPoints;
274        this.callSiteFlags           = compiler.getScriptEnvironment()._callsite_flags;
275        this.log                     = initLogger(compiler.getContext());
276    }
277
278    @Override
279    public DebugLogger getLogger() {
280        return log;
281    }
282
283    @Override
284    public DebugLogger initLogger(final Context context) {
285        return context.getLogger(this.getClass());
286    }
287
288    /**
289     * Gets the call site flags, adding the strict flag if the current function
290     * being generated is in strict mode
291     *
292     * @return the correct flags for a call site in the current function
293     */
294    int getCallSiteFlags() {
295        return lc.getCurrentFunction().getCallSiteFlags() | callSiteFlags;
296    }
297
298    /**
299     * Gets the flags for a scope call site.
300     * @param symbol a scope symbol
301     * @return the correct flags for the scope call site
302     */
303    private int getScopeCallSiteFlags(final Symbol symbol) {
304        assert symbol.isScope();
305        final int flags = getCallSiteFlags() | CALLSITE_SCOPE;
306        if (isEvalCode() && symbol.isGlobal()) {
307            return flags; // Don't set fast-scope flag on non-declared globals in eval code - see JDK-8077955.
308        }
309        return isFastScope(symbol) ? flags | CALLSITE_FAST_SCOPE : flags;
310    }
311
312    /**
313     * Are we generating code for 'eval' code?
314     * @return true if currently compiled code is 'eval' code.
315     */
316    boolean isEvalCode() {
317        return evalCode;
318    }
319
320    /**
321     * Are we using dual primitive/object field representation?
322     * @return true if using dual field representation, false for object-only fields
323     */
324    boolean useDualFields() {
325        return compiler.getContext().useDualFields();
326    }
327
328    /**
329     * Load an identity node
330     *
331     * @param identNode an identity node to load
332     * @return the method generator used
333     */
334    private MethodEmitter loadIdent(final IdentNode identNode, final TypeBounds resultBounds) {
335        checkTemporalDeadZone(identNode);
336        final Symbol symbol = identNode.getSymbol();
337
338        if (!symbol.isScope()) {
339            final Type type = identNode.getType();
340            if(type == Type.UNDEFINED) {
341                return method.loadUndefined(resultBounds.widest);
342            }
343
344            assert symbol.hasSlot() || symbol.isParam();
345            return method.load(identNode);
346        }
347
348        assert identNode.getSymbol().isScope() : identNode + " is not in scope!";
349        final int flags = getScopeCallSiteFlags(symbol);
350        if (isFastScope(symbol)) {
351            // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
352            if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD && !isOptimisticOrRestOf()) {
353                method.loadCompilerConstant(SCOPE);
354                // As shared scope vars are only used in non-optimistic compilation, we switch from using TypeBounds to
355                // just a single definitive type, resultBounds.widest.
356                loadSharedScopeVar(resultBounds.widest, symbol, flags);
357            } else {
358                new LoadFastScopeVar(identNode, resultBounds, flags).emit();
359            }
360        } else {
361            //slow scope load, we have no proto depth
362            new LoadScopeVar(identNode, resultBounds, flags).emit();
363        }
364
365        return method;
366    }
367
368    // Any access to LET and CONST variables before their declaration must throw ReferenceError.
369    // This is called the temporal dead zone (TDZ). See https://gist.github.com/rwaldron/f0807a758aa03bcdd58a
370    private void checkTemporalDeadZone(final IdentNode identNode) {
371        if (identNode.isDead()) {
372            method.load(identNode.getSymbol().getName()).invoke(ScriptRuntime.THROW_REFERENCE_ERROR);
373        }
374    }
375
376    // Runtime check for assignment to ES6 const
377    private void checkAssignTarget(final Expression expression) {
378        if (expression instanceof IdentNode && ((IdentNode)expression).getSymbol().isConst()) {
379            method.load(((IdentNode)expression).getSymbol().getName()).invoke(ScriptRuntime.THROW_CONST_TYPE_ERROR);
380        }
381    }
382
383    private boolean isRestOf() {
384        return continuationEntryPoints != null;
385    }
386
387    private boolean isOptimisticOrRestOf() {
388        return useOptimisticTypes() || isRestOf();
389    }
390
391    private boolean isCurrentContinuationEntryPoint(final int programPoint) {
392        return isRestOf() && getCurrentContinuationEntryPoint() == programPoint;
393    }
394
395    private int[] getContinuationEntryPoints() {
396        return isRestOf() ? continuationEntryPoints : null;
397    }
398
399    private int getCurrentContinuationEntryPoint() {
400        return isRestOf() ? continuationEntryPoints[0] : INVALID_PROGRAM_POINT;
401    }
402
403    private boolean isContinuationEntryPoint(final int programPoint) {
404        if (isRestOf()) {
405            assert continuationEntryPoints != null;
406            for (final int cep : continuationEntryPoints) {
407                if (cep == programPoint) {
408                    return true;
409                }
410            }
411        }
412        return false;
413    }
414
415    /**
416     * Check if this symbol can be accessed directly with a putfield or getfield or dynamic load
417     *
418     * @param symbol symbol to check for fast scope
419     * @return true if fast scope
420     */
421    private boolean isFastScope(final Symbol symbol) {
422        if (!symbol.isScope()) {
423            return false;
424        }
425
426        if (!lc.inDynamicScope()) {
427            // If there's no with or eval in context, and the symbol is marked as scoped, it is fast scoped. Such a
428            // symbol must either be global, or its defining block must need scope.
429            assert symbol.isGlobal() || lc.getDefiningBlock(symbol).needsScope() : symbol.getName();
430            return true;
431        }
432
433        if (symbol.isGlobal()) {
434            // Shortcut: if there's a with or eval in context, globals can't be fast scoped
435            return false;
436        }
437
438        // Otherwise, check if there's a dynamic scope between use of the symbol and its definition
439        final String name = symbol.getName();
440        boolean previousWasBlock = false;
441        for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
442            final LexicalContextNode node = it.next();
443            if (node instanceof Block) {
444                // If this block defines the symbol, then we can fast scope the symbol.
445                final Block block = (Block)node;
446                if (block.getExistingSymbol(name) == symbol) {
447                    assert block.needsScope();
448                    return true;
449                }
450                previousWasBlock = true;
451            } else {
452                if (node instanceof WithNode && previousWasBlock || node instanceof FunctionNode && ((FunctionNode)node).needsDynamicScope()) {
453                    // If we hit a scope that can have symbols introduced into it at run time before finding the defining
454                    // block, the symbol can't be fast scoped. A WithNode only counts if we've immediately seen a block
455                    // before - its block. Otherwise, we are currently processing the WithNode's expression, and that's
456                    // obviously not subjected to introducing new symbols.
457                    return false;
458                }
459                previousWasBlock = false;
460            }
461        }
462        // Should've found the symbol defined in a block
463        throw new AssertionError();
464    }
465
466    private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) {
467        assert !isOptimisticOrRestOf();
468        if (isFastScope(symbol)) {
469            method.load(getScopeProtoDepth(lc.getCurrentBlock(), symbol));
470        } else {
471            method.load(-1);
472        }
473        return lc.getScopeGet(unit, symbol, valueType, flags).generateInvoke(method);
474    }
475
476    private class LoadScopeVar extends OptimisticOperation {
477        final IdentNode identNode;
478        private final int flags;
479
480        LoadScopeVar(final IdentNode identNode, final TypeBounds resultBounds, final int flags) {
481            super(identNode, resultBounds);
482            this.identNode = identNode;
483            this.flags = flags;
484        }
485
486        @Override
487        void loadStack() {
488            method.loadCompilerConstant(SCOPE);
489            getProto();
490        }
491
492        void getProto() {
493            //empty
494        }
495
496        @Override
497        void consumeStack() {
498            // If this is either __FILE__, __DIR__, or __LINE__ then load the property initially as Object as we'd convert
499            // it anyway for replaceLocationPropertyPlaceholder.
500            if(identNode.isCompileTimePropertyName()) {
501                method.dynamicGet(Type.OBJECT, identNode.getSymbol().getName(), flags, identNode.isFunction(), false);
502                replaceCompileTimeProperty();
503            } else {
504                dynamicGet(identNode.getSymbol().getName(), flags, identNode.isFunction(), false);
505            }
506        }
507    }
508
509    private class LoadFastScopeVar extends LoadScopeVar {
510        LoadFastScopeVar(final IdentNode identNode, final TypeBounds resultBounds, final int flags) {
511            super(identNode, resultBounds, flags);
512        }
513
514        @Override
515        void getProto() {
516            loadFastScopeProto(identNode.getSymbol(), false);
517        }
518    }
519
520    private MethodEmitter storeFastScopeVar(final Symbol symbol, final int flags) {
521        loadFastScopeProto(symbol, true);
522        method.dynamicSet(symbol.getName(), flags, false);
523        return method;
524    }
525
526    private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) {
527        //walk up the chain from starting block and when we bump into the current function boundary, add the external
528        //information.
529        final FunctionNode fn   = lc.getCurrentFunction();
530        final int externalDepth = compiler.getScriptFunctionData(fn.getId()).getExternalSymbolDepth(symbol.getName());
531
532        //count the number of scopes from this place to the start of the function
533
534        final int internalDepth = FindScopeDepths.findInternalDepth(lc, fn, startingBlock, symbol);
535        final int scopesToStart = FindScopeDepths.findScopesToStart(lc, fn, startingBlock);
536        int depth = 0;
537        if (internalDepth == -1) {
538            depth = scopesToStart + externalDepth;
539        } else {
540            assert internalDepth <= scopesToStart;
541            depth = internalDepth;
542        }
543
544        return depth;
545    }
546
547    private void loadFastScopeProto(final Symbol symbol, final boolean swap) {
548        final int depth = getScopeProtoDepth(lc.getCurrentBlock(), symbol);
549        assert depth != -1 : "Couldn't find scope depth for symbol " + symbol.getName() + " in " + lc.getCurrentFunction();
550        if (depth > 0) {
551            if (swap) {
552                method.swap();
553            }
554            for (int i = 0; i < depth; i++) {
555                method.invoke(ScriptObject.GET_PROTO);
556            }
557            if (swap) {
558                method.swap();
559            }
560        }
561    }
562
563    /**
564     * Generate code that loads this node to the stack, not constraining its type
565     *
566     * @param expr node to load
567     *
568     * @return the method emitter used
569     */
570    private MethodEmitter loadExpressionUnbounded(final Expression expr) {
571        return loadExpression(expr, TypeBounds.UNBOUNDED);
572    }
573
574    private MethodEmitter loadExpressionAsObject(final Expression expr) {
575        return loadExpression(expr, TypeBounds.OBJECT);
576    }
577
578    MethodEmitter loadExpressionAsBoolean(final Expression expr) {
579        return loadExpression(expr, TypeBounds.BOOLEAN);
580    }
581
582    // Test whether conversion from source to target involves a call of ES 9.1 ToPrimitive
583    // with possible side effects from calling an object's toString or valueOf methods.
584    private static boolean noToPrimitiveConversion(final Type source, final Type target) {
585        // Object to boolean conversion does not cause ToPrimitive call
586        return source.isJSPrimitive() || !target.isJSPrimitive() || target.isBoolean();
587    }
588
589    MethodEmitter loadBinaryOperands(final BinaryNode binaryNode) {
590        return loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), TypeBounds.UNBOUNDED.notWiderThan(binaryNode.getWidestOperandType()), false, false);
591    }
592
593    private MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final TypeBounds explicitOperandBounds, final boolean baseAlreadyOnStack, final boolean forceConversionSeparation) {
594        // ECMAScript 5.1 specification (sections 11.5-11.11 and 11.13) prescribes that when evaluating a binary
595        // expression "LEFT op RIGHT", the order of operations must be: LOAD LEFT, LOAD RIGHT, CONVERT LEFT, CONVERT
596        // RIGHT, EXECUTE OP. Unfortunately, doing it in this order defeats potential optimizations that arise when we
597        // can combine a LOAD with a CONVERT operation (e.g. use a dynamic getter with the conversion target type as its
598        // return value). What we do here is reorder LOAD RIGHT and CONVERT LEFT when possible; it is possible only when
599        // we can prove that executing CONVERT LEFT can't have a side effect that changes the value of LOAD RIGHT.
600        // Basically, if we know that either LEFT already is a primitive value, or does not have to be converted to
601        // a primitive value, or RIGHT is an expression that loads without side effects, then we can do the
602        // reordering and collapse LOAD/CONVERT into a single operation; otherwise we need to do the more costly
603        // separate operations to preserve specification semantics.
604
605        // Operands' load type should not be narrower than the narrowest of the individual operand types, nor narrower
606        // than the lower explicit bound, but it should also not be wider than
607        final Type lhsType = undefinedToNumber(lhs.getType());
608        final Type rhsType = undefinedToNumber(rhs.getType());
609        final Type narrowestOperandType = Type.narrowest(Type.widest(lhsType, rhsType), explicitOperandBounds.widest);
610        final TypeBounds operandBounds = explicitOperandBounds.notNarrowerThan(narrowestOperandType);
611        if (noToPrimitiveConversion(lhsType, explicitOperandBounds.widest) || rhs.isLocal()) {
612            // Can reorder. We might still need to separate conversion, but at least we can do it with reordering
613            if (forceConversionSeparation) {
614                // Can reorder, but can't move conversion into the operand as the operation depends on operands
615                // exact types for its overflow guarantees. E.g. with {L}{%I}expr1 {L}* {L}{%I}expr2 we are not allowed
616                // to merge {L}{%I} into {%L}, as that can cause subsequent overflows; test for JDK-8058610 contains
617                // concrete cases where this could happen.
618                final TypeBounds safeConvertBounds = TypeBounds.UNBOUNDED.notNarrowerThan(narrowestOperandType);
619                loadExpression(lhs, safeConvertBounds, baseAlreadyOnStack);
620                method.convert(operandBounds.within(method.peekType()));
621                loadExpression(rhs, safeConvertBounds, false);
622                method.convert(operandBounds.within(method.peekType()));
623            } else {
624                // Can reorder and move conversion into the operand. Combine load and convert into single operations.
625                loadExpression(lhs, operandBounds, baseAlreadyOnStack);
626                loadExpression(rhs, operandBounds, false);
627            }
628        } else {
629            // Can't reorder. Load and convert separately.
630            final TypeBounds safeConvertBounds = TypeBounds.UNBOUNDED.notNarrowerThan(narrowestOperandType);
631            loadExpression(lhs, safeConvertBounds, baseAlreadyOnStack);
632            final Type lhsLoadedType = method.peekType();
633            loadExpression(rhs, safeConvertBounds, false);
634            final Type convertedLhsType = operandBounds.within(method.peekType());
635            if (convertedLhsType != lhsLoadedType) {
636                // Do it conditionally, so that if conversion is a no-op we don't introduce a SWAP, SWAP.
637                method.swap().convert(convertedLhsType).swap();
638            }
639            method.convert(operandBounds.within(method.peekType()));
640        }
641        assert Type.generic(method.peekType()) == operandBounds.narrowest;
642        assert Type.generic(method.peekType(1)) == operandBounds.narrowest;
643
644        return method;
645    }
646
647    /**
648     * Similar to {@link #loadBinaryOperands(BinaryNode)} but used specifically for loading operands of
649     * relational and equality comparison operators where at least one argument is non-object. (When both
650     * arguments are objects, we use {@link ScriptRuntime#EQ(Object, Object)}, {@link ScriptRuntime#LT(Object, Object)}
651     * etc. methods instead. Additionally, {@code ScriptRuntime} methods are used for strict (in)equality comparison
652     * of a boolean to anything that isn't a boolean.) This method handles the special case where one argument
653     * is an object and another is a primitive. Naively, these could also be delegated to {@code ScriptRuntime} methods
654     * by boxing the primitive. However, in all such cases the comparison is performed on numeric values, so it is
655     * possible to strength-reduce the operation by taking the number value of the object argument instead and
656     * comparing that to the primitive value ("primitive" will always be int, long, double, or boolean, and booleans
657     * compare as ints in these cases, so they're essentially numbers too). This method will emit code for loading
658     * arguments for such strength-reduced comparison. When both arguments are primitives, it just delegates to
659     * {@link #loadBinaryOperands(BinaryNode)}.
660     *
661     * @param cmp the comparison operation for which the operands need to be loaded on stack.
662     * @return the current method emitter.
663     */
664    MethodEmitter loadComparisonOperands(final BinaryNode cmp) {
665        final Expression lhs = cmp.lhs();
666        final Expression rhs = cmp.rhs();
667        final Type lhsType = lhs.getType();
668        final Type rhsType = rhs.getType();
669
670        // Only used when not both are object, for that we have ScriptRuntime.LT etc.
671        assert !(lhsType.isObject() && rhsType.isObject());
672
673        if (lhsType.isObject() || rhsType.isObject()) {
674            // We can reorder CONVERT LEFT and LOAD RIGHT only if either the left is a primitive, or the right
675            // is a local. This is more strict than loadBinaryNode reorder criteria, as it can allow JS primitive
676            // types too (notably: String is a JS primitive, but not a JVM primitive). We disallow String otherwise
677            // we would prematurely convert it to number when comparing to an optimistic expression, e.g. in
678            // "Hello" === String("Hello") the RHS starts out as an optimistic-int function call. If we allowed
679            // reordering, we'd end up with ToNumber("Hello") === {I%}String("Hello") that is obviously incorrect.
680            final boolean canReorder = lhsType.isPrimitive() || rhs.isLocal();
681            // If reordering is allowed, and we're using a relational operator (that is, <, <=, >, >=) and not an
682            // (in)equality operator, then we encourage combining of LOAD and CONVERT into a single operation.
683            // This is because relational operators' semantics prescribes vanilla ToNumber() conversion, while
684            // (in)equality operators need the specialized JSType.toNumberFor[Strict]Equals. E.g. in the code snippet
685            // "i < obj.size" (where i is primitive and obj.size is statically an object), ".size" will thus be allowed
686            // to compile as:
687            //   invokedynamic dyn:getProp|getElem|getMethod:size(Object;)D
688            // instead of the more costly:
689            //   invokedynamic dyn:getProp|getElem|getMethod:size(Object;)Object
690            //   invokestatic JSType.toNumber(Object)D
691            // Note also that even if this is allowed, we're only using it on operands that are non-optimistic, as
692            // otherwise the logic for determining effective optimistic-ness would turn an optimistic double return
693            // into a freely coercible one, which would be wrong.
694            final boolean canCombineLoadAndConvert = canReorder && cmp.isRelational();
695
696            // LOAD LEFT
697            loadExpression(lhs, canCombineLoadAndConvert && !lhs.isOptimistic() ? TypeBounds.NUMBER : TypeBounds.UNBOUNDED);
698
699            final Type lhsLoadedType = method.peekType();
700            final TokenType tt = cmp.tokenType();
701            if (canReorder) {
702                // Can reorder CONVERT LEFT and LOAD RIGHT
703                emitObjectToNumberComparisonConversion(method, tt);
704                loadExpression(rhs, canCombineLoadAndConvert && !rhs.isOptimistic() ? TypeBounds.NUMBER : TypeBounds.UNBOUNDED);
705            } else {
706                // Can't reorder CONVERT LEFT and LOAD RIGHT
707                loadExpression(rhs, TypeBounds.UNBOUNDED);
708                if (lhsLoadedType != Type.NUMBER) {
709                    method.swap();
710                    emitObjectToNumberComparisonConversion(method, tt);
711                    method.swap();
712                }
713            }
714
715            // CONVERT RIGHT
716            emitObjectToNumberComparisonConversion(method, tt);
717            return method;
718        }
719        // For primitive operands, just don't do anything special.
720        return loadBinaryOperands(cmp);
721    }
722
723    private static void emitObjectToNumberComparisonConversion(final MethodEmitter method, final TokenType tt) {
724        switch(tt) {
725        case EQ:
726        case NE:
727            if (method.peekType().isObject()) {
728                TO_NUMBER_FOR_EQ.invoke(method);
729                return;
730            }
731            break;
732        case EQ_STRICT:
733        case NE_STRICT:
734            if (method.peekType().isObject()) {
735                TO_NUMBER_FOR_STRICT_EQ.invoke(method);
736                return;
737            }
738            break;
739        default:
740            break;
741        }
742        method.convert(Type.NUMBER);
743    }
744
745    private static final Type undefinedToNumber(final Type type) {
746        return type == Type.UNDEFINED ? Type.NUMBER : type;
747    }
748
749    private static final class TypeBounds {
750        final Type narrowest;
751        final Type widest;
752
753        static final TypeBounds UNBOUNDED = new TypeBounds(Type.UNKNOWN, Type.OBJECT);
754        static final TypeBounds INT = exact(Type.INT);
755        static final TypeBounds NUMBER = exact(Type.NUMBER);
756        static final TypeBounds OBJECT = exact(Type.OBJECT);
757        static final TypeBounds BOOLEAN = exact(Type.BOOLEAN);
758
759        static TypeBounds exact(final Type type) {
760            return new TypeBounds(type, type);
761        }
762
763        TypeBounds(final Type narrowest, final Type widest) {
764            assert widest    != null && widest    != Type.UNDEFINED && widest != Type.UNKNOWN : widest;
765            assert narrowest != null && narrowest != Type.UNDEFINED : narrowest;
766            assert !narrowest.widerThan(widest) : narrowest + " wider than " + widest;
767            assert !widest.narrowerThan(narrowest);
768            this.narrowest = Type.generic(narrowest);
769            this.widest = Type.generic(widest);
770        }
771
772        TypeBounds notNarrowerThan(final Type type) {
773            return maybeNew(Type.narrowest(Type.widest(narrowest, type), widest), widest);
774        }
775
776        TypeBounds notWiderThan(final Type type) {
777            return maybeNew(Type.narrowest(narrowest, type), Type.narrowest(widest, type));
778        }
779
780        boolean canBeNarrowerThan(final Type type) {
781            return narrowest.narrowerThan(type);
782        }
783
784        TypeBounds maybeNew(final Type newNarrowest, final Type newWidest) {
785            if(newNarrowest == narrowest && newWidest == widest) {
786                return this;
787            }
788            return new TypeBounds(newNarrowest, newWidest);
789        }
790
791        TypeBounds booleanToInt() {
792            return maybeNew(CodeGenerator.booleanToInt(narrowest), CodeGenerator.booleanToInt(widest));
793        }
794
795        TypeBounds objectToNumber() {
796            return maybeNew(CodeGenerator.objectToNumber(narrowest), CodeGenerator.objectToNumber(widest));
797        }
798
799        Type within(final Type type) {
800            if(type.narrowerThan(narrowest)) {
801                return narrowest;
802            }
803            if(type.widerThan(widest)) {
804                return widest;
805            }
806            return type;
807        }
808
809        @Override
810        public String toString() {
811            return "[" + narrowest + ", " + widest + "]";
812        }
813    }
814
815    private static Type booleanToInt(final Type t) {
816        return t == Type.BOOLEAN ? Type.INT : t;
817    }
818
819    private static Type objectToNumber(final Type t) {
820        return t.isObject() ? Type.NUMBER : t;
821    }
822
823    MethodEmitter loadExpressionAsType(final Expression expr, final Type type) {
824        if(type == Type.BOOLEAN) {
825            return loadExpressionAsBoolean(expr);
826        } else if(type == Type.UNDEFINED) {
827            assert expr.getType() == Type.UNDEFINED;
828            return loadExpressionAsObject(expr);
829        }
830        // having no upper bound preserves semantics of optimistic operations in the expression (by not having them
831        // converted early) and then applies explicit conversion afterwards.
832        return loadExpression(expr, TypeBounds.UNBOUNDED.notNarrowerThan(type)).convert(type);
833    }
834
835    private MethodEmitter loadExpression(final Expression expr, final TypeBounds resultBounds) {
836        return loadExpression(expr, resultBounds, false);
837    }
838
839    /**
840     * Emits code for evaluating an expression and leaving its value on top of the stack, narrowing or widening it if
841     * necessary.
842     * @param expr the expression to load
843     * @param resultBounds the incoming type bounds. The value on the top of the stack is guaranteed to not be of narrower
844     * type than the narrowest bound, or wider type than the widest bound after it is loaded.
845     * @param baseAlreadyOnStack true if the base of an access or index node is already on the stack. Used to avoid
846     * double evaluation of bases in self-assignment expressions to access and index nodes. {@code Type.OBJECT} is used
847     * to indicate the widest possible type.
848     * @return the method emitter
849     */
850    private MethodEmitter loadExpression(final Expression expr, final TypeBounds resultBounds, final boolean baseAlreadyOnStack) {
851
852        /*
853         * The load may be of type IdentNode, e.g. "x", AccessNode, e.g. "x.y"
854         * or IndexNode e.g. "x[y]". Both AccessNodes and IndexNodes are
855         * BaseNodes and the logic for loading the base object is reused
856         */
857        final CodeGenerator codegen = this;
858
859        final boolean isCurrentDiscard = codegen.lc.isCurrentDiscard(expr);
860        expr.accept(new NodeOperatorVisitor<LexicalContext>(new LexicalContext()) {
861            @Override
862            public boolean enterIdentNode(final IdentNode identNode) {
863                loadIdent(identNode, resultBounds);
864                return false;
865            }
866
867            @Override
868            public boolean enterAccessNode(final AccessNode accessNode) {
869                new OptimisticOperation(accessNode, resultBounds) {
870                    @Override
871                    void loadStack() {
872                        if (!baseAlreadyOnStack) {
873                            loadExpressionAsObject(accessNode.getBase());
874                        }
875                        assert method.peekType().isObject();
876                    }
877                    @Override
878                    void consumeStack() {
879                        final int flags = getCallSiteFlags();
880                        dynamicGet(accessNode.getProperty(), flags, accessNode.isFunction(), accessNode.isIndex());
881                    }
882                }.emit(baseAlreadyOnStack ? 1 : 0);
883                return false;
884            }
885
886            @Override
887            public boolean enterIndexNode(final IndexNode indexNode) {
888                new OptimisticOperation(indexNode, resultBounds) {
889                    @Override
890                    void loadStack() {
891                        if (!baseAlreadyOnStack) {
892                            loadExpressionAsObject(indexNode.getBase());
893                            loadExpressionUnbounded(indexNode.getIndex());
894                        }
895                    }
896                    @Override
897                    void consumeStack() {
898                        final int flags = getCallSiteFlags();
899                        dynamicGetIndex(flags, indexNode.isFunction());
900                    }
901                }.emit(baseAlreadyOnStack ? 2 : 0);
902                return false;
903            }
904
905            @Override
906            public boolean enterFunctionNode(final FunctionNode functionNode) {
907                // function nodes will always leave a constructed function object on stack, no need to load the symbol
908                // separately as in enterDefault()
909                lc.pop(functionNode);
910                functionNode.accept(codegen);
911                // NOTE: functionNode.accept() will produce a different FunctionNode that we discard. This incidentally
912                // doesn't cause problems as we're never touching FunctionNode again after it's visited here - codegen
913                // is the last element in the compilation pipeline, the AST it produces is not used externally. So, we
914                // re-push the original functionNode.
915                lc.push(functionNode);
916                return false;
917            }
918
919            @Override
920            public boolean enterASSIGN(final BinaryNode binaryNode) {
921                checkAssignTarget(binaryNode.lhs());
922                loadASSIGN(binaryNode);
923                return false;
924            }
925
926            @Override
927            public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
928                checkAssignTarget(binaryNode.lhs());
929                loadASSIGN_ADD(binaryNode);
930                return false;
931            }
932
933            @Override
934            public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
935                checkAssignTarget(binaryNode.lhs());
936                loadASSIGN_BIT_AND(binaryNode);
937                return false;
938            }
939
940            @Override
941            public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
942                checkAssignTarget(binaryNode.lhs());
943                loadASSIGN_BIT_OR(binaryNode);
944                return false;
945            }
946
947            @Override
948            public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
949                checkAssignTarget(binaryNode.lhs());
950                loadASSIGN_BIT_XOR(binaryNode);
951                return false;
952            }
953
954            @Override
955            public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
956                checkAssignTarget(binaryNode.lhs());
957                loadASSIGN_DIV(binaryNode);
958                return false;
959            }
960
961            @Override
962            public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
963                checkAssignTarget(binaryNode.lhs());
964                loadASSIGN_MOD(binaryNode);
965                return false;
966            }
967
968            @Override
969            public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
970                checkAssignTarget(binaryNode.lhs());
971                loadASSIGN_MUL(binaryNode);
972                return false;
973            }
974
975            @Override
976            public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
977                checkAssignTarget(binaryNode.lhs());
978                loadASSIGN_SAR(binaryNode);
979                return false;
980            }
981
982            @Override
983            public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
984                checkAssignTarget(binaryNode.lhs());
985                loadASSIGN_SHL(binaryNode);
986                return false;
987            }
988
989            @Override
990            public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
991                checkAssignTarget(binaryNode.lhs());
992                loadASSIGN_SHR(binaryNode);
993                return false;
994            }
995
996            @Override
997            public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
998                checkAssignTarget(binaryNode.lhs());
999                loadASSIGN_SUB(binaryNode);
1000                return false;
1001            }
1002
1003            @Override
1004            public boolean enterCallNode(final CallNode callNode) {
1005                return loadCallNode(callNode, resultBounds);
1006            }
1007
1008            @Override
1009            public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
1010                loadLiteral(literalNode, resultBounds);
1011                return false;
1012            }
1013
1014            @Override
1015            public boolean enterTernaryNode(final TernaryNode ternaryNode) {
1016                loadTernaryNode(ternaryNode, resultBounds);
1017                return false;
1018            }
1019
1020            @Override
1021            public boolean enterADD(final BinaryNode binaryNode) {
1022                loadADD(binaryNode, resultBounds);
1023                return false;
1024            }
1025
1026            @Override
1027            public boolean enterSUB(final UnaryNode unaryNode) {
1028                loadSUB(unaryNode, resultBounds);
1029                return false;
1030            }
1031
1032            @Override
1033            public boolean enterSUB(final BinaryNode binaryNode) {
1034                loadSUB(binaryNode, resultBounds);
1035                return false;
1036            }
1037
1038            @Override
1039            public boolean enterMUL(final BinaryNode binaryNode) {
1040                loadMUL(binaryNode, resultBounds);
1041                return false;
1042            }
1043
1044            @Override
1045            public boolean enterDIV(final BinaryNode binaryNode) {
1046                loadDIV(binaryNode, resultBounds);
1047                return false;
1048            }
1049
1050            @Override
1051            public boolean enterMOD(final BinaryNode binaryNode) {
1052                loadMOD(binaryNode, resultBounds);
1053                return false;
1054            }
1055
1056            @Override
1057            public boolean enterSAR(final BinaryNode binaryNode) {
1058                loadSAR(binaryNode);
1059                return false;
1060            }
1061
1062            @Override
1063            public boolean enterSHL(final BinaryNode binaryNode) {
1064                loadSHL(binaryNode);
1065                return false;
1066            }
1067
1068            @Override
1069            public boolean enterSHR(final BinaryNode binaryNode) {
1070                loadSHR(binaryNode);
1071                return false;
1072            }
1073
1074            @Override
1075            public boolean enterCOMMALEFT(final BinaryNode binaryNode) {
1076                loadCOMMALEFT(binaryNode, resultBounds);
1077                return false;
1078            }
1079
1080            @Override
1081            public boolean enterCOMMARIGHT(final BinaryNode binaryNode) {
1082                loadCOMMARIGHT(binaryNode, resultBounds);
1083                return false;
1084            }
1085
1086            @Override
1087            public boolean enterAND(final BinaryNode binaryNode) {
1088                loadAND_OR(binaryNode, resultBounds, true);
1089                return false;
1090            }
1091
1092            @Override
1093            public boolean enterOR(final BinaryNode binaryNode) {
1094                loadAND_OR(binaryNode, resultBounds, false);
1095                return false;
1096            }
1097
1098            @Override
1099            public boolean enterNOT(final UnaryNode unaryNode) {
1100                loadNOT(unaryNode);
1101                return false;
1102            }
1103
1104            @Override
1105            public boolean enterADD(final UnaryNode unaryNode) {
1106                loadADD(unaryNode, resultBounds);
1107                return false;
1108            }
1109
1110            @Override
1111            public boolean enterBIT_NOT(final UnaryNode unaryNode) {
1112                loadBIT_NOT(unaryNode);
1113                return false;
1114            }
1115
1116            @Override
1117            public boolean enterBIT_AND(final BinaryNode binaryNode) {
1118                loadBIT_AND(binaryNode);
1119                return false;
1120            }
1121
1122            @Override
1123            public boolean enterBIT_OR(final BinaryNode binaryNode) {
1124                loadBIT_OR(binaryNode);
1125                return false;
1126            }
1127
1128            @Override
1129            public boolean enterBIT_XOR(final BinaryNode binaryNode) {
1130                loadBIT_XOR(binaryNode);
1131                return false;
1132            }
1133
1134            @Override
1135            public boolean enterVOID(final UnaryNode unaryNode) {
1136                loadVOID(unaryNode, resultBounds);
1137                return false;
1138            }
1139
1140            @Override
1141            public boolean enterEQ(final BinaryNode binaryNode) {
1142                loadCmp(binaryNode, Condition.EQ);
1143                return false;
1144            }
1145
1146            @Override
1147            public boolean enterEQ_STRICT(final BinaryNode binaryNode) {
1148                loadCmp(binaryNode, Condition.EQ);
1149                return false;
1150            }
1151
1152            @Override
1153            public boolean enterGE(final BinaryNode binaryNode) {
1154                loadCmp(binaryNode, Condition.GE);
1155                return false;
1156            }
1157
1158            @Override
1159            public boolean enterGT(final BinaryNode binaryNode) {
1160                loadCmp(binaryNode, Condition.GT);
1161                return false;
1162            }
1163
1164            @Override
1165            public boolean enterLE(final BinaryNode binaryNode) {
1166                loadCmp(binaryNode, Condition.LE);
1167                return false;
1168            }
1169
1170            @Override
1171            public boolean enterLT(final BinaryNode binaryNode) {
1172                loadCmp(binaryNode, Condition.LT);
1173                return false;
1174            }
1175
1176            @Override
1177            public boolean enterNE(final BinaryNode binaryNode) {
1178                loadCmp(binaryNode, Condition.NE);
1179                return false;
1180            }
1181
1182            @Override
1183            public boolean enterNE_STRICT(final BinaryNode binaryNode) {
1184                loadCmp(binaryNode, Condition.NE);
1185                return false;
1186            }
1187
1188            @Override
1189            public boolean enterObjectNode(final ObjectNode objectNode) {
1190                loadObjectNode(objectNode);
1191                return false;
1192            }
1193
1194            @Override
1195            public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
1196                loadRuntimeNode(runtimeNode);
1197                return false;
1198            }
1199
1200            @Override
1201            public boolean enterNEW(final UnaryNode unaryNode) {
1202                loadNEW(unaryNode);
1203                return false;
1204            }
1205
1206            @Override
1207            public boolean enterDECINC(final UnaryNode unaryNode) {
1208                checkAssignTarget(unaryNode.getExpression());
1209                loadDECINC(unaryNode);
1210                return false;
1211            }
1212
1213            @Override
1214            public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinExpr) {
1215                loadMaybeDiscard(joinExpr, joinExpr.getExpression(), resultBounds);
1216                return false;
1217            }
1218
1219            @Override
1220            public boolean enterGetSplitState(final GetSplitState getSplitState) {
1221                method.loadScope();
1222                method.invoke(Scope.GET_SPLIT_STATE);
1223                return false;
1224            }
1225
1226            @Override
1227            public boolean enterDefault(final Node otherNode) {
1228                // Must have handled all expressions that can legally be encountered.
1229                throw new AssertionError(otherNode.getClass().getName());
1230            }
1231        });
1232        if(!isCurrentDiscard) {
1233            coerceStackTop(resultBounds);
1234        }
1235        return method;
1236    }
1237
1238    private MethodEmitter coerceStackTop(final TypeBounds typeBounds) {
1239        return method.convert(typeBounds.within(method.peekType()));
1240    }
1241
1242    /**
1243     * Closes any still open entries for this block's local variables in the bytecode local variable table.
1244     *
1245     * @param block block containing symbols.
1246     */
1247    private void closeBlockVariables(final Block block) {
1248        for (final Symbol symbol : block.getSymbols()) {
1249            if (symbol.isBytecodeLocal()) {
1250                method.closeLocalVariable(symbol, block.getBreakLabel());
1251            }
1252        }
1253    }
1254
1255    @Override
1256    public boolean enterBlock(final Block block) {
1257        final Label entryLabel = block.getEntryLabel();
1258        if (entryLabel.isBreakTarget()) {
1259            // Entry label is a break target only for an inlined finally block.
1260            assert !method.isReachable();
1261            method.breakLabel(entryLabel, lc.getUsedSlotCount());
1262        } else {
1263            method.label(entryLabel);
1264        }
1265        if(!method.isReachable()) {
1266            return false;
1267        }
1268        if(lc.isFunctionBody() && emittedMethods.contains(lc.getCurrentFunction().getName())) {
1269            return false;
1270        }
1271        initLocals(block);
1272
1273        assert lc.getUsedSlotCount() == method.getFirstTemp();
1274        return true;
1275    }
1276
1277    boolean useOptimisticTypes() {
1278        return !lc.inSplitNode() && compiler.useOptimisticTypes();
1279    }
1280
1281    @Override
1282    public Node leaveBlock(final Block block) {
1283        popBlockScope(block);
1284        method.beforeJoinPoint(block);
1285
1286        closeBlockVariables(block);
1287        lc.releaseSlots();
1288        assert !method.isReachable() || (lc.isFunctionBody() ? 0 : lc.getUsedSlotCount()) == method.getFirstTemp() :
1289            "reachable="+method.isReachable() +
1290            " isFunctionBody=" + lc.isFunctionBody() +
1291            " usedSlotCount=" + lc.getUsedSlotCount() +
1292            " firstTemp=" + method.getFirstTemp();
1293
1294        return block;
1295    }
1296
1297    private void popBlockScope(final Block block) {
1298        final Label breakLabel = block.getBreakLabel();
1299
1300        if(!block.needsScope() || lc.isFunctionBody()) {
1301            emitBlockBreakLabel(breakLabel);
1302            return;
1303        }
1304
1305        final Label beginTryLabel = scopeEntryLabels.pop();
1306        final Label recoveryLabel = new Label("block_popscope_catch");
1307        emitBlockBreakLabel(breakLabel);
1308        final boolean bodyCanThrow = breakLabel.isAfter(beginTryLabel);
1309        if(bodyCanThrow) {
1310            method._try(beginTryLabel, breakLabel, recoveryLabel);
1311        }
1312
1313        Label afterCatchLabel = null;
1314
1315        if(method.isReachable()) {
1316            popScope();
1317            if(bodyCanThrow) {
1318                afterCatchLabel = new Label("block_after_catch");
1319                method._goto(afterCatchLabel);
1320            }
1321        }
1322
1323        if(bodyCanThrow) {
1324            assert !method.isReachable();
1325            method._catch(recoveryLabel);
1326            popScopeException();
1327            method.athrow();
1328        }
1329        if(afterCatchLabel != null) {
1330            method.label(afterCatchLabel);
1331        }
1332    }
1333
1334    private void emitBlockBreakLabel(final Label breakLabel) {
1335        // TODO: this is totally backwards. Block should not be breakable, LabelNode should be breakable.
1336        final LabelNode labelNode = lc.getCurrentBlockLabelNode();
1337        if(labelNode != null) {
1338            // Only have conversions if we're reachable
1339            assert labelNode.getLocalVariableConversion() == null || method.isReachable();
1340            method.beforeJoinPoint(labelNode);
1341            method.breakLabel(breakLabel, labeledBlockBreakLiveLocals.pop());
1342        } else {
1343            method.label(breakLabel);
1344        }
1345    }
1346
1347    private void popScope() {
1348        popScopes(1);
1349    }
1350
1351    /**
1352     * Pop scope as part of an exception handler. Similar to {@code popScope()} but also takes care of adjusting the
1353     * number of scopes that needs to be popped in case a rest-of continuation handler encounters an exception while
1354     * performing a ToPrimitive conversion.
1355     */
1356    private void popScopeException() {
1357        popScope();
1358        final ContinuationInfo ci = getContinuationInfo();
1359        if(ci != null) {
1360            final Label catchLabel = ci.catchLabel;
1361            if(catchLabel != METHOD_BOUNDARY && catchLabel == catchLabels.peek()) {
1362                ++ci.exceptionScopePops;
1363            }
1364        }
1365    }
1366
1367    private void popScopesUntil(final LexicalContextNode until) {
1368        popScopes(lc.getScopeNestingLevelTo(until));
1369    }
1370
1371    private void popScopes(final int count) {
1372        if(count == 0) {
1373            return;
1374        }
1375        assert count > 0; // together with count == 0 check, asserts nonnegative count
1376        if (!method.hasScope()) {
1377            // We can sometimes invoke this method even if the method has no slot for the scope object. Typical example:
1378            // for(;;) { with({}) { break; } }. WithNode normally creates a scope, but if it uses no identifiers and
1379            // nothing else forces creation of a scope in the method, we just won't have the :scope local variable.
1380            return;
1381        }
1382        method.loadCompilerConstant(SCOPE);
1383        for(int i = 0; i < count; ++i) {
1384            method.invoke(ScriptObject.GET_PROTO);
1385        }
1386        method.storeCompilerConstant(SCOPE);
1387    }
1388
1389    @Override
1390    public boolean enterBreakNode(final BreakNode breakNode) {
1391        return enterJumpStatement(breakNode);
1392    }
1393
1394    @Override
1395    public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) {
1396        return enterJumpStatement(jumpToInlinedFinally);
1397    }
1398
1399    private boolean enterJumpStatement(final JumpStatement jump) {
1400        if(!method.isReachable()) {
1401            return false;
1402        }
1403        enterStatement(jump);
1404
1405        method.beforeJoinPoint(jump);
1406        popScopesUntil(jump.getPopScopeLimit(lc));
1407        final Label targetLabel = jump.getTargetLabel(lc);
1408        targetLabel.markAsBreakTarget();
1409        method._goto(targetLabel);
1410
1411        return false;
1412    }
1413
1414    private int loadArgs(final List<Expression> args) {
1415        final int argCount = args.size();
1416        // arg have already been converted to objects here.
1417        if (argCount > LinkerCallSite.ARGLIMIT) {
1418            loadArgsArray(args);
1419            return 1;
1420        }
1421
1422        for (final Expression arg : args) {
1423            assert arg != null;
1424            loadExpressionUnbounded(arg);
1425        }
1426        return argCount;
1427    }
1428
1429    private boolean loadCallNode(final CallNode callNode, final TypeBounds resultBounds) {
1430        lineNumber(callNode.getLineNumber());
1431
1432        final List<Expression> args = callNode.getArgs();
1433        final Expression function = callNode.getFunction();
1434        final Block currentBlock = lc.getCurrentBlock();
1435        final CodeGeneratorLexicalContext codegenLexicalContext = lc;
1436
1437        function.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
1438
1439            private MethodEmitter sharedScopeCall(final IdentNode identNode, final int flags) {
1440                final Symbol symbol = identNode.getSymbol();
1441                final boolean isFastScope = isFastScope(symbol);
1442                new OptimisticOperation(callNode, resultBounds) {
1443                    @Override
1444                    void loadStack() {
1445                        method.loadCompilerConstant(SCOPE);
1446                        if (isFastScope) {
1447                            method.load(getScopeProtoDepth(currentBlock, symbol));
1448                        } else {
1449                            method.load(-1); // Bypass fast-scope code in shared callsite
1450                        }
1451                        loadArgs(args);
1452                    }
1453                    @Override
1454                    void consumeStack() {
1455                        final Type[] paramTypes = method.getTypesFromStack(args.size());
1456                        // We have trouble finding e.g. in Type.typeFor(asm.Type) because it can't see the Context class
1457                        // loader, so we need to weaken reference signatures to Object.
1458                        for(int i = 0; i < paramTypes.length; ++i) {
1459                            paramTypes[i] = Type.generic(paramTypes[i]);
1460                        }
1461                        // As shared scope calls are only used in non-optimistic compilation, we switch from using
1462                        // TypeBounds to just a single definitive type, resultBounds.widest.
1463                        final SharedScopeCall scopeCall = codegenLexicalContext.getScopeCall(unit, symbol,
1464                                identNode.getType(), resultBounds.widest, paramTypes, flags);
1465                        scopeCall.generateInvoke(method);
1466                    }
1467                }.emit();
1468                return method;
1469            }
1470
1471            private void scopeCall(final IdentNode ident, final int flags) {
1472                new OptimisticOperation(callNode, resultBounds) {
1473                    int argsCount;
1474                    @Override
1475                    void loadStack() {
1476                        loadExpressionAsObject(ident); // foo() makes no sense if foo == 3
1477                        // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
1478                        method.loadUndefined(Type.OBJECT); //the 'this'
1479                        argsCount = loadArgs(args);
1480                    }
1481                    @Override
1482                    void consumeStack() {
1483                        dynamicCall(2 + argsCount, flags);
1484                    }
1485                }.emit();
1486            }
1487
1488            private void evalCall(final IdentNode ident, final int flags) {
1489                final Label invoke_direct_eval  = new Label("invoke_direct_eval");
1490                final Label is_not_eval  = new Label("is_not_eval");
1491                final Label eval_done = new Label("eval_done");
1492
1493                new OptimisticOperation(callNode, resultBounds) {
1494                    int argsCount;
1495                    @Override
1496                    void loadStack() {
1497                        /**
1498                         * We want to load 'eval' to check if it is indeed global builtin eval.
1499                         * If this eval call is inside a 'with' statement, dyn:getMethod|getProp|getElem
1500                         * would be generated if ident is a "isFunction". But, that would result in a
1501                         * bound function from WithObject. We don't want that as bound function as that
1502                         * won't be detected as builtin eval. So, we make ident as "not a function" which
1503                         * results in "dyn:getProp|getElem|getMethod" being generated and so WithObject
1504                         * would return unbounded eval function.
1505                         *
1506                         * Example:
1507                         *
1508                         *  var global = this;
1509                         *  function func() {
1510                         *      with({ eval: global.eval) { eval("var x = 10;") }
1511                         *  }
1512                         */
1513                        loadExpressionAsObject(ident.setIsNotFunction()); // Type.OBJECT as foo() makes no sense if foo == 3
1514                        globalIsEval();
1515                        method.ifeq(is_not_eval);
1516
1517                        // Load up self (scope).
1518                        method.loadCompilerConstant(SCOPE);
1519                        final List<Expression> evalArgs = callNode.getEvalArgs().getArgs();
1520                        // load evaluated code
1521                        loadExpressionAsObject(evalArgs.get(0));
1522                        // load second and subsequent args for side-effect
1523                        final int numArgs = evalArgs.size();
1524                        for (int i = 1; i < numArgs; i++) {
1525                            loadAndDiscard(evalArgs.get(i));
1526                        }
1527                        method._goto(invoke_direct_eval);
1528
1529                        method.label(is_not_eval);
1530                        // load this time but with dyn:getMethod|getProp|getElem
1531                        loadExpressionAsObject(ident); // Type.OBJECT as foo() makes no sense if foo == 3
1532                        // This is some scope 'eval' or global eval replaced by user
1533                        // but not the built-in ECMAScript 'eval' function call
1534                        method.loadNull();
1535                        argsCount = loadArgs(callNode.getArgs());
1536                    }
1537
1538                    @Override
1539                    void consumeStack() {
1540                        // Ordinary call
1541                        dynamicCall(2 + argsCount, flags);
1542                        method._goto(eval_done);
1543
1544                        method.label(invoke_direct_eval);
1545                        // Special/extra 'eval' arguments. These can be loaded late (in consumeStack) as we know none of
1546                        // them can ever be optimistic.
1547                        method.loadCompilerConstant(THIS);
1548                        method.load(callNode.getEvalArgs().getLocation());
1549                        method.load(CodeGenerator.this.lc.getCurrentFunction().isStrict());
1550                        // direct call to Global.directEval
1551                        globalDirectEval();
1552                        convertOptimisticReturnValue();
1553                        coerceStackTop(resultBounds);
1554                    }
1555                }.emit();
1556
1557                method.label(eval_done);
1558            }
1559
1560            @Override
1561            public boolean enterIdentNode(final IdentNode node) {
1562                final Symbol symbol = node.getSymbol();
1563
1564                if (symbol.isScope()) {
1565                    final int flags = getScopeCallSiteFlags(symbol);
1566                    final int useCount = symbol.getUseCount();
1567
1568                    // Threshold for generating shared scope callsite is lower for fast scope symbols because we know
1569                    // we can dial in the correct scope. However, we also need to enable it for non-fast scopes to
1570                    // support huge scripts like mandreel.js.
1571                    if (callNode.isEval()) {
1572                        evalCall(node, flags);
1573                    } else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD
1574                            || !isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD
1575                            || CodeGenerator.this.lc.inDynamicScope()
1576                            || isOptimisticOrRestOf()) {
1577                        scopeCall(node, flags);
1578                    } else {
1579                        sharedScopeCall(node, flags);
1580                    }
1581                    assert method.peekType().equals(resultBounds.within(callNode.getType())) : method.peekType() + " != " + resultBounds + "(" + callNode.getType() + ")";
1582                } else {
1583                    enterDefault(node);
1584                }
1585
1586                return false;
1587            }
1588
1589            @Override
1590            public boolean enterAccessNode(final AccessNode node) {
1591                //check if this is an apply to call node. only real applies, that haven't been
1592                //shadowed from their way to the global scope counts
1593
1594                //call nodes have program points.
1595
1596                final int flags = getCallSiteFlags() | (callNode.isApplyToCall() ? CALLSITE_APPLY_TO_CALL : 0);
1597
1598                new OptimisticOperation(callNode, resultBounds) {
1599                    int argCount;
1600                    @Override
1601                    void loadStack() {
1602                        loadExpressionAsObject(node.getBase());
1603                        method.dup();
1604                        // NOTE: not using a nested OptimisticOperation on this dynamicGet, as we expect to get back
1605                        // a callable object. Nobody in their right mind would optimistically type this call site.
1606                        assert !node.isOptimistic();
1607                        method.dynamicGet(node.getType(), node.getProperty(), flags, true, node.isIndex());
1608                        method.swap();
1609                        argCount = loadArgs(args);
1610                    }
1611                    @Override
1612                    void consumeStack() {
1613                        dynamicCall(2 + argCount, flags);
1614                    }
1615                }.emit();
1616
1617                return false;
1618            }
1619
1620            @Override
1621            public boolean enterFunctionNode(final FunctionNode origCallee) {
1622                new OptimisticOperation(callNode, resultBounds) {
1623                    FunctionNode callee;
1624                    int argsCount;
1625                    @Override
1626                    void loadStack() {
1627                        callee = (FunctionNode)origCallee.accept(CodeGenerator.this);
1628                        if (callee.isStrict()) { // "this" is undefined
1629                            method.loadUndefined(Type.OBJECT);
1630                        } else { // get global from scope (which is the self)
1631                            globalInstance();
1632                        }
1633                        argsCount = loadArgs(args);
1634                    }
1635
1636                    @Override
1637                    void consumeStack() {
1638                        final int flags = getCallSiteFlags();
1639                        //assert callNodeType.equals(callee.getReturnType()) : callNodeType + " != " + callee.getReturnType();
1640                        dynamicCall(2 + argsCount, flags);
1641                    }
1642                }.emit();
1643                return false;
1644            }
1645
1646            @Override
1647            public boolean enterIndexNode(final IndexNode node) {
1648                new OptimisticOperation(callNode, resultBounds) {
1649                    int argsCount;
1650                    @Override
1651                    void loadStack() {
1652                        loadExpressionAsObject(node.getBase());
1653                        method.dup();
1654                        final Type indexType = node.getIndex().getType();
1655                        if (indexType.isObject() || indexType.isBoolean()) {
1656                            loadExpressionAsObject(node.getIndex()); //TODO boolean
1657                        } else {
1658                            loadExpressionUnbounded(node.getIndex());
1659                        }
1660                        // NOTE: not using a nested OptimisticOperation on this dynamicGetIndex, as we expect to get
1661                        // back a callable object. Nobody in their right mind would optimistically type this call site.
1662                        assert !node.isOptimistic();
1663                        method.dynamicGetIndex(node.getType(), getCallSiteFlags(), true);
1664                        method.swap();
1665                        argsCount = loadArgs(args);
1666                    }
1667                    @Override
1668                    void consumeStack() {
1669                        final int flags = getCallSiteFlags();
1670                        dynamicCall(2 + argsCount, flags);
1671                    }
1672                }.emit();
1673                return false;
1674            }
1675
1676            @Override
1677            protected boolean enterDefault(final Node node) {
1678                new OptimisticOperation(callNode, resultBounds) {
1679                    int argsCount;
1680                    @Override
1681                    void loadStack() {
1682                        // Load up function.
1683                        loadExpressionAsObject(function); //TODO, e.g. booleans can be used as functions
1684                        method.loadUndefined(Type.OBJECT); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE
1685                        argsCount = loadArgs(args);
1686                        }
1687                        @Override
1688                        void consumeStack() {
1689                            final int flags = getCallSiteFlags() | CALLSITE_SCOPE;
1690                            dynamicCall(2 + argsCount, flags);
1691                        }
1692                }.emit();
1693                return false;
1694            }
1695        });
1696
1697        return false;
1698    }
1699
1700    /**
1701     * Returns the flags with optimistic flag and program point removed.
1702     * @param flags the flags that need optimism stripped from them.
1703     * @return flags without optimism
1704     */
1705    static int nonOptimisticFlags(final int flags) {
1706        return flags & ~(CALLSITE_OPTIMISTIC | -1 << CALLSITE_PROGRAM_POINT_SHIFT);
1707    }
1708
1709    @Override
1710    public boolean enterContinueNode(final ContinueNode continueNode) {
1711        return enterJumpStatement(continueNode);
1712    }
1713
1714    @Override
1715    public boolean enterEmptyNode(final EmptyNode emptyNode) {
1716        // Don't even record the line number, it's irrelevant as there's no code.
1717        return false;
1718    }
1719
1720    @Override
1721    public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
1722        if(!method.isReachable()) {
1723            return false;
1724        }
1725        enterStatement(expressionStatement);
1726
1727        loadAndDiscard(expressionStatement.getExpression());
1728        assert method.getStackSize() == 0;
1729
1730        return false;
1731    }
1732
1733    @Override
1734    public boolean enterBlockStatement(final BlockStatement blockStatement) {
1735        if(!method.isReachable()) {
1736            return false;
1737        }
1738        enterStatement(blockStatement);
1739
1740        blockStatement.getBlock().accept(this);
1741
1742        return false;
1743    }
1744
1745    @Override
1746    public boolean enterForNode(final ForNode forNode) {
1747        if(!method.isReachable()) {
1748            return false;
1749        }
1750        enterStatement(forNode);
1751        if (forNode.isForIn()) {
1752            enterForIn(forNode);
1753        } else {
1754            final Expression init = forNode.getInit();
1755            if (init != null) {
1756                loadAndDiscard(init);
1757            }
1758            enterForOrWhile(forNode, forNode.getModify());
1759        }
1760
1761        return false;
1762    }
1763
1764    private void enterForIn(final ForNode forNode) {
1765        loadExpression(forNode.getModify(), TypeBounds.OBJECT);
1766        method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR);
1767        final Symbol iterSymbol = forNode.getIterator();
1768        final int iterSlot = iterSymbol.getSlot(Type.OBJECT);
1769        method.store(iterSymbol, ITERATOR_TYPE);
1770
1771        method.beforeJoinPoint(forNode);
1772
1773        final Label continueLabel = forNode.getContinueLabel();
1774        final Label breakLabel    = forNode.getBreakLabel();
1775
1776        method.label(continueLabel);
1777        method.load(ITERATOR_TYPE, iterSlot);
1778        method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "hasNext", boolean.class));
1779        final JoinPredecessorExpression test = forNode.getTest();
1780        final Block body = forNode.getBody();
1781        if(LocalVariableConversion.hasLiveConversion(test)) {
1782            final Label afterConversion = new Label("for_in_after_test_conv");
1783            method.ifne(afterConversion);
1784            method.beforeJoinPoint(test);
1785            method._goto(breakLabel);
1786            method.label(afterConversion);
1787        } else {
1788            method.ifeq(breakLabel);
1789        }
1790
1791        new Store<Expression>(forNode.getInit()) {
1792            @Override
1793            protected void storeNonDiscard() {
1794                // This expression is neither part of a discard, nor needs to be left on the stack after it was
1795                // stored, so we override storeNonDiscard to be a no-op.
1796            }
1797
1798            @Override
1799            protected void evaluate() {
1800                new OptimisticOperation((Optimistic)forNode.getInit(), TypeBounds.UNBOUNDED) {
1801                    @Override
1802                    void loadStack() {
1803                        method.load(ITERATOR_TYPE, iterSlot);
1804                    }
1805
1806                    @Override
1807                    void consumeStack() {
1808                        method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "next", Object.class));
1809                        convertOptimisticReturnValue();
1810                    }
1811                }.emit();
1812            }
1813        }.store();
1814        body.accept(this);
1815
1816        if(method.isReachable()) {
1817            method._goto(continueLabel);
1818        }
1819        method.label(breakLabel);
1820    }
1821
1822    /**
1823     * Initialize the slots in a frame to undefined.
1824     *
1825     * @param block block with local vars.
1826     */
1827    private void initLocals(final Block block) {
1828        lc.onEnterBlock(block);
1829
1830        final boolean isFunctionBody = lc.isFunctionBody();
1831        final FunctionNode function = lc.getCurrentFunction();
1832        if (isFunctionBody) {
1833            initializeMethodParameters(function);
1834            if(!function.isVarArg()) {
1835                expandParameterSlots(function);
1836            }
1837            if (method.hasScope()) {
1838                if (function.needsParentScope()) {
1839                    method.loadCompilerConstant(CALLEE);
1840                    method.invoke(ScriptFunction.GET_SCOPE);
1841                } else {
1842                    assert function.hasScopeBlock();
1843                    method.loadNull();
1844                }
1845                method.storeCompilerConstant(SCOPE);
1846            }
1847            if (function.needsArguments()) {
1848                initArguments(function);
1849            }
1850        }
1851
1852        /*
1853         * Determine if block needs scope, if not, just do initSymbols for this block.
1854         */
1855        if (block.needsScope()) {
1856            /*
1857             * Determine if function is varargs and consequently variables have to
1858             * be in the scope.
1859             */
1860            final boolean varsInScope = function.allVarsInScope();
1861
1862            // TODO for LET we can do better: if *block* does not contain any eval/with, we don't need its vars in scope.
1863
1864            final boolean hasArguments = function.needsArguments();
1865            final List<MapTuple<Symbol>> tuples = new ArrayList<>();
1866            final Iterator<IdentNode> paramIter = function.getParameters().iterator();
1867            for (final Symbol symbol : block.getSymbols()) {
1868                if (symbol.isInternal() || symbol.isThis()) {
1869                    continue;
1870                }
1871
1872                if (symbol.isVar()) {
1873                    assert !varsInScope || symbol.isScope();
1874                    if (varsInScope || symbol.isScope()) {
1875                        assert symbol.isScope()   : "scope for " + symbol + " should have been set in Lower already " + function.getName();
1876                        assert !symbol.hasSlot()  : "slot for " + symbol + " should have been removed in Lower already" + function.getName();
1877
1878                        //this tuple will not be put fielded, as it has no value, just a symbol
1879                        tuples.add(new MapTuple<Symbol>(symbol.getName(), symbol, null));
1880                    } else {
1881                        assert symbol.hasSlot() || symbol.slotCount() == 0 : symbol + " should have a slot only, no scope";
1882                    }
1883                } else if (symbol.isParam() && (varsInScope || hasArguments || symbol.isScope())) {
1884                    assert symbol.isScope()   : "scope for " + symbol + " should have been set in AssignSymbols already " + function.getName() + " varsInScope="+varsInScope+" hasArguments="+hasArguments+" symbol.isScope()=" + symbol.isScope();
1885                    assert !(hasArguments && symbol.hasSlot())  : "slot for " + symbol + " should have been removed in Lower already " + function.getName();
1886
1887                    final Type   paramType;
1888                    final Symbol paramSymbol;
1889
1890                    if (hasArguments) {
1891                        assert !symbol.hasSlot()  : "slot for " + symbol + " should have been removed in Lower already ";
1892                        paramSymbol = null;
1893                        paramType   = null;
1894                    } else {
1895                        paramSymbol = symbol;
1896                        // NOTE: We're relying on the fact here that Block.symbols is a LinkedHashMap, hence it will
1897                        // return symbols in the order they were defined, and parameters are defined in the same order
1898                        // they appear in the function. That's why we can have a single pass over the parameter list
1899                        // with an iterator, always just scanning forward for the next parameter that matches the symbol
1900                        // name.
1901                        for(;;) {
1902                            final IdentNode nextParam = paramIter.next();
1903                            if(nextParam.getName().equals(symbol.getName())) {
1904                                paramType = nextParam.getType();
1905                                break;
1906                            }
1907                        }
1908                    }
1909
1910                    tuples.add(new MapTuple<Symbol>(symbol.getName(), symbol, paramType, paramSymbol) {
1911                        //this symbol will be put fielded, we can't initialize it as undefined with a known type
1912                        @Override
1913                        public Class<?> getValueType() {
1914                            if (!useDualFields() ||  value == null || paramType == null || paramType.isBoolean()) {
1915                                return Object.class;
1916                            }
1917                            return paramType.getTypeClass();
1918                        }
1919                    });
1920                }
1921            }
1922
1923            /*
1924             * Create a new object based on the symbols and values, generate
1925             * bootstrap code for object
1926             */
1927            new FieldObjectCreator<Symbol>(this, tuples, true, hasArguments) {
1928                @Override
1929                protected void loadValue(final Symbol value, final Type type) {
1930                    method.load(value, type);
1931                }
1932            }.makeObject(method);
1933            // program function: merge scope into global
1934            if (isFunctionBody && function.isProgram()) {
1935                method.invoke(ScriptRuntime.MERGE_SCOPE);
1936            }
1937
1938            method.storeCompilerConstant(SCOPE);
1939            if(!isFunctionBody) {
1940                // Function body doesn't need a try/catch to restore scope, as it'd be a dead store anyway. Allowing it
1941                // actually causes issues with UnwarrantedOptimismException handlers as ASM will sort this handler to
1942                // the top of the exception handler table, so it'll be triggered instead of the UOE handlers.
1943                final Label scopeEntryLabel = new Label("scope_entry");
1944                scopeEntryLabels.push(scopeEntryLabel);
1945                method.label(scopeEntryLabel);
1946            }
1947        } else if (isFunctionBody && function.isVarArg()) {
1948            // Since we don't have a scope, parameters didn't get assigned array indices by the FieldObjectCreator, so
1949            // we need to assign them separately here.
1950            int nextParam = 0;
1951            for (final IdentNode param : function.getParameters()) {
1952                param.getSymbol().setFieldIndex(nextParam++);
1953            }
1954        }
1955
1956        // Debugging: print symbols? @see --print-symbols flag
1957        printSymbols(block, function, (isFunctionBody ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName()));
1958    }
1959
1960    /**
1961     * Incoming method parameters are always declared on method entry; declare them in the local variable table.
1962     * @param function function for which code is being generated.
1963     */
1964    private void initializeMethodParameters(final FunctionNode function) {
1965        final Label functionStart = new Label("fn_start");
1966        method.label(functionStart);
1967        int nextSlot = 0;
1968        if(function.needsCallee()) {
1969            initializeInternalFunctionParameter(CALLEE, function, functionStart, nextSlot++);
1970        }
1971        initializeInternalFunctionParameter(THIS, function, functionStart, nextSlot++);
1972        if(function.isVarArg()) {
1973            initializeInternalFunctionParameter(VARARGS, function, functionStart, nextSlot++);
1974        } else {
1975            for(final IdentNode param: function.getParameters()) {
1976                final Symbol symbol = param.getSymbol();
1977                if(symbol.isBytecodeLocal()) {
1978                    method.initializeMethodParameter(symbol, param.getType(), functionStart);
1979                }
1980            }
1981        }
1982    }
1983
1984    private void initializeInternalFunctionParameter(final CompilerConstants cc, final FunctionNode fn, final Label functionStart, final int slot) {
1985        final Symbol symbol = initializeInternalFunctionOrSplitParameter(cc, fn, functionStart, slot);
1986        // Internal function params (:callee, this, and :varargs) are never expanded to multiple slots
1987        assert symbol.getFirstSlot() == slot;
1988    }
1989
1990    private Symbol initializeInternalFunctionOrSplitParameter(final CompilerConstants cc, final FunctionNode fn, final Label functionStart, final int slot) {
1991        final Symbol symbol = fn.getBody().getExistingSymbol(cc.symbolName());
1992        final Type type = Type.typeFor(cc.type());
1993        method.initializeMethodParameter(symbol, type, functionStart);
1994        method.onLocalStore(type, slot);
1995        return symbol;
1996    }
1997
1998    /**
1999     * Parameters come into the method packed into local variable slots next to each other. Nashorn on the other hand
2000     * can use 1-6 slots for a local variable depending on all the types it needs to store. When this method is invoked,
2001     * the symbols are already allocated such wider slots, but the values are still in tightly packed incoming slots,
2002     * and we need to spread them into their new locations.
2003     * @param function the function for which parameter-spreading code needs to be emitted
2004     */
2005    private void expandParameterSlots(final FunctionNode function) {
2006        final List<IdentNode> parameters = function.getParameters();
2007        // Calculate the total number of incoming parameter slots
2008        int currentIncomingSlot = function.needsCallee() ? 2 : 1;
2009        for(final IdentNode parameter: parameters) {
2010            currentIncomingSlot += parameter.getType().getSlots();
2011        }
2012        // Starting from last parameter going backwards, move the parameter values into their new slots.
2013        for(int i = parameters.size(); i-- > 0;) {
2014            final IdentNode parameter = parameters.get(i);
2015            final Type parameterType = parameter.getType();
2016            final int typeWidth = parameterType.getSlots();
2017            currentIncomingSlot -= typeWidth;
2018            final Symbol symbol = parameter.getSymbol();
2019            final int slotCount = symbol.slotCount();
2020            assert slotCount > 0;
2021            // Scoped parameters must not hold more than one value
2022            assert symbol.isBytecodeLocal() || slotCount == typeWidth;
2023
2024            // Mark it as having its value stored into it by the method invocation.
2025            method.onLocalStore(parameterType, currentIncomingSlot);
2026            if(currentIncomingSlot != symbol.getSlot(parameterType)) {
2027                method.load(parameterType, currentIncomingSlot);
2028                method.store(symbol, parameterType);
2029            }
2030        }
2031    }
2032
2033    private void initArguments(final FunctionNode function) {
2034        method.loadCompilerConstant(VARARGS);
2035        if (function.needsCallee()) {
2036            method.loadCompilerConstant(CALLEE);
2037        } else {
2038            // If function is strict mode, "arguments.callee" is not populated, so we don't necessarily need the
2039            // caller.
2040            assert function.isStrict();
2041            method.loadNull();
2042        }
2043        method.load(function.getParameters().size());
2044        globalAllocateArguments();
2045        method.storeCompilerConstant(ARGUMENTS);
2046    }
2047
2048    private boolean skipFunction(final FunctionNode functionNode) {
2049        final ScriptEnvironment env = compiler.getScriptEnvironment();
2050        final boolean lazy = env._lazy_compilation;
2051        final boolean onDemand = compiler.isOnDemandCompilation();
2052
2053        // If this is on-demand or lazy compilation, don't compile a nested (not topmost) function.
2054        if((onDemand || lazy) && lc.getOutermostFunction() != functionNode) {
2055            return true;
2056        }
2057
2058        // If lazy compiling with optimistic types, don't compile the program eagerly either. It will soon be
2059        // invalidated anyway. In presence of a class cache, this further means that an obsoleted program version
2060        // lingers around. Also, currently loading previously persisted optimistic types information only works if
2061        // we're on-demand compiling a function, so with this strategy the :program method can also have the warmup
2062        // benefit of using previously persisted types.
2063        //
2064        // NOTE that this means the first compiled class will effectively just have a :createProgramFunction method, and
2065        // the RecompilableScriptFunctionData (RSFD) object in its constants array. It won't even have the :program
2066        // method. This is by design. It does mean that we're wasting one compiler execution (and we could minimize this
2067        // by just running it up to scope depth calculation, which creates the RSFDs and then this limited codegen).
2068        // We could emit an initial separate compile unit with the initial version of :program in it to better utilize
2069        // the compilation pipeline, but that would need more invasive changes, as currently the assumption that
2070        // :program is emitted into the first compilation unit of the function lives in many places.
2071        return !onDemand && lazy && env._optimistic_types && functionNode.isProgram();
2072    }
2073
2074    @Override
2075    public boolean enterFunctionNode(final FunctionNode functionNode) {
2076        final int fnId = functionNode.getId();
2077
2078        if (skipFunction(functionNode)) {
2079            // In case we are not generating code for the function, we must create or retrieve the function object and
2080            // load it on the stack here.
2081            newFunctionObject(functionNode, false);
2082            return false;
2083        }
2084
2085        final String fnName = functionNode.getName();
2086
2087        // NOTE: we only emit the method for a function with the given name once. We can have multiple functions with
2088        // the same name as a result of inlining finally blocks. However, in the future -- with type specialization,
2089        // notably -- we might need to check for both name *and* signature. Of course, even that might not be
2090        // sufficient; the function might have a code dependency on the type of the variables in its enclosing scopes,
2091        // and the type of such a variable can be different in catch and finally blocks. So, in the future we will have
2092        // to decide to either generate a unique method for each inlined copy of the function, maybe figure out its
2093        // exact type closure and deduplicate based on that, or just decide that functions in finally blocks aren't
2094        // worth it, and generate one method with most generic type closure.
2095        if (!emittedMethods.contains(fnName)) {
2096            log.info("=== BEGIN ", fnName);
2097
2098            assert functionNode.getCompileUnit() != null : "no compile unit for " + fnName + " " + Debug.id(functionNode);
2099            unit = lc.pushCompileUnit(functionNode.getCompileUnit());
2100            assert lc.hasCompileUnits();
2101
2102            final ClassEmitter classEmitter = unit.getClassEmitter();
2103            pushMethodEmitter(isRestOf() ? classEmitter.restOfMethod(functionNode) : classEmitter.method(functionNode));
2104            method.setPreventUndefinedLoad();
2105            if(useOptimisticTypes()) {
2106                lc.pushUnwarrantedOptimismHandlers();
2107            }
2108
2109            // new method - reset last line number
2110            lastLineNumber = -1;
2111
2112            method.begin();
2113
2114            if (isRestOf()) {
2115                final ContinuationInfo ci = new ContinuationInfo();
2116                fnIdToContinuationInfo.put(fnId, ci);
2117                method.gotoLoopStart(ci.getHandlerLabel());
2118            }
2119        }
2120
2121        return true;
2122    }
2123
2124    private void pushMethodEmitter(final MethodEmitter newMethod) {
2125        method = lc.pushMethodEmitter(newMethod);
2126        catchLabels.push(METHOD_BOUNDARY);
2127    }
2128
2129    private void popMethodEmitter() {
2130        method = lc.popMethodEmitter(method);
2131        assert catchLabels.peek() == METHOD_BOUNDARY;
2132        catchLabels.pop();
2133    }
2134
2135    @Override
2136    public Node leaveFunctionNode(final FunctionNode functionNode) {
2137        try {
2138            final boolean markOptimistic;
2139            if (emittedMethods.add(functionNode.getName())) {
2140                markOptimistic = generateUnwarrantedOptimismExceptionHandlers(functionNode);
2141                generateContinuationHandler();
2142                method.end(); // wrap up this method
2143                unit   = lc.popCompileUnit(functionNode.getCompileUnit());
2144                popMethodEmitter();
2145                log.info("=== END ", functionNode.getName());
2146            } else {
2147                markOptimistic = false;
2148            }
2149
2150            FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.BYTECODE_GENERATED);
2151            if (markOptimistic) {
2152                newFunctionNode = newFunctionNode.setFlag(lc, FunctionNode.IS_DEOPTIMIZABLE);
2153            }
2154
2155            newFunctionObject(newFunctionNode, true);
2156            return newFunctionNode;
2157        } catch (final Throwable t) {
2158            Context.printStackTrace(t);
2159            final VerifyError e = new VerifyError("Code generation bug in \"" + functionNode.getName() + "\": likely stack misaligned: " + t + " " + functionNode.getSource().getName());
2160            e.initCause(t);
2161            throw e;
2162        }
2163    }
2164
2165    @Override
2166    public boolean enterIfNode(final IfNode ifNode) {
2167        if(!method.isReachable()) {
2168            return false;
2169        }
2170        enterStatement(ifNode);
2171
2172        final Expression test = ifNode.getTest();
2173        final Block pass = ifNode.getPass();
2174        final Block fail = ifNode.getFail();
2175
2176        if (Expression.isAlwaysTrue(test)) {
2177            loadAndDiscard(test);
2178            pass.accept(this);
2179            return false;
2180        } else if (Expression.isAlwaysFalse(test)) {
2181            loadAndDiscard(test);
2182            if (fail != null) {
2183                fail.accept(this);
2184            }
2185            return false;
2186        }
2187
2188        final boolean hasFailConversion = LocalVariableConversion.hasLiveConversion(ifNode);
2189
2190        final Label failLabel  = new Label("if_fail");
2191        final Label afterLabel = (fail == null && !hasFailConversion) ? null : new Label("if_done");
2192
2193        emitBranch(test, failLabel, false);
2194
2195        pass.accept(this);
2196        if(method.isReachable() && afterLabel != null) {
2197            method._goto(afterLabel); //don't fallthru to fail block
2198        }
2199        method.label(failLabel);
2200
2201        if (fail != null) {
2202            fail.accept(this);
2203        } else if(hasFailConversion) {
2204            method.beforeJoinPoint(ifNode);
2205        }
2206
2207        if(afterLabel != null && afterLabel.isReachable()) {
2208            method.label(afterLabel);
2209        }
2210
2211        return false;
2212    }
2213
2214    private void emitBranch(final Expression test, final Label label, final boolean jumpWhenTrue) {
2215        new BranchOptimizer(this, method).execute(test, label, jumpWhenTrue);
2216    }
2217
2218    private void enterStatement(final Statement statement) {
2219        lineNumber(statement);
2220    }
2221
2222    private void lineNumber(final Statement statement) {
2223        lineNumber(statement.getLineNumber());
2224    }
2225
2226    private void lineNumber(final int lineNumber) {
2227        if (lineNumber != lastLineNumber && lineNumber != Node.NO_LINE_NUMBER) {
2228            method.lineNumber(lineNumber);
2229            lastLineNumber = lineNumber;
2230        }
2231    }
2232
2233    int getLastLineNumber() {
2234        return lastLineNumber;
2235    }
2236
2237    /**
2238     * Load a list of nodes as an array of a specific type
2239     * The array will contain the visited nodes.
2240     *
2241     * @param arrayLiteralNode the array of contents
2242     * @param arrayType        the type of the array, e.g. ARRAY_NUMBER or ARRAY_OBJECT
2243     *
2244     * @return the method generator that was used
2245     */
2246    private MethodEmitter loadArray(final ArrayLiteralNode arrayLiteralNode, final ArrayType arrayType) {
2247        assert arrayType == Type.INT_ARRAY || arrayType == Type.LONG_ARRAY || arrayType == Type.NUMBER_ARRAY || arrayType == Type.OBJECT_ARRAY;
2248
2249        final Expression[]    nodes    = arrayLiteralNode.getValue();
2250        final Object          presets  = arrayLiteralNode.getPresets();
2251        final int[]           postsets = arrayLiteralNode.getPostsets();
2252        final Class<?>        type     = arrayType.getTypeClass();
2253        final List<ArrayUnit> units    = arrayLiteralNode.getUnits();
2254
2255        loadConstant(presets);
2256
2257        final Type elementType = arrayType.getElementType();
2258
2259        if (units != null) {
2260            final MethodEmitter savedMethod     = method;
2261            final FunctionNode  currentFunction = lc.getCurrentFunction();
2262
2263            for (final ArrayUnit arrayUnit : units) {
2264                unit = lc.pushCompileUnit(arrayUnit.getCompileUnit());
2265
2266                final String className = unit.getUnitClassName();
2267                assert unit != null;
2268                final String name      = currentFunction.uniqueName(SPLIT_PREFIX.symbolName());
2269                final String signature = methodDescriptor(type, ScriptFunction.class, Object.class, ScriptObject.class, type);
2270
2271                pushMethodEmitter(unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature));
2272
2273                method.setFunctionNode(currentFunction);
2274                method.begin();
2275
2276                defineCommonSplitMethodParameters();
2277                defineSplitMethodParameter(CompilerConstants.SPLIT_ARRAY_ARG.slot(), arrayType);
2278
2279                // NOTE: when this is no longer needed, SplitIntoFunctions will no longer have to add IS_SPLIT
2280                // to synthetic functions, and FunctionNode.needsCallee() will no longer need to test for isSplit().
2281                final int arraySlot = fixScopeSlot(currentFunction, 3);
2282
2283                lc.enterSplitNode();
2284
2285                for (int i = arrayUnit.getLo(); i < arrayUnit.getHi(); i++) {
2286                    method.load(arrayType, arraySlot);
2287                    storeElement(nodes, elementType, postsets[i]);
2288                }
2289
2290                method.load(arrayType, arraySlot);
2291                method._return();
2292                lc.exitSplitNode();
2293                method.end();
2294                lc.releaseSlots();
2295                popMethodEmitter();
2296
2297                assert method == savedMethod;
2298                method.loadCompilerConstant(CALLEE);
2299                method.swap();
2300                method.loadCompilerConstant(THIS);
2301                method.swap();
2302                method.loadCompilerConstant(SCOPE);
2303                method.swap();
2304                method.invokestatic(className, name, signature);
2305
2306                unit = lc.popCompileUnit(unit);
2307            }
2308
2309            return method;
2310        }
2311
2312        if(postsets.length > 0) {
2313            final int arraySlot = method.getUsedSlotsWithLiveTemporaries();
2314            method.storeTemp(arrayType, arraySlot);
2315            for (final int postset : postsets) {
2316                method.load(arrayType, arraySlot);
2317                storeElement(nodes, elementType, postset);
2318            }
2319            method.load(arrayType, arraySlot);
2320        }
2321        return method;
2322    }
2323
2324    private void storeElement(final Expression[] nodes, final Type elementType, final int index) {
2325        method.load(index);
2326
2327        final Expression element = nodes[index];
2328
2329        if (element == null) {
2330            method.loadEmpty(elementType);
2331        } else {
2332            loadExpressionAsType(element, elementType);
2333        }
2334
2335        method.arraystore();
2336    }
2337
2338    private MethodEmitter loadArgsArray(final List<Expression> args) {
2339        final Object[] array = new Object[args.size()];
2340        loadConstant(array);
2341
2342        for (int i = 0; i < args.size(); i++) {
2343            method.dup();
2344            method.load(i);
2345            loadExpression(args.get(i), TypeBounds.OBJECT); // variable arity methods always take objects
2346            method.arraystore();
2347        }
2348
2349        return method;
2350    }
2351
2352    /**
2353     * Load a constant from the constant array. This is only public to be callable from the objects
2354     * subpackage. Do not call directly.
2355     *
2356     * @param string string to load
2357     */
2358    void loadConstant(final String string) {
2359        final String       unitClassName = unit.getUnitClassName();
2360        final ClassEmitter classEmitter  = unit.getClassEmitter();
2361        final int          index         = compiler.getConstantData().add(string);
2362
2363        method.load(index);
2364        method.invokestatic(unitClassName, GET_STRING.symbolName(), methodDescriptor(String.class, int.class));
2365        classEmitter.needGetConstantMethod(String.class);
2366    }
2367
2368    /**
2369     * Load a constant from the constant array. This is only public to be callable from the objects
2370     * subpackage. Do not call directly.
2371     *
2372     * @param object object to load
2373     */
2374    void loadConstant(final Object object) {
2375        loadConstant(object, unit, method);
2376    }
2377
2378    private void loadConstant(final Object object, final CompileUnit compileUnit, final MethodEmitter methodEmitter) {
2379        final String       unitClassName = compileUnit.getUnitClassName();
2380        final ClassEmitter classEmitter  = compileUnit.getClassEmitter();
2381        final int          index         = compiler.getConstantData().add(object);
2382        final Class<?>     cls           = object.getClass();
2383
2384        if (cls == PropertyMap.class) {
2385            methodEmitter.load(index);
2386            methodEmitter.invokestatic(unitClassName, GET_MAP.symbolName(), methodDescriptor(PropertyMap.class, int.class));
2387            classEmitter.needGetConstantMethod(PropertyMap.class);
2388        } else if (cls.isArray()) {
2389            methodEmitter.load(index);
2390            final String methodName = ClassEmitter.getArrayMethodName(cls);
2391            methodEmitter.invokestatic(unitClassName, methodName, methodDescriptor(cls, int.class));
2392            classEmitter.needGetConstantMethod(cls);
2393        } else {
2394            methodEmitter.loadConstants().load(index).arrayload();
2395            if (object instanceof ArrayData) {
2396                methodEmitter.checkcast(ArrayData.class);
2397                methodEmitter.invoke(virtualCallNoLookup(ArrayData.class, "copy", ArrayData.class));
2398            } else if (cls != Object.class) {
2399                methodEmitter.checkcast(cls);
2400            }
2401        }
2402    }
2403
2404    private void loadConstantsAndIndex(final Object object, final MethodEmitter methodEmitter) {
2405        methodEmitter.loadConstants().load(compiler.getConstantData().add(object));
2406    }
2407
2408    // literal values
2409    private void loadLiteral(final LiteralNode<?> node, final TypeBounds resultBounds) {
2410        final Object value = node.getValue();
2411
2412        if (value == null) {
2413            method.loadNull();
2414        } else if (value instanceof Undefined) {
2415            method.loadUndefined(resultBounds.within(Type.OBJECT));
2416        } else if (value instanceof String) {
2417            final String string = (String)value;
2418
2419            if (string.length() > MethodEmitter.LARGE_STRING_THRESHOLD / 3) { // 3 == max bytes per encoded char
2420                loadConstant(string);
2421            } else {
2422                method.load(string);
2423            }
2424        } else if (value instanceof RegexToken) {
2425            loadRegex((RegexToken)value);
2426        } else if (value instanceof Boolean) {
2427            method.load((Boolean)value);
2428        } else if (value instanceof Integer) {
2429            if(!resultBounds.canBeNarrowerThan(Type.OBJECT)) {
2430                method.load((Integer)value);
2431                method.convert(Type.OBJECT);
2432            } else if(!resultBounds.canBeNarrowerThan(Type.NUMBER)) {
2433                method.load(((Integer)value).doubleValue());
2434            } else if(!resultBounds.canBeNarrowerThan(Type.LONG)) {
2435                method.load(((Integer)value).longValue());
2436            } else {
2437                method.load((Integer)value);
2438            }
2439        } else if (value instanceof Long) {
2440            if(!resultBounds.canBeNarrowerThan(Type.OBJECT)) {
2441                method.load((Long)value);
2442                method.convert(Type.OBJECT);
2443            } else if(!resultBounds.canBeNarrowerThan(Type.NUMBER)) {
2444                method.load(((Long)value).doubleValue());
2445            } else {
2446                method.load((Long)value);
2447            }
2448        } else if (value instanceof Double) {
2449            if(!resultBounds.canBeNarrowerThan(Type.OBJECT)) {
2450                method.load((Double)value);
2451                method.convert(Type.OBJECT);
2452            } else {
2453                method.load((Double)value);
2454            }
2455        } else if (node instanceof ArrayLiteralNode) {
2456            final ArrayLiteralNode arrayLiteral = (ArrayLiteralNode)node;
2457            final ArrayType atype = arrayLiteral.getArrayType();
2458            loadArray(arrayLiteral, atype);
2459            globalAllocateArray(atype);
2460        } else {
2461            throw new UnsupportedOperationException("Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value);
2462        }
2463    }
2464
2465    private MethodEmitter loadRegexToken(final RegexToken value) {
2466        method.load(value.getExpression());
2467        method.load(value.getOptions());
2468        return globalNewRegExp();
2469    }
2470
2471    private MethodEmitter loadRegex(final RegexToken regexToken) {
2472        if (regexFieldCount > MAX_REGEX_FIELDS) {
2473            return loadRegexToken(regexToken);
2474        }
2475        // emit field
2476        final String       regexName    = lc.getCurrentFunction().uniqueName(REGEX_PREFIX.symbolName());
2477        final ClassEmitter classEmitter = unit.getClassEmitter();
2478
2479        classEmitter.field(EnumSet.of(PRIVATE, STATIC), regexName, Object.class);
2480        regexFieldCount++;
2481
2482        // get field, if null create new regex, finally clone regex object
2483        method.getStatic(unit.getUnitClassName(), regexName, typeDescriptor(Object.class));
2484        method.dup();
2485        final Label cachedLabel = new Label("cached");
2486        method.ifnonnull(cachedLabel);
2487
2488        method.pop();
2489        loadRegexToken(regexToken);
2490        method.dup();
2491        method.putStatic(unit.getUnitClassName(), regexName, typeDescriptor(Object.class));
2492
2493        method.label(cachedLabel);
2494        globalRegExpCopy();
2495
2496        return method;
2497    }
2498
2499    /**
2500     * Check if a property value contains a particular program point
2501     * @param value value
2502     * @param pp    program point
2503     * @return true if it's there.
2504     */
2505    private static boolean propertyValueContains(final Expression value, final int pp) {
2506        return new Supplier<Boolean>() {
2507            boolean contains;
2508
2509            @Override
2510            public Boolean get() {
2511                value.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
2512                    @Override
2513                    public boolean enterFunctionNode(final FunctionNode functionNode) {
2514                        return false;
2515                    }
2516
2517                    @Override
2518                    public boolean enterObjectNode(final ObjectNode objectNode) {
2519                        return false;
2520                    }
2521
2522                    @Override
2523                    public boolean enterDefault(final Node node) {
2524                        if (contains) {
2525                            return false;
2526                        }
2527                        if (node instanceof Optimistic && ((Optimistic)node).getProgramPoint() == pp) {
2528                            contains = true;
2529                            return false;
2530                        }
2531                        return true;
2532                    }
2533                });
2534
2535                return contains;
2536            }
2537        }.get();
2538    }
2539
2540    private void loadObjectNode(final ObjectNode objectNode) {
2541        final List<PropertyNode> elements = objectNode.getElements();
2542
2543        final List<MapTuple<Expression>> tuples = new ArrayList<>();
2544        final List<PropertyNode> gettersSetters = new ArrayList<>();
2545        final int ccp = getCurrentContinuationEntryPoint();
2546
2547        Expression protoNode = null;
2548        boolean restOfProperty = false;
2549
2550        for (final PropertyNode propertyNode : elements) {
2551            final Expression value = propertyNode.getValue();
2552            final String key = propertyNode.getKeyName();
2553            // Just use a pseudo-symbol. We just need something non null; use the name and zero flags.
2554            final Symbol symbol = value == null ? null : new Symbol(key, 0);
2555
2556            if (value == null) {
2557                gettersSetters.add(propertyNode);
2558            } else if (propertyNode.getKey() instanceof IdentNode &&
2559                       key.equals(ScriptObject.PROTO_PROPERTY_NAME)) {
2560                // ES6 draft compliant __proto__ inside object literal
2561                // Identifier key and name is __proto__
2562                protoNode = value;
2563                continue;
2564            }
2565
2566            restOfProperty |=
2567                value != null &&
2568                isValid(ccp) &&
2569                propertyValueContains(value, ccp);
2570
2571            //for literals, a value of null means object type, i.e. the value null or getter setter function
2572            //(I think)
2573            final Class<?> valueType = (!useDualFields() || value == null || value.getType().isBoolean()) ? Object.class : value.getType().getTypeClass();
2574            tuples.add(new MapTuple<Expression>(key, symbol, Type.typeFor(valueType), value) {
2575                @Override
2576                public Class<?> getValueType() {
2577                    return type.getTypeClass();
2578                }
2579            });
2580        }
2581
2582        final ObjectCreator<?> oc;
2583        if (elements.size() > OBJECT_SPILL_THRESHOLD) {
2584            oc = new SpillObjectCreator(this, tuples);
2585        } else {
2586            oc = new FieldObjectCreator<Expression>(this, tuples) {
2587                @Override
2588                protected void loadValue(final Expression node, final Type type) {
2589                    loadExpressionAsType(node, type);
2590                }};
2591        }
2592        oc.makeObject(method);
2593
2594        //if this is a rest of method and our continuation point was found as one of the values
2595        //in the properties above, we need to reset the map to oc.getMap() in the continuation
2596        //handler
2597        if (restOfProperty) {
2598            final ContinuationInfo ci = getContinuationInfo();
2599            // Can be set at most once for a single rest-of method
2600            assert ci.getObjectLiteralMap() == null;
2601            ci.setObjectLiteralMap(oc.getMap());
2602            ci.setObjectLiteralStackDepth(method.getStackSize());
2603        }
2604
2605        method.dup();
2606        if (protoNode != null) {
2607            loadExpressionAsObject(protoNode);
2608            // take care of { __proto__: 34 } or some such!
2609            method.convert(Type.OBJECT);
2610            method.invoke(ScriptObject.SET_PROTO_FROM_LITERAL);
2611        } else {
2612            method.invoke(ScriptObject.SET_GLOBAL_OBJECT_PROTO);
2613        }
2614
2615        for (final PropertyNode propertyNode : gettersSetters) {
2616            final FunctionNode getter = propertyNode.getGetter();
2617            final FunctionNode setter = propertyNode.getSetter();
2618
2619            assert getter != null || setter != null;
2620
2621            method.dup().loadKey(propertyNode.getKey());
2622            if (getter == null) {
2623                method.loadNull();
2624            } else {
2625                getter.accept(this);
2626            }
2627
2628            if (setter == null) {
2629                method.loadNull();
2630            } else {
2631                setter.accept(this);
2632            }
2633
2634            method.invoke(ScriptObject.SET_USER_ACCESSORS);
2635        }
2636    }
2637
2638    @Override
2639    public boolean enterReturnNode(final ReturnNode returnNode) {
2640        if(!method.isReachable()) {
2641            return false;
2642        }
2643        enterStatement(returnNode);
2644
2645        final Type returnType = lc.getCurrentFunction().getReturnType();
2646
2647        final Expression expression = returnNode.getExpression();
2648        if (expression != null) {
2649            loadExpressionUnbounded(expression);
2650        } else {
2651            method.loadUndefined(returnType);
2652        }
2653
2654        method._return(returnType);
2655
2656        return false;
2657    }
2658
2659    private boolean undefinedCheck(final RuntimeNode runtimeNode, final List<Expression> args) {
2660        final Request request = runtimeNode.getRequest();
2661
2662        if (!Request.isUndefinedCheck(request)) {
2663            return false;
2664        }
2665
2666        final Expression lhs = args.get(0);
2667        final Expression rhs = args.get(1);
2668
2669        final Symbol lhsSymbol = lhs instanceof IdentNode ? ((IdentNode)lhs).getSymbol() : null;
2670        final Symbol rhsSymbol = rhs instanceof IdentNode ? ((IdentNode)rhs).getSymbol() : null;
2671        // One must be a "undefined" identifier, otherwise we can't get here
2672        assert lhsSymbol != null || rhsSymbol != null;
2673
2674        final Symbol undefinedSymbol;
2675        if (isUndefinedSymbol(lhsSymbol)) {
2676            undefinedSymbol = lhsSymbol;
2677        } else {
2678            assert isUndefinedSymbol(rhsSymbol);
2679            undefinedSymbol = rhsSymbol;
2680        }
2681
2682        assert undefinedSymbol != null; //remove warning
2683        if (!undefinedSymbol.isScope()) {
2684            return false; //disallow undefined as local var or parameter
2685        }
2686
2687        if (lhsSymbol == undefinedSymbol && lhs.getType().isPrimitive()) {
2688            //we load the undefined first. never mind, because this will deoptimize anyway
2689            return false;
2690        }
2691
2692        if(isDeoptimizedExpression(lhs)) {
2693            // This is actually related to "lhs.getType().isPrimitive()" above: any expression being deoptimized in
2694            // the current chain of rest-of compilations used to have a type narrower than Object (so it was primitive).
2695            // We must not perform undefined check specialization for them, as then we'd violate the basic rule of
2696            // "Thou shalt not alter the stack shape between a deoptimized method and any of its (transitive) rest-ofs."
2697            return false;
2698        }
2699
2700        //make sure that undefined has not been overridden or scoped as a local var
2701        //between us and global
2702        if (!compiler.isGlobalSymbol(lc.getCurrentFunction(), "undefined")) {
2703            return false;
2704        }
2705
2706        final boolean isUndefinedCheck = request == Request.IS_UNDEFINED;
2707        final Expression expr = undefinedSymbol == lhsSymbol ? rhs : lhs;
2708        if (expr.getType().isPrimitive()) {
2709            loadAndDiscard(expr); //throw away lhs, but it still needs to be evaluated for side effects, even if not in scope, as it can be optimistic
2710            method.load(!isUndefinedCheck);
2711        } else {
2712            final Label checkTrue  = new Label("ud_check_true");
2713            final Label end        = new Label("end");
2714            loadExpressionAsObject(expr);
2715            method.loadUndefined(Type.OBJECT);
2716            method.if_acmpeq(checkTrue);
2717            method.load(!isUndefinedCheck);
2718            method._goto(end);
2719            method.label(checkTrue);
2720            method.load(isUndefinedCheck);
2721            method.label(end);
2722        }
2723
2724        return true;
2725    }
2726
2727    private static boolean isUndefinedSymbol(final Symbol symbol) {
2728        return symbol != null && "undefined".equals(symbol.getName());
2729    }
2730
2731    private static boolean isNullLiteral(final Node node) {
2732        return node instanceof LiteralNode<?> && ((LiteralNode<?>) node).isNull();
2733    }
2734
2735    private boolean nullCheck(final RuntimeNode runtimeNode, final List<Expression> args) {
2736        final Request request = runtimeNode.getRequest();
2737
2738        if (!Request.isEQ(request) && !Request.isNE(request)) {
2739            return false;
2740        }
2741
2742        assert args.size() == 2 : "EQ or NE or TYPEOF need two args";
2743
2744        Expression lhs = args.get(0);
2745        Expression rhs = args.get(1);
2746
2747        if (isNullLiteral(lhs)) {
2748            final Expression tmp = lhs;
2749            lhs = rhs;
2750            rhs = tmp;
2751        }
2752
2753        if (!isNullLiteral(rhs)) {
2754            return false;
2755        }
2756
2757        if (!lhs.getType().isObject()) {
2758            return false;
2759        }
2760
2761        if(isDeoptimizedExpression(lhs)) {
2762            // This is actually related to "!lhs.getType().isObject()" above: any expression being deoptimized in
2763            // the current chain of rest-of compilations used to have a type narrower than Object. We must not
2764            // perform null check specialization for them, as then we'd no longer be loading aconst_null on stack
2765            // and thus violate the basic rule of "Thou shalt not alter the stack shape between a deoptimized
2766            // method and any of its (transitive) rest-ofs."
2767            // NOTE also that if we had a representation for well-known constants (e.g. null, 0, 1, -1, etc.) in
2768            // Label$Stack.localLoads then this wouldn't be an issue, as we would never (somewhat ridiculously)
2769            // allocate a temporary local to hold the result of aconst_null before attempting an optimistic
2770            // operation.
2771            return false;
2772        }
2773
2774        // this is a null literal check, so if there is implicit coercion
2775        // involved like {D}x=null, we will fail - this is very rare
2776        final Label trueLabel  = new Label("trueLabel");
2777        final Label falseLabel = new Label("falseLabel");
2778        final Label endLabel   = new Label("end");
2779
2780        loadExpressionUnbounded(lhs);    //lhs
2781        final Label popLabel;
2782        if (!Request.isStrict(request)) {
2783            method.dup(); //lhs lhs
2784            popLabel = new Label("pop");
2785        } else {
2786            popLabel = null;
2787        }
2788
2789        if (Request.isEQ(request)) {
2790            method.ifnull(!Request.isStrict(request) ? popLabel : trueLabel);
2791            if (!Request.isStrict(request)) {
2792                method.loadUndefined(Type.OBJECT);
2793                method.if_acmpeq(trueLabel);
2794            }
2795            method.label(falseLabel);
2796            method.load(false);
2797            method._goto(endLabel);
2798            if (!Request.isStrict(request)) {
2799                method.label(popLabel);
2800                method.pop();
2801            }
2802            method.label(trueLabel);
2803            method.load(true);
2804            method.label(endLabel);
2805        } else if (Request.isNE(request)) {
2806            method.ifnull(!Request.isStrict(request) ? popLabel : falseLabel);
2807            if (!Request.isStrict(request)) {
2808                method.loadUndefined(Type.OBJECT);
2809                method.if_acmpeq(falseLabel);
2810            }
2811            method.label(trueLabel);
2812            method.load(true);
2813            method._goto(endLabel);
2814            if (!Request.isStrict(request)) {
2815                method.label(popLabel);
2816                method.pop();
2817            }
2818            method.label(falseLabel);
2819            method.load(false);
2820            method.label(endLabel);
2821        }
2822
2823        assert runtimeNode.getType().isBoolean();
2824        method.convert(runtimeNode.getType());
2825
2826        return true;
2827    }
2828
2829    /**
2830     * Was this expression or any of its subexpressions deoptimized in the current recompilation chain of rest-of methods?
2831     * @param rootExpr the expression being tested
2832     * @return true if the expression or any of its subexpressions was deoptimized in the current recompilation chain.
2833     */
2834    private boolean isDeoptimizedExpression(final Expression rootExpr) {
2835        if(!isRestOf()) {
2836            return false;
2837        }
2838        return new Supplier<Boolean>() {
2839            boolean contains;
2840            @Override
2841            public Boolean get() {
2842                rootExpr.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
2843                    @Override
2844                    public boolean enterFunctionNode(final FunctionNode functionNode) {
2845                        return false;
2846                    }
2847                    @Override
2848                    public boolean enterDefault(final Node node) {
2849                        if(!contains && node instanceof Optimistic) {
2850                            final int pp = ((Optimistic)node).getProgramPoint();
2851                            contains = isValid(pp) && isContinuationEntryPoint(pp);
2852                        }
2853                        return !contains;
2854                    }
2855                });
2856                return contains;
2857            }
2858        }.get();
2859    }
2860
2861    private void loadRuntimeNode(final RuntimeNode runtimeNode) {
2862        final List<Expression> args = new ArrayList<>(runtimeNode.getArgs());
2863        if (nullCheck(runtimeNode, args)) {
2864           return;
2865        } else if(undefinedCheck(runtimeNode, args)) {
2866            return;
2867        }
2868        // Revert a false undefined check to a strict equality check
2869        final RuntimeNode newRuntimeNode;
2870        final Request request = runtimeNode.getRequest();
2871        if (Request.isUndefinedCheck(request)) {
2872            newRuntimeNode = runtimeNode.setRequest(request == Request.IS_UNDEFINED ? Request.EQ_STRICT : Request.NE_STRICT);
2873        } else {
2874            newRuntimeNode = runtimeNode;
2875        }
2876
2877        for (final Expression arg : args) {
2878            loadExpression(arg, TypeBounds.OBJECT);
2879        }
2880
2881        method.invokestatic(
2882                CompilerConstants.className(ScriptRuntime.class),
2883                newRuntimeNode.getRequest().toString(),
2884                new FunctionSignature(
2885                    false,
2886                    false,
2887                    newRuntimeNode.getType(),
2888                    args.size()).toString());
2889
2890        method.convert(newRuntimeNode.getType());
2891    }
2892
2893    private void defineCommonSplitMethodParameters() {
2894        defineSplitMethodParameter(0, CALLEE);
2895        defineSplitMethodParameter(1, THIS);
2896        defineSplitMethodParameter(2, SCOPE);
2897    }
2898
2899    private void defineSplitMethodParameter(final int slot, final CompilerConstants cc) {
2900        defineSplitMethodParameter(slot, Type.typeFor(cc.type()));
2901    }
2902
2903    private void defineSplitMethodParameter(final int slot, final Type type) {
2904        method.defineBlockLocalVariable(slot, slot + type.getSlots());
2905        method.onLocalStore(type, slot);
2906    }
2907
2908    private int fixScopeSlot(final FunctionNode functionNode, final int extraSlot) {
2909        // TODO hack to move the scope to the expected slot (needed because split methods reuse the same slots as the root method)
2910        final int actualScopeSlot = functionNode.compilerConstant(SCOPE).getSlot(SCOPE_TYPE);
2911        final int defaultScopeSlot = SCOPE.slot();
2912        int newExtraSlot = extraSlot;
2913        if (actualScopeSlot != defaultScopeSlot) {
2914            if (actualScopeSlot == extraSlot) {
2915                newExtraSlot = extraSlot + 1;
2916                method.defineBlockLocalVariable(newExtraSlot, newExtraSlot + 1);
2917                method.load(Type.OBJECT, extraSlot);
2918                method.storeHidden(Type.OBJECT, newExtraSlot);
2919            } else {
2920                method.defineBlockLocalVariable(actualScopeSlot, actualScopeSlot + 1);
2921            }
2922            method.load(SCOPE_TYPE, defaultScopeSlot);
2923            method.storeCompilerConstant(SCOPE);
2924        }
2925        return newExtraSlot;
2926    }
2927
2928    @Override
2929    public boolean enterSplitReturn(final SplitReturn splitReturn) {
2930        if (method.isReachable()) {
2931            method.loadUndefined(lc.getCurrentFunction().getReturnType())._return();
2932        }
2933        return false;
2934    }
2935
2936    @Override
2937    public boolean enterSetSplitState(final SetSplitState setSplitState) {
2938        if (method.isReachable()) {
2939            method.setSplitState(setSplitState.getState());
2940        }
2941        return false;
2942    }
2943
2944    @Override
2945    public boolean enterSwitchNode(final SwitchNode switchNode) {
2946        if(!method.isReachable()) {
2947            return false;
2948        }
2949        enterStatement(switchNode);
2950
2951        final Expression     expression  = switchNode.getExpression();
2952        final List<CaseNode> cases       = switchNode.getCases();
2953
2954        if (cases.isEmpty()) {
2955            // still evaluate expression for side-effects.
2956            loadAndDiscard(expression);
2957            return false;
2958        }
2959
2960        final CaseNode defaultCase       = switchNode.getDefaultCase();
2961        final Label    breakLabel        = switchNode.getBreakLabel();
2962        final int      liveLocalsOnBreak = method.getUsedSlotsWithLiveTemporaries();
2963
2964        if (defaultCase != null && cases.size() == 1) {
2965            // default case only
2966            assert cases.get(0) == defaultCase;
2967            loadAndDiscard(expression);
2968            defaultCase.getBody().accept(this);
2969            method.breakLabel(breakLabel, liveLocalsOnBreak);
2970            return false;
2971        }
2972
2973        // NOTE: it can still change in the tableswitch/lookupswitch case if there's no default case
2974        // but we need to add a synthetic default case for local variable conversions
2975        Label defaultLabel = defaultCase != null ? defaultCase.getEntry() : breakLabel;
2976        final boolean hasSkipConversion = LocalVariableConversion.hasLiveConversion(switchNode);
2977
2978        if (switchNode.isUniqueInteger()) {
2979            // Tree for sorting values.
2980            final TreeMap<Integer, Label> tree = new TreeMap<>();
2981
2982            // Build up sorted tree.
2983            for (final CaseNode caseNode : cases) {
2984                final Node test = caseNode.getTest();
2985
2986                if (test != null) {
2987                    final Integer value = (Integer)((LiteralNode<?>)test).getValue();
2988                    final Label   entry = caseNode.getEntry();
2989
2990                    // Take first duplicate.
2991                    if (!tree.containsKey(value)) {
2992                        tree.put(value, entry);
2993                    }
2994                }
2995            }
2996
2997            // Copy values and labels to arrays.
2998            final int       size   = tree.size();
2999            final Integer[] values = tree.keySet().toArray(new Integer[size]);
3000            final Label[]   labels = tree.values().toArray(new Label[size]);
3001
3002            // Discern low, high and range.
3003            final int lo    = values[0];
3004            final int hi    = values[size - 1];
3005            final long range = (long)hi - (long)lo + 1;
3006
3007            // Find an unused value for default.
3008            int deflt = Integer.MIN_VALUE;
3009            for (final int value : values) {
3010                if (deflt == value) {
3011                    deflt++;
3012                } else if (deflt < value) {
3013                    break;
3014                }
3015            }
3016
3017            // Load switch expression.
3018            loadExpressionUnbounded(expression);
3019            final Type type = expression.getType();
3020
3021            // If expression not int see if we can convert, if not use deflt to trigger default.
3022            if (!type.isInteger()) {
3023                method.load(deflt);
3024                final Class<?> exprClass = type.getTypeClass();
3025                method.invoke(staticCallNoLookup(ScriptRuntime.class, "switchTagAsInt", int.class, exprClass.isPrimitive()? exprClass : Object.class, int.class));
3026            }
3027
3028            if(hasSkipConversion) {
3029                assert defaultLabel == breakLabel;
3030                defaultLabel = new Label("switch_skip");
3031            }
3032            // TABLESWITCH needs (range + 3) 32-bit values; LOOKUPSWITCH needs ((size * 2) + 2). Choose the one with
3033            // smaller representation, favor TABLESWITCH when they're equal size.
3034            if (range + 1 <= (size * 2) && range <= Integer.MAX_VALUE) {
3035                final Label[] table = new Label[(int)range];
3036                Arrays.fill(table, defaultLabel);
3037                for (int i = 0; i < size; i++) {
3038                    final int value = values[i];
3039                    table[value - lo] = labels[i];
3040                }
3041
3042                method.tableswitch(lo, hi, defaultLabel, table);
3043            } else {
3044                final int[] ints = new int[size];
3045                for (int i = 0; i < size; i++) {
3046                    ints[i] = values[i];
3047                }
3048
3049                method.lookupswitch(defaultLabel, ints, labels);
3050            }
3051            // This is a synthetic "default case" used in absence of actual default case, created if we need to apply
3052            // local variable conversions if neither case is taken.
3053            if(hasSkipConversion) {
3054                method.label(defaultLabel);
3055                method.beforeJoinPoint(switchNode);
3056                method._goto(breakLabel);
3057            }
3058        } else {
3059            final Symbol tagSymbol = switchNode.getTag();
3060            // TODO: we could have non-object tag
3061            final int tagSlot = tagSymbol.getSlot(Type.OBJECT);
3062            loadExpressionAsObject(expression);
3063            method.store(tagSymbol, Type.OBJECT);
3064
3065            for (final CaseNode caseNode : cases) {
3066                final Expression test = caseNode.getTest();
3067
3068                if (test != null) {
3069                    method.load(Type.OBJECT, tagSlot);
3070                    loadExpressionAsObject(test);
3071                    method.invoke(ScriptRuntime.EQ_STRICT);
3072                    method.ifne(caseNode.getEntry());
3073                }
3074            }
3075
3076            if (defaultCase != null) {
3077                method._goto(defaultLabel);
3078            } else {
3079                method.beforeJoinPoint(switchNode);
3080                method._goto(breakLabel);
3081            }
3082        }
3083
3084        // First case is only reachable through jump
3085        assert !method.isReachable();
3086
3087        for (final CaseNode caseNode : cases) {
3088            final Label fallThroughLabel;
3089            if(caseNode.getLocalVariableConversion() != null && method.isReachable()) {
3090                fallThroughLabel = new Label("fallthrough");
3091                method._goto(fallThroughLabel);
3092            } else {
3093                fallThroughLabel = null;
3094            }
3095            method.label(caseNode.getEntry());
3096            method.beforeJoinPoint(caseNode);
3097            if(fallThroughLabel != null) {
3098                method.label(fallThroughLabel);
3099            }
3100            caseNode.getBody().accept(this);
3101        }
3102
3103        method.breakLabel(breakLabel, liveLocalsOnBreak);
3104
3105        return false;
3106    }
3107
3108    @Override
3109    public boolean enterThrowNode(final ThrowNode throwNode) {
3110        if(!method.isReachable()) {
3111            return false;
3112        }
3113        enterStatement(throwNode);
3114
3115        if (throwNode.isSyntheticRethrow()) {
3116            method.beforeJoinPoint(throwNode);
3117
3118            //do not wrap whatever this is in an ecma exception, just rethrow it
3119            final IdentNode exceptionExpr = (IdentNode)throwNode.getExpression();
3120            final Symbol exceptionSymbol = exceptionExpr.getSymbol();
3121            method.load(exceptionSymbol, EXCEPTION_TYPE);
3122            method.checkcast(EXCEPTION_TYPE.getTypeClass());
3123            method.athrow();
3124            return false;
3125        }
3126
3127        final Source     source     = getCurrentSource();
3128        final Expression expression = throwNode.getExpression();
3129        final int        position   = throwNode.position();
3130        final int        line       = throwNode.getLineNumber();
3131        final int        column     = source.getColumn(position);
3132
3133        // NOTE: we first evaluate the expression, and only after it was evaluated do we create the new ECMAException
3134        // object and then somewhat cumbersomely move it beneath the evaluated expression on the stack. The reason for
3135        // this is that if expression is optimistic (or contains an optimistic subexpression), we'd potentially access
3136        // the not-yet-<init>ialized object on the stack from the UnwarrantedOptimismException handler, and bytecode
3137        // verifier forbids that.
3138        loadExpressionAsObject(expression);
3139
3140        method.load(source.getName());
3141        method.load(line);
3142        method.load(column);
3143        method.invoke(ECMAException.CREATE);
3144
3145        method.beforeJoinPoint(throwNode);
3146        method.athrow();
3147
3148        return false;
3149    }
3150
3151    private Source getCurrentSource() {
3152        return lc.getCurrentFunction().getSource();
3153    }
3154
3155    @Override
3156    public boolean enterTryNode(final TryNode tryNode) {
3157        if(!method.isReachable()) {
3158            return false;
3159        }
3160        enterStatement(tryNode);
3161
3162        final Block       body        = tryNode.getBody();
3163        final List<Block> catchBlocks = tryNode.getCatchBlocks();
3164        final Symbol      vmException = tryNode.getException();
3165        final Label       entry       = new Label("try");
3166        final Label       recovery    = new Label("catch");
3167        final Label       exit        = new Label("end_try");
3168        final Label       skip        = new Label("skip");
3169
3170        method.canThrow(recovery);
3171        // Effect any conversions that might be observed at the entry of the catch node before entering the try node.
3172        // This is because even the first instruction in the try block must be presumed to be able to transfer control
3173        // to the catch block. Note that this doesn't kill the original values; in this regard it works a lot like
3174        // conversions of assignments within the try block.
3175        method.beforeTry(tryNode, recovery);
3176        method.label(entry);
3177        catchLabels.push(recovery);
3178        try {
3179            body.accept(this);
3180        } finally {
3181            assert catchLabels.peek() == recovery;
3182            catchLabels.pop();
3183        }
3184
3185        method.label(exit);
3186        final boolean bodyCanThrow = exit.isAfter(entry);
3187        if(!bodyCanThrow) {
3188            // The body can't throw an exception; don't even bother emitting the catch handlers, they're all dead code.
3189            return false;
3190        }
3191
3192        method._try(entry, exit, recovery, Throwable.class);
3193
3194        if (method.isReachable()) {
3195            method._goto(skip);
3196        }
3197
3198        for (final Block inlinedFinally : tryNode.getInlinedFinallies()) {
3199            TryNode.getLabelledInlinedFinallyBlock(inlinedFinally).accept(this);
3200            // All inlined finallies end with a jump or a return
3201            assert !method.isReachable();
3202        }
3203
3204
3205        method._catch(recovery);
3206        method.store(vmException, EXCEPTION_TYPE);
3207
3208        final int catchBlockCount = catchBlocks.size();
3209        final Label afterCatch = new Label("after_catch");
3210        for (int i = 0; i < catchBlockCount; i++) {
3211            assert method.isReachable();
3212            final Block catchBlock = catchBlocks.get(i);
3213
3214            // Because of the peculiarities of the flow control, we need to use an explicit push/enterBlock/leaveBlock
3215            // here.
3216            lc.push(catchBlock);
3217            enterBlock(catchBlock);
3218
3219            final CatchNode  catchNode          = (CatchNode)catchBlocks.get(i).getStatements().get(0);
3220            final IdentNode  exception          = catchNode.getException();
3221            final Expression exceptionCondition = catchNode.getExceptionCondition();
3222            final Block      catchBody          = catchNode.getBody();
3223
3224            new Store<IdentNode>(exception) {
3225                @Override
3226                protected void storeNonDiscard() {
3227                    // This expression is neither part of a discard, nor needs to be left on the stack after it was
3228                    // stored, so we override storeNonDiscard to be a no-op.
3229                }
3230
3231                @Override
3232                protected void evaluate() {
3233                    if (catchNode.isSyntheticRethrow()) {
3234                        method.load(vmException, EXCEPTION_TYPE);
3235                        return;
3236                    }
3237                    /*
3238                     * If caught object is an instance of ECMAException, then
3239                     * bind obj.thrown to the script catch var. Or else bind the
3240                     * caught object itself to the script catch var.
3241                     */
3242                    final Label notEcmaException = new Label("no_ecma_exception");
3243                    method.load(vmException, EXCEPTION_TYPE).dup()._instanceof(ECMAException.class).ifeq(notEcmaException);
3244                    method.checkcast(ECMAException.class); //TODO is this necessary?
3245                    method.getField(ECMAException.THROWN);
3246                    method.label(notEcmaException);
3247                }
3248            }.store();
3249
3250            final boolean isConditionalCatch = exceptionCondition != null;
3251            final Label nextCatch;
3252            if (isConditionalCatch) {
3253                loadExpressionAsBoolean(exceptionCondition);
3254                nextCatch = new Label("next_catch");
3255                nextCatch.markAsBreakTarget();
3256                method.ifeq(nextCatch);
3257            } else {
3258                nextCatch = null;
3259            }
3260
3261            catchBody.accept(this);
3262            leaveBlock(catchBlock);
3263            lc.pop(catchBlock);
3264            if(nextCatch != null) {
3265                if(method.isReachable()) {
3266                    method._goto(afterCatch);
3267                }
3268                method.breakLabel(nextCatch, lc.getUsedSlotCount());
3269            }
3270        }
3271
3272        // afterCatch could be the same as skip, except that we need to establish that the vmException is dead.
3273        method.label(afterCatch);
3274        if(method.isReachable()) {
3275            method.markDeadLocalVariable(vmException);
3276        }
3277        method.label(skip);
3278
3279        // Finally body is always inlined elsewhere so it doesn't need to be emitted
3280        assert tryNode.getFinallyBody() == null;
3281
3282        return false;
3283    }
3284
3285    @Override
3286    public boolean enterVarNode(final VarNode varNode) {
3287        if(!method.isReachable()) {
3288            return false;
3289        }
3290        final Expression init = varNode.getInit();
3291        final IdentNode identNode = varNode.getName();
3292        final Symbol identSymbol = identNode.getSymbol();
3293        assert identSymbol != null : "variable node " + varNode + " requires a name with a symbol";
3294        final boolean needsScope = identSymbol.isScope();
3295
3296        if (init == null) {
3297            if (needsScope && varNode.isBlockScoped()) {
3298                // block scoped variables need a DECLARE flag to signal end of temporal dead zone (TDZ)
3299                method.loadCompilerConstant(SCOPE);
3300                method.loadUndefined(Type.OBJECT);
3301                final int flags = getScopeCallSiteFlags(identSymbol) | (varNode.isBlockScoped() ? CALLSITE_DECLARE : 0);
3302                assert isFastScope(identSymbol);
3303                storeFastScopeVar(identSymbol, flags);
3304            }
3305            return false;
3306        }
3307
3308        enterStatement(varNode);
3309        assert method != null;
3310
3311        if (needsScope) {
3312            method.loadCompilerConstant(SCOPE);
3313        }
3314
3315        if (needsScope) {
3316            loadExpressionUnbounded(init);
3317            // block scoped variables need a DECLARE flag to signal end of temporal dead zone (TDZ)
3318            final int flags = getScopeCallSiteFlags(identSymbol) | (varNode.isBlockScoped() ? CALLSITE_DECLARE : 0);
3319            if (isFastScope(identSymbol)) {
3320                storeFastScopeVar(identSymbol, flags);
3321            } else {
3322                method.dynamicSet(identNode.getName(), flags, false);
3323            }
3324        } else {
3325            final Type identType = identNode.getType();
3326            if(identType == Type.UNDEFINED) {
3327                // The initializer is either itself undefined (explicit assignment of undefined to undefined),
3328                // or the left hand side is a dead variable.
3329                assert init.getType() == Type.UNDEFINED || identNode.getSymbol().slotCount() == 0;
3330                loadAndDiscard(init);
3331                return false;
3332            }
3333            loadExpressionAsType(init, identType);
3334            storeIdentWithCatchConversion(identNode, identType);
3335        }
3336
3337        return false;
3338    }
3339
3340    private void storeIdentWithCatchConversion(final IdentNode identNode, final Type type) {
3341        // Assignments happening in try/catch blocks need to ensure that they also store a possibly wider typed value
3342        // that will be live at the exit from the try block
3343        final LocalVariableConversion conversion = identNode.getLocalVariableConversion();
3344        final Symbol symbol = identNode.getSymbol();
3345        if(conversion != null && conversion.isLive()) {
3346            assert symbol == conversion.getSymbol();
3347            assert symbol.isBytecodeLocal();
3348            // Only a single conversion from the target type to the join type is expected.
3349            assert conversion.getNext() == null;
3350            assert conversion.getFrom() == type;
3351            // We must propagate potential type change to the catch block
3352            final Label catchLabel = catchLabels.peek();
3353            assert catchLabel != METHOD_BOUNDARY; // ident conversion only exists in try blocks
3354            assert catchLabel.isReachable();
3355            final Type joinType = conversion.getTo();
3356            final Label.Stack catchStack = catchLabel.getStack();
3357            final int joinSlot = symbol.getSlot(joinType);
3358            // With nested try/catch blocks (incl. synthetic ones for finally), we can have a supposed conversion for
3359            // the exception symbol in the nested catch, but it isn't live in the outer catch block, so prevent doing
3360            // conversions for it. E.g. in "try { try { ... } catch(e) { e = 1; } } catch(e2) { ... }", we must not
3361            // introduce an I->O conversion on "e = 1" assignment as "e" is not live in "catch(e2)".
3362            if(catchStack.getUsedSlotsWithLiveTemporaries() > joinSlot) {
3363                method.dup();
3364                method.convert(joinType);
3365                method.store(symbol, joinType);
3366                catchLabel.getStack().onLocalStore(joinType, joinSlot, true);
3367                method.canThrow(catchLabel);
3368                // Store but keep the previous store live too.
3369                method.store(symbol, type, false);
3370                return;
3371            }
3372        }
3373
3374        method.store(symbol, type, true);
3375    }
3376
3377    @Override
3378    public boolean enterWhileNode(final WhileNode whileNode) {
3379        if(!method.isReachable()) {
3380            return false;
3381        }
3382        if(whileNode.isDoWhile()) {
3383            enterDoWhile(whileNode);
3384        } else {
3385            enterStatement(whileNode);
3386            enterForOrWhile(whileNode, null);
3387        }
3388        return false;
3389    }
3390
3391    private void enterForOrWhile(final LoopNode loopNode, final JoinPredecessorExpression modify) {
3392        // NOTE: the usual pattern for compiling test-first loops is "GOTO test; body; test; IFNE body". We use the less
3393        // conventional "test; IFEQ break; body; GOTO test; break;". It has one extra unconditional GOTO in each repeat
3394        // of the loop, but it's not a problem for modern JIT compilers. We do this because our local variable type
3395        // tracking is unfortunately not really prepared for out-of-order execution, e.g. compiling the following
3396        // contrived but legal JavaScript code snippet would fail because the test changes the type of "i" from object
3397        // to double: var i = {valueOf: function() { return 1} }; while(--i >= 0) { ... }
3398        // Instead of adding more complexity to the local variable type tracking, we instead choose to emit this
3399        // different code shape.
3400        final int liveLocalsOnBreak = method.getUsedSlotsWithLiveTemporaries();
3401        final JoinPredecessorExpression test = loopNode.getTest();
3402        if(Expression.isAlwaysFalse(test)) {
3403            loadAndDiscard(test);
3404            return;
3405        }
3406
3407        method.beforeJoinPoint(loopNode);
3408
3409        final Label continueLabel = loopNode.getContinueLabel();
3410        final Label repeatLabel = modify != null ? new Label("for_repeat") : continueLabel;
3411        method.label(repeatLabel);
3412        final int liveLocalsOnContinue = method.getUsedSlotsWithLiveTemporaries();
3413
3414        final Block   body                  = loopNode.getBody();
3415        final Label   breakLabel            = loopNode.getBreakLabel();
3416        final boolean testHasLiveConversion = test != null && LocalVariableConversion.hasLiveConversion(test);
3417
3418        if(Expression.isAlwaysTrue(test)) {
3419            if(test != null) {
3420                loadAndDiscard(test);
3421                if(testHasLiveConversion) {
3422                    method.beforeJoinPoint(test);
3423                }
3424            }
3425        } else if (test != null) {
3426            if (testHasLiveConversion) {
3427                emitBranch(test.getExpression(), body.getEntryLabel(), true);
3428                method.beforeJoinPoint(test);
3429                method._goto(breakLabel);
3430            } else {
3431                emitBranch(test.getExpression(), breakLabel, false);
3432            }
3433        }
3434
3435        body.accept(this);
3436        if(repeatLabel != continueLabel) {
3437            emitContinueLabel(continueLabel, liveLocalsOnContinue);
3438        }
3439
3440        if (loopNode.hasPerIterationScope() && lc.getCurrentBlock().needsScope()) {
3441            // ES6 for loops with LET init need a new scope for each iteration. We just create a shallow copy here.
3442            method.loadCompilerConstant(SCOPE);
3443            method.invoke(virtualCallNoLookup(ScriptObject.class, "copy", ScriptObject.class));
3444            method.storeCompilerConstant(SCOPE);
3445        }
3446
3447        if(method.isReachable()) {
3448            if(modify != null) {
3449                lineNumber(loopNode);
3450                loadAndDiscard(modify);
3451                method.beforeJoinPoint(modify);
3452            }
3453            method._goto(repeatLabel);
3454        }
3455
3456        method.breakLabel(breakLabel, liveLocalsOnBreak);
3457    }
3458
3459    private void emitContinueLabel(final Label continueLabel, final int liveLocals) {
3460        final boolean reachable = method.isReachable();
3461        method.breakLabel(continueLabel, liveLocals);
3462        // If we reach here only through a continue statement (e.g. body does not exit normally) then the
3463        // continueLabel can have extra non-temp symbols (e.g. exception from a try/catch contained in the body). We
3464        // must make sure those are thrown away.
3465        if(!reachable) {
3466            method.undefineLocalVariables(lc.getUsedSlotCount(), false);
3467        }
3468    }
3469
3470    private void enterDoWhile(final WhileNode whileNode) {
3471        final int liveLocalsOnContinueOrBreak = method.getUsedSlotsWithLiveTemporaries();
3472        method.beforeJoinPoint(whileNode);
3473
3474        final Block body = whileNode.getBody();
3475        body.accept(this);
3476
3477        emitContinueLabel(whileNode.getContinueLabel(), liveLocalsOnContinueOrBreak);
3478        if(method.isReachable()) {
3479            lineNumber(whileNode);
3480            final JoinPredecessorExpression test = whileNode.getTest();
3481            final Label bodyEntryLabel = body.getEntryLabel();
3482            final boolean testHasLiveConversion = LocalVariableConversion.hasLiveConversion(test);
3483            if(Expression.isAlwaysFalse(test)) {
3484                loadAndDiscard(test);
3485                if(testHasLiveConversion) {
3486                    method.beforeJoinPoint(test);
3487                }
3488            } else if(testHasLiveConversion) {
3489                // If we have conversions after the test in do-while, they need to be effected on both branches.
3490                final Label beforeExit = new Label("do_while_preexit");
3491                emitBranch(test.getExpression(), beforeExit, false);
3492                method.beforeJoinPoint(test);
3493                method._goto(bodyEntryLabel);
3494                method.label(beforeExit);
3495                method.beforeJoinPoint(test);
3496            } else {
3497                emitBranch(test.getExpression(), bodyEntryLabel, true);
3498            }
3499        }
3500        method.breakLabel(whileNode.getBreakLabel(), liveLocalsOnContinueOrBreak);
3501    }
3502
3503
3504    @Override
3505    public boolean enterWithNode(final WithNode withNode) {
3506        if(!method.isReachable()) {
3507            return false;
3508        }
3509        enterStatement(withNode);
3510        final Expression expression = withNode.getExpression();
3511        final Block      body       = withNode.getBody();
3512
3513        // It is possible to have a "pathological" case where the with block does not reference *any* identifiers. It's
3514        // pointless, but legal. In that case, if nothing else in the method forced the assignment of a slot to the
3515        // scope object, its' possible that it won't have a slot assigned. In this case we'll only evaluate expression
3516        // for its side effect and visit the body, and not bother opening and closing a WithObject.
3517        final boolean hasScope = method.hasScope();
3518
3519        if (hasScope) {
3520            method.loadCompilerConstant(SCOPE);
3521        }
3522
3523        loadExpressionAsObject(expression);
3524
3525        final Label tryLabel;
3526        if (hasScope) {
3527            // Construct a WithObject if we have a scope
3528            method.invoke(ScriptRuntime.OPEN_WITH);
3529            method.storeCompilerConstant(SCOPE);
3530            tryLabel = new Label("with_try");
3531            method.label(tryLabel);
3532        } else {
3533            // We just loaded the expression for its side effect and to check
3534            // for null or undefined value.
3535            globalCheckObjectCoercible();
3536            tryLabel = null;
3537        }
3538
3539        // Always process body
3540        body.accept(this);
3541
3542        if (hasScope) {
3543            // Ensure we always close the WithObject
3544            final Label endLabel   = new Label("with_end");
3545            final Label catchLabel = new Label("with_catch");
3546            final Label exitLabel  = new Label("with_exit");
3547
3548            method.label(endLabel);
3549            // Somewhat conservatively presume that if the body is not empty, it can throw an exception. In any case,
3550            // we must prevent trying to emit a try-catch for empty range, as it causes a verification error.
3551            final boolean bodyCanThrow = endLabel.isAfter(tryLabel);
3552            if(bodyCanThrow) {
3553                method._try(tryLabel, endLabel, catchLabel);
3554            }
3555
3556            final boolean reachable = method.isReachable();
3557            if(reachable) {
3558                popScope();
3559                if(bodyCanThrow) {
3560                    method._goto(exitLabel);
3561                }
3562            }
3563
3564            if(bodyCanThrow) {
3565                method._catch(catchLabel);
3566                popScopeException();
3567                method.athrow();
3568                if(reachable) {
3569                    method.label(exitLabel);
3570                }
3571            }
3572        }
3573        return false;
3574    }
3575
3576    private void loadADD(final UnaryNode unaryNode, final TypeBounds resultBounds) {
3577        loadExpression(unaryNode.getExpression(), resultBounds.booleanToInt().notWiderThan(Type.NUMBER));
3578        if(method.peekType() == Type.BOOLEAN) {
3579            // It's a no-op in bytecode, but we must make sure it is treated as an int for purposes of type signatures
3580            method.convert(Type.INT);
3581        }
3582    }
3583
3584    private void loadBIT_NOT(final UnaryNode unaryNode) {
3585        loadExpression(unaryNode.getExpression(), TypeBounds.INT).load(-1).xor();
3586    }
3587
3588    private void loadDECINC(final UnaryNode unaryNode) {
3589        final Expression operand     = unaryNode.getExpression();
3590        final Type       type        = unaryNode.getType();
3591        final TypeBounds typeBounds  = new TypeBounds(type, Type.NUMBER);
3592        final TokenType  tokenType   = unaryNode.tokenType();
3593        final boolean    isPostfix   = tokenType == TokenType.DECPOSTFIX || tokenType == TokenType.INCPOSTFIX;
3594        final boolean    isIncrement = tokenType == TokenType.INCPREFIX || tokenType == TokenType.INCPOSTFIX;
3595
3596        assert !type.isObject();
3597
3598        new SelfModifyingStore<UnaryNode>(unaryNode, operand) {
3599
3600            private void loadRhs() {
3601                loadExpression(operand, typeBounds, true);
3602            }
3603
3604            @Override
3605            protected void evaluate() {
3606                if(isPostfix) {
3607                    loadRhs();
3608                } else {
3609                    new OptimisticOperation(unaryNode, typeBounds) {
3610                        @Override
3611                        void loadStack() {
3612                            loadRhs();
3613                            loadMinusOne();
3614                        }
3615                        @Override
3616                        void consumeStack() {
3617                            doDecInc(getProgramPoint());
3618                        }
3619                    }.emit(getOptimisticIgnoreCountForSelfModifyingExpression(operand));
3620                }
3621            }
3622
3623            @Override
3624            protected void storeNonDiscard() {
3625                super.storeNonDiscard();
3626                if (isPostfix) {
3627                    new OptimisticOperation(unaryNode, typeBounds) {
3628                        @Override
3629                        void loadStack() {
3630                            loadMinusOne();
3631                        }
3632                        @Override
3633                        void consumeStack() {
3634                            doDecInc(getProgramPoint());
3635                        }
3636                    }.emit(1); // 1 for non-incremented result on the top of the stack pushed in evaluate()
3637                }
3638            }
3639
3640            private void loadMinusOne() {
3641                if (type.isInteger()) {
3642                    method.load(isIncrement ? 1 : -1);
3643                } else if (type.isLong()) {
3644                    method.load(isIncrement ? 1L : -1L);
3645                } else {
3646                    method.load(isIncrement ? 1.0 : -1.0);
3647                }
3648            }
3649
3650            private void doDecInc(final int programPoint) {
3651                method.add(programPoint);
3652            }
3653        }.store();
3654    }
3655
3656    private static int getOptimisticIgnoreCountForSelfModifyingExpression(final Expression target) {
3657        return target instanceof AccessNode ? 1 : target instanceof IndexNode ? 2 : 0;
3658    }
3659
3660    private void loadAndDiscard(final Expression expr) {
3661        // TODO: move checks for discarding to actual expression load code (e.g. as we do with void). That way we might
3662        // be able to eliminate even more checks.
3663        if(expr instanceof PrimitiveLiteralNode | isLocalVariable(expr)) {
3664            assert !lc.isCurrentDiscard(expr);
3665            // Don't bother evaluating expressions without side effects. Typical usage is "void 0" for reliably generating
3666            // undefined.
3667            return;
3668        }
3669
3670        lc.pushDiscard(expr);
3671        loadExpression(expr, TypeBounds.UNBOUNDED);
3672        if (lc.popDiscardIfCurrent(expr)) {
3673            assert !expr.isAssignment();
3674            // NOTE: if we had a way to load with type void, we could avoid popping
3675            method.pop();
3676        }
3677    }
3678
3679    /**
3680     * Loads the expression with the specified type bounds, but if the parent expression is the current discard,
3681     * then instead loads and discards the expression.
3682     * @param parent the parent expression that's tested for being the current discard
3683     * @param expr the expression that's either normally loaded or discard-loaded
3684     * @param resultBounds result bounds for when loading the expression normally
3685     */
3686    private void loadMaybeDiscard(final Expression parent, final Expression expr, final TypeBounds resultBounds) {
3687        loadMaybeDiscard(lc.popDiscardIfCurrent(parent), expr, resultBounds);
3688    }
3689
3690    /**
3691     * Loads the expression with the specified type bounds, or loads and discards the expression, depending on the
3692     * value of the discard flag. Useful as a helper for expressions with control flow where you often can't combine
3693     * testing for being the current discard and loading the subexpressions.
3694     * @param discard if true, the expression is loaded and discarded
3695     * @param expr the expression that's either normally loaded or discard-loaded
3696     * @param resultBounds result bounds for when loading the expression normally
3697     */
3698    private void loadMaybeDiscard(final boolean discard, final Expression expr, final TypeBounds resultBounds) {
3699        if (discard) {
3700            loadAndDiscard(expr);
3701        } else {
3702            loadExpression(expr, resultBounds);
3703        }
3704    }
3705
3706    private void loadNEW(final UnaryNode unaryNode) {
3707        final CallNode callNode = (CallNode)unaryNode.getExpression();
3708        final List<Expression> args   = callNode.getArgs();
3709
3710        // Load function reference.
3711        loadExpressionAsObject(callNode.getFunction()); // must detect type error
3712
3713        method.dynamicNew(1 + loadArgs(args), getCallSiteFlags());
3714    }
3715
3716    private void loadNOT(final UnaryNode unaryNode) {
3717        final Expression expr = unaryNode.getExpression();
3718        if(expr instanceof UnaryNode && expr.isTokenType(TokenType.NOT)) {
3719            // !!x is idiomatic boolean cast in JavaScript
3720            loadExpressionAsBoolean(((UnaryNode)expr).getExpression());
3721        } else {
3722            final Label trueLabel  = new Label("true");
3723            final Label afterLabel = new Label("after");
3724
3725            emitBranch(expr, trueLabel, true);
3726            method.load(true);
3727            method._goto(afterLabel);
3728            method.label(trueLabel);
3729            method.load(false);
3730            method.label(afterLabel);
3731        }
3732    }
3733
3734    private void loadSUB(final UnaryNode unaryNode, final TypeBounds resultBounds) {
3735        final Type type = unaryNode.getType();
3736        assert type.isNumeric();
3737        final TypeBounds numericBounds = resultBounds.booleanToInt();
3738        new OptimisticOperation(unaryNode, numericBounds) {
3739            @Override
3740            void loadStack() {
3741                final Expression expr = unaryNode.getExpression();
3742                loadExpression(expr, numericBounds.notWiderThan(Type.NUMBER));
3743            }
3744            @Override
3745            void consumeStack() {
3746                // Must do an explicit conversion to the operation's type when it's double so that we correctly handle
3747                // negation of an int 0 to a double -0. With this, we get the correct negation of a local variable after
3748                // it deoptimized, e.g. "iload_2; i2d; dneg". Without this, we get "iload_2; ineg; i2d".
3749                if(type.isNumber()) {
3750                    method.convert(type);
3751                }
3752                method.neg(getProgramPoint());
3753            }
3754        }.emit();
3755    }
3756
3757    public void loadVOID(final UnaryNode unaryNode, final TypeBounds resultBounds) {
3758        loadAndDiscard(unaryNode.getExpression());
3759        if (!lc.popDiscardIfCurrent(unaryNode)) {
3760            method.loadUndefined(resultBounds.widest);
3761        }
3762    }
3763
3764    public void loadADD(final BinaryNode binaryNode, final TypeBounds resultBounds) {
3765        new OptimisticOperation(binaryNode, resultBounds) {
3766            @Override
3767            void loadStack() {
3768                final TypeBounds operandBounds;
3769                final boolean isOptimistic = isValid(getProgramPoint());
3770                boolean forceConversionSeparation = false;
3771                if(isOptimistic) {
3772                    operandBounds = new TypeBounds(binaryNode.getType(), Type.OBJECT);
3773                } else {
3774                    // Non-optimistic, non-FP +. Allow it to overflow.
3775                    final Type widestOperationType = binaryNode.getWidestOperationType();
3776                    operandBounds = new TypeBounds(Type.narrowest(binaryNode.getWidestOperandType(), resultBounds.widest), widestOperationType);
3777                    forceConversionSeparation = widestOperationType.narrowerThan(resultBounds.widest);
3778                }
3779                loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), operandBounds, false, forceConversionSeparation);
3780            }
3781
3782            @Override
3783            void consumeStack() {
3784                method.add(getProgramPoint());
3785            }
3786        }.emit();
3787    }
3788
3789    private void loadAND_OR(final BinaryNode binaryNode, final TypeBounds resultBounds, final boolean isAnd) {
3790        final Type narrowestOperandType = Type.widestReturnType(binaryNode.lhs().getType(), binaryNode.rhs().getType());
3791
3792        final boolean isCurrentDiscard = lc.popDiscardIfCurrent(binaryNode);
3793
3794        final Label skip = new Label("skip");
3795        if(narrowestOperandType == Type.BOOLEAN) {
3796            // optimize all-boolean logical expressions
3797            final Label onTrue = new Label("andor_true");
3798            emitBranch(binaryNode, onTrue, true);
3799            if (isCurrentDiscard) {
3800                method.label(onTrue);
3801                method.pop();
3802            } else {
3803                method.load(false);
3804                method._goto(skip);
3805                method.label(onTrue);
3806                method.load(true);
3807                method.label(skip);
3808            }
3809            return;
3810        }
3811
3812        final TypeBounds outBounds = resultBounds.notNarrowerThan(narrowestOperandType);
3813        final JoinPredecessorExpression lhs = (JoinPredecessorExpression)binaryNode.lhs();
3814        final boolean lhsConvert = LocalVariableConversion.hasLiveConversion(lhs);
3815        final Label evalRhs = lhsConvert ? new Label("eval_rhs") : null;
3816
3817        loadExpression(lhs, outBounds);
3818        if (!isCurrentDiscard) {
3819            method.dup();
3820        }
3821        method.convert(Type.BOOLEAN);
3822        if (isAnd) {
3823            if(lhsConvert) {
3824                method.ifne(evalRhs);
3825            } else {
3826                method.ifeq(skip);
3827            }
3828        } else if(lhsConvert) {
3829            method.ifeq(evalRhs);
3830        } else {
3831            method.ifne(skip);
3832        }
3833
3834        if(lhsConvert) {
3835            method.beforeJoinPoint(lhs);
3836            method._goto(skip);
3837            method.label(evalRhs);
3838        }
3839
3840        if (!isCurrentDiscard) {
3841            method.pop();
3842        }
3843        final JoinPredecessorExpression rhs = (JoinPredecessorExpression)binaryNode.rhs();
3844        loadMaybeDiscard(isCurrentDiscard, rhs, outBounds);
3845        method.beforeJoinPoint(rhs);
3846        method.label(skip);
3847    }
3848
3849    private static boolean isLocalVariable(final Expression lhs) {
3850        return lhs instanceof IdentNode && isLocalVariable((IdentNode)lhs);
3851    }
3852
3853    private static boolean isLocalVariable(final IdentNode lhs) {
3854        return lhs.getSymbol().isBytecodeLocal();
3855    }
3856
3857    // NOTE: does not use resultBounds as the assignment is driven by the type of the RHS
3858    private void loadASSIGN(final BinaryNode binaryNode) {
3859        final Expression lhs = binaryNode.lhs();
3860        final Expression rhs = binaryNode.rhs();
3861
3862        final Type rhsType = rhs.getType();
3863        // Detect dead assignments
3864        if(lhs instanceof IdentNode) {
3865            final Symbol symbol = ((IdentNode)lhs).getSymbol();
3866            if(!symbol.isScope() && !symbol.hasSlotFor(rhsType) && lc.popDiscardIfCurrent(binaryNode)) {
3867                loadAndDiscard(rhs);
3868                method.markDeadLocalVariable(symbol);
3869                return;
3870            }
3871        }
3872
3873        new Store<BinaryNode>(binaryNode, lhs) {
3874            @Override
3875            protected void evaluate() {
3876                // NOTE: we're loading with "at least as wide as" so optimistic operations on the right hand side
3877                // remain optimistic, and then explicitly convert to the required type if needed.
3878                loadExpressionAsType(rhs, rhsType);
3879            }
3880        }.store();
3881    }
3882
3883    /**
3884     * Binary self-assignment that can be optimistic: +=, -=, *=, and /=.
3885     */
3886    private abstract class BinaryOptimisticSelfAssignment extends SelfModifyingStore<BinaryNode> {
3887
3888        /**
3889         * Constructor
3890         *
3891         * @param node the assign op node
3892         */
3893        BinaryOptimisticSelfAssignment(final BinaryNode node) {
3894            super(node, node.lhs());
3895        }
3896
3897        protected abstract void op(OptimisticOperation oo);
3898
3899        @Override
3900        protected void evaluate() {
3901            final Expression lhs = assignNode.lhs();
3902            final Expression rhs = assignNode.rhs();
3903            final Type widestOperationType = assignNode.getWidestOperationType();
3904            final TypeBounds bounds = new TypeBounds(assignNode.getType(), widestOperationType);
3905            new OptimisticOperation(assignNode, bounds) {
3906                @Override
3907                void loadStack() {
3908                    final boolean forceConversionSeparation;
3909                    if (isValid(getProgramPoint()) || widestOperationType == Type.NUMBER) {
3910                        forceConversionSeparation = false;
3911                    } else {
3912                        final Type operandType = Type.widest(booleanToInt(objectToNumber(lhs.getType())), booleanToInt(objectToNumber(rhs.getType())));
3913                        forceConversionSeparation = operandType.narrowerThan(widestOperationType);
3914                    }
3915                    loadBinaryOperands(lhs, rhs, bounds, true, forceConversionSeparation);
3916                }
3917                @Override
3918                void consumeStack() {
3919                    op(this);
3920                }
3921            }.emit(getOptimisticIgnoreCountForSelfModifyingExpression(lhs));
3922            method.convert(assignNode.getType());
3923        }
3924    }
3925
3926    /**
3927     * Non-optimistic binary self-assignment operation. Basically, everything except +=, -=, *=, and /=.
3928     */
3929    private abstract class BinarySelfAssignment extends SelfModifyingStore<BinaryNode> {
3930        BinarySelfAssignment(final BinaryNode node) {
3931            super(node, node.lhs());
3932        }
3933
3934        protected abstract void op();
3935
3936        @Override
3937        protected void evaluate() {
3938            loadBinaryOperands(assignNode.lhs(), assignNode.rhs(), TypeBounds.UNBOUNDED.notWiderThan(assignNode.getWidestOperandType()), true, false);
3939            op();
3940        }
3941    }
3942
3943    private void loadASSIGN_ADD(final BinaryNode binaryNode) {
3944        new BinaryOptimisticSelfAssignment(binaryNode) {
3945            @Override
3946            protected void op(final OptimisticOperation oo) {
3947                assert !(binaryNode.getType().isObject() && oo.isOptimistic);
3948                method.add(oo.getProgramPoint());
3949            }
3950        }.store();
3951    }
3952
3953    private void loadASSIGN_BIT_AND(final BinaryNode binaryNode) {
3954        new BinarySelfAssignment(binaryNode) {
3955            @Override
3956            protected void op() {
3957                method.and();
3958            }
3959        }.store();
3960    }
3961
3962    private void loadASSIGN_BIT_OR(final BinaryNode binaryNode) {
3963        new BinarySelfAssignment(binaryNode) {
3964            @Override
3965            protected void op() {
3966                method.or();
3967            }
3968        }.store();
3969    }
3970
3971    private void loadASSIGN_BIT_XOR(final BinaryNode binaryNode) {
3972        new BinarySelfAssignment(binaryNode) {
3973            @Override
3974            protected void op() {
3975                method.xor();
3976            }
3977        }.store();
3978    }
3979
3980    private void loadASSIGN_DIV(final BinaryNode binaryNode) {
3981        new BinaryOptimisticSelfAssignment(binaryNode) {
3982            @Override
3983            protected void op(final OptimisticOperation oo) {
3984                method.div(oo.getProgramPoint());
3985            }
3986        }.store();
3987    }
3988
3989    private void loadASSIGN_MOD(final BinaryNode binaryNode) {
3990        new BinaryOptimisticSelfAssignment(binaryNode) {
3991            @Override
3992            protected void op(final OptimisticOperation oo) {
3993                method.rem(oo.getProgramPoint());
3994            }
3995        }.store();
3996    }
3997
3998    private void loadASSIGN_MUL(final BinaryNode binaryNode) {
3999        new BinaryOptimisticSelfAssignment(binaryNode) {
4000            @Override
4001            protected void op(final OptimisticOperation oo) {
4002                method.mul(oo.getProgramPoint());
4003            }
4004        }.store();
4005    }
4006
4007    private void loadASSIGN_SAR(final BinaryNode binaryNode) {
4008        new BinarySelfAssignment(binaryNode) {
4009            @Override
4010            protected void op() {
4011                method.sar();
4012            }
4013        }.store();
4014    }
4015
4016    private void loadASSIGN_SHL(final BinaryNode binaryNode) {
4017        new BinarySelfAssignment(binaryNode) {
4018            @Override
4019            protected void op() {
4020                method.shl();
4021            }
4022        }.store();
4023    }
4024
4025    private void loadASSIGN_SHR(final BinaryNode binaryNode) {
4026        new BinarySelfAssignment(binaryNode) {
4027            @Override
4028            protected void op() {
4029                doSHR();
4030            }
4031
4032        }.store();
4033    }
4034
4035    private void doSHR() {
4036        // TODO: make SHR optimistic
4037        method.shr();
4038        toUint();
4039    }
4040
4041    private void toUint() {
4042        JSType.TO_UINT32_I.invoke(method);
4043    }
4044
4045    private void loadASSIGN_SUB(final BinaryNode binaryNode) {
4046        new BinaryOptimisticSelfAssignment(binaryNode) {
4047            @Override
4048            protected void op(final OptimisticOperation oo) {
4049                method.sub(oo.getProgramPoint());
4050            }
4051        }.store();
4052    }
4053
4054    /**
4055     * Helper class for binary arithmetic ops
4056     */
4057    private abstract class BinaryArith {
4058        protected abstract void op(int programPoint);
4059
4060        protected void evaluate(final BinaryNode node, final TypeBounds resultBounds) {
4061            final TypeBounds numericBounds = resultBounds.booleanToInt().objectToNumber();
4062            new OptimisticOperation(node, numericBounds) {
4063                @Override
4064                void loadStack() {
4065                    final TypeBounds operandBounds;
4066                    boolean forceConversionSeparation = false;
4067                    if(numericBounds.narrowest == Type.NUMBER) {
4068                        // Result should be double always. Propagate it into the operands so we don't have lots of I2D
4069                        // and L2D after operand evaluation.
4070                        assert numericBounds.widest == Type.NUMBER;
4071                        operandBounds = numericBounds;
4072                    } else {
4073                        final boolean isOptimistic = isValid(getProgramPoint());
4074                        if(isOptimistic || node.isTokenType(TokenType.DIV) || node.isTokenType(TokenType.MOD)) {
4075                            operandBounds = new TypeBounds(node.getType(), Type.NUMBER);
4076                        } else {
4077                            // Non-optimistic, non-FP subtraction or multiplication. Allow them to overflow.
4078                            operandBounds = new TypeBounds(Type.narrowest(node.getWidestOperandType(),
4079                                    numericBounds.widest), Type.NUMBER);
4080                            forceConversionSeparation = node.getWidestOperationType().narrowerThan(numericBounds.widest);
4081                        }
4082                    }
4083                    loadBinaryOperands(node.lhs(), node.rhs(), operandBounds, false, forceConversionSeparation);
4084                }
4085
4086                @Override
4087                void consumeStack() {
4088                    op(getProgramPoint());
4089                }
4090            }.emit();
4091        }
4092    }
4093
4094    private void loadBIT_AND(final BinaryNode binaryNode) {
4095        loadBinaryOperands(binaryNode);
4096        method.and();
4097    }
4098
4099    private void loadBIT_OR(final BinaryNode binaryNode) {
4100        // Optimize x|0 to (int)x
4101        if (isRhsZero(binaryNode)) {
4102            loadExpressionAsType(binaryNode.lhs(), Type.INT);
4103        } else {
4104            loadBinaryOperands(binaryNode);
4105            method.or();
4106        }
4107    }
4108
4109    private static boolean isRhsZero(final BinaryNode binaryNode) {
4110        final Expression rhs = binaryNode.rhs();
4111        return rhs instanceof LiteralNode && INT_ZERO.equals(((LiteralNode<?>)rhs).getValue());
4112    }
4113
4114    private void loadBIT_XOR(final BinaryNode binaryNode) {
4115        loadBinaryOperands(binaryNode);
4116        method.xor();
4117    }
4118
4119    private void loadCOMMARIGHT(final BinaryNode binaryNode, final TypeBounds resultBounds) {
4120        loadAndDiscard(binaryNode.lhs());
4121        loadMaybeDiscard(binaryNode, binaryNode.rhs(), resultBounds);
4122    }
4123
4124    private void loadCOMMALEFT(final BinaryNode binaryNode, final TypeBounds resultBounds) {
4125        loadMaybeDiscard(binaryNode, binaryNode.lhs(), resultBounds);
4126        loadAndDiscard(binaryNode.rhs());
4127    }
4128
4129    private void loadDIV(final BinaryNode binaryNode, final TypeBounds resultBounds) {
4130        new BinaryArith() {
4131            @Override
4132            protected void op(final int programPoint) {
4133                method.div(programPoint);
4134            }
4135        }.evaluate(binaryNode, resultBounds);
4136    }
4137
4138    private void loadCmp(final BinaryNode binaryNode, final Condition cond) {
4139        loadComparisonOperands(binaryNode);
4140
4141        final Label trueLabel  = new Label("trueLabel");
4142        final Label afterLabel = new Label("skip");
4143
4144        method.conditionalJump(cond, trueLabel);
4145
4146        method.load(Boolean.FALSE);
4147        method._goto(afterLabel);
4148        method.label(trueLabel);
4149        method.load(Boolean.TRUE);
4150        method.label(afterLabel);
4151    }
4152
4153    private void loadMOD(final BinaryNode binaryNode, final TypeBounds resultBounds) {
4154        new BinaryArith() {
4155            @Override
4156            protected void op(final int programPoint) {
4157                method.rem(programPoint);
4158            }
4159        }.evaluate(binaryNode, resultBounds);
4160    }
4161
4162    private void loadMUL(final BinaryNode binaryNode, final TypeBounds resultBounds) {
4163        new BinaryArith() {
4164            @Override
4165            protected void op(final int programPoint) {
4166                method.mul(programPoint);
4167            }
4168        }.evaluate(binaryNode, resultBounds);
4169    }
4170
4171    private void loadSAR(final BinaryNode binaryNode) {
4172        loadBinaryOperands(binaryNode);
4173        method.sar();
4174    }
4175
4176    private void loadSHL(final BinaryNode binaryNode) {
4177        loadBinaryOperands(binaryNode);
4178        method.shl();
4179    }
4180
4181    private void loadSHR(final BinaryNode binaryNode) {
4182        // Optimize x >>> 0 to (uint)x
4183        if (isRhsZero(binaryNode)) {
4184            loadExpressionAsType(binaryNode.lhs(), Type.INT);
4185            toUint();
4186        } else {
4187            loadBinaryOperands(binaryNode);
4188            doSHR();
4189        }
4190    }
4191
4192    private void loadSUB(final BinaryNode binaryNode, final TypeBounds resultBounds) {
4193        new BinaryArith() {
4194            @Override
4195            protected void op(final int programPoint) {
4196                method.sub(programPoint);
4197            }
4198        }.evaluate(binaryNode, resultBounds);
4199    }
4200
4201    @Override
4202    public boolean enterLabelNode(final LabelNode labelNode) {
4203        labeledBlockBreakLiveLocals.push(lc.getUsedSlotCount());
4204        return true;
4205    }
4206
4207    @Override
4208    protected boolean enterDefault(final Node node) {
4209        throw new AssertionError("Code generator entered node of type " + node.getClass().getName());
4210    }
4211
4212    private void loadTernaryNode(final TernaryNode ternaryNode, final TypeBounds resultBounds) {
4213        final Expression test = ternaryNode.getTest();
4214        final JoinPredecessorExpression trueExpr  = ternaryNode.getTrueExpression();
4215        final JoinPredecessorExpression falseExpr = ternaryNode.getFalseExpression();
4216
4217        final Label falseLabel = new Label("ternary_false");
4218        final Label exitLabel  = new Label("ternary_exit");
4219
4220        final Type outNarrowest = Type.narrowest(resultBounds.widest, Type.generic(Type.widestReturnType(trueExpr.getType(), falseExpr.getType())));
4221        final TypeBounds outBounds = resultBounds.notNarrowerThan(outNarrowest);
4222
4223        emitBranch(test, falseLabel, false);
4224
4225        final boolean isCurrentDiscard = lc.popDiscardIfCurrent(ternaryNode);
4226        loadMaybeDiscard(isCurrentDiscard, trueExpr.getExpression(), outBounds);
4227        assert isCurrentDiscard || Type.generic(method.peekType()) == outBounds.narrowest;
4228        method.beforeJoinPoint(trueExpr);
4229        method._goto(exitLabel);
4230        method.label(falseLabel);
4231        loadMaybeDiscard(isCurrentDiscard, falseExpr.getExpression(), outBounds);
4232        assert isCurrentDiscard || Type.generic(method.peekType()) == outBounds.narrowest;
4233        method.beforeJoinPoint(falseExpr);
4234        method.label(exitLabel);
4235    }
4236
4237    /**
4238     * Generate all shared scope calls generated during codegen.
4239     */
4240    void generateScopeCalls() {
4241        for (final SharedScopeCall scopeAccess : lc.getScopeCalls()) {
4242            scopeAccess.generateScopeCall();
4243        }
4244    }
4245
4246    /**
4247     * Debug code used to print symbols
4248     *
4249     * @param block the block we are in
4250     * @param function the function we are in
4251     * @param ident identifier for block or function where applicable
4252     */
4253    private void printSymbols(final Block block, final FunctionNode function, final String ident) {
4254        if (compiler.getScriptEnvironment()._print_symbols || function.getFlag(FunctionNode.IS_PRINT_SYMBOLS)) {
4255            final PrintWriter out = compiler.getScriptEnvironment().getErr();
4256            out.println("[BLOCK in '" + ident + "']");
4257            if (!block.printSymbols(out)) {
4258                out.println("<no symbols>");
4259            }
4260            out.println();
4261        }
4262    }
4263
4264
4265    /**
4266     * The difference between a store and a self modifying store is that
4267     * the latter may load part of the target on the stack, e.g. the base
4268     * of an AccessNode or the base and index of an IndexNode. These are used
4269     * both as target and as an extra source. Previously it was problematic
4270     * for self modifying stores if the target/lhs didn't belong to one
4271     * of three trivial categories: IdentNode, AcessNodes, IndexNodes. In that
4272     * case it was evaluated and tagged as "resolved", which meant at the second
4273     * time the lhs of this store was read (e.g. in a = a (second) + b for a += b,
4274     * it would be evaluated to a nop in the scope and cause stack underflow
4275     *
4276     * see NASHORN-703
4277     *
4278     * @param <T>
4279     */
4280    private abstract class SelfModifyingStore<T extends Expression> extends Store<T> {
4281        protected SelfModifyingStore(final T assignNode, final Expression target) {
4282            super(assignNode, target);
4283        }
4284
4285        @Override
4286        protected boolean isSelfModifying() {
4287            return true;
4288        }
4289    }
4290
4291    /**
4292     * Helper class to generate stores
4293     */
4294    private abstract class Store<T extends Expression> {
4295
4296        /** An assignment node, e.g. x += y */
4297        protected final T assignNode;
4298
4299        /** The target node to store to, e.g. x */
4300        private final Expression target;
4301
4302        /** How deep on the stack do the arguments go if this generates an indy call */
4303        private int depth;
4304
4305        /** If we have too many arguments, we need temporary storage, this is stored in 'quick' */
4306        private IdentNode quick;
4307
4308        /**
4309         * Constructor
4310         *
4311         * @param assignNode the node representing the whole assignment
4312         * @param target     the target node of the assignment (destination)
4313         */
4314        protected Store(final T assignNode, final Expression target) {
4315            this.assignNode = assignNode;
4316            this.target = target;
4317        }
4318
4319        /**
4320         * Constructor
4321         *
4322         * @param assignNode the node representing the whole assignment
4323         */
4324        protected Store(final T assignNode) {
4325            this(assignNode, assignNode);
4326        }
4327
4328        /**
4329         * Is this a self modifying store operation, e.g. *= or ++
4330         * @return true if self modifying store
4331         */
4332        protected boolean isSelfModifying() {
4333            return false;
4334        }
4335
4336        private void prologue() {
4337            /**
4338             * This loads the parts of the target, e.g base and index. they are kept
4339             * on the stack throughout the store and used at the end to execute it
4340             */
4341
4342            target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
4343                @Override
4344                public boolean enterIdentNode(final IdentNode node) {
4345                    if (node.getSymbol().isScope()) {
4346                        method.loadCompilerConstant(SCOPE);
4347                        depth += Type.SCOPE.getSlots();
4348                        assert depth == 1;
4349                    }
4350                    return false;
4351                }
4352
4353                private void enterBaseNode() {
4354                    assert target instanceof BaseNode : "error - base node " + target + " must be instanceof BaseNode";
4355                    final BaseNode   baseNode = (BaseNode)target;
4356                    final Expression base     = baseNode.getBase();
4357
4358                    loadExpressionAsObject(base);
4359                    depth += Type.OBJECT.getSlots();
4360                    assert depth == 1;
4361
4362                    if (isSelfModifying()) {
4363                        method.dup();
4364                    }
4365                }
4366
4367                @Override
4368                public boolean enterAccessNode(final AccessNode node) {
4369                    enterBaseNode();
4370                    return false;
4371                }
4372
4373                @Override
4374                public boolean enterIndexNode(final IndexNode node) {
4375                    enterBaseNode();
4376
4377                    final Expression index = node.getIndex();
4378                    if (!index.getType().isNumeric()) {
4379                        // could be boolean here as well
4380                        loadExpressionAsObject(index);
4381                    } else {
4382                        loadExpressionUnbounded(index);
4383                    }
4384                    depth += index.getType().getSlots();
4385
4386                    if (isSelfModifying()) {
4387                        //convert "base base index" to "base index base index"
4388                        method.dup(1);
4389                    }
4390
4391                    return false;
4392                }
4393
4394            });
4395        }
4396
4397        /**
4398         * Generates an extra local variable, always using the same slot, one that is available after the end of the
4399         * frame.
4400         *
4401         * @param type the type of the variable
4402         *
4403         * @return the quick variable
4404         */
4405        private IdentNode quickLocalVariable(final Type type) {
4406            final String name = lc.getCurrentFunction().uniqueName(QUICK_PREFIX.symbolName());
4407            final Symbol symbol = new Symbol(name, IS_INTERNAL | HAS_SLOT);
4408            symbol.setHasSlotFor(type);
4409            symbol.setFirstSlot(lc.quickSlot(type));
4410
4411            final IdentNode quickIdent = IdentNode.createInternalIdentifier(symbol).setType(type);
4412
4413            return quickIdent;
4414        }
4415
4416        // store the result that "lives on" after the op, e.g. "i" in i++ postfix.
4417        protected void storeNonDiscard() {
4418            if (lc.popDiscardIfCurrent(assignNode)) {
4419                assert assignNode.isAssignment();
4420                return;
4421            }
4422
4423            if (method.dup(depth) == null) {
4424                method.dup();
4425                final Type quickType = method.peekType();
4426                this.quick = quickLocalVariable(quickType);
4427                final Symbol quickSymbol = quick.getSymbol();
4428                method.storeTemp(quickType, quickSymbol.getFirstSlot());
4429            }
4430        }
4431
4432        private void epilogue() {
4433            /**
4434             * Take the original target args from the stack and use them
4435             * together with the value to be stored to emit the store code
4436             *
4437             * The case that targetSymbol is in scope (!hasSlot) and we actually
4438             * need to do a conversion on non-equivalent types exists, but is
4439             * very rare. See for example test/script/basic/access-specializer.js
4440             */
4441            target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
4442                @Override
4443                protected boolean enterDefault(final Node node) {
4444                    throw new AssertionError("Unexpected node " + node + " in store epilogue");
4445                }
4446
4447                @Override
4448                public boolean enterIdentNode(final IdentNode node) {
4449                    final Symbol symbol = node.getSymbol();
4450                    assert symbol != null;
4451                    if (symbol.isScope()) {
4452                        final int flags = getScopeCallSiteFlags(symbol);
4453                        if (isFastScope(symbol)) {
4454                            storeFastScopeVar(symbol, flags);
4455                        } else {
4456                            method.dynamicSet(node.getName(), flags, false);
4457                        }
4458                    } else {
4459                        final Type storeType = assignNode.getType();
4460                        if (symbol.hasSlotFor(storeType)) {
4461                            // Only emit a convert for a store known to be live; converts for dead stores can
4462                            // give us an unnecessary ClassCastException.
4463                            method.convert(storeType);
4464                        }
4465                        storeIdentWithCatchConversion(node, storeType);
4466                    }
4467                    return false;
4468
4469                }
4470
4471                @Override
4472                public boolean enterAccessNode(final AccessNode node) {
4473                    method.dynamicSet(node.getProperty(), getCallSiteFlags(), node.isIndex());
4474                    return false;
4475                }
4476
4477                @Override
4478                public boolean enterIndexNode(final IndexNode node) {
4479                    method.dynamicSetIndex(getCallSiteFlags());
4480                    return false;
4481                }
4482            });
4483
4484
4485            // whatever is on the stack now is the final answer
4486        }
4487
4488        protected abstract void evaluate();
4489
4490        void store() {
4491            if (target instanceof IdentNode) {
4492                checkTemporalDeadZone((IdentNode)target);
4493            }
4494            prologue();
4495            evaluate(); // leaves an operation of whatever the operationType was on the stack
4496            storeNonDiscard();
4497            epilogue();
4498            if (quick != null) {
4499                method.load(quick);
4500            }
4501        }
4502    }
4503
4504    private void newFunctionObject(final FunctionNode functionNode, final boolean addInitializer) {
4505        assert lc.peek() == functionNode;
4506
4507        final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(functionNode.getId());
4508
4509        if (functionNode.isProgram() && !compiler.isOnDemandCompilation()) {
4510            final MethodEmitter createFunction = functionNode.getCompileUnit().getClassEmitter().method(
4511                    EnumSet.of(Flag.PUBLIC, Flag.STATIC), CREATE_PROGRAM_FUNCTION.symbolName(),
4512                    ScriptFunction.class, ScriptObject.class);
4513            createFunction.begin();
4514            loadConstantsAndIndex(data, createFunction);
4515            createFunction.load(SCOPE_TYPE, 0);
4516            createFunction.invoke(CREATE_FUNCTION_OBJECT);
4517            createFunction._return();
4518            createFunction.end();
4519        }
4520
4521        if (addInitializer && !compiler.isOnDemandCompilation()) {
4522            functionNode.getCompileUnit().addFunctionInitializer(data, functionNode);
4523        }
4524
4525        // We don't emit a ScriptFunction on stack for the outermost compiled function (as there's no code being
4526        // generated in its outer context that'd need it as a callee).
4527        if (lc.getOutermostFunction() == functionNode) {
4528            return;
4529        }
4530
4531        loadConstantsAndIndex(data, method);
4532
4533        if (functionNode.needsParentScope()) {
4534            method.loadCompilerConstant(SCOPE);
4535            method.invoke(CREATE_FUNCTION_OBJECT);
4536        } else {
4537            method.invoke(CREATE_FUNCTION_OBJECT_NO_SCOPE);
4538        }
4539    }
4540
4541    // calls on Global class.
4542    private MethodEmitter globalInstance() {
4543        return method.invokestatic(GLOBAL_OBJECT, "instance", "()L" + GLOBAL_OBJECT + ';');
4544    }
4545
4546    private MethodEmitter globalAllocateArguments() {
4547        return method.invokestatic(GLOBAL_OBJECT, "allocateArguments", methodDescriptor(ScriptObject.class, Object[].class, Object.class, int.class));
4548    }
4549
4550    private MethodEmitter globalNewRegExp() {
4551        return method.invokestatic(GLOBAL_OBJECT, "newRegExp", methodDescriptor(Object.class, String.class, String.class));
4552    }
4553
4554    private MethodEmitter globalRegExpCopy() {
4555        return method.invokestatic(GLOBAL_OBJECT, "regExpCopy", methodDescriptor(Object.class, Object.class));
4556    }
4557
4558    private MethodEmitter globalAllocateArray(final ArrayType type) {
4559        //make sure the native array is treated as an array type
4560        return method.invokestatic(GLOBAL_OBJECT, "allocate", "(" + type.getDescriptor() + ")Ljdk/nashorn/internal/objects/NativeArray;");
4561    }
4562
4563    private MethodEmitter globalIsEval() {
4564        return method.invokestatic(GLOBAL_OBJECT, "isEval", methodDescriptor(boolean.class, Object.class));
4565    }
4566
4567    private MethodEmitter globalReplaceLocationPropertyPlaceholder() {
4568        return method.invokestatic(GLOBAL_OBJECT, "replaceLocationPropertyPlaceholder", methodDescriptor(Object.class, Object.class, Object.class));
4569    }
4570
4571    private MethodEmitter globalCheckObjectCoercible() {
4572        return method.invokestatic(GLOBAL_OBJECT, "checkObjectCoercible", methodDescriptor(void.class, Object.class));
4573    }
4574
4575    private MethodEmitter globalDirectEval() {
4576        return method.invokestatic(GLOBAL_OBJECT, "directEval",
4577                methodDescriptor(Object.class, Object.class, Object.class, Object.class, Object.class, boolean.class));
4578    }
4579
4580    private abstract class OptimisticOperation {
4581        private final boolean isOptimistic;
4582        // expression and optimistic are the same reference
4583        private final Expression expression;
4584        private final Optimistic optimistic;
4585        private final TypeBounds resultBounds;
4586
4587        OptimisticOperation(final Optimistic optimistic, final TypeBounds resultBounds) {
4588            this.optimistic = optimistic;
4589            this.expression = (Expression)optimistic;
4590            this.resultBounds = resultBounds;
4591            this.isOptimistic = isOptimistic(optimistic) && useOptimisticTypes() &&
4592                    // Operation is only effectively optimistic if its type, after being coerced into the result bounds
4593                    // is narrower than the upper bound.
4594                    resultBounds.within(Type.generic(((Expression)optimistic).getType())).narrowerThan(resultBounds.widest);
4595        }
4596
4597        MethodEmitter emit() {
4598            return emit(0);
4599        }
4600
4601        MethodEmitter emit(final int ignoredArgCount) {
4602            final int     programPoint                  = optimistic.getProgramPoint();
4603            final boolean optimisticOrContinuation      = isOptimistic || isContinuationEntryPoint(programPoint);
4604            final boolean currentContinuationEntryPoint = isCurrentContinuationEntryPoint(programPoint);
4605            final int     stackSizeOnEntry              = method.getStackSize() - ignoredArgCount;
4606
4607            // First store the values on the stack opportunistically into local variables. Doing it before loadStack()
4608            // allows us to not have to pop/load any arguments that are pushed onto it by loadStack() in the second
4609            // storeStack().
4610            storeStack(ignoredArgCount, optimisticOrContinuation);
4611
4612            // Now, load the stack
4613            loadStack();
4614
4615            // Now store the values on the stack ultimately into local variables. In vast majority of cases, this is
4616            // (aside from creating the local types map) a no-op, as the first opportunistic stack store will already
4617            // store all variables. However, there can be operations in the loadStack() that invalidate some of the
4618            // stack stores, e.g. in "x[i] = x[++i]", "++i" will invalidate the already stored value for "i". In such
4619            // unfortunate cases this second storeStack() will restore the invariant that everything on the stack is
4620            // stored into a local variable, although at the cost of doing a store/load on the loaded arguments as well.
4621            final int liveLocalsCount = storeStack(method.getStackSize() - stackSizeOnEntry, optimisticOrContinuation);
4622            assert optimisticOrContinuation == (liveLocalsCount != -1);
4623
4624            final Label beginTry;
4625            final Label catchLabel;
4626            final Label afterConsumeStack = isOptimistic || currentContinuationEntryPoint ? new Label("after_consume_stack") : null;
4627            if(isOptimistic) {
4628                beginTry = new Label("try_optimistic");
4629                final String catchLabelName = (afterConsumeStack == null ? "" : afterConsumeStack.toString()) + "_handler";
4630                catchLabel = new Label(catchLabelName);
4631                method.label(beginTry);
4632            } else {
4633                beginTry = catchLabel = null;
4634            }
4635
4636            consumeStack();
4637
4638            if(isOptimistic) {
4639                method._try(beginTry, afterConsumeStack, catchLabel, UnwarrantedOptimismException.class);
4640            }
4641
4642            if(isOptimistic || currentContinuationEntryPoint) {
4643                method.label(afterConsumeStack);
4644
4645                final int[] localLoads = method.getLocalLoadsOnStack(0, stackSizeOnEntry);
4646                assert everyStackValueIsLocalLoad(localLoads) : Arrays.toString(localLoads) + ", " + stackSizeOnEntry + ", " + ignoredArgCount;
4647                final List<Type> localTypesList = method.getLocalVariableTypes();
4648                final int usedLocals = method.getUsedSlotsWithLiveTemporaries();
4649                final List<Type> localTypes = method.getWidestLiveLocals(localTypesList.subList(0, usedLocals));
4650                assert everyLocalLoadIsValid(localLoads, usedLocals) : Arrays.toString(localLoads) + " ~ " + localTypes;
4651
4652                if(isOptimistic) {
4653                    addUnwarrantedOptimismHandlerLabel(localTypes, catchLabel);
4654                }
4655                if(currentContinuationEntryPoint) {
4656                    final ContinuationInfo ci = getContinuationInfo();
4657                    assert ci != null : "no continuation info found for " + lc.getCurrentFunction();
4658                    assert !ci.hasTargetLabel(); // No duplicate program points
4659                    ci.setTargetLabel(afterConsumeStack);
4660                    ci.getHandlerLabel().markAsOptimisticContinuationHandlerFor(afterConsumeStack);
4661                    // Can't rely on targetLabel.stack.localVariableTypes.length, as it can be higher due to effectively
4662                    // dead local variables.
4663                    ci.lvarCount = localTypes.size();
4664                    ci.setStackStoreSpec(localLoads);
4665                    ci.setStackTypes(Arrays.copyOf(method.getTypesFromStack(method.getStackSize()), stackSizeOnEntry));
4666                    assert ci.getStackStoreSpec().length == ci.getStackTypes().length;
4667                    ci.setReturnValueType(method.peekType());
4668                    ci.lineNumber = getLastLineNumber();
4669                    ci.catchLabel = catchLabels.peek();
4670                }
4671            }
4672            return method;
4673        }
4674
4675        /**
4676         * Stores the current contents of the stack into local variables so they are not lost before invoking something that
4677         * can result in an {@code UnwarantedOptimizationException}.
4678         * @param ignoreArgCount the number of topmost arguments on stack to ignore when deciding on the shape of the catch
4679         * block. Those are used in the situations when we could not place the call to {@code storeStack} early enough
4680         * (before emitting code for pushing the arguments that the optimistic call will pop). This is admittedly a
4681         * deficiency in the design of the code generator when it deals with self-assignments and we should probably look
4682         * into fixing it.
4683         * @return types of the significant local variables after the stack was stored (types for local variables used
4684         * for temporary storage of ignored arguments are not returned).
4685         * @param optimisticOrContinuation if false, this method should not execute
4686         * a label for a catch block for the {@code UnwarantedOptimizationException}, suitable for capturing the
4687         * currently live local variables, tailored to their types.
4688         */
4689        private int storeStack(final int ignoreArgCount, final boolean optimisticOrContinuation) {
4690            if(!optimisticOrContinuation) {
4691                return -1; // NOTE: correct value to return is lc.getUsedSlotCount(), but it wouldn't be used anyway
4692            }
4693
4694            final int stackSize = method.getStackSize();
4695            final Type[] stackTypes = method.getTypesFromStack(stackSize);
4696            final int[] localLoadsOnStack = method.getLocalLoadsOnStack(0, stackSize);
4697            final int usedSlots = method.getUsedSlotsWithLiveTemporaries();
4698
4699            final int firstIgnored = stackSize - ignoreArgCount;
4700            // Find the first value on the stack (from the bottom) that is not a load from a local variable.
4701            int firstNonLoad = 0;
4702            while(firstNonLoad < firstIgnored && localLoadsOnStack[firstNonLoad] != Label.Stack.NON_LOAD) {
4703                firstNonLoad++;
4704            }
4705
4706            // Only do the store/load if first non-load is not an ignored argument. Otherwise, do nothing and return
4707            // the number of used slots as the number of live local variables.
4708            if(firstNonLoad >= firstIgnored) {
4709                return usedSlots;
4710            }
4711
4712            // Find the number of new temporary local variables that we need; it's the number of values on the stack that
4713            // are not direct loads of existing local variables.
4714            int tempSlotsNeeded = 0;
4715            for(int i = firstNonLoad; i < stackSize; ++i) {
4716                if(localLoadsOnStack[i] == Label.Stack.NON_LOAD) {
4717                    tempSlotsNeeded += stackTypes[i].getSlots();
4718                }
4719            }
4720
4721            // Ensure all values on the stack that weren't directly loaded from a local variable are stored in a local
4722            // variable. We're starting from highest local variable index, so that in case ignoreArgCount > 0 the ignored
4723            // ones end up at the end of the local variable table.
4724            int lastTempSlot = usedSlots + tempSlotsNeeded;
4725            int ignoreSlotCount = 0;
4726            for(int i = stackSize; i -- > firstNonLoad;) {
4727                final int loadSlot = localLoadsOnStack[i];
4728                if(loadSlot == Label.Stack.NON_LOAD) {
4729                    final Type type = stackTypes[i];
4730                    final int slots = type.getSlots();
4731                    lastTempSlot -= slots;
4732                    if(i >= firstIgnored) {
4733                        ignoreSlotCount += slots;
4734                    }
4735                    method.storeTemp(type, lastTempSlot);
4736                } else {
4737                    method.pop();
4738                }
4739            }
4740            assert lastTempSlot == usedSlots; // used all temporary locals
4741
4742            final List<Type> localTypesList = method.getLocalVariableTypes();
4743
4744            // Load values back on stack.
4745            for(int i = firstNonLoad; i < stackSize; ++i) {
4746                final int loadSlot = localLoadsOnStack[i];
4747                final Type stackType = stackTypes[i];
4748                final boolean isLoad = loadSlot != Label.Stack.NON_LOAD;
4749                final int lvarSlot = isLoad ? loadSlot : lastTempSlot;
4750                final Type lvarType = localTypesList.get(lvarSlot);
4751                method.load(lvarType, lvarSlot);
4752                if(isLoad) {
4753                    // Conversion operators (I2L etc.) preserve "load"-ness of the value despite the fact that, in the
4754                    // strict sense they are creating a derived value from the loaded value. This special behavior of
4755                    // on-stack conversion operators is necessary to accommodate for differences in local variable types
4756                    // after deoptimization; having a conversion operator throw away "load"-ness would create different
4757                    // local variable table shapes between optimism-failed code and its deoptimized rest-of method).
4758                    // After we load the value back, we need to redo the conversion to the stack type if stack type is
4759                    // different.
4760                    // NOTE: this would only strictly be necessary for widening conversions (I2L, L2D, I2D), and not for
4761                    // narrowing ones (L2I, D2L, D2I) as only widening conversions are the ones that can get eliminated
4762                    // in a deoptimized method, as their original input argument got widened. Maybe experiment with
4763                    // throwing away "load"-ness for narrowing conversions in MethodEmitter.convert()?
4764                    method.convert(stackType);
4765                } else {
4766                    // temporary stores never needs a convert, as their type is always the same as the stack type.
4767                    assert lvarType == stackType;
4768                    lastTempSlot += lvarType.getSlots();
4769                }
4770            }
4771            // used all temporaries
4772            assert lastTempSlot == usedSlots + tempSlotsNeeded;
4773
4774            return lastTempSlot - ignoreSlotCount;
4775        }
4776
4777        private void addUnwarrantedOptimismHandlerLabel(final List<Type> localTypes, final Label label) {
4778            final String lvarTypesDescriptor = getLvarTypesDescriptor(localTypes);
4779            final Map<String, Collection<Label>> unwarrantedOptimismHandlers = lc.getUnwarrantedOptimismHandlers();
4780            Collection<Label> labels = unwarrantedOptimismHandlers.get(lvarTypesDescriptor);
4781            if(labels == null) {
4782                labels = new LinkedList<>();
4783                unwarrantedOptimismHandlers.put(lvarTypesDescriptor, labels);
4784            }
4785            method.markLabelAsOptimisticCatchHandler(label, localTypes.size());
4786            labels.add(label);
4787        }
4788
4789        abstract void loadStack();
4790
4791        // Make sure that whatever indy call site you emit from this method uses {@code getCallSiteFlagsOptimistic(node)}
4792        // or otherwise ensure optimistic flag is correctly set in the call site, otherwise it doesn't make much sense
4793        // to use OptimisticExpression for emitting it.
4794        abstract void consumeStack();
4795
4796        /**
4797         * Emits the correct dynamic getter code. Normally just delegates to method emitter, except when the target
4798         * expression is optimistic, and the desired type is narrower than the optimistic type. In that case, it'll emit a
4799         * dynamic getter with its original optimistic type, and explicitly insert a narrowing conversion. This way we can
4800         * preserve the optimism of the values even if they're subsequently immediately coerced into a narrower type. This
4801         * is beneficial because in this case we can still presume that since the original getter was optimistic, the
4802         * conversion has no side effects.
4803         * @param name the name of the property being get
4804         * @param flags call site flags
4805         * @param isMethod whether we're preferrably retrieving a function
4806         * @return the current method emitter
4807         */
4808        MethodEmitter dynamicGet(final String name, final int flags, final boolean isMethod, final boolean isIndex) {
4809            if(isOptimistic) {
4810                return method.dynamicGet(getOptimisticCoercedType(), name, getOptimisticFlags(flags), isMethod, isIndex);
4811            }
4812            return method.dynamicGet(resultBounds.within(expression.getType()), name, nonOptimisticFlags(flags), isMethod, isIndex);
4813        }
4814
4815        MethodEmitter dynamicGetIndex(final int flags, final boolean isMethod) {
4816            if(isOptimistic) {
4817                return method.dynamicGetIndex(getOptimisticCoercedType(), getOptimisticFlags(flags), isMethod);
4818            }
4819            return method.dynamicGetIndex(resultBounds.within(expression.getType()), nonOptimisticFlags(flags), isMethod);
4820        }
4821
4822        MethodEmitter dynamicCall(final int argCount, final int flags) {
4823            if (isOptimistic) {
4824                return method.dynamicCall(getOptimisticCoercedType(), argCount, getOptimisticFlags(flags));
4825            }
4826            return method.dynamicCall(resultBounds.within(expression.getType()), argCount, nonOptimisticFlags(flags));
4827        }
4828
4829        int getOptimisticFlags(final int flags) {
4830            return flags | CALLSITE_OPTIMISTIC | (optimistic.getProgramPoint() << CALLSITE_PROGRAM_POINT_SHIFT); //encode program point in high bits
4831        }
4832
4833        int getProgramPoint() {
4834            return isOptimistic ? optimistic.getProgramPoint() : INVALID_PROGRAM_POINT;
4835        }
4836
4837        void convertOptimisticReturnValue() {
4838            if (isOptimistic) {
4839                final Type optimisticType = getOptimisticCoercedType();
4840                if(!optimisticType.isObject()) {
4841                    method.load(optimistic.getProgramPoint());
4842                    if(optimisticType.isInteger()) {
4843                        method.invoke(ENSURE_INT);
4844                    } else if(optimisticType.isLong()) {
4845                        method.invoke(ENSURE_LONG);
4846                    } else if(optimisticType.isNumber()) {
4847                        method.invoke(ENSURE_NUMBER);
4848                    } else {
4849                        throw new AssertionError(optimisticType);
4850                    }
4851                }
4852            }
4853        }
4854
4855        void replaceCompileTimeProperty() {
4856            final IdentNode identNode = (IdentNode)expression;
4857            final String name = identNode.getSymbol().getName();
4858            if (CompilerConstants.__FILE__.name().equals(name)) {
4859                replaceCompileTimeProperty(getCurrentSource().getName());
4860            } else if (CompilerConstants.__DIR__.name().equals(name)) {
4861                replaceCompileTimeProperty(getCurrentSource().getBase());
4862            } else if (CompilerConstants.__LINE__.name().equals(name)) {
4863                replaceCompileTimeProperty(getCurrentSource().getLine(identNode.position()));
4864            }
4865        }
4866
4867        /**
4868         * When an ident with name __FILE__, __DIR__, or __LINE__ is loaded, we'll try to look it up as any other
4869         * identifier. However, if it gets all the way up to the Global object, it will send back a special value that
4870         * represents a placeholder for these compile-time location properties. This method will generate code that loads
4871         * the value of the compile-time location property and then invokes a method in Global that will replace the
4872         * placeholder with the value. Effectively, if the symbol for these properties is defined anywhere in the lexical
4873         * scope, they take precedence, but if they aren't, then they resolve to the compile-time location property.
4874         * @param propertyValue the actual value of the property
4875         */
4876        private void replaceCompileTimeProperty(final Object propertyValue) {
4877            assert method.peekType().isObject();
4878            if(propertyValue instanceof String || propertyValue == null) {
4879                method.load((String)propertyValue);
4880            } else if(propertyValue instanceof Integer) {
4881                method.load(((Integer)propertyValue).intValue());
4882                method.convert(Type.OBJECT);
4883            } else {
4884                throw new AssertionError();
4885            }
4886            globalReplaceLocationPropertyPlaceholder();
4887            convertOptimisticReturnValue();
4888        }
4889
4890        /**
4891         * Returns the type that should be used as the return type of the dynamic invocation that is emitted as the code
4892         * for the current optimistic operation. If the type bounds is exact boolean or narrower than the expression's
4893         * optimistic type, then the optimistic type is returned, otherwise the coercing type. Effectively, this method
4894         * allows for moving the coercion into the optimistic type when it won't adversely affect the optimistic
4895         * evaluation semantics, and for preserving the optimistic type and doing a separate coercion when it would
4896         * affect it.
4897         * @return
4898         */
4899        private Type getOptimisticCoercedType() {
4900            final Type optimisticType = expression.getType();
4901            assert resultBounds.widest.widerThan(optimisticType);
4902            final Type narrowest = resultBounds.narrowest;
4903
4904            if(narrowest.isBoolean() || narrowest.narrowerThan(optimisticType)) {
4905                assert !optimisticType.isObject();
4906                return optimisticType;
4907            }
4908            assert !narrowest.isObject();
4909            return narrowest;
4910        }
4911    }
4912
4913    private static boolean isOptimistic(final Optimistic optimistic) {
4914        if(!optimistic.canBeOptimistic()) {
4915            return false;
4916        }
4917        final Expression expr = (Expression)optimistic;
4918        return expr.getType().narrowerThan(expr.getWidestOperationType());
4919    }
4920
4921    private static boolean everyLocalLoadIsValid(final int[] loads, final int localCount) {
4922        for (final int load : loads) {
4923            if(load < 0 || load >= localCount) {
4924                return false;
4925            }
4926        }
4927        return true;
4928    }
4929
4930    private static boolean everyStackValueIsLocalLoad(final int[] loads) {
4931        for (final int load : loads) {
4932            if(load == Label.Stack.NON_LOAD) {
4933                return false;
4934            }
4935        }
4936        return true;
4937    }
4938
4939    private String getLvarTypesDescriptor(final List<Type> localVarTypes) {
4940        final int count = localVarTypes.size();
4941        final StringBuilder desc = new StringBuilder(count);
4942        for(int i = 0; i < count;) {
4943            i += appendType(desc, localVarTypes.get(i));
4944        }
4945        return method.markSymbolBoundariesInLvarTypesDescriptor(desc.toString());
4946    }
4947
4948    private static int appendType(final StringBuilder b, final Type t) {
4949        b.append(t.getBytecodeStackType());
4950        return t.getSlots();
4951    }
4952
4953    private static int countSymbolsInLvarTypeDescriptor(final String lvarTypeDescriptor) {
4954        int count = 0;
4955        for(int i = 0; i < lvarTypeDescriptor.length(); ++i) {
4956            if(Character.isUpperCase(lvarTypeDescriptor.charAt(i))) {
4957                ++count;
4958            }
4959        }
4960        return count;
4961
4962    }
4963    /**
4964     * Generates all the required {@code UnwarrantedOptimismException} handlers for the current function. The employed
4965     * strategy strives to maximize code reuse. Every handler constructs an array to hold the local variables, then
4966     * fills in some trailing part of the local variables (those for which it has a unique suffix in the descriptor),
4967     * then jumps to a handler for a prefix that's shared with other handlers. A handler that fills up locals up to
4968     * position 0 will not jump to a prefix handler (as it has no prefix), but instead end with constructing and
4969     * throwing a {@code RewriteException}. Since we lexicographically sort the entries, we only need to check every
4970     * entry to its immediately preceding one for longest matching prefix.
4971     * @return true if there is at least one exception handler
4972     */
4973    private boolean generateUnwarrantedOptimismExceptionHandlers(final FunctionNode fn) {
4974        if(!useOptimisticTypes()) {
4975            return false;
4976        }
4977
4978        // Take the mapping of lvarSpecs -> labels, and turn them into a descending lexicographically sorted list of
4979        // handler specifications.
4980        final Map<String, Collection<Label>> unwarrantedOptimismHandlers = lc.popUnwarrantedOptimismHandlers();
4981        if(unwarrantedOptimismHandlers.isEmpty()) {
4982            return false;
4983        }
4984
4985        method.lineNumber(0);
4986
4987        final List<OptimismExceptionHandlerSpec> handlerSpecs = new ArrayList<>(unwarrantedOptimismHandlers.size() * 4/3);
4988        for(final String spec: unwarrantedOptimismHandlers.keySet()) {
4989            handlerSpecs.add(new OptimismExceptionHandlerSpec(spec, true));
4990        }
4991        Collections.sort(handlerSpecs, Collections.reverseOrder());
4992
4993        // Map of local variable specifications to labels for populating the array for that local variable spec.
4994        final Map<String, Label> delegationLabels = new HashMap<>();
4995
4996        // Do everything in a single pass over the handlerSpecs list. Note that the list can actually grow as we're
4997        // passing through it as we might add new prefix handlers into it, so can't hoist size() outside of the loop.
4998        for(int handlerIndex = 0; handlerIndex < handlerSpecs.size(); ++handlerIndex) {
4999            final OptimismExceptionHandlerSpec spec = handlerSpecs.get(handlerIndex);
5000            final String lvarSpec = spec.lvarSpec;
5001            if(spec.catchTarget) {
5002                assert !method.isReachable();
5003                // Start a catch block and assign the labels for this lvarSpec with it.
5004                method._catch(unwarrantedOptimismHandlers.get(lvarSpec));
5005                // This spec is a catch target, so emit array creation code. The length of the array is the number of
5006                // symbols - the number of uppercase characters.
5007                method.load(countSymbolsInLvarTypeDescriptor(lvarSpec));
5008                method.newarray(Type.OBJECT_ARRAY);
5009            }
5010            if(spec.delegationTarget) {
5011                // If another handler can delegate to this handler as its prefix, then put a jump target here for the
5012                // shared code (after the array creation code, which is never shared).
5013                method.label(delegationLabels.get(lvarSpec)); // label must exist
5014            }
5015
5016            final boolean lastHandler = handlerIndex == handlerSpecs.size() - 1;
5017
5018            int lvarIndex;
5019            final int firstArrayIndex;
5020            final int firstLvarIndex;
5021            Label delegationLabel;
5022            final String commonLvarSpec;
5023            if(lastHandler) {
5024                // Last handler block, doesn't delegate to anything.
5025                lvarIndex = 0;
5026                firstLvarIndex = 0;
5027                firstArrayIndex = 0;
5028                delegationLabel = null;
5029                commonLvarSpec = null;
5030            } else {
5031                // Not yet the last handler block, will definitely delegate to another handler; let's figure out which
5032                // one. It can be an already declared handler further down the list, or it might need to declare a new
5033                // prefix handler.
5034
5035                // Since we're lexicographically ordered, the common prefix handler is defined by the common prefix of
5036                // this handler and the next handler on the list.
5037                final int nextHandlerIndex = handlerIndex + 1;
5038                final String nextLvarSpec = handlerSpecs.get(nextHandlerIndex).lvarSpec;
5039                commonLvarSpec = commonPrefix(lvarSpec, nextLvarSpec);
5040                // We don't chop symbols in half
5041                assert Character.isUpperCase(commonLvarSpec.charAt(commonLvarSpec.length() - 1));
5042
5043                // Let's find if we already have a declaration for such handler, or we need to insert it.
5044                {
5045                    boolean addNewHandler = true;
5046                    int commonHandlerIndex = nextHandlerIndex;
5047                    for(; commonHandlerIndex < handlerSpecs.size(); ++commonHandlerIndex) {
5048                        final OptimismExceptionHandlerSpec forwardHandlerSpec = handlerSpecs.get(commonHandlerIndex);
5049                        final String forwardLvarSpec = forwardHandlerSpec.lvarSpec;
5050                        if(forwardLvarSpec.equals(commonLvarSpec)) {
5051                            // We already have a handler for the common prefix.
5052                            addNewHandler = false;
5053                            // Make sure we mark it as a delegation target.
5054                            forwardHandlerSpec.delegationTarget = true;
5055                            break;
5056                        } else if(!forwardLvarSpec.startsWith(commonLvarSpec)) {
5057                            break;
5058                        }
5059                    }
5060                    if(addNewHandler) {
5061                        // We need to insert a common prefix handler. Note handlers created with catchTarget == false
5062                        // will automatically have delegationTarget == true (because that's the only reason for their
5063                        // existence).
5064                        handlerSpecs.add(commonHandlerIndex, new OptimismExceptionHandlerSpec(commonLvarSpec, false));
5065                    }
5066                }
5067
5068                firstArrayIndex = countSymbolsInLvarTypeDescriptor(commonLvarSpec);
5069                lvarIndex = 0;
5070                for(int j = 0; j < commonLvarSpec.length(); ++j) {
5071                    lvarIndex += CodeGeneratorLexicalContext.getTypeForSlotDescriptor(commonLvarSpec.charAt(j)).getSlots();
5072                }
5073                firstLvarIndex = lvarIndex;
5074
5075                // Create a delegation label if not already present
5076                delegationLabel = delegationLabels.get(commonLvarSpec);
5077                if(delegationLabel == null) {
5078                    // uo_pa == "unwarranted optimism, populate array"
5079                    delegationLabel = new Label("uo_pa_" + commonLvarSpec);
5080                    delegationLabels.put(commonLvarSpec, delegationLabel);
5081                }
5082            }
5083
5084            // Load local variables handled by this handler on stack
5085            int args = 0;
5086            boolean symbolHadValue = false;
5087            for(int typeIndex = commonLvarSpec == null ? 0 : commonLvarSpec.length(); typeIndex < lvarSpec.length(); ++typeIndex) {
5088                final char typeDesc = lvarSpec.charAt(typeIndex);
5089                final Type lvarType = CodeGeneratorLexicalContext.getTypeForSlotDescriptor(typeDesc);
5090                if (!lvarType.isUnknown()) {
5091                    method.load(lvarType, lvarIndex);
5092                    symbolHadValue = true;
5093                    args++;
5094                } else if(typeDesc == 'U' && !symbolHadValue) {
5095                    // Symbol boundary with undefined last value. Check if all previous values for this symbol were also
5096                    // undefined; if so, emit one explicit Undefined. This serves to ensure that we're emiting exactly
5097                    // one value for every symbol that uses local slots. While we could in theory ignore symbols that
5098                    // are undefined (in other words, dead) at the point where this exception was thrown, unfortunately
5099                    // we can't do it in practice. The reason for this is that currently our liveness analysis is
5100                    // coarse (it can determine whether a symbol has not been read with a particular type anywhere in
5101                    // the function being compiled, but that's it), and a symbol being promoted to Object due to a
5102                    // deoptimization will suddenly show up as "live for Object type", and previously dead U->O
5103                    // conversions on loop entries will suddenly become alive in the deoptimized method which will then
5104                    // expect a value for that slot in its continuation handler. If we had precise liveness analysis, we
5105                    // could go back to excluding known dead symbols from the payload of the RewriteException.
5106                    if(method.peekType() == Type.UNDEFINED) {
5107                        method.dup();
5108                    } else {
5109                        method.loadUndefined(Type.OBJECT);
5110                    }
5111                    args++;
5112                }
5113                if(Character.isUpperCase(typeDesc)) {
5114                    // Reached symbol boundary; reset flag for the next symbol.
5115                    symbolHadValue = false;
5116                }
5117                lvarIndex += lvarType.getSlots();
5118            }
5119            assert args > 0;
5120            // Delegate actual storing into array to an array populator utility method.
5121            //on the stack:
5122            // object array to be populated
5123            // start index
5124            // a lot of types
5125            method.dynamicArrayPopulatorCall(args + 1, firstArrayIndex);
5126            if(delegationLabel != null) {
5127                // We cascade to a prefix handler to fill out the rest of the local variables and throw the
5128                // RewriteException.
5129                assert !lastHandler;
5130                assert commonLvarSpec != null;
5131                // Must undefine the local variables that we have already processed for the sake of correct join on the
5132                // delegate label
5133                method.undefineLocalVariables(firstLvarIndex, true);
5134                final OptimismExceptionHandlerSpec nextSpec = handlerSpecs.get(handlerIndex + 1);
5135                // If the delegate immediately follows, and it's not a catch target (so it doesn't have array setup
5136                // code) don't bother emitting a jump, as we'd just jump to the next instruction.
5137                if(!nextSpec.lvarSpec.equals(commonLvarSpec) || nextSpec.catchTarget) {
5138                    method._goto(delegationLabel);
5139                }
5140            } else {
5141                assert lastHandler;
5142                // Nothing to delegate to, so this handler must create and throw the RewriteException.
5143                // At this point we have the UnwarrantedOptimismException and the Object[] with local variables on
5144                // stack. We need to create a RewriteException, push two references to it below the constructor
5145                // arguments, invoke the constructor, and throw the exception.
5146                loadConstant(getByteCodeSymbolNames(fn));
5147                if (isRestOf()) {
5148                    loadConstant(getContinuationEntryPoints());
5149                    method.invoke(CREATE_REWRITE_EXCEPTION_REST_OF);
5150                } else {
5151                    method.invoke(CREATE_REWRITE_EXCEPTION);
5152                }
5153                method.athrow();
5154            }
5155        }
5156        return true;
5157    }
5158
5159    private static String[] getByteCodeSymbolNames(final FunctionNode fn) {
5160        // Only names of local variables on the function level are captured. This information is used to reduce
5161        // deoptimizations, so as much as we can capture will help. We rely on the fact that function wide variables are
5162        // all live all the time, so the array passed to rewrite exception contains one element for every slotted symbol
5163        // here.
5164        final List<String> names = new ArrayList<>();
5165        for (final Symbol symbol: fn.getBody().getSymbols()) {
5166            if (symbol.hasSlot()) {
5167                if (symbol.isScope()) {
5168                    // slot + scope can only be true for parameters
5169                    assert symbol.isParam();
5170                    names.add(null);
5171                } else {
5172                    names.add(symbol.getName());
5173                }
5174            }
5175        }
5176        return names.toArray(new String[names.size()]);
5177    }
5178
5179    private static String commonPrefix(final String s1, final String s2) {
5180        final int l1 = s1.length();
5181        final int l = Math.min(l1, s2.length());
5182        int lms = -1; // last matching symbol
5183        for(int i = 0; i < l; ++i) {
5184            final char c1 = s1.charAt(i);
5185            if(c1 != s2.charAt(i)) {
5186                return s1.substring(0, lms + 1);
5187            } else if(Character.isUpperCase(c1)) {
5188                lms = i;
5189            }
5190        }
5191        return l == l1 ? s1 : s2;
5192    }
5193
5194    private static class OptimismExceptionHandlerSpec implements Comparable<OptimismExceptionHandlerSpec> {
5195        private final String lvarSpec;
5196        private final boolean catchTarget;
5197        private boolean delegationTarget;
5198
5199        OptimismExceptionHandlerSpec(final String lvarSpec, final boolean catchTarget) {
5200            this.lvarSpec = lvarSpec;
5201            this.catchTarget = catchTarget;
5202            if(!catchTarget) {
5203                delegationTarget = true;
5204            }
5205        }
5206
5207        @Override
5208        public int compareTo(final OptimismExceptionHandlerSpec o) {
5209            return lvarSpec.compareTo(o.lvarSpec);
5210        }
5211
5212        @Override
5213        public String toString() {
5214            final StringBuilder b = new StringBuilder(64).append("[HandlerSpec ").append(lvarSpec);
5215            if(catchTarget) {
5216                b.append(", catchTarget");
5217            }
5218            if(delegationTarget) {
5219                b.append(", delegationTarget");
5220            }
5221            return b.append("]").toString();
5222        }
5223    }
5224
5225    private static class ContinuationInfo {
5226        private final Label handlerLabel;
5227        private Label targetLabel; // Label for the target instruction.
5228        int lvarCount;
5229        // Indices of local variables that need to be loaded on the stack when this node completes
5230        private int[] stackStoreSpec;
5231        // Types of values loaded on the stack
5232        private Type[] stackTypes;
5233        // If non-null, this node should perform the requisite type conversion
5234        private Type returnValueType;
5235        // If we are in the middle of an object literal initialization, we need to update the map
5236        private PropertyMap objectLiteralMap;
5237        // Object literal stack depth for object literal - not necessarly top if property is a tree
5238        private int objectLiteralStackDepth = -1;
5239        // The line number at the continuation point
5240        private int lineNumber;
5241        // The active catch label, in case the continuation point is in a try/catch block
5242        private Label catchLabel;
5243        // The number of scopes that need to be popped before control is transferred to the catch label.
5244        private int exceptionScopePops;
5245
5246        ContinuationInfo() {
5247            this.handlerLabel = new Label("continuation_handler");
5248        }
5249
5250        Label getHandlerLabel() {
5251            return handlerLabel;
5252        }
5253
5254        boolean hasTargetLabel() {
5255            return targetLabel != null;
5256        }
5257
5258        Label getTargetLabel() {
5259            return targetLabel;
5260        }
5261
5262        void setTargetLabel(final Label targetLabel) {
5263            this.targetLabel = targetLabel;
5264        }
5265
5266        int[] getStackStoreSpec() {
5267            return stackStoreSpec.clone();
5268        }
5269
5270        void setStackStoreSpec(final int[] stackStoreSpec) {
5271            this.stackStoreSpec = stackStoreSpec;
5272        }
5273
5274        Type[] getStackTypes() {
5275            return stackTypes.clone();
5276        }
5277
5278        void setStackTypes(final Type[] stackTypes) {
5279            this.stackTypes = stackTypes;
5280        }
5281
5282        Type getReturnValueType() {
5283            return returnValueType;
5284        }
5285
5286        void setReturnValueType(final Type returnValueType) {
5287            this.returnValueType = returnValueType;
5288        }
5289
5290        int getObjectLiteralStackDepth() {
5291            return objectLiteralStackDepth;
5292        }
5293
5294        void setObjectLiteralStackDepth(final int objectLiteralStackDepth) {
5295            this.objectLiteralStackDepth = objectLiteralStackDepth;
5296        }
5297
5298        PropertyMap getObjectLiteralMap() {
5299            return objectLiteralMap;
5300        }
5301
5302        void setObjectLiteralMap(final PropertyMap objectLiteralMap) {
5303            this.objectLiteralMap = objectLiteralMap;
5304        }
5305
5306        @Override
5307        public String toString() {
5308             return "[localVariableTypes=" + targetLabel.getStack().getLocalVariableTypesCopy() + ", stackStoreSpec=" +
5309                     Arrays.toString(stackStoreSpec) + ", returnValueType=" + returnValueType + "]";
5310        }
5311    }
5312
5313    private ContinuationInfo getContinuationInfo() {
5314        return fnIdToContinuationInfo.get(lc.getCurrentFunction().getId());
5315    }
5316
5317    private void generateContinuationHandler() {
5318        if (!isRestOf()) {
5319            return;
5320        }
5321
5322        final ContinuationInfo ci = getContinuationInfo();
5323        method.label(ci.getHandlerLabel());
5324
5325        // There should never be an exception thrown from the continuation handler, but in case there is (meaning,
5326        // Nashorn has a bug), then line number 0 will be an indication of where it came from (line numbers are Uint16).
5327        method.lineNumber(0);
5328
5329        final Label.Stack stack = ci.getTargetLabel().getStack();
5330        final List<Type> lvarTypes = stack.getLocalVariableTypesCopy();
5331        final BitSet symbolBoundary = stack.getSymbolBoundaryCopy();
5332        final int lvarCount = ci.lvarCount;
5333
5334        final Type rewriteExceptionType = Type.typeFor(RewriteException.class);
5335        // Store the RewriteException into an unused local variable slot.
5336        method.load(rewriteExceptionType, 0);
5337        method.storeTemp(rewriteExceptionType, lvarCount);
5338        // Get local variable array
5339        method.load(rewriteExceptionType, 0);
5340        method.invoke(RewriteException.GET_BYTECODE_SLOTS);
5341        // Store local variables. Note that deoptimization might introduce new value types for existing local variables,
5342        // so we must use both liveLocals and symbolBoundary, as in some cases (when the continuation is inside of a try
5343        // block) we need to store the incoming value into multiple slots. The optimism exception handlers will have
5344        // exactly one array element for every symbol that uses bytecode storage. If in the originating method the value
5345        // was undefined, there will be an explicit Undefined value in the array.
5346        int arrayIndex = 0;
5347        for(int lvarIndex = 0; lvarIndex < lvarCount;) {
5348            final Type lvarType = lvarTypes.get(lvarIndex);
5349            if(!lvarType.isUnknown()) {
5350                method.dup();
5351                method.load(arrayIndex).arrayload();
5352                final Class<?> typeClass = lvarType.getTypeClass();
5353                // Deoptimization in array initializers can cause arrays to undergo component type widening
5354                if(typeClass == long[].class) {
5355                    method.load(rewriteExceptionType, lvarCount);
5356                    method.invoke(RewriteException.TO_LONG_ARRAY);
5357                } else if(typeClass == double[].class) {
5358                    method.load(rewriteExceptionType, lvarCount);
5359                    method.invoke(RewriteException.TO_DOUBLE_ARRAY);
5360                } else if(typeClass == Object[].class) {
5361                    method.load(rewriteExceptionType, lvarCount);
5362                    method.invoke(RewriteException.TO_OBJECT_ARRAY);
5363                } else {
5364                    if(!(typeClass.isPrimitive() || typeClass == Object.class)) {
5365                        // NOTE: this can only happen with dead stores. E.g. for the program "1; []; f();" in which the
5366                        // call to f() will deoptimize the call site, but it'll expect :return to have the type
5367                        // NativeArray. However, in the more optimal version, :return's only live type is int, therefore
5368                        // "{O}:return = []" is a dead store, and the variable will be sent into the continuation as
5369                        // Undefined, however NativeArray can't hold Undefined instance.
5370                        method.loadType(Type.getInternalName(typeClass));
5371                        method.invoke(RewriteException.INSTANCE_OR_NULL);
5372                    }
5373                    method.convert(lvarType);
5374                }
5375                method.storeHidden(lvarType, lvarIndex, false);
5376            }
5377            final int nextLvarIndex = lvarIndex + lvarType.getSlots();
5378            if(symbolBoundary.get(nextLvarIndex - 1)) {
5379                ++arrayIndex;
5380            }
5381            lvarIndex = nextLvarIndex;
5382        }
5383        if (AssertsEnabled.assertsEnabled()) {
5384            method.load(arrayIndex);
5385            method.invoke(RewriteException.ASSERT_ARRAY_LENGTH);
5386        } else {
5387            method.pop();
5388        }
5389
5390        final int[]   stackStoreSpec = ci.getStackStoreSpec();
5391        final Type[]  stackTypes     = ci.getStackTypes();
5392        final boolean isStackEmpty   = stackStoreSpec.length == 0;
5393        boolean replacedObjectLiteralMap = false;
5394        if(!isStackEmpty) {
5395            // Load arguments on the stack
5396            final int objectLiteralStackDepth = ci.getObjectLiteralStackDepth();
5397            for(int i = 0; i < stackStoreSpec.length; ++i) {
5398                final int slot = stackStoreSpec[i];
5399                method.load(lvarTypes.get(slot), slot);
5400                method.convert(stackTypes[i]);
5401                // stack: s0=object literal being initialized
5402                // change map of s0 so that the property we are initilizing when we failed
5403                // is now ci.returnValueType
5404                if (i == objectLiteralStackDepth) {
5405                    method.dup();
5406                    assert ci.getObjectLiteralMap() != null;
5407                    assert ScriptObject.class.isAssignableFrom(method.peekType().getTypeClass()) : method.peekType().getTypeClass() + " is not a script object";
5408                    loadConstant(ci.getObjectLiteralMap());
5409                    method.invoke(ScriptObject.SET_MAP);
5410                    replacedObjectLiteralMap = true;
5411                }
5412            }
5413        }
5414        // Must have emitted the code for replacing the map of an object literal if we have a set object literal stack depth
5415        assert ci.getObjectLiteralStackDepth() == -1 || replacedObjectLiteralMap;
5416        // Load RewriteException back.
5417        method.load(rewriteExceptionType, lvarCount);
5418        // Get rid of the stored reference
5419        method.loadNull();
5420        method.storeHidden(Type.OBJECT, lvarCount);
5421        // Mark it dead
5422        method.markDeadSlots(lvarCount, Type.OBJECT.getSlots());
5423
5424        // Load return value on the stack
5425        method.invoke(RewriteException.GET_RETURN_VALUE);
5426
5427        final Type returnValueType = ci.getReturnValueType();
5428
5429        // Set up an exception handler for primitive type conversion of return value if needed
5430        boolean needsCatch = false;
5431        final Label targetCatchLabel = ci.catchLabel;
5432        Label _try = null;
5433        if(returnValueType.isPrimitive()) {
5434            // If the conversion throws an exception, we want to report the line number of the continuation point.
5435            method.lineNumber(ci.lineNumber);
5436
5437            if(targetCatchLabel != METHOD_BOUNDARY) {
5438                _try = new Label("");
5439                method.label(_try);
5440                needsCatch = true;
5441            }
5442        }
5443
5444        // Convert return value
5445        method.convert(returnValueType);
5446
5447        final int scopePopCount = needsCatch ? ci.exceptionScopePops : 0;
5448
5449        // Declare a try/catch for the conversion. If no scopes need to be popped until the target catch block, just
5450        // jump into it. Otherwise, we'll need to create a scope-popping catch block below.
5451        final Label catchLabel = scopePopCount > 0 ? new Label("") : targetCatchLabel;
5452        if(needsCatch) {
5453            final Label _end_try = new Label("");
5454            method.label(_end_try);
5455            method._try(_try, _end_try, catchLabel);
5456        }
5457
5458        // Jump to continuation point
5459        method._goto(ci.getTargetLabel());
5460
5461        // Make a scope-popping exception delegate if needed
5462        if(catchLabel != targetCatchLabel) {
5463            method.lineNumber(0);
5464            assert scopePopCount > 0;
5465            method._catch(catchLabel);
5466            popScopes(scopePopCount);
5467            method.uncheckedGoto(targetCatchLabel);
5468        }
5469    }
5470}
5471