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