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

Thu, 31 Jan 2013 18:34:42 +0100

author
attila
date
Thu, 31 Jan 2013 18:34:42 +0100
changeset 62
f7825c1a11d3
parent 57
59970b70ebb5
child 65
bb86bf840f9f
permissions
-rw-r--r--

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 }

mercurial