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