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

Sat, 09 Feb 2013 16:58:48 +0100

author
attila
date
Sat, 09 Feb 2013 16:58:48 +0100
changeset 81
5ead5333fa59
parent 66
bee7c8a45a04
child 90
5a820fb11814
permissions
-rw-r--r--

8006943: Fix order of function method arguments to be (callee, thisObject)
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.internal.org.objectweb.asm.Opcodes.ATHROW;
jlaskey@3 29 import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST;
jlaskey@3 30 import static jdk.internal.org.objectweb.asm.Opcodes.DUP2;
jlaskey@3 31 import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD;
jlaskey@3 32 import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC;
jlaskey@3 33 import static jdk.internal.org.objectweb.asm.Opcodes.GOTO;
jlaskey@3 34 import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
jlaskey@3 35 import static jdk.internal.org.objectweb.asm.Opcodes.IFEQ;
jlaskey@3 36 import static jdk.internal.org.objectweb.asm.Opcodes.IFGE;
jlaskey@3 37 import static jdk.internal.org.objectweb.asm.Opcodes.IFGT;
jlaskey@3 38 import static jdk.internal.org.objectweb.asm.Opcodes.IFLE;
jlaskey@3 39 import static jdk.internal.org.objectweb.asm.Opcodes.IFLT;
jlaskey@3 40 import static jdk.internal.org.objectweb.asm.Opcodes.IFNE;
jlaskey@3 41 import static jdk.internal.org.objectweb.asm.Opcodes.IFNONNULL;
jlaskey@3 42 import static jdk.internal.org.objectweb.asm.Opcodes.IFNULL;
jlaskey@3 43 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ACMPEQ;
jlaskey@3 44 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ACMPNE;
jlaskey@3 45 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPEQ;
jlaskey@3 46 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPGE;
jlaskey@3 47 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPGT;
jlaskey@3 48 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPLE;
jlaskey@3 49 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPLT;
jlaskey@3 50 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPNE;
jlaskey@3 51 import static jdk.internal.org.objectweb.asm.Opcodes.INSTANCEOF;
jlaskey@3 52 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE;
jlaskey@3 53 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
jlaskey@3 54 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
jlaskey@3 55 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
jlaskey@3 56 import static jdk.internal.org.objectweb.asm.Opcodes.NEW;
jlaskey@3 57 import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
jlaskey@3 58 import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
jlaskey@3 59 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
jlaskey@3 60 import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
jlaskey@3 61 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
jlaskey@3 62 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS_DEBUGGER;
jlaskey@3 63 import static jdk.nashorn.internal.codegen.CompilerConstants.className;
jlaskey@3 64 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
jlaskey@3 65 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
jlaskey@3 66 import static jdk.nashorn.internal.codegen.CompilerConstants.staticField;
jlaskey@3 67 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
jlaskey@3 68
jlaskey@3 69 import java.io.PrintStream;
jlaskey@3 70 import java.lang.reflect.Array;
jlaskey@3 71 import java.util.ArrayDeque;
jlaskey@3 72 import java.util.EnumSet;
jlaskey@3 73 import java.util.Iterator;
jlaskey@3 74 import jdk.internal.org.objectweb.asm.Handle;
jlaskey@3 75 import jdk.internal.org.objectweb.asm.MethodVisitor;
jlaskey@3 76 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
jlaskey@3 77 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
jlaskey@3 78 import jdk.nashorn.internal.codegen.CompilerConstants.FieldAccess;
jlaskey@3 79 import jdk.nashorn.internal.codegen.types.ArrayType;
jlaskey@3 80 import jdk.nashorn.internal.codegen.types.BitwiseType;
jlaskey@3 81 import jdk.nashorn.internal.codegen.types.NumericType;
jlaskey@3 82 import jdk.nashorn.internal.codegen.types.Type;
jlaskey@3 83 import jdk.nashorn.internal.ir.FunctionNode;
jlaskey@3 84 import jdk.nashorn.internal.ir.IdentNode;
jlaskey@3 85 import jdk.nashorn.internal.ir.LiteralNode;
jlaskey@3 86 import jdk.nashorn.internal.ir.RuntimeNode;
jlaskey@3 87 import jdk.nashorn.internal.ir.SplitNode;
jlaskey@3 88 import jdk.nashorn.internal.ir.Symbol;
attila@62 89 import jdk.nashorn.internal.runtime.ArgumentSetter;
jlaskey@3 90 import jdk.nashorn.internal.runtime.Context;
jlaskey@3 91 import jdk.nashorn.internal.runtime.DebugLogger;
jlaskey@3 92 import jdk.nashorn.internal.runtime.JSType;
jlaskey@3 93 import jdk.nashorn.internal.runtime.Scope;
attila@62 94 import jdk.nashorn.internal.runtime.ScriptObject;
jlaskey@3 95 import jdk.nashorn.internal.runtime.linker.Bootstrap;
jlaskey@3 96 import jdk.nashorn.internal.runtime.options.Options;
jlaskey@3 97 import org.dynalang.dynalink.support.NameCodec;
jlaskey@3 98
jlaskey@3 99 /**
jlaskey@3 100 * This is the main function responsible for emitting method code
jlaskey@3 101 * in a class. It maintains a type stack and keeps track of control
jlaskey@3 102 * flow to make sure that the registered instructions don't violate
jlaskey@3 103 * byte code verification.
jlaskey@3 104 *
jlaskey@3 105 * Running Nashorn with -ea will assert as soon as a type stack
jlaskey@3 106 * becomes corrupt, for easier debugging
jlaskey@3 107 *
jlaskey@3 108 * Running Nashorn with -Dnashorn.codegen.debug=true will print
jlaskey@3 109 * all generated bytecode and labels to stderr, for easier debugging,
jlaskey@3 110 * including bytecode stack contents
jlaskey@3 111 */
jlaskey@3 112 public class MethodEmitter implements Emitter {
jlaskey@3 113 /** The ASM MethodVisitor we are plugged into */
jlaskey@3 114 private final MethodVisitor method;
jlaskey@3 115
jlaskey@3 116 /** Current type stack for current evaluation */
jlaskey@3 117 protected ArrayDeque<Type> stack;
jlaskey@3 118
jlaskey@3 119 /** Parent classEmitter representing the class of this method */
jlaskey@3 120 private final ClassEmitter classEmitter;
jlaskey@3 121
jlaskey@3 122 /** FunctionNode representing this method, or null if none exists */
jlaskey@3 123 private FunctionNode functionNode;
jlaskey@3 124
jlaskey@3 125 /** SplitNode representing the current split, or null if none exists */
jlaskey@3 126 private SplitNode splitNode;
jlaskey@3 127
jlaskey@3 128 /** The context */
jlaskey@3 129 private final Context context;
jlaskey@3 130
jlaskey@3 131 /** Threshold in chars for when string constants should be split */
jlaskey@3 132 static final int LARGE_STRING_THRESHOLD = 32 * 1024;
jlaskey@3 133
jlaskey@3 134 /** Debug flag, should we dump all generated bytecode along with stacks? */
jlaskey@3 135 private static final DebugLogger LOG = new DebugLogger("codegen", "nashorn.codegen.debug");
jlaskey@3 136 private static final boolean DEBUG = LOG.isEnabled();
jlaskey@3 137
jlaskey@3 138 /** dump stack on a particular line, or -1 if disabled */
jlaskey@3 139 private static final int DEBUG_TRACE_LINE;
jlaskey@3 140
jlaskey@3 141 static {
jlaskey@3 142 final String tl = Options.getStringProperty("nashorn.codegen.debug.trace", "-1");
jlaskey@3 143 int line = -1;
jlaskey@3 144 try {
jlaskey@3 145 line = Integer.parseInt(tl);
jlaskey@3 146 } catch (final NumberFormatException e) {
jlaskey@3 147 //fallthru
jlaskey@3 148 }
jlaskey@3 149 DEBUG_TRACE_LINE = line;
jlaskey@3 150 }
jlaskey@3 151
jlaskey@3 152 /** Bootstrap for normal indy:s */
jlaskey@3 153 private static final Handle LINKERBOOTSTRAP = new Handle(H_INVOKESTATIC, Bootstrap.BOOTSTRAP.className(), Bootstrap.BOOTSTRAP.name(), Bootstrap.BOOTSTRAP.descriptor());
jlaskey@3 154
jlaskey@3 155 /** Bootstrap for runtime node indy:s */
jlaskey@3 156 private static final Handle RUNTIMEBOOTSTRAP = new Handle(H_INVOKESTATIC, RuntimeCallSite.BOOTSTRAP.className(), RuntimeCallSite.BOOTSTRAP.name(), RuntimeCallSite.BOOTSTRAP.descriptor());
jlaskey@3 157
jlaskey@3 158 /**
jlaskey@3 159 * Constructor - internal use from ClassEmitter only
sundar@10 160 * @see ClassEmitter#method
jlaskey@3 161 *
jlaskey@3 162 * @param classEmitter the class emitter weaving the class this method is in
jlaskey@3 163 * @param method a method visitor
jlaskey@3 164 */
jlaskey@3 165 MethodEmitter(final ClassEmitter classEmitter, final MethodVisitor method) {
jlaskey@3 166 this(classEmitter, method, null);
jlaskey@3 167 }
jlaskey@3 168
jlaskey@3 169 /**
jlaskey@3 170 * Constructor - internal use from ClassEmitter only
sundar@10 171 * @see ClassEmitter#method
jlaskey@3 172 *
jlaskey@3 173 * @param classEmitter the class emitter weaving the class this method is in
jlaskey@3 174 * @param method a method visitor
jlaskey@3 175 * @param functionNode a function node representing this method
jlaskey@3 176 */
jlaskey@3 177 MethodEmitter(final ClassEmitter classEmitter, final MethodVisitor method, final FunctionNode functionNode) {
jlaskey@3 178 this.context = classEmitter.getContext();
jlaskey@3 179 this.classEmitter = classEmitter;
jlaskey@3 180 this.method = method;
jlaskey@3 181 this.functionNode = functionNode;
jlaskey@3 182 this.stack = null;
jlaskey@3 183 }
jlaskey@3 184
jlaskey@3 185 /**
jlaskey@3 186 * Begin a method
jlaskey@3 187 * @see Emitter
jlaskey@3 188 */
jlaskey@3 189 @Override
jlaskey@3 190 public void begin() {
jlaskey@3 191 classEmitter.beginMethod(this);
jlaskey@3 192 stack = new ArrayDeque<>();
jlaskey@3 193 method.visitCode();
jlaskey@3 194 }
jlaskey@3 195
jlaskey@3 196 /**
jlaskey@3 197 * End a method
jlaskey@3 198 * @see Emitter
jlaskey@3 199 */
jlaskey@3 200 @Override
jlaskey@3 201 public void end() {
jlaskey@3 202 method.visitMaxs(0, 0);
jlaskey@3 203 method.visitEnd();
jlaskey@3 204
jlaskey@3 205 classEmitter.endMethod(this);
jlaskey@3 206 }
jlaskey@3 207
jlaskey@3 208 @Override
jlaskey@3 209 public String toString() {
jlaskey@3 210 return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString();
jlaskey@3 211 }
jlaskey@3 212
jlaskey@3 213 /**
jlaskey@3 214 * Push a type to the existing stack
jlaskey@3 215 * @param type the type
jlaskey@3 216 */
jlaskey@3 217 private void pushType(final Type type) {
jlaskey@3 218 if (type != null) {
jlaskey@3 219 stack.push(type);
jlaskey@3 220 }
jlaskey@3 221 }
jlaskey@3 222
jlaskey@3 223 /**
jlaskey@3 224 * Pop a type from the existing stack
jlaskey@3 225 *
jlaskey@3 226 * @param expected expected type - will assert if wrong
jlaskey@3 227 *
jlaskey@3 228 * @return the type that was retrieved
jlaskey@3 229 */
jlaskey@3 230 private Type popType(final Type expected) {
jlaskey@3 231 final Type type = stack.pop();
jlaskey@3 232 assert type.isObject() && expected.isObject() ||
jlaskey@3 233 type.isEquivalentTo(expected) : type + " is not compatible with " + expected;
jlaskey@3 234 return type;
jlaskey@3 235 }
jlaskey@3 236
jlaskey@3 237 /**
jlaskey@3 238 * Pop a type from the existing stack, no matter what it is.
jlaskey@3 239 *
jlaskey@3 240 * @return the type
jlaskey@3 241 */
jlaskey@3 242 private Type popType() {
jlaskey@3 243 return stack.pop();
jlaskey@3 244 }
jlaskey@3 245
jlaskey@3 246 /**
jlaskey@3 247 * Pop a type from the existing stack, ensuring that it is numeric,
jlaskey@3 248 * assert if not
jlaskey@3 249 *
jlaskey@3 250 * @return the type
jlaskey@3 251 */
jlaskey@3 252 private NumericType popNumeric() {
jlaskey@3 253 final Type type = stack.pop();
jlaskey@3 254 assert type.isNumeric() : type + " is not numeric";
jlaskey@3 255 return (NumericType)type;
jlaskey@3 256 }
jlaskey@3 257
jlaskey@3 258 /**
jlaskey@3 259 * Pop a type from the existing stack, ensuring that it is an integer type
jlaskey@3 260 * (integer or long), assert if not
jlaskey@3 261 *
jlaskey@3 262 * @return the type
jlaskey@3 263 */
jlaskey@3 264 private BitwiseType popInteger() {
jlaskey@3 265 final Type type = stack.pop();
jlaskey@3 266 assert type.isInteger() || type.isLong() : type + " is not an integer or long";
jlaskey@3 267 return (BitwiseType)type;
jlaskey@3 268 }
jlaskey@3 269
jlaskey@3 270 /**
jlaskey@3 271 * Pop a type from the existing stack, ensuring that it is an array type,
jlaskey@3 272 * assert if not
jlaskey@3 273 *
jlaskey@3 274 * @return the type
jlaskey@3 275 */
jlaskey@3 276 private ArrayType popArray() {
jlaskey@3 277 final Type type = stack.pop();
attila@62 278 assert type.isArray() : type;
jlaskey@3 279 return (ArrayType)type;
jlaskey@3 280 }
jlaskey@3 281
jlaskey@3 282 /**
jlaskey@3 283 * Peek a given number of slots from the top of the stack and return the
jlaskey@3 284 * type in that slot
jlaskey@3 285 *
jlaskey@3 286 * @param pos the number of positions from the top, 0 is the top element
jlaskey@3 287 *
jlaskey@3 288 * @return the type at position "pos" on the stack
jlaskey@3 289 */
jlaskey@3 290 public final Type peekType(final int pos) {
jlaskey@3 291 final Iterator<Type> iter = stack.iterator();
jlaskey@3 292 for (int i = 0; i < pos; i++) {
jlaskey@3 293 iter.next();
jlaskey@3 294 }
jlaskey@3 295 return iter.next();
jlaskey@3 296 }
jlaskey@3 297
jlaskey@3 298 /**
jlaskey@3 299 * Peek at the type at the top of the stack
jlaskey@3 300 *
jlaskey@3 301 * @return the type at the top of the stack
jlaskey@3 302 */
jlaskey@3 303 public final Type peekType() {
jlaskey@3 304 return stack.peek();
jlaskey@3 305 }
jlaskey@3 306
jlaskey@3 307 /**
jlaskey@3 308 * Generate code a for instantiating a new object and push the
jlaskey@3 309 * object type on the stack
jlaskey@3 310 *
jlaskey@3 311 * @param classDescriptor class descriptor for the object type
jlaskey@3 312 *
jlaskey@3 313 * @return the method emitter
jlaskey@3 314 */
jlaskey@3 315 public MethodEmitter _new(final String classDescriptor) {
attila@62 316 debug("new", classDescriptor);
jlaskey@3 317 method.visitTypeInsn(NEW, classDescriptor);
jlaskey@3 318 pushType(Type.OBJECT);
jlaskey@3 319 return this;
jlaskey@3 320 }
jlaskey@3 321
jlaskey@3 322 /**
jlaskey@3 323 * Generate code a for instantiating a new object and push the
jlaskey@3 324 * object type on the stack
jlaskey@3 325 *
jlaskey@3 326 * @param clazz class type to instatiate
jlaskey@3 327 *
jlaskey@3 328 * @return the method emitter
jlaskey@3 329 */
jlaskey@3 330 public MethodEmitter _new(final Class<?> clazz) {
jlaskey@3 331 return _new(className(clazz));
jlaskey@3 332 }
jlaskey@3 333
jlaskey@3 334 /**
jlaskey@3 335 * Generate code to call the empty constructor for a class
jlaskey@3 336 *
jlaskey@3 337 * @param clazz class type to instatiate
jlaskey@3 338 *
jlaskey@3 339 * @return the method emitter
jlaskey@3 340 */
jlaskey@3 341 public MethodEmitter newInstance(final Class<?> clazz) {
jlaskey@3 342 return invoke(constructorNoLookup(clazz));
jlaskey@3 343 }
jlaskey@3 344
jlaskey@3 345 /**
jlaskey@3 346 * Perform a dup, that is, duplicate the top element and
jlaskey@3 347 * push the duplicate down a given number of positions
jlaskey@3 348 * on the stack. This is totally type agnostic.
jlaskey@3 349 *
jlaskey@3 350 * @param depth the depth on which to put the copy
jlaskey@3 351 *
jlaskey@3 352 * @return the method emitter, or null if depth is illegal and
jlaskey@3 353 * has no instruction equivalent.
jlaskey@3 354 */
jlaskey@3 355 public MethodEmitter dup(final int depth) {
jlaskey@3 356 if (peekType().dup(method, depth) == null) {
jlaskey@3 357 return null;
jlaskey@3 358 }
jlaskey@3 359
jlaskey@3 360 debug("dup", depth);
jlaskey@3 361
jlaskey@3 362 switch (depth) {
jlaskey@3 363 case 0:
jlaskey@3 364 pushType(peekType());
jlaskey@3 365 break;
jlaskey@3 366 case 1: {
jlaskey@3 367 final Type p0 = popType();
jlaskey@3 368 final Type p1 = popType();
jlaskey@3 369 pushType(p0);
jlaskey@3 370 pushType(p1);
jlaskey@3 371 pushType(p0);
jlaskey@3 372 break;
jlaskey@3 373 }
jlaskey@3 374 case 2: {
jlaskey@3 375 final Type p0 = popType();
jlaskey@3 376 final Type p1 = popType();
jlaskey@3 377 final Type p2 = popType();
jlaskey@3 378 pushType(p0);
jlaskey@3 379 pushType(p2);
jlaskey@3 380 pushType(p1);
jlaskey@3 381 pushType(p0);
jlaskey@3 382 break;
jlaskey@3 383 }
jlaskey@3 384 default:
jlaskey@3 385 assert false : "illegal dup depth = " + depth;
jlaskey@3 386 return null;
jlaskey@3 387 }
jlaskey@3 388
jlaskey@3 389 return this;
jlaskey@3 390 }
jlaskey@3 391
jlaskey@3 392 /**
jlaskey@3 393 * Perform a dup2, that is, duplicate the top element if it
jlaskey@3 394 * is a category 2 type, or two top elements if they are category
jlaskey@3 395 * 1 types, and push them on top of the stack
jlaskey@3 396 *
jlaskey@3 397 * @return the method emitter
jlaskey@3 398 */
jlaskey@3 399 public MethodEmitter dup2() {
jlaskey@3 400 debug("dup2");
jlaskey@3 401
jlaskey@3 402 if (peekType().isCategory2()) {
jlaskey@3 403 pushType(peekType());
jlaskey@3 404 } else {
jlaskey@3 405 final Type type = get2();
jlaskey@3 406 pushType(type);
jlaskey@3 407 pushType(type);
jlaskey@3 408 pushType(type);
jlaskey@3 409 pushType(type);
jlaskey@3 410 }
jlaskey@3 411 method.visitInsn(DUP2);
jlaskey@3 412 return this;
jlaskey@3 413 }
jlaskey@3 414
jlaskey@3 415 /**
jlaskey@3 416 * Duplicate the top element on the stack and push it
jlaskey@3 417 *
jlaskey@3 418 * @return the method emitter
jlaskey@3 419 */
jlaskey@3 420 public MethodEmitter dup() {
jlaskey@3 421 return dup(0);
jlaskey@3 422 }
jlaskey@3 423
jlaskey@3 424 /**
jlaskey@3 425 * Pop the top element of the stack and throw it away
jlaskey@3 426 *
jlaskey@3 427 * @return the method emitter
jlaskey@3 428 */
jlaskey@3 429 public MethodEmitter pop() {
jlaskey@3 430 debug("pop", peekType());
jlaskey@3 431 popType().pop(method);
jlaskey@3 432 return this;
jlaskey@3 433 }
jlaskey@3 434
jlaskey@3 435 /**
jlaskey@3 436 * Pop the top element of the stack if category 2 type, or the two
jlaskey@3 437 * top elements of the stack if category 1 types
jlaskey@3 438 *
jlaskey@3 439 * @return the method emitter
jlaskey@3 440 */
jlaskey@3 441 public MethodEmitter pop2() {
jlaskey@3 442 if (peekType().isCategory2()) {
jlaskey@3 443 popType();
jlaskey@3 444 } else {
jlaskey@3 445 get2n();
jlaskey@3 446 }
jlaskey@3 447 return this;
jlaskey@3 448 }
jlaskey@3 449
jlaskey@3 450 /**
jlaskey@3 451 * Swap the top two elements of the stack. This is totally
jlaskey@3 452 * type agnostic and works for all types
jlaskey@3 453 *
jlaskey@3 454 * @return the method emitter
jlaskey@3 455 */
jlaskey@3 456 public MethodEmitter swap() {
jlaskey@3 457 debug("swap");
jlaskey@3 458
jlaskey@3 459 final Type p0 = popType();
jlaskey@3 460 final Type p1 = popType();
jlaskey@3 461 p0.swap(method, p1);
jlaskey@3 462
jlaskey@3 463 pushType(p0);
jlaskey@3 464 pushType(p1);
jlaskey@3 465 debug("after ", p0, p1);
jlaskey@3 466 return this;
jlaskey@3 467 }
jlaskey@3 468
jlaskey@3 469 /**
jlaskey@3 470 * Add a local variable. This is a nop if the symbol has no slot
jlaskey@3 471 *
jlaskey@3 472 * @param symbol symbol for the local variable
jlaskey@3 473 * @param start start of scope
jlaskey@3 474 * @param end end of scope
jlaskey@3 475 */
jlaskey@3 476 public void localVariable(final Symbol symbol, final Label start, final Label end) {
jlaskey@3 477 if (!symbol.hasSlot()) {
jlaskey@3 478 return;
jlaskey@3 479 }
jlaskey@3 480
jlaskey@3 481 String name = symbol.getName();
jlaskey@3 482
jlaskey@3 483 if (name.equals(THIS.tag())) {
jlaskey@3 484 name = THIS_DEBUGGER.tag();
jlaskey@3 485 }
jlaskey@3 486
jlaskey@3 487 method.visitLocalVariable(name, symbol.getSymbolType().getDescriptor(), null, start, end, symbol.getSlot());
jlaskey@3 488 }
jlaskey@3 489
jlaskey@3 490 /**
jlaskey@3 491 * Create a new string builder, call the constructor and push the instance to the stack.
jlaskey@3 492 *
jlaskey@3 493 * @return the method emitter
jlaskey@3 494 */
jlaskey@3 495 public MethodEmitter newStringBuilder() {
jlaskey@3 496 return invoke(constructorNoLookup(StringBuilder.class)).dup();
jlaskey@3 497 }
jlaskey@3 498
jlaskey@3 499 /**
jlaskey@3 500 * Pop a string and a StringBuilder from the top of the stack and call the append
jlaskey@3 501 * function of the StringBuilder, appending the string. Pushes the StringBuilder to
jlaskey@3 502 * the stack when finished.
jlaskey@3 503 *
jlaskey@3 504 * @return the method emitter
jlaskey@3 505 */
jlaskey@3 506 public MethodEmitter stringBuilderAppend() {
jlaskey@3 507 convert(Type.STRING);
jlaskey@3 508 return invoke(virtualCallNoLookup(StringBuilder.class, "append", StringBuilder.class, String.class));
jlaskey@3 509 }
jlaskey@3 510
jlaskey@3 511 /**
jlaskey@3 512 * Associate a variable with a given range
jlaskey@3 513 *
jlaskey@3 514 * @param name name of the variable
jlaskey@3 515 * @param start start
jlaskey@3 516 * @param end end
jlaskey@3 517 */
jlaskey@3 518 public void markerVariable(final String name, final Label start, final Label end) {
jlaskey@3 519 method.visitLocalVariable(name, Type.OBJECT.getDescriptor(), null, start, end, 0);
jlaskey@3 520 }
jlaskey@3 521
jlaskey@3 522 /**
jlaskey@3 523 * Pops two integer types from the stack, performs a bitwise and and pushes
jlaskey@3 524 * the result
jlaskey@3 525 *
jlaskey@3 526 * @return the method emitter
jlaskey@3 527 */
jlaskey@3 528 public MethodEmitter and() {
jlaskey@3 529 debug("and");
jlaskey@3 530 pushType(get2i().and(method));
jlaskey@3 531 return this;
jlaskey@3 532 }
jlaskey@3 533
jlaskey@3 534 /**
jlaskey@3 535 * Pops two integer types from the stack, performs a bitwise or and pushes
jlaskey@3 536 * the result
jlaskey@3 537 *
jlaskey@3 538 * @return the method emitter
jlaskey@3 539 */
jlaskey@3 540 public MethodEmitter or() {
jlaskey@3 541 debug("or");
jlaskey@3 542 pushType(get2i().or(method));
jlaskey@3 543 return this;
jlaskey@3 544 }
jlaskey@3 545
jlaskey@3 546 /**
jlaskey@3 547 * Pops two integer types from the stack, performs a bitwise xor and pushes
jlaskey@3 548 * the result
jlaskey@3 549 *
jlaskey@3 550 * @return the method emitter
jlaskey@3 551 */
jlaskey@3 552 public MethodEmitter xor() {
jlaskey@3 553 debug("xor");
jlaskey@3 554 pushType(get2i().xor(method));
jlaskey@3 555 return this;
jlaskey@3 556 }
jlaskey@3 557
jlaskey@3 558 /**
jlaskey@3 559 * Pops two integer types from the stack, performs a bitwise logic shift right and pushes
jlaskey@3 560 * the result. The shift count, the first element, must be INT.
jlaskey@3 561 *
jlaskey@3 562 * @return the method emitter
jlaskey@3 563 */
jlaskey@3 564 public MethodEmitter shr() {
jlaskey@3 565 debug("shr");
jlaskey@3 566 popType(Type.INT);
jlaskey@3 567 pushType(popInteger().shr(method));
jlaskey@3 568 return this;
jlaskey@3 569 }
jlaskey@3 570
jlaskey@3 571 /**
jlaskey@3 572 * Pops two integer types from the stack, performs a bitwise shift left and and pushes
jlaskey@3 573 * the result. The shift count, the first element, must be INT.
jlaskey@3 574 *
jlaskey@3 575 * @return the method emitter
jlaskey@3 576 */
jlaskey@3 577 public MethodEmitter shl() {
jlaskey@3 578 debug("shl");
jlaskey@3 579 popType(Type.INT);
jlaskey@3 580 pushType(popInteger().shl(method));
jlaskey@3 581 return this;
jlaskey@3 582 }
jlaskey@3 583
jlaskey@3 584 /**
jlaskey@3 585 * Pops two integer types from the stack, performs a bitwise arithetic shift right and pushes
jlaskey@3 586 * the result. The shift count, the first element, must be INT.
jlaskey@3 587 *
jlaskey@3 588 * @return the method emitter
jlaskey@3 589 */
jlaskey@3 590 public MethodEmitter sar() {
jlaskey@3 591 debug("sar");
jlaskey@3 592 popType(Type.INT);
jlaskey@3 593 pushType(popInteger().sar(method));
jlaskey@3 594 return this;
jlaskey@3 595 }
jlaskey@3 596
jlaskey@3 597 /**
jlaskey@3 598 * Pops a numeric type from the stack, negates it and pushes the result
jlaskey@3 599 *
jlaskey@3 600 * @return the method emitter
jlaskey@3 601 */
jlaskey@3 602 public MethodEmitter neg() {
jlaskey@3 603 debug("neg");
jlaskey@3 604 pushType(popNumeric().neg(method));
jlaskey@3 605 return this;
jlaskey@3 606 }
jlaskey@3 607
jlaskey@3 608 /**
jlaskey@3 609 * Add label for the start of a catch block and push the exception to the
jlaskey@3 610 * stack
jlaskey@3 611 *
jlaskey@3 612 * @param recovery label pointing to start of catch block
jlaskey@3 613 */
jlaskey@3 614 public void _catch(final Label recovery) {
jlaskey@3 615 stack.clear();
jlaskey@3 616 stack.push(Type.OBJECT);
jlaskey@3 617 label(recovery);
jlaskey@3 618 }
jlaskey@3 619
jlaskey@3 620 /**
jlaskey@3 621 * Start a try/catch block.
jlaskey@3 622 *
jlaskey@3 623 * @param entry start label for try
jlaskey@3 624 * @param exit end label for try
jlaskey@3 625 * @param recovery start label for catch
jlaskey@3 626 * @param typeDescriptor type descriptor for exception
jlaskey@3 627 */
jlaskey@3 628 public void _try(final Label entry, final Label exit, final Label recovery, final String typeDescriptor) {
jlaskey@3 629 method.visitTryCatchBlock(entry, exit, recovery, typeDescriptor);
jlaskey@3 630 }
jlaskey@3 631
jlaskey@3 632 /**
jlaskey@3 633 * Start a try/catch block.
jlaskey@3 634 *
jlaskey@3 635 * @param entry start label for try
jlaskey@3 636 * @param exit end label for try
jlaskey@3 637 * @param recovery start label for catch
jlaskey@3 638 * @param clazz exception class
jlaskey@3 639 */
jlaskey@3 640 public void _try(final Label entry, final Label exit, final Label recovery, final Class<?> clazz) {
jlaskey@3 641 method.visitTryCatchBlock(entry, exit, recovery, CompilerConstants.className(clazz));
jlaskey@3 642 }
jlaskey@3 643
jlaskey@3 644 /**
jlaskey@3 645 * Start a try/catch block. The catch is "Throwable" - i.e. catch-all
jlaskey@3 646 *
jlaskey@3 647 * @param entry start label for try
jlaskey@3 648 * @param exit end label for try
jlaskey@3 649 * @param recovery start label for catch
jlaskey@3 650 */
jlaskey@3 651 public void _try(final Label entry, final Label exit, final Label recovery) {
jlaskey@3 652 _try(entry, exit, recovery, (String)null);
jlaskey@3 653 }
jlaskey@3 654
jlaskey@3 655
jlaskey@3 656 /**
jlaskey@3 657 * Load the constants array
jlaskey@3 658 * @param unitClassName name of the compile unit from which to load constants
jlaskey@3 659 * @return this method emitter
jlaskey@3 660 */
jlaskey@3 661 public MethodEmitter loadConstants(final String unitClassName) {
jlaskey@3 662 getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor());
jlaskey@3 663 assert peekType().isArray() : peekType();
jlaskey@3 664 return this;
jlaskey@3 665 }
jlaskey@3 666
jlaskey@3 667 /**
jlaskey@3 668 * Push the undefined value for the given type, i.e.
jlaskey@3 669 * UNDEFINED or UNDEFINEDNUMBER. Currently we have no way of
jlaskey@3 670 * representing UNDEFINED for INTs and LONGs, so they are not
jlaskey@3 671 * allowed to be local variables (yet)
jlaskey@3 672 *
jlaskey@3 673 * @param type the type for which to push UNDEFINED
jlaskey@3 674 * @return the method emitter
jlaskey@3 675 */
jlaskey@3 676 public MethodEmitter loadUndefined(final Type type) {
jlaskey@3 677 debug("load undefined " + type);
jlaskey@3 678 pushType(type.loadUndefined(method));
jlaskey@3 679 return this;
jlaskey@3 680 }
jlaskey@3 681
jlaskey@3 682 /**
jlaskey@3 683 * Push the empty value for the given type, i.e. EMPTY.
jlaskey@3 684 *
jlaskey@3 685 * @param type the type
jlaskey@3 686 * @return the method emitter
jlaskey@3 687 */
jlaskey@3 688 public MethodEmitter loadEmpty(final Type type) {
jlaskey@3 689 debug("load empty " + type);
jlaskey@3 690 pushType(type.loadEmpty(method));
jlaskey@3 691 return this;
jlaskey@3 692 }
jlaskey@3 693
jlaskey@3 694 /**
jlaskey@3 695 * Push null to stack
jlaskey@3 696 *
jlaskey@3 697 * @return the method emitter
jlaskey@3 698 */
jlaskey@3 699 public MethodEmitter loadNull() {
jlaskey@3 700 debug("aconst_null");
jlaskey@3 701 pushType(Type.OBJECT.ldc(method, null));
jlaskey@3 702 return this;
jlaskey@3 703 }
jlaskey@3 704
jlaskey@3 705 /**
jlaskey@3 706 * Push a handle representing this class top stack
jlaskey@3 707 *
jlaskey@3 708 * @param className name of the class
jlaskey@3 709 *
jlaskey@3 710 * @return the method emitter
jlaskey@3 711 */
jlaskey@3 712 public MethodEmitter loadType(final String className) {
jlaskey@3 713 debug("load type", className);
jlaskey@3 714 method.visitLdcInsn(jdk.internal.org.objectweb.asm.Type.getObjectType(className));
jlaskey@3 715 pushType(Type.OBJECT);
jlaskey@3 716 return this;
jlaskey@3 717 }
jlaskey@3 718
jlaskey@3 719 /**
jlaskey@3 720 * Push a boolean constant to the stack.
jlaskey@3 721 *
jlaskey@3 722 * @param b value of boolean
jlaskey@3 723 *
jlaskey@3 724 * @return the method emitter
jlaskey@3 725 */
jlaskey@3 726 public MethodEmitter load(final boolean b) {
jlaskey@3 727 debug("load boolean", b);
jlaskey@3 728 pushType(Type.BOOLEAN.ldc(method, b));
jlaskey@3 729 return this;
jlaskey@3 730 }
jlaskey@3 731
jlaskey@3 732 /**
jlaskey@3 733 * Push an int constant to the stack
jlaskey@3 734 *
jlaskey@3 735 * @param i value of the int
jlaskey@3 736 *
jlaskey@3 737 * @return the method emitter
jlaskey@3 738 */
jlaskey@3 739 public MethodEmitter load(final int i) {
jlaskey@3 740 debug("load int", i);
jlaskey@3 741 pushType(Type.INT.ldc(method, i));
jlaskey@3 742 return this;
jlaskey@3 743 }
jlaskey@3 744
jlaskey@3 745 /**
jlaskey@3 746 * Push a double constant to the stack
jlaskey@3 747 *
jlaskey@3 748 * @param d value of the double
jlaskey@3 749 *
jlaskey@3 750 * @return the method emitter
jlaskey@3 751 */
jlaskey@3 752 public MethodEmitter load(final double d) {
jlaskey@3 753 debug("load double", d);
jlaskey@3 754 pushType(Type.NUMBER.ldc(method, d));
jlaskey@3 755 return this;
jlaskey@3 756 }
jlaskey@3 757
jlaskey@3 758 /**
jlaskey@3 759 * Push an long constant to the stack
jlaskey@3 760 *
jlaskey@3 761 * @param l value of the long
jlaskey@3 762 *
jlaskey@3 763 * @return the method emitter
jlaskey@3 764 */
jlaskey@3 765 public MethodEmitter load(final long l) {
jlaskey@3 766 debug("load long", l);
jlaskey@3 767 pushType(Type.LONG.ldc(method, l));
jlaskey@3 768 return this;
jlaskey@3 769 }
jlaskey@3 770
jlaskey@3 771 /**
jlaskey@3 772 * Fetch the length of an array.
jlaskey@3 773 * @return Array length.
jlaskey@3 774 */
jlaskey@3 775 public MethodEmitter arraylength() {
jlaskey@3 776 debug("arraylength");
jlaskey@3 777 popType(Type.OBJECT);
jlaskey@3 778 pushType(Type.OBJECT_ARRAY.arraylength(method));
jlaskey@3 779 return this;
jlaskey@3 780 }
jlaskey@3 781
jlaskey@3 782 /**
jlaskey@3 783 * Push a String constant to the stack
jlaskey@3 784 *
jlaskey@3 785 * @param s value of the String
jlaskey@3 786 *
jlaskey@3 787 * @return the method emitter
jlaskey@3 788 */
jlaskey@3 789 public MethodEmitter load(final String s) {
jlaskey@3 790 debug("load string", s);
jlaskey@3 791
jlaskey@3 792 if (s == null) {
jlaskey@3 793 loadNull();
jlaskey@3 794 return this;
jlaskey@3 795 }
jlaskey@3 796
jlaskey@3 797 //NASHORN-142 - split too large string
jlaskey@3 798 final int length = s.length();
jlaskey@3 799 if (length > LARGE_STRING_THRESHOLD) {
jlaskey@3 800
jlaskey@3 801 _new(StringBuilder.class);
jlaskey@3 802 dup();
jlaskey@3 803 load(length);
jlaskey@3 804 invoke(constructorNoLookup(StringBuilder.class, int.class));
jlaskey@3 805
jlaskey@3 806 for (int n = 0; n < length; n += LARGE_STRING_THRESHOLD) {
jlaskey@3 807 final String part = s.substring(n, Math.min(n + LARGE_STRING_THRESHOLD, length));
jlaskey@3 808 load(part);
jlaskey@3 809 stringBuilderAppend();
jlaskey@3 810 }
jlaskey@3 811
jlaskey@3 812 invoke(virtualCallNoLookup(StringBuilder.class, "toString", String.class));
jlaskey@3 813
jlaskey@3 814 return this;
jlaskey@3 815 }
jlaskey@3 816
jlaskey@3 817 pushType(Type.OBJECT.ldc(method, s));
jlaskey@3 818 return this;
jlaskey@3 819 }
jlaskey@3 820
jlaskey@3 821 /**
jlaskey@3 822 * Push an local variable to the stack. If the symbol representing
jlaskey@3 823 * the local variable doesn't have a slot, this is a NOP
jlaskey@3 824 *
jlaskey@3 825 * @param symbol the symbol representing the local variable.
jlaskey@3 826 *
jlaskey@3 827 * @return the method emitter
jlaskey@3 828 */
jlaskey@3 829 public MethodEmitter load(final Symbol symbol) {
jlaskey@3 830 assert symbol != null;
jlaskey@3 831 if (symbol.hasSlot()) {
attila@65 832 final int slot = symbol.getSlot();
attila@65 833 debug("load symbol", symbol.getName(), " slot=", slot);
attila@81 834 final Type type = symbol.getSymbolType().load(method, slot);
attila@81 835 pushType(type == Type.OBJECT && symbol.isThis() ? Type.THIS : type);
attila@65 836 } else if (symbol.isParam()) {
attila@65 837 assert !symbol.isScope();
attila@65 838 assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
attila@65 839 final int index = symbol.getFieldIndex();
attila@65 840 if(functionNode.needsArguments()) {
attila@65 841 // ScriptObject.getArgument(int) on arguments
attila@65 842 debug("load symbol", symbol.getName(), " arguments index=", index);
attila@65 843 loadArguments();
attila@65 844 load(index);
attila@65 845 ScriptObject.GET_ARGUMENT.invoke(this);
attila@65 846 } else {
attila@65 847 // array load from __varargs__
attila@65 848 debug("load symbol", symbol.getName(), " array index=", index);
attila@65 849 loadVarArgs();
attila@65 850 load(symbol.getFieldIndex());
attila@65 851 arrayload();
attila@65 852 }
attila@62 853 }
attila@62 854 return this;
attila@62 855 }
attila@62 856
attila@62 857 /**
jlaskey@3 858 * Push a local variable to the stack, given an explicit bytecode slot
jlaskey@3 859 * This is used e.g. for stub generation where we know where items like
jlaskey@3 860 * "this" and "scope" reside.
jlaskey@3 861 *
jlaskey@3 862 * @param type the type of the variable
jlaskey@3 863 * @param slot the slot the variable is in
jlaskey@3 864 *
jlaskey@3 865 * @return the method emitter
jlaskey@3 866 */
jlaskey@3 867 public MethodEmitter load(final Type type, final int slot) {
jlaskey@3 868 debug("explicit load", type, slot);
attila@81 869 final Type loadType = type.load(method, slot);
attila@81 870 pushType(loadType == Type.OBJECT && isThisSlot(slot) ? Type.THIS : loadType);
jlaskey@3 871 return this;
jlaskey@3 872 }
jlaskey@3 873
attila@81 874 private boolean isThisSlot(final int slot) {
attila@81 875 if(functionNode == null) {
attila@81 876 return slot == CompilerConstants.JAVA_THIS.slot();
attila@81 877 }
attila@81 878 final int thisSlot = functionNode.getThisNode().getSymbol().getSlot();
attila@81 879 assert !functionNode.needsCallee() || thisSlot == 1; // needsCallee -> thisSlot == 1
attila@81 880 assert functionNode.needsCallee() || thisSlot == 0; // !needsCallee -> thisSlot == 0
attila@81 881 return slot == thisSlot;
attila@81 882 }
attila@81 883
jlaskey@3 884 /**
jlaskey@3 885 * Push the this object to the stack.
jlaskey@3 886 *
jlaskey@3 887 * @return the method emitter
jlaskey@3 888 */
jlaskey@3 889 public MethodEmitter loadThis() {
jlaskey@3 890 load(functionNode.getThisNode().getSymbol());
jlaskey@3 891 return this;
jlaskey@3 892 }
jlaskey@3 893
jlaskey@3 894 /**
jlaskey@3 895 * Push the scope object to the stack.
jlaskey@3 896 *
jlaskey@3 897 * @return the method emitter
jlaskey@3 898 */
jlaskey@3 899 public MethodEmitter loadScope() {
jlaskey@3 900 if (peekType() == Type.SCOPE) {
jlaskey@3 901 dup();
jlaskey@3 902 return this;
jlaskey@3 903 }
jlaskey@3 904 load(functionNode.getScopeNode().getSymbol());
jlaskey@3 905 return this;
jlaskey@3 906 }
jlaskey@3 907
jlaskey@3 908 /**
jlaskey@3 909 * Push the return object to the stack.
jlaskey@3 910 *
jlaskey@3 911 * @return the method emitter
jlaskey@3 912 */
jlaskey@3 913 public MethodEmitter loadResult() {
jlaskey@3 914 load(functionNode.getResultNode().getSymbol());
jlaskey@3 915 return this;
jlaskey@3 916 }
jlaskey@3 917
jlaskey@3 918
jlaskey@3 919 /**
jlaskey@3 920 * Push a method handle to the stack
jlaskey@3 921 *
jlaskey@3 922 * @param className class name
jlaskey@3 923 * @param methodName method name
jlaskey@3 924 * @param descName descriptor
jlaskey@3 925 * @param flags flags that describe this handle, e.g. invokespecial new, or invoke virtual
jlaskey@3 926 *
jlaskey@3 927 * @see ClassEmitter.Flag
jlaskey@3 928 *
jlaskey@3 929 * @return the method emitter
jlaskey@3 930 */
jlaskey@3 931 public MethodEmitter loadHandle(final String className, final String methodName, final String descName, final EnumSet<Flag> flags) {
jlaskey@3 932 debug("load handle ");
jlaskey@3 933 pushType(Type.OBJECT.ldc(method, new Handle(Flag.getValue(flags), className, methodName, descName)));
jlaskey@3 934 return this;
jlaskey@3 935 }
jlaskey@3 936
jlaskey@3 937 /**
jlaskey@3 938 * Push the varargs object to the stack
jlaskey@3 939 *
jlaskey@3 940 * @return the method emitter
jlaskey@3 941 */
jlaskey@3 942 public MethodEmitter loadVarArgs() {
jlaskey@3 943 debug("load var args " + functionNode.getVarArgsNode().getSymbol());
jlaskey@3 944 return load(functionNode.getVarArgsNode().getSymbol());
jlaskey@3 945 }
jlaskey@3 946
jlaskey@3 947 /**
jlaskey@3 948 * Push the arguments array to the stack
jlaskey@3 949 *
jlaskey@3 950 * @return the method emitter
jlaskey@3 951 */
jlaskey@3 952 public MethodEmitter loadArguments() {
attila@62 953 debug("load arguments ", functionNode.getArgumentsNode().getSymbol());
jlaskey@3 954 assert functionNode.getArgumentsNode().getSymbol().getSlot() != 0;
jlaskey@3 955 return load(functionNode.getArgumentsNode().getSymbol());
jlaskey@3 956 }
jlaskey@3 957
jlaskey@3 958 /**
jlaskey@3 959 * Push the callee object to the stack
jlaskey@3 960 *
jlaskey@3 961 * @return the method emitter
jlaskey@3 962 */
jlaskey@3 963 public MethodEmitter loadCallee() {
attila@81 964 final Symbol calleeSymbol = functionNode.getCalleeNode().getSymbol();
attila@81 965 debug("load callee ", calleeSymbol);
attila@81 966 assert calleeSymbol.getSlot() == 0 : "callee has wrong slot " + calleeSymbol.getSlot() + " in " + functionNode.getName();
jlaskey@3 967
attila@81 968 return load(calleeSymbol);
jlaskey@3 969 }
jlaskey@3 970
jlaskey@3 971 /**
jlaskey@3 972 * Pop the scope from the stack and store it in its predefined slot
jlaskey@3 973 */
jlaskey@3 974 public void storeScope() {
jlaskey@3 975 debug("store scope");
jlaskey@3 976 store(functionNode.getScopeNode().getSymbol());
jlaskey@3 977 }
jlaskey@3 978
jlaskey@3 979 /**
jlaskey@3 980 * Pop the return from the stack and store it in its predefined slot
jlaskey@3 981 */
jlaskey@3 982 public void storeResult() {
jlaskey@3 983 debug("store result");
jlaskey@3 984 store(functionNode.getResultNode().getSymbol());
jlaskey@3 985 }
jlaskey@3 986
jlaskey@3 987 /**
jlaskey@3 988 * Pop the arguments array from the stack and store it in its predefined slot
jlaskey@3 989 */
jlaskey@3 990 public void storeArguments() {
jlaskey@3 991 debug("store arguments");
jlaskey@3 992 store(functionNode.getArgumentsNode().getSymbol());
jlaskey@3 993 }
jlaskey@3 994
jlaskey@3 995 /**
jlaskey@3 996 * Load an element from an array, determining type automatically
jlaskey@3 997 * @return the method emitter
jlaskey@3 998 */
jlaskey@3 999 public MethodEmitter arrayload() {
jlaskey@3 1000 debug("Xaload");
jlaskey@3 1001 popType(Type.INT);
jlaskey@3 1002 pushType(popArray().aload(method));
jlaskey@3 1003 return this;
jlaskey@3 1004 }
jlaskey@3 1005
jlaskey@3 1006 /**
jlaskey@3 1007 * Pop a value, an index and an array from the stack and store
jlaskey@3 1008 * the value at the given index in the array.
jlaskey@3 1009 */
jlaskey@3 1010 public void arraystore() {
jlaskey@3 1011 debug("Xastore");
jlaskey@3 1012 final Type value = popType();
jlaskey@3 1013 final Type index = popType(Type.INT);
jlaskey@3 1014 assert index.isInteger() : "array index is not integer, but " + index;
jlaskey@3 1015 final ArrayType array = popArray();
jlaskey@3 1016
jlaskey@3 1017 assert value.isEquivalentTo(array.getElementType()) : "Storing "+value+" into "+array;
jlaskey@3 1018 assert array.isObject();
jlaskey@3 1019 array.astore(method);
jlaskey@3 1020 }
jlaskey@3 1021
jlaskey@3 1022 /**
jlaskey@3 1023 * Pop a value from the stack and store it in a local variable represented
jlaskey@3 1024 * by the given symbol. If the symbol has no slot, this is a NOP
jlaskey@3 1025 *
jlaskey@3 1026 * @param symbol symbol to store stack to
jlaskey@3 1027 */
jlaskey@3 1028 public void store(final Symbol symbol) {
lagergren@57 1029 assert symbol != null : "No symbol to store";
jlaskey@3 1030 if (symbol.hasSlot()) {
attila@65 1031 final int slot = symbol.getSlot();
attila@65 1032 debug("store symbol", symbol.getName(), " slot=", slot);
attila@65 1033 popType(symbol.getSymbolType()).store(method, slot);
attila@65 1034 } else if (symbol.isParam()) {
attila@65 1035 assert !symbol.isScope();
attila@65 1036 assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
attila@65 1037 final int index = symbol.getFieldIndex();
attila@65 1038 if(functionNode.needsArguments()) {
attila@65 1039 debug("store symbol", symbol.getName(), " arguments index=", index);
attila@65 1040 loadArguments();
attila@65 1041 load(index);
attila@65 1042 ArgumentSetter.SET_ARGUMENT.invoke(this);
attila@65 1043 } else {
attila@65 1044 // varargs without arguments object - just do array store to __varargs__
attila@65 1045 debug("store symbol", symbol.getName(), " array index=", index);
attila@65 1046 loadVarArgs();
attila@65 1047 load(index);
attila@65 1048 ArgumentSetter.SET_ARRAY_ELEMENT.invoke(this);
attila@65 1049 }
jlaskey@3 1050 }
jlaskey@3 1051 }
jlaskey@3 1052
jlaskey@3 1053 /**
jlaskey@3 1054 * Pop a value from the stack and store it in a given local variable
jlaskey@3 1055 * slot.
jlaskey@3 1056 *
jlaskey@3 1057 * @param type the type to pop
jlaskey@3 1058 * @param slot the slot
jlaskey@3 1059 */
jlaskey@3 1060 public void store(final Type type, final int slot) {
jlaskey@3 1061 popType(type);
jlaskey@3 1062 type.store(method, slot);
jlaskey@3 1063 }
jlaskey@3 1064
jlaskey@3 1065 /**
jlaskey@3 1066 * Increment/Decrement a local integer by the given value.
jlaskey@3 1067 *
jlaskey@3 1068 * @param slot the int slot
jlaskey@3 1069 * @param increment the amount to increment
jlaskey@3 1070 */
jlaskey@3 1071 public void iinc(final int slot, final int increment) {
jlaskey@3 1072 debug("iinc");
jlaskey@3 1073 method.visitIincInsn(slot, increment);
jlaskey@3 1074 }
jlaskey@3 1075
jlaskey@3 1076 /**
jlaskey@3 1077 * Pop an exception object from the stack and generate code
jlaskey@3 1078 * for throwing it
jlaskey@3 1079 */
jlaskey@3 1080 public void athrow() {
jlaskey@3 1081 debug("athrow");
jlaskey@3 1082 final Type receiver = popType(Type.OBJECT);
jlaskey@3 1083 assert receiver.isObject();
jlaskey@3 1084 method.visitInsn(ATHROW);
jlaskey@3 1085 stack = null;
jlaskey@3 1086 }
jlaskey@3 1087
jlaskey@3 1088 /**
jlaskey@3 1089 * Pop an object from the stack and perform an instanceof
jlaskey@3 1090 * operation, given a classDescriptor to compare it to.
jlaskey@3 1091 * Push the boolean result 1/0 as an int to the stack
jlaskey@3 1092 *
jlaskey@3 1093 * @param classDescriptor descriptor of the class to type check against
jlaskey@3 1094 *
jlaskey@3 1095 * @return the method emitter
jlaskey@3 1096 */
jlaskey@3 1097 public MethodEmitter _instanceof(final String classDescriptor) {
jlaskey@3 1098 debug("instanceof", classDescriptor);
jlaskey@3 1099 popType(Type.OBJECT);
jlaskey@3 1100 method.visitTypeInsn(INSTANCEOF, classDescriptor);
jlaskey@3 1101 pushType(Type.INT);
jlaskey@3 1102 return this;
jlaskey@3 1103 }
jlaskey@3 1104
jlaskey@3 1105 /**
jlaskey@3 1106 * Pop an object from the stack and perform an instanceof
jlaskey@3 1107 * operation, given a classDescriptor to compare it to.
jlaskey@3 1108 * Push the boolean result 1/0 as an int to the stack
jlaskey@3 1109 *
jlaskey@3 1110 * @param clazz the type to check instanceof against
jlaskey@3 1111 *
jlaskey@3 1112 * @return the method emitter
jlaskey@3 1113 */
jlaskey@3 1114 public MethodEmitter _instanceof(final Class<?> clazz) {
jlaskey@3 1115 return _instanceof(CompilerConstants.className(clazz));
jlaskey@3 1116 }
jlaskey@3 1117
jlaskey@3 1118 /**
jlaskey@3 1119 * Perform a checkcast operation on the object at the top of the
jlaskey@3 1120 * stack.
jlaskey@3 1121 *
jlaskey@3 1122 * @param classDescriptor descriptor of the class to type check against
jlaskey@3 1123 *
jlaskey@3 1124 * @return the method emitter
jlaskey@3 1125 */
jlaskey@3 1126 public MethodEmitter checkcast(final String classDescriptor) {
jlaskey@3 1127 debug("checkcast", classDescriptor);
jlaskey@3 1128 assert peekType().isObject();
jlaskey@3 1129 method.visitTypeInsn(CHECKCAST, classDescriptor);
jlaskey@3 1130 return this;
jlaskey@3 1131 }
jlaskey@3 1132
jlaskey@3 1133 /**
jlaskey@3 1134 * Perform a checkcast operation on the object at the top of the
jlaskey@3 1135 * stack.
jlaskey@3 1136 *
jlaskey@3 1137 * @param clazz class to checkcast against
jlaskey@3 1138 *
jlaskey@3 1139 * @return the method emitter
jlaskey@3 1140 */
jlaskey@3 1141 public MethodEmitter checkcast(final Class<?> clazz) {
jlaskey@3 1142 return checkcast(CompilerConstants.className(clazz));
jlaskey@3 1143 }
jlaskey@3 1144
jlaskey@3 1145 /**
jlaskey@3 1146 * Instantiate a new array given a length that is popped
jlaskey@3 1147 * from the stack and the array type
jlaskey@3 1148 *
jlaskey@3 1149 * @param arrayType the type of the array
jlaskey@3 1150 *
jlaskey@3 1151 * @return the method emitter
jlaskey@3 1152 */
jlaskey@3 1153 public MethodEmitter newarray(final ArrayType arrayType) {
jlaskey@3 1154 debug("newarray ", "arrayType=" + arrayType);
jlaskey@3 1155 popType(Type.INT); //LENGTH
jlaskey@3 1156 pushType(arrayType.newarray(method));
jlaskey@3 1157 return this;
jlaskey@3 1158 }
jlaskey@3 1159
jlaskey@3 1160 /**
jlaskey@3 1161 * Instantiate a multidimensional array with a given number of dimensions.
jlaskey@3 1162 * On the stack are dim lengths of the sub arrays.
jlaskey@3 1163 *
jlaskey@3 1164 * @param arrayType type of the array
jlaskey@3 1165 * @param dims number of dimensions
jlaskey@3 1166 *
jlaskey@3 1167 * @return the method emitter
jlaskey@3 1168 */
jlaskey@3 1169 public MethodEmitter multinewarray(final ArrayType arrayType, final int dims) {
jlaskey@3 1170 debug("multianewarray ", arrayType, dims);
jlaskey@3 1171 for (int i = 0; i < dims; i++) {
jlaskey@3 1172 popType(Type.INT); //LENGTH
jlaskey@3 1173 }
jlaskey@3 1174 pushType(arrayType.newarray(method, dims));
jlaskey@3 1175 return this;
jlaskey@3 1176 }
jlaskey@3 1177
jlaskey@3 1178 /**
jlaskey@3 1179 * Helper function to pop and type check the appropriate arguments
jlaskey@3 1180 * from the stack given a method signature
jlaskey@3 1181 *
jlaskey@3 1182 * @param signature method signature
jlaskey@3 1183 *
jlaskey@3 1184 * @return return type of method
jlaskey@3 1185 */
jlaskey@3 1186 private Type fixParamStack(final String signature) {
jlaskey@3 1187 final Type[] params = Type.getMethodArguments(signature);
jlaskey@3 1188 for (int i = params.length - 1; i >= 0; i--) {
jlaskey@3 1189 popType(params[i]);
jlaskey@3 1190 }
jlaskey@3 1191 final Type returnType = Type.getMethodReturnType(signature);
jlaskey@3 1192 return returnType;
jlaskey@3 1193 }
jlaskey@3 1194
jlaskey@3 1195 /**
jlaskey@3 1196 * Generate an invocation to a Call structure
jlaskey@3 1197 * @see CompilerConstants
jlaskey@3 1198 *
jlaskey@3 1199 * @param call the call object
jlaskey@3 1200 *
jlaskey@3 1201 * @return the method emitter
jlaskey@3 1202 */
jlaskey@3 1203 public MethodEmitter invoke(final Call call) {
jlaskey@3 1204 return call.invoke(this);
jlaskey@3 1205 }
jlaskey@3 1206
jlaskey@3 1207 private MethodEmitter invoke(final int opcode, final String className, final String methodName, final String methodDescriptor, final boolean hasReceiver) {
jlaskey@3 1208 final Type returnType = fixParamStack(methodDescriptor);
jlaskey@3 1209
jlaskey@3 1210 if (hasReceiver) {
jlaskey@3 1211 popType(Type.OBJECT);
jlaskey@3 1212 }
jlaskey@3 1213
jlaskey@3 1214 method.visitMethodInsn(opcode, className, methodName, methodDescriptor);
jlaskey@3 1215
jlaskey@3 1216 if (returnType != null) {
jlaskey@3 1217 pushType(returnType);
jlaskey@3 1218 }
jlaskey@3 1219
jlaskey@3 1220 return this;
jlaskey@3 1221 }
jlaskey@3 1222
jlaskey@3 1223 /**
jlaskey@3 1224 * Pop receiver from stack, perform an invoke special
jlaskey@3 1225 *
jlaskey@3 1226 * @param className class name
jlaskey@3 1227 * @param methodName method name
jlaskey@3 1228 * @param methodDescriptor descriptor
jlaskey@3 1229 *
jlaskey@3 1230 * @return the method emitter
jlaskey@3 1231 */
jlaskey@3 1232 public MethodEmitter invokeSpecial(final String className, final String methodName, final String methodDescriptor) {
jlaskey@3 1233 debug("invokespecial", className + "." + methodName + methodDescriptor);
jlaskey@3 1234 return invoke(INVOKESPECIAL, className, methodName, methodDescriptor, true);
jlaskey@3 1235 }
jlaskey@3 1236
jlaskey@3 1237 /**
jlaskey@3 1238 * Pop receiver from stack, perform an invoke virtual, push return value if any
jlaskey@3 1239 *
jlaskey@3 1240 * @param className class name
jlaskey@3 1241 * @param methodName method name
jlaskey@3 1242 * @param methodDescriptor descriptor
jlaskey@3 1243 *
jlaskey@3 1244 * @return the method emitter
jlaskey@3 1245 */
jlaskey@3 1246 public MethodEmitter invokeVirtual(final String className, final String methodName, final String methodDescriptor) {
jlaskey@3 1247 debug("invokevirtual", className + "." + methodName + methodDescriptor + " " + stack);
jlaskey@3 1248 return invoke(INVOKEVIRTUAL, className, methodName, methodDescriptor, true);
jlaskey@3 1249 }
jlaskey@3 1250
jlaskey@3 1251 /**
jlaskey@3 1252 * Perform an invoke static and push the return value if any
jlaskey@3 1253 *
jlaskey@3 1254 * @param className class name
jlaskey@3 1255 * @param methodName method name
jlaskey@3 1256 * @param methodDescriptor descriptor
jlaskey@3 1257 *
jlaskey@3 1258 * @return the method emitter
jlaskey@3 1259 */
jlaskey@3 1260 public MethodEmitter invokeStatic(final String className, final String methodName, final String methodDescriptor) {
jlaskey@3 1261 debug("invokestatic", className + "." + methodName + methodDescriptor);
jlaskey@3 1262 invoke(INVOKESTATIC, className, methodName, methodDescriptor, false);
jlaskey@3 1263 return this;
jlaskey@3 1264 }
jlaskey@3 1265
jlaskey@3 1266 /**
jlaskey@3 1267 * Perform an invoke static and replace the return type if we know better, e.g. Global.allocate
jlaskey@3 1268 * that allocates an array should return an ObjectArray type as a NativeArray counts as that
jlaskey@3 1269 *
jlaskey@3 1270 * @param className class name
jlaskey@3 1271 * @param methodName method name
jlaskey@3 1272 * @param methodDescriptor descriptor
jlaskey@3 1273 * @param returnType return type override
jlaskey@3 1274 *
jlaskey@3 1275 * @return the method emitter
jlaskey@3 1276 */
jlaskey@3 1277 public MethodEmitter invokeStatic(final String className, final String methodName, final String methodDescriptor, final Type returnType) {
jlaskey@3 1278 invokeStatic(className, methodName, methodDescriptor);
jlaskey@3 1279 popType();
jlaskey@3 1280 pushType(returnType);
jlaskey@3 1281 return this;
jlaskey@3 1282 }
jlaskey@3 1283
jlaskey@3 1284 /**
jlaskey@3 1285 * Pop receiver from stack, perform an invoke interface and push return value if any
jlaskey@3 1286 *
jlaskey@3 1287 * @param className class name
jlaskey@3 1288 * @param methodName method name
jlaskey@3 1289 * @param methodDescriptor descriptor
jlaskey@3 1290 *
jlaskey@3 1291 * @return the method emitter
jlaskey@3 1292 */
jlaskey@3 1293 public MethodEmitter invokeInterface(final String className, final String methodName, final String methodDescriptor) {
jlaskey@3 1294 debug("invokeinterface", className + "." + methodName + methodDescriptor);
jlaskey@3 1295 return invoke(INVOKEINTERFACE, className, methodName, methodDescriptor, true);
jlaskey@3 1296 }
jlaskey@3 1297
jlaskey@3 1298 /**
jlaskey@3 1299 * Generate a lookup switch, popping the switch value from the stack
jlaskey@3 1300 *
jlaskey@3 1301 * @param defaultLabel default label
jlaskey@3 1302 * @param values case values for the table
jlaskey@3 1303 * @param table default label
jlaskey@3 1304 */
jlaskey@3 1305 public void lookupSwitch(final Label defaultLabel, final int[] values, final Label[] table) {
jlaskey@3 1306 debug("lookupswitch", peekType());
jlaskey@3 1307 popType(Type.INT);
jlaskey@3 1308 method.visitLookupSwitchInsn(defaultLabel, values, table);
jlaskey@3 1309 }
jlaskey@3 1310
jlaskey@3 1311 /**
jlaskey@3 1312 * Generate a table switch
jlaskey@3 1313 * @param lo low value
jlaskey@3 1314 * @param hi high value
jlaskey@3 1315 * @param defaultLabel default label
jlaskey@3 1316 * @param table label table
jlaskey@3 1317 */
jlaskey@3 1318 public void tableSwitch(final int lo, final int hi, final Label defaultLabel, final Label[] table) {
jlaskey@3 1319 debug("tableswitch", peekType());
jlaskey@3 1320 popType(Type.INT);
jlaskey@3 1321 method.visitTableSwitchInsn(lo, hi, defaultLabel, table);
jlaskey@3 1322 }
jlaskey@3 1323
jlaskey@3 1324 /**
jlaskey@3 1325 * Abstraction for performing a conditional jump of any type
jlaskey@3 1326 *
jlaskey@3 1327 * @see MethodEmitter.Condition
jlaskey@3 1328 *
jlaskey@3 1329 * @param cond the condition to test
jlaskey@3 1330 * @param trueLabel the destination label is condition is true
jlaskey@3 1331 */
jlaskey@3 1332 void conditionalJump(final Condition cond, final Label trueLabel) {
jlaskey@3 1333 conditionalJump(cond, cond != Condition.GT && cond != Condition.GE, trueLabel);
jlaskey@3 1334 }
jlaskey@3 1335
jlaskey@3 1336 /**
jlaskey@3 1337 * Abstraction for performing a conditional jump of any type,
jlaskey@3 1338 * including a dcmpg/dcmpl semantic for doubles.
jlaskey@3 1339 *
jlaskey@3 1340 * @param cond the condition to test
jlaskey@3 1341 * @param isCmpG is this a dcmpg for numbers, false if it's a dcmpl
jlaskey@3 1342 * @param trueLabel the destination label if condition is true
jlaskey@3 1343 */
jlaskey@3 1344 void conditionalJump(final Condition cond, final boolean isCmpG, final Label trueLabel) {
jlaskey@3 1345 if (peekType().isCategory2()) {
jlaskey@3 1346 debug("[ld]cmp isCmpG=" + isCmpG);
jlaskey@3 1347 pushType(get2n().cmp(method, isCmpG));
jlaskey@3 1348 jump(Condition.toUnary(cond), trueLabel, 1);
jlaskey@3 1349 } else {
jlaskey@3 1350 debug("if" + cond);
jlaskey@3 1351 jump(Condition.toBinary(cond, peekType().isObject()), trueLabel, 2);
jlaskey@3 1352 }
jlaskey@3 1353 }
jlaskey@3 1354
jlaskey@3 1355 /**
jlaskey@3 1356 * Perform a non void return, popping the type from the stack
jlaskey@3 1357 *
jlaskey@3 1358 * @param type the type for the return
jlaskey@3 1359 */
jlaskey@3 1360 public void _return(final Type type) {
jlaskey@3 1361 debug("return", type);
jlaskey@3 1362 assert stack.size() == 1 : "Only return value on stack allowed at return point - depth=" + stack.size() + " stack = " + stack;
jlaskey@3 1363 final Type stackType = peekType();
jlaskey@3 1364 if (!Type.areEquivalent(type, stackType)) {
jlaskey@3 1365 convert(type);
jlaskey@3 1366 }
jlaskey@3 1367 popType(type)._return(method);
jlaskey@3 1368 stack = null;
jlaskey@3 1369 }
jlaskey@3 1370
jlaskey@3 1371 /**
jlaskey@3 1372 * Perform a return using the stack top value as the guide for the type
jlaskey@3 1373 */
jlaskey@3 1374 public void _return() {
jlaskey@3 1375 _return(peekType());
jlaskey@3 1376 }
jlaskey@3 1377
jlaskey@3 1378 /**
jlaskey@3 1379 * Perform a void return.
jlaskey@3 1380 */
jlaskey@3 1381 public void returnVoid() {
jlaskey@3 1382 debug("return [void]");
jlaskey@3 1383 assert stack.isEmpty() : stack;
jlaskey@3 1384 method.visitInsn(RETURN);
jlaskey@3 1385 stack = null;
jlaskey@3 1386 }
jlaskey@3 1387
jlaskey@3 1388 /**
jlaskey@3 1389 * Goto, possibly when splitting is taking place. If
jlaskey@3 1390 * a splitNode exists, we need to handle the case that the
jlaskey@3 1391 * jump target is another method
jlaskey@3 1392 *
jlaskey@3 1393 * @param label destination label
jlaskey@3 1394 */
jlaskey@3 1395 public void splitAwareGoto(final Label label) {
jlaskey@3 1396
jlaskey@3 1397 if (splitNode != null) {
jlaskey@3 1398 final int index = splitNode.getExternalTargets().indexOf(label);
jlaskey@3 1399
jlaskey@3 1400 if (index > -1) {
jlaskey@3 1401 loadScope();
jlaskey@3 1402 checkcast(Scope.class);
jlaskey@3 1403 load(index + 1);
jlaskey@3 1404 invoke(Scope.SET_SPLIT_STATE);
jlaskey@3 1405 loadUndefined(Type.OBJECT);
jlaskey@3 1406 _return(functionNode.getReturnType());
jlaskey@3 1407 return;
jlaskey@3 1408 }
jlaskey@3 1409 }
jlaskey@3 1410
jlaskey@3 1411 _goto(label);
jlaskey@3 1412 }
jlaskey@3 1413
jlaskey@3 1414 /**
jlaskey@3 1415 * Perform a comparison of two number types that are popped from the stack
jlaskey@3 1416 *
jlaskey@3 1417 * @param isCmpG is this a dcmpg semantic, false if it's a dcmpl semantic
jlaskey@3 1418 *
jlaskey@3 1419 * @return the method emitter
jlaskey@3 1420 */
jlaskey@3 1421 public MethodEmitter cmp(final boolean isCmpG) {
jlaskey@3 1422 pushType(get2n().cmp(method, isCmpG));
jlaskey@3 1423 return this;
jlaskey@3 1424 }
jlaskey@3 1425
jlaskey@3 1426 /**
jlaskey@3 1427 * Helper function for jumps, conditional or not
jlaskey@3 1428 * @param opcode opcode for jump
jlaskey@3 1429 * @param label destination
jlaskey@3 1430 * @param n elements on stack to compare, 0-2
jlaskey@3 1431 */
jlaskey@3 1432 private void jump(final int opcode, final Label label, final int n) {
jlaskey@3 1433 for (int i = 0; i < n; i++) {
jlaskey@3 1434 assert peekType().isInteger() || peekType().isBoolean() || peekType().isObject() : "expecting integer type or object for jump, but found " + peekType();
jlaskey@3 1435 popType();
jlaskey@3 1436 }
jlaskey@3 1437 mergeStackTo(label);
jlaskey@3 1438 method.visitJumpInsn(opcode, label);
jlaskey@3 1439 }
jlaskey@3 1440
jlaskey@3 1441 /**
jlaskey@3 1442 * Generate an if_acmpeq
jlaskey@3 1443 *
jlaskey@3 1444 * @param label label to true case
jlaskey@3 1445 */
jlaskey@3 1446 public void if_acmpeq(final Label label) {
jlaskey@3 1447 debug("if_acmpeq", label);
jlaskey@3 1448 jump(IF_ACMPEQ, label, 2);
jlaskey@3 1449 }
jlaskey@3 1450
jlaskey@3 1451 /**
jlaskey@3 1452 * Generate an if_acmpne
jlaskey@3 1453 *
jlaskey@3 1454 * @param label label to true case
jlaskey@3 1455 */
jlaskey@3 1456 public void if_acmpne(final Label label) {
jlaskey@3 1457 debug("if_acmpne", label);
jlaskey@3 1458 jump(IF_ACMPNE, label, 2);
jlaskey@3 1459 }
jlaskey@3 1460
jlaskey@3 1461 /**
jlaskey@3 1462 * Generate an ifnull
jlaskey@3 1463 *
jlaskey@3 1464 * @param label label to true case
jlaskey@3 1465 */
jlaskey@3 1466 public void ifnull(final Label label) {
jlaskey@3 1467 debug("ifnull", label);
jlaskey@3 1468 jump(IFNULL, label, 1);
jlaskey@3 1469 }
jlaskey@3 1470
jlaskey@3 1471 /**
jlaskey@3 1472 * Generate an ifnonnull
jlaskey@3 1473 *
jlaskey@3 1474 * @param label label to true case
jlaskey@3 1475 */
jlaskey@3 1476 public void ifnonnull(final Label label) {
jlaskey@3 1477 debug("ifnonnull", label);
jlaskey@3 1478 jump(IFNONNULL, label, 1);
jlaskey@3 1479 }
jlaskey@3 1480
jlaskey@3 1481 /**
jlaskey@3 1482 * Generate an ifeq
jlaskey@3 1483 *
jlaskey@3 1484 * @param label label to true case
jlaskey@3 1485 */
jlaskey@3 1486 public void ifeq(final Label label) {
jlaskey@3 1487 debug("ifeq ", label);
jlaskey@3 1488 jump(IFEQ, label, 1);
jlaskey@3 1489 }
jlaskey@3 1490
jlaskey@3 1491 /**
jlaskey@3 1492 * Generate an if_icmpeq
jlaskey@3 1493 *
jlaskey@3 1494 * @param label label to true case
jlaskey@3 1495 */
jlaskey@3 1496 public void if_icmpeq(final Label label) {
jlaskey@3 1497 debug("if_icmpeq", label);
jlaskey@3 1498 jump(IF_ICMPEQ, label, 2);
jlaskey@3 1499 }
jlaskey@3 1500
jlaskey@3 1501 /**
jlaskey@3 1502 * Generate an if_ne
jlaskey@3 1503 *
jlaskey@3 1504 * @param label label to true case
jlaskey@3 1505 */
jlaskey@3 1506
jlaskey@3 1507 public void ifne(final Label label) {
jlaskey@3 1508 debug("ifne", label);
jlaskey@3 1509 jump(IFNE, label, 1);
jlaskey@3 1510 }
jlaskey@3 1511
jlaskey@3 1512 /**
jlaskey@3 1513 * Generate an if_icmpne
jlaskey@3 1514 *
jlaskey@3 1515 * @param label label to true case
jlaskey@3 1516 */
jlaskey@3 1517 public void if_icmpne(final Label label) {
jlaskey@3 1518 debug("if_icmpne", label);
jlaskey@3 1519 jump(IF_ICMPNE, label, 2);
jlaskey@3 1520 }
jlaskey@3 1521
jlaskey@3 1522 /**
jlaskey@3 1523 * Generate an iflt
jlaskey@3 1524 *
jlaskey@3 1525 * @param label label to true case
jlaskey@3 1526 */
jlaskey@3 1527 public void iflt(final Label label) {
jlaskey@3 1528 debug("iflt", label);
jlaskey@3 1529 jump(IFLT, label, 1);
jlaskey@3 1530 }
jlaskey@3 1531
jlaskey@3 1532 /**
jlaskey@3 1533 * Generate an ifle
jlaskey@3 1534 *
jlaskey@3 1535 * @param label label to true case
jlaskey@3 1536 */
jlaskey@3 1537 public void ifle(final Label label) {
jlaskey@3 1538 debug("ifle", label);
jlaskey@3 1539 jump(IFLE, label, 1);
jlaskey@3 1540 }
jlaskey@3 1541
jlaskey@3 1542 /**
jlaskey@3 1543 * Generate an ifgt
jlaskey@3 1544 *
jlaskey@3 1545 * @param label label to true case
jlaskey@3 1546 */
jlaskey@3 1547 public void ifgt(final Label label) {
jlaskey@3 1548 debug("ifgt", label);
jlaskey@3 1549 jump(IFGT, label, 1);
jlaskey@3 1550 }
jlaskey@3 1551
jlaskey@3 1552 /**
jlaskey@3 1553 * Generate an ifge
jlaskey@3 1554 *
jlaskey@3 1555 * @param label label to true case
jlaskey@3 1556 */
jlaskey@3 1557 public void ifge(final Label label) {
jlaskey@3 1558 debug("ifge", label);
jlaskey@3 1559 jump(IFGE, label, 1);
jlaskey@3 1560 }
jlaskey@3 1561
jlaskey@3 1562 /**
jlaskey@3 1563 * Unconditional jump to a label
jlaskey@3 1564 *
jlaskey@3 1565 * @param label destination label
jlaskey@3 1566 */
jlaskey@3 1567 public void _goto(final Label label) {
jlaskey@3 1568 debug("goto", label);
jlaskey@3 1569 jump(GOTO, label, 0);
jlaskey@3 1570 stack = null;
jlaskey@3 1571 }
jlaskey@3 1572
jlaskey@3 1573 /**
jlaskey@3 1574 * Examine two stacks and make sure they are of the same size and their
jlaskey@3 1575 * contents are equivalent to each other
jlaskey@3 1576 * @param s0 first stack
jlaskey@3 1577 * @param s1 second stack
jlaskey@3 1578 *
jlaskey@3 1579 * @return true if stacks are equivalent, false otherwise
jlaskey@3 1580 */
jlaskey@3 1581 private boolean stacksEquivalent(final ArrayDeque<Type> s0, final ArrayDeque<Type> s1) {
jlaskey@3 1582 if (s0.size() != s1.size()) {
jlaskey@3 1583 debug("different stack sizes", s0, s1);
jlaskey@3 1584 return false;
jlaskey@3 1585 }
jlaskey@3 1586
jlaskey@3 1587 final Type[] s0a = s0.toArray(new Type[s0.size()]);
jlaskey@3 1588 final Type[] s1a = s1.toArray(new Type[s1.size()]);
jlaskey@3 1589 for (int i = 0; i < s0.size(); i++) {
jlaskey@3 1590 if (!s0a[i].isEquivalentTo(s1a[i])) {
jlaskey@3 1591 debug("different stack element", s0a[i], s1a[i]);
jlaskey@3 1592 return false;
jlaskey@3 1593 }
jlaskey@3 1594 }
jlaskey@3 1595
jlaskey@3 1596 return true;
jlaskey@3 1597 }
jlaskey@3 1598
jlaskey@3 1599 /**
jlaskey@3 1600 * A join in control flow - helper function that makes sure all entry stacks
jlaskey@3 1601 * discovered for the join point so far are equivalent
jlaskey@3 1602 * @param label
jlaskey@3 1603 */
jlaskey@3 1604 private void mergeStackTo(final Label label) {
jlaskey@3 1605 final ArrayDeque<Type> labelStack = label.getStack();
jlaskey@3 1606 //debug(labelStack == null ? " >> Control flow - first visit " + label : " >> Control flow - JOIN with " + labelStack + " at " + label);
jlaskey@3 1607 if (labelStack == null) {
jlaskey@3 1608 assert stack != null;
jlaskey@3 1609 label.setStack(stack.clone());
jlaskey@3 1610 return;
jlaskey@3 1611 }
lagergren@57 1612 assert stacksEquivalent(stack, labelStack) : "stacks " + stack + " is not equivalent with " + labelStack + " at join point";
jlaskey@3 1613 }
jlaskey@3 1614
jlaskey@3 1615 /**
jlaskey@3 1616 * Register a new label, enter it here.
jlaskey@3 1617 *
jlaskey@3 1618 * @param label the label
jlaskey@3 1619 */
jlaskey@3 1620 public void label(final Label label) {
jlaskey@3 1621 /*
jlaskey@3 1622 * If stack == null, this means that we came here not through a fallthrough.
jlaskey@3 1623 * E.g. a label after an athrow. Then we create a new stack if one doesn't exist
jlaskey@3 1624 * for this location already.
jlaskey@3 1625 */
jlaskey@3 1626 if (stack == null) {
jlaskey@3 1627 stack = label.getStack();
jlaskey@3 1628 if (stack == null) {
jlaskey@3 1629 stack = new ArrayDeque<>(); //we don't have a stack at this point.
jlaskey@3 1630 }
jlaskey@3 1631 }
jlaskey@3 1632 debug_label(label);
jlaskey@3 1633
jlaskey@3 1634 mergeStackTo(label); //we have to merge our stack to whatever is in the label
jlaskey@3 1635
jlaskey@3 1636 method.visitLabel(label);
jlaskey@3 1637 }
jlaskey@3 1638
jlaskey@3 1639 /**
jlaskey@3 1640 * Pop element from stack, convert to given type
jlaskey@3 1641 *
jlaskey@3 1642 * @param to type to convert to
jlaskey@3 1643 *
jlaskey@3 1644 * @return the method emitter
jlaskey@3 1645 */
jlaskey@3 1646 public MethodEmitter convert(final Type to) {
jlaskey@3 1647 final Type type = peekType().convert(method, to);
jlaskey@3 1648 if (type != null) {
jlaskey@3 1649 if (peekType() != to) {
jlaskey@3 1650 debug("convert", peekType(), "->", to);
jlaskey@3 1651 }
jlaskey@3 1652 popType();
jlaskey@3 1653 pushType(type);
jlaskey@3 1654 }
jlaskey@3 1655 return this;
jlaskey@3 1656 }
jlaskey@3 1657
jlaskey@3 1658 /**
jlaskey@3 1659 * Helper function - expect two types that are equivalent
jlaskey@3 1660 *
jlaskey@3 1661 * @return common type
jlaskey@3 1662 */
jlaskey@3 1663 private Type get2() {
jlaskey@3 1664 final Type p0 = popType();
jlaskey@3 1665 final Type p1 = popType();
jlaskey@3 1666 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1;
jlaskey@3 1667 return p0;
jlaskey@3 1668 }
jlaskey@3 1669
jlaskey@3 1670 /**
jlaskey@3 1671 * Helper function - expect two types that are integer types and equivalent
jlaskey@3 1672 *
jlaskey@3 1673 * @return common type
jlaskey@3 1674 */
jlaskey@3 1675 private BitwiseType get2i() {
jlaskey@3 1676 final BitwiseType p0 = popInteger();
jlaskey@3 1677 final BitwiseType p1 = popInteger();
jlaskey@3 1678 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1;
jlaskey@3 1679 return p0;
jlaskey@3 1680 }
jlaskey@3 1681
jlaskey@3 1682 /**
jlaskey@3 1683 * Helper function - expect two types that are numbers and equivalent
jlaskey@3 1684 *
jlaskey@3 1685 * @return common type
jlaskey@3 1686 */
jlaskey@3 1687 private NumericType get2n() {
jlaskey@3 1688 final NumericType p0 = popNumeric();
jlaskey@3 1689 final NumericType p1 = popNumeric();
jlaskey@3 1690 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1;
jlaskey@3 1691 return p0;
jlaskey@3 1692 }
jlaskey@3 1693
jlaskey@3 1694 /**
jlaskey@3 1695 * Pop two numbers, perform addition and push result
jlaskey@3 1696 *
jlaskey@3 1697 * @return the method emitter
jlaskey@3 1698 */
jlaskey@3 1699 public MethodEmitter add() {
jlaskey@3 1700 debug("add");
jlaskey@3 1701 pushType(get2().add(method));
jlaskey@3 1702 return this;
jlaskey@3 1703 }
jlaskey@3 1704
jlaskey@3 1705 /**
jlaskey@3 1706 * Pop two numbers, perform subtraction and push result
jlaskey@3 1707 *
jlaskey@3 1708 * @return the method emitter
jlaskey@3 1709 */
jlaskey@3 1710 public MethodEmitter sub() {
jlaskey@3 1711 debug("sub");
jlaskey@3 1712 pushType(get2n().sub(method));
jlaskey@3 1713 return this;
jlaskey@3 1714 }
jlaskey@3 1715
jlaskey@3 1716 /**
jlaskey@3 1717 * Pop two numbers, perform multiplication and push result
jlaskey@3 1718 *
jlaskey@3 1719 * @return the method emitter
jlaskey@3 1720 */
jlaskey@3 1721 public MethodEmitter mul() {
jlaskey@3 1722 debug("mul ");
jlaskey@3 1723 pushType(get2n().mul(method));
jlaskey@3 1724 return this;
jlaskey@3 1725 }
jlaskey@3 1726
jlaskey@3 1727 /**
jlaskey@3 1728 * Pop two numbers, perform division and push result
jlaskey@3 1729 *
jlaskey@3 1730 * @return the method emitter
jlaskey@3 1731 */
jlaskey@3 1732 public MethodEmitter div() {
jlaskey@3 1733 debug("div");
jlaskey@3 1734 pushType(get2n().div(method));
jlaskey@3 1735 return this;
jlaskey@3 1736 }
jlaskey@3 1737
jlaskey@3 1738 /**
jlaskey@3 1739 * Pop two numbers, calculate remainder and push result
jlaskey@3 1740 *
jlaskey@3 1741 * @return the method emitter
jlaskey@3 1742 */
jlaskey@3 1743 public MethodEmitter rem() {
jlaskey@3 1744 debug("rem");
jlaskey@3 1745 pushType(get2n().rem(method));
jlaskey@3 1746 return this;
jlaskey@3 1747 }
jlaskey@3 1748
jlaskey@3 1749 /**
jlaskey@3 1750 * Retrieve the top <tt>count</tt> types on the stack without modifying it.
jlaskey@3 1751 *
jlaskey@3 1752 * @param count number of types to return
jlaskey@3 1753 * @return array of Types
jlaskey@3 1754 */
jlaskey@3 1755 protected Type[] getTypesFromStack(final int count) {
jlaskey@3 1756 final Iterator<Type> iter = stack.iterator();
jlaskey@3 1757 final Type[] types = new Type[count];
jlaskey@3 1758
jlaskey@3 1759 for (int i = count - 1; i >= 0; i--) {
jlaskey@3 1760 types[i] = iter.next();
jlaskey@3 1761 }
jlaskey@3 1762
jlaskey@3 1763 return types;
jlaskey@3 1764 }
jlaskey@3 1765
jlaskey@3 1766 /**
jlaskey@3 1767 * Helper function to generate a function signature based on stack contents
jlaskey@3 1768 * and argument count and return type
jlaskey@3 1769 *
jlaskey@3 1770 * @param returnType return type
jlaskey@3 1771 * @param argCount argument count
jlaskey@3 1772 *
jlaskey@3 1773 * @return function signature for stack contents
jlaskey@3 1774 */
lagergren@66 1775 private String getDynamicSignature(final Type returnType, final int argCount) {
lagergren@66 1776 final Iterator<Type> iter = stack.iterator();
lagergren@66 1777 final Type[] paramTypes = new Type[argCount];
jlaskey@3 1778
jlaskey@3 1779 for (int i = argCount - 1; i >= 0; i--) {
lagergren@66 1780 paramTypes[i] = iter.next();
lagergren@66 1781 }
lagergren@66 1782 final String descriptor = Type.getMethodDescriptor(returnType, paramTypes);
lagergren@66 1783 for (int i = 0; i < argCount; i++) {
lagergren@66 1784 popType(paramTypes[argCount - i - 1]);
jlaskey@3 1785 }
jlaskey@3 1786
lagergren@66 1787 return descriptor;
jlaskey@3 1788 }
jlaskey@3 1789
jlaskey@3 1790 /**
jlaskey@3 1791 * Generate a dynamic new
jlaskey@3 1792 *
jlaskey@3 1793 * @param argCount number of arguments
jlaskey@3 1794 * @param flags callsite flags
jlaskey@3 1795 *
jlaskey@3 1796 * @return the method emitter
jlaskey@3 1797 */
jlaskey@3 1798 public MethodEmitter dynamicNew(final int argCount, final int flags) {
jlaskey@3 1799 debug("dynamic_new", "argcount=" + argCount);
lagergren@66 1800 final String signature = getDynamicSignature(Type.OBJECT, argCount);
jlaskey@3 1801 method.visitInvokeDynamicInsn("dyn:new", signature, LINKERBOOTSTRAP, flags);
jlaskey@3 1802 pushType(Type.OBJECT); //TODO fix result type
jlaskey@3 1803 return this;
jlaskey@3 1804 }
jlaskey@3 1805
jlaskey@3 1806 /**
jlaskey@3 1807 * Generate a dynamic call
jlaskey@3 1808 *
jlaskey@3 1809 * @param returnType return type
jlaskey@3 1810 * @param argCount number of arguments
jlaskey@3 1811 * @param flags callsite flags
jlaskey@3 1812 *
jlaskey@3 1813 * @return the method emitter
jlaskey@3 1814 */
jlaskey@3 1815 public MethodEmitter dynamicCall(final Type returnType, final int argCount, final int flags) {
jlaskey@3 1816 debug("dynamic_call", "args=" + argCount, "returnType=" + returnType);
lagergren@66 1817 final String signature = getDynamicSignature(returnType, argCount); // +1 because the function itself is the 1st parameter for dynamic calls (what you call - call target)
jlaskey@3 1818 debug(" signature", signature);
jlaskey@3 1819 method.visitInvokeDynamicInsn("dyn:call", signature, LINKERBOOTSTRAP, flags);
jlaskey@3 1820 pushType(returnType);
jlaskey@3 1821
jlaskey@3 1822 return this;
jlaskey@3 1823 }
jlaskey@3 1824
jlaskey@3 1825 /**
jlaskey@3 1826 * Generate a dynamic call for a runtime node
jlaskey@3 1827 *
jlaskey@3 1828 * @param name tag for the invoke dynamic for this runtime node
jlaskey@3 1829 * @param returnType return type
jlaskey@3 1830 * @param request RuntimeNode request
jlaskey@3 1831 *
jlaskey@3 1832 * @return the method emitter
jlaskey@3 1833 */
jlaskey@3 1834 public MethodEmitter dynamicRuntimeCall(final String name, final Type returnType, final RuntimeNode.Request request) {
jlaskey@3 1835 debug("dynamic_runtime_call", name, "args=" + request.getArity(), "returnType=" + returnType);
lagergren@66 1836 final String signature = getDynamicSignature(returnType, request.getArity());
jlaskey@3 1837 debug(" signature", signature);
jlaskey@3 1838 method.visitInvokeDynamicInsn(name, signature, RUNTIMEBOOTSTRAP);
jlaskey@3 1839 pushType(returnType);
jlaskey@3 1840
jlaskey@3 1841 return this;
jlaskey@3 1842 }
jlaskey@3 1843
jlaskey@3 1844 /**
jlaskey@3 1845 * Generate dynamic getter. Pop scope from stack. Push result
jlaskey@3 1846 *
jlaskey@3 1847 * @param valueType type of the value to set
jlaskey@3 1848 * @param name name of property
jlaskey@3 1849 * @param flags call site flags
jlaskey@3 1850 * @param isMethod should it prefer retrieving methods
jlaskey@3 1851 *
jlaskey@3 1852 * @return the method emitter
jlaskey@3 1853 */
jlaskey@3 1854 public MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod) {
jlaskey@3 1855 debug("dynamic_get", name, valueType);
jlaskey@3 1856
jlaskey@3 1857 Type type = valueType;
jlaskey@3 1858 if (type.isObject() || type.isBoolean()) {
jlaskey@3 1859 type = Type.OBJECT; //promote e.g strings to object generic setter
jlaskey@3 1860 }
jlaskey@3 1861
jlaskey@3 1862 popType(Type.SCOPE);
jlaskey@3 1863 method.visitInvokeDynamicInsn((isMethod ? "dyn:getMethod|getProp|getElem:" : "dyn:getProp|getElem|getMethod:") +
jlaskey@3 1864 NameCodec.encode(name), Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags);
jlaskey@3 1865
jlaskey@3 1866 pushType(type);
jlaskey@3 1867
jlaskey@3 1868 convert(valueType); //most probably a nop
jlaskey@3 1869
jlaskey@3 1870 return this;
jlaskey@3 1871 }
jlaskey@3 1872
jlaskey@3 1873 /**
jlaskey@3 1874 * Generate dynamic setter. Pop receiver and property from stack.
jlaskey@3 1875 *
jlaskey@3 1876 * @param valueType the type of the value to set
jlaskey@3 1877 * @param name name of property
jlaskey@3 1878 * @param flags call site flags
jlaskey@3 1879 */
jlaskey@3 1880 public void dynamicSet(final Type valueType, final String name, final int flags) {
jlaskey@3 1881 debug("dynamic_set", name, peekType());
jlaskey@3 1882
jlaskey@3 1883 Type type = valueType;
jlaskey@3 1884 if (type.isObject() || type.isBoolean()) { //promote strings to objects etc
jlaskey@3 1885 type = Type.OBJECT;
jlaskey@3 1886 convert(Type.OBJECT); //TODO bad- until we specialize boolean setters,
jlaskey@3 1887 }
jlaskey@3 1888
jlaskey@3 1889 popType(type);
jlaskey@3 1890 popType(Type.SCOPE);
jlaskey@3 1891
jlaskey@3 1892 method.visitInvokeDynamicInsn("dyn:setProp|setElem:" + NameCodec.encode(name), methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags);
jlaskey@3 1893 }
jlaskey@3 1894
jlaskey@3 1895 /**
jlaskey@3 1896 * Dynamic getter for indexed structures. Pop index and receiver from stack,
jlaskey@3 1897 * generate appropriate signatures based on types
jlaskey@3 1898 *
jlaskey@3 1899 * @param result result type for getter
jlaskey@3 1900 * @param flags call site flags for getter
jlaskey@3 1901 * @param isMethod should it prefer retrieving methods
jlaskey@3 1902 *
jlaskey@3 1903 * @return the method emitter
jlaskey@3 1904 */
jlaskey@3 1905 public MethodEmitter dynamicGetIndex(final Type result, final int flags, final boolean isMethod) {
jlaskey@3 1906 debug("dynamic_get_index", peekType(1) + "[" + peekType() + "]");
jlaskey@3 1907
jlaskey@3 1908 Type resultType = result;
jlaskey@3 1909 if (result.isBoolean()) {
jlaskey@3 1910 resultType = Type.OBJECT; // INT->OBJECT to avoid another dimension of cross products in the getters. TODO
jlaskey@3 1911 }
jlaskey@3 1912
jlaskey@3 1913 Type index = peekType();
jlaskey@3 1914 if (index.isObject() || index.isBoolean()) {
jlaskey@3 1915 index = Type.OBJECT; //e.g. string->object
jlaskey@3 1916 convert(Type.OBJECT);
jlaskey@3 1917 }
jlaskey@3 1918 popType();
jlaskey@3 1919
jlaskey@3 1920 popType(Type.OBJECT);
jlaskey@3 1921
jlaskey@3 1922 final String signature = Type.getMethodDescriptor(resultType, Type.OBJECT /*e.g STRING->OBJECT*/, index);
jlaskey@3 1923
jlaskey@3 1924 method.visitInvokeDynamicInsn(isMethod ? "dyn:getMethod|getElem|getProp" : "dyn:getElem|getProp|getMethod",
jlaskey@3 1925 signature, LINKERBOOTSTRAP, flags);
jlaskey@3 1926 pushType(resultType);
jlaskey@3 1927
jlaskey@3 1928 if (result.isBoolean()) {
jlaskey@3 1929 convert(Type.BOOLEAN);
jlaskey@3 1930 }
jlaskey@3 1931
jlaskey@3 1932 return this;
jlaskey@3 1933 }
jlaskey@3 1934
jlaskey@3 1935 /**
jlaskey@3 1936 * Dynamic setter for indexed structures. Pop value, index and receiver from
jlaskey@3 1937 * stack, generate appropriate signature based on types
jlaskey@3 1938 *
jlaskey@3 1939 * @param flags call site flags for setter
jlaskey@3 1940 */
jlaskey@3 1941 public void dynamicSetIndex(final int flags) {
jlaskey@3 1942 debug("dynamic_set_index", peekType(2) + "[" + peekType(1) + "] =", peekType());
jlaskey@3 1943
jlaskey@3 1944 Type value = peekType();
jlaskey@3 1945 if (value.isObject() || value.isBoolean()) {
jlaskey@3 1946 value = Type.OBJECT; //e.g. STRING->OBJECT - one descriptor for all object types
jlaskey@3 1947 convert(Type.OBJECT);
jlaskey@3 1948 }
jlaskey@3 1949 popType();
jlaskey@3 1950
jlaskey@3 1951 Type index = peekType();
jlaskey@3 1952 if (index.isObject() || index.isBoolean()) {
jlaskey@3 1953 index = Type.OBJECT; //e.g. string->object
jlaskey@3 1954 convert(Type.OBJECT);
jlaskey@3 1955 }
jlaskey@3 1956 popType(index);
jlaskey@3 1957
jlaskey@3 1958 final Type receiver = popType(Type.OBJECT);
jlaskey@3 1959 assert receiver.isObject();
jlaskey@3 1960
jlaskey@3 1961 method.visitInvokeDynamicInsn("dyn:setElem|setProp", methodDescriptor(void.class, receiver.getTypeClass(), index.getTypeClass(), value.getTypeClass()), LINKERBOOTSTRAP, flags);
jlaskey@3 1962 }
jlaskey@3 1963
jlaskey@3 1964 /**
jlaskey@3 1965 * Load a key value in the proper form.
jlaskey@3 1966 *
jlaskey@3 1967 * @param key
jlaskey@3 1968 */
jlaskey@3 1969 //TODO move this and break it apart
jlaskey@3 1970 MethodEmitter loadKey(final Object key) {
jlaskey@3 1971 if (key instanceof IdentNode) {
jlaskey@3 1972 method.visitLdcInsn(((IdentNode) key).getName());
jlaskey@3 1973 } else if (key instanceof LiteralNode) {
jlaskey@3 1974 method.visitLdcInsn(((LiteralNode<?>)key).getString());
jlaskey@3 1975 } else {
jlaskey@3 1976 method.visitLdcInsn(JSType.toString(key));
jlaskey@3 1977 }
jlaskey@3 1978 pushType(Type.OBJECT); //STRING
jlaskey@3 1979 return this;
jlaskey@3 1980 }
jlaskey@3 1981
jlaskey@3 1982 @SuppressWarnings("fallthrough")
jlaskey@3 1983 private static Type fieldType(final String desc) {
jlaskey@3 1984 switch (desc) {
jlaskey@3 1985 case "Z":
jlaskey@3 1986 case "B":
jlaskey@3 1987 case "C":
jlaskey@3 1988 case "S":
jlaskey@3 1989 case "I":
jlaskey@3 1990 return Type.INT;
jlaskey@3 1991 case "F":
jlaskey@3 1992 assert false;
jlaskey@3 1993 case "D":
jlaskey@3 1994 return Type.NUMBER;
jlaskey@3 1995 case "J":
jlaskey@3 1996 return Type.LONG;
jlaskey@3 1997 default:
jlaskey@3 1998 assert desc.startsWith("[") || desc.startsWith("L") : desc + " is not an object type";
jlaskey@3 1999 switch (desc.charAt(0)) {
jlaskey@3 2000 case 'L':
jlaskey@3 2001 return Type.OBJECT;
jlaskey@3 2002 case '[':
jlaskey@3 2003 return Type.typeFor(Array.newInstance(fieldType(desc.substring(1)).getTypeClass(), 0).getClass());
jlaskey@3 2004 default:
jlaskey@3 2005 assert false;
jlaskey@3 2006 }
jlaskey@3 2007 return Type.OBJECT;
jlaskey@3 2008 }
jlaskey@3 2009 }
jlaskey@3 2010
jlaskey@3 2011 /**
jlaskey@3 2012 * Generate get for a field access
jlaskey@3 2013 *
jlaskey@3 2014 * @param fa the field access
jlaskey@3 2015 *
jlaskey@3 2016 * @return the method emitter
jlaskey@3 2017 */
jlaskey@3 2018 public MethodEmitter getField(final FieldAccess fa) {
jlaskey@3 2019 return fa.get(this);
jlaskey@3 2020 }
jlaskey@3 2021
jlaskey@3 2022 /**
jlaskey@3 2023 * Generate set for a field access
jlaskey@3 2024 *
jlaskey@3 2025 * @param fa the field access
jlaskey@3 2026 */
jlaskey@3 2027 public void putField(final FieldAccess fa) {
jlaskey@3 2028 fa.put(this);
jlaskey@3 2029 }
jlaskey@3 2030
jlaskey@3 2031 /**
jlaskey@3 2032 * Get the value of a non-static field, pop the receiver from the stack,
jlaskey@3 2033 * push value to the stack
jlaskey@3 2034 *
jlaskey@3 2035 * @param className class
jlaskey@3 2036 * @param fieldName field name
jlaskey@3 2037 * @param fieldDescriptor field descriptor
jlaskey@3 2038 *
jlaskey@3 2039 * @return the method emitter
jlaskey@3 2040 */
jlaskey@3 2041 public MethodEmitter getField(final String className, final String fieldName, final String fieldDescriptor) {
jlaskey@3 2042 debug("getfield", "receiver=" + peekType(), className + "." + fieldName + fieldDescriptor);
jlaskey@3 2043 final Type receiver = popType();
jlaskey@3 2044 assert receiver.isObject();
jlaskey@3 2045 method.visitFieldInsn(GETFIELD, className, fieldName, fieldDescriptor);
jlaskey@3 2046 pushType(fieldType(fieldDescriptor));
jlaskey@3 2047 return this;
jlaskey@3 2048 }
jlaskey@3 2049
jlaskey@3 2050 /**
jlaskey@3 2051 * Get the value of a static field, push it to the stack
jlaskey@3 2052 *
jlaskey@3 2053 * @param className class
jlaskey@3 2054 * @param fieldName field name
jlaskey@3 2055 * @param fieldDescriptor field descriptor
jlaskey@3 2056 *
jlaskey@3 2057 * @return the method emitter
jlaskey@3 2058 */
jlaskey@3 2059 public MethodEmitter getStatic(final String className, final String fieldName, final String fieldDescriptor) {
jlaskey@3 2060 debug("getstatic", className + "." + fieldName + "." + fieldDescriptor);
jlaskey@3 2061 method.visitFieldInsn(GETSTATIC, className, fieldName, fieldDescriptor);
jlaskey@3 2062 pushType(fieldType(fieldDescriptor));
jlaskey@3 2063 return this;
jlaskey@3 2064 }
jlaskey@3 2065
jlaskey@3 2066 /**
jlaskey@3 2067 * Pop value and field from stack and write to a non-static field
jlaskey@3 2068 *
jlaskey@3 2069 * @param className class
jlaskey@3 2070 * @param fieldName field name
jlaskey@3 2071 * @param fieldDescriptor field descriptor
jlaskey@3 2072 */
jlaskey@3 2073 public void putField(final String className, final String fieldName, final String fieldDescriptor) {
jlaskey@3 2074 debug("putfield", "receiver=" + peekType(1), "value=" + peekType());
jlaskey@3 2075 popType(fieldType(fieldDescriptor));
jlaskey@3 2076 popType(Type.OBJECT);
jlaskey@3 2077 method.visitFieldInsn(PUTFIELD, className, fieldName, fieldDescriptor);
jlaskey@3 2078 }
jlaskey@3 2079
jlaskey@3 2080 /**
jlaskey@3 2081 * Pop value from stack and write to a static field
jlaskey@3 2082 *
jlaskey@3 2083 * @param className class
jlaskey@3 2084 * @param fieldName field name
jlaskey@3 2085 * @param fieldDescriptor field descriptor
jlaskey@3 2086 */
jlaskey@3 2087 public void putStatic(final String className, final String fieldName, final String fieldDescriptor) {
jlaskey@3 2088 debug("putfield", "value=" + peekType());
jlaskey@3 2089 popType(fieldType(fieldDescriptor));
jlaskey@3 2090 method.visitFieldInsn(PUTSTATIC, className, fieldName, fieldDescriptor);
jlaskey@3 2091 }
jlaskey@3 2092
jlaskey@3 2093 /**
jlaskey@3 2094 * Register line number at a label
jlaskey@3 2095 *
jlaskey@3 2096 * @param line line number
jlaskey@3 2097 * @param label label
jlaskey@3 2098 */
jlaskey@3 2099 public void lineNumber(final int line, final Label label) {
jlaskey@3 2100 method.visitLineNumber(line, label);
jlaskey@3 2101 }
jlaskey@3 2102
jlaskey@3 2103 /*
jlaskey@3 2104 * Debugging below
jlaskey@3 2105 */
jlaskey@3 2106
jlaskey@3 2107 private final FieldAccess ERR_STREAM = staticField(System.class, "err", PrintStream.class);
jlaskey@3 2108 private final Call PRINT = virtualCallNoLookup(PrintStream.class, "print", void.class, Object.class);
jlaskey@3 2109 private final Call PRINTLN = virtualCallNoLookup(PrintStream.class, "println", void.class, Object.class);
jlaskey@3 2110 private final Call PRINT_STACKTRACE = virtualCallNoLookup(Throwable.class, "printStackTrace", void.class);
jlaskey@3 2111
jlaskey@3 2112 /**
jlaskey@3 2113 * Emit a System.err.print statement of whatever is on top of the bytecode stack
jlaskey@3 2114 */
jlaskey@3 2115 public void print() {
jlaskey@3 2116 getField(ERR_STREAM);
jlaskey@3 2117 swap();
jlaskey@3 2118 convert(Type.OBJECT);
jlaskey@3 2119 invoke(PRINT);
jlaskey@3 2120 }
jlaskey@3 2121
jlaskey@3 2122 /**
jlaskey@3 2123 * Emit a System.err.println statement of whatever is on top of the bytecode stack
jlaskey@3 2124 */
jlaskey@3 2125 public void println() {
jlaskey@3 2126 getField(ERR_STREAM);
jlaskey@3 2127 swap();
jlaskey@3 2128 convert(Type.OBJECT);
jlaskey@3 2129 invoke(PRINTLN);
jlaskey@3 2130 }
jlaskey@3 2131
jlaskey@3 2132 /**
jlaskey@3 2133 * Emit a System.err.print statement
jlaskey@3 2134 * @param string string to print
jlaskey@3 2135 */
jlaskey@3 2136 public void print(final String string) {
jlaskey@3 2137 getField(ERR_STREAM);
jlaskey@3 2138 load(string);
jlaskey@3 2139 invoke(PRINT);
jlaskey@3 2140 }
jlaskey@3 2141
jlaskey@3 2142 /**
jlaskey@3 2143 * Emit a System.err.println statement
jlaskey@3 2144 * @param string string to print
jlaskey@3 2145 */
jlaskey@3 2146 public void println(final String string) {
jlaskey@3 2147 getField(ERR_STREAM);
jlaskey@3 2148 load(string);
jlaskey@3 2149 invoke(PRINTLN);
jlaskey@3 2150 }
jlaskey@3 2151
jlaskey@3 2152 /**
jlaskey@3 2153 * Print a stacktrace to S
jlaskey@3 2154 */
jlaskey@3 2155 public void stacktrace() {
jlaskey@3 2156 _new(Throwable.class);
jlaskey@3 2157 dup();
jlaskey@3 2158 invoke(constructorNoLookup(Throwable.class));
jlaskey@3 2159 invoke(PRINT_STACKTRACE);
jlaskey@3 2160 }
jlaskey@3 2161
jlaskey@3 2162 private static int linePrefix = 0;
jlaskey@3 2163
jlaskey@3 2164 /**
jlaskey@3 2165 * Debug function that outputs generated bytecode and stack contents
jlaskey@3 2166 *
jlaskey@3 2167 * @param args debug information to print
jlaskey@3 2168 */
jlaskey@3 2169 private void debug(final Object... args) {
jlaskey@3 2170 debug(30, args);
jlaskey@3 2171 }
jlaskey@3 2172
jlaskey@3 2173 /**
jlaskey@3 2174 * Debug function that outputs generated bytecode and stack contents
jlaskey@3 2175 * for a label - indentation is currently the only thing that differs
jlaskey@3 2176 *
jlaskey@3 2177 * @param args debug information to print
jlaskey@3 2178 */
jlaskey@3 2179 private void debug_label(final Object... args) {
jlaskey@3 2180 debug(26, args);
jlaskey@3 2181 }
jlaskey@3 2182
jlaskey@3 2183 private void debug(final int padConstant, final Object... args) {
jlaskey@3 2184 if (DEBUG) {
jlaskey@3 2185 final StringBuilder sb = new StringBuilder();
jlaskey@3 2186 int pad;
jlaskey@3 2187
jlaskey@3 2188 sb.append('#');
jlaskey@3 2189 sb.append(++linePrefix);
jlaskey@3 2190
jlaskey@3 2191 pad = 5 - sb.length();
jlaskey@3 2192 while (pad > 0) {
jlaskey@3 2193 sb.append(' ');
jlaskey@3 2194 pad--;
jlaskey@3 2195 }
jlaskey@3 2196
jlaskey@3 2197 if (!stack.isEmpty()) {
jlaskey@3 2198 sb.append("{");
jlaskey@3 2199 sb.append(stack.size());
jlaskey@3 2200 sb.append(":");
jlaskey@3 2201 for (final Iterator<Type> iter = stack.iterator(); iter.hasNext();) {
jlaskey@3 2202 final Type t = iter.next();
jlaskey@3 2203
jlaskey@3 2204 if (t == Type.SCOPE) {
jlaskey@3 2205 sb.append("scope");
jlaskey@3 2206 } else if (t == Type.THIS) {
jlaskey@3 2207 sb.append("this");
jlaskey@3 2208 } else if (t.isObject()) {
jlaskey@3 2209 String desc = t.getDescriptor();
lagergren@66 2210 int i;
lagergren@66 2211 for (i = 0; desc.charAt(i) == '[' && i < desc.length(); i++) {
lagergren@66 2212 sb.append('[');
lagergren@66 2213 }
lagergren@66 2214 desc = desc.substring(i);
jlaskey@3 2215 final int slash = desc.lastIndexOf('/');
jlaskey@3 2216 if (slash != -1) {
jlaskey@3 2217 desc = desc.substring(slash + 1, desc.length() - 1);
jlaskey@3 2218 }
jlaskey@3 2219 if ("Object".equals(desc)) {
jlaskey@3 2220 sb.append('O');
jlaskey@3 2221 } else {
jlaskey@3 2222 sb.append(desc);
jlaskey@3 2223 }
jlaskey@3 2224 } else {
jlaskey@3 2225 sb.append(t.getDescriptor());
jlaskey@3 2226 }
jlaskey@3 2227
jlaskey@3 2228 if (iter.hasNext()) {
jlaskey@3 2229 sb.append(' ');
jlaskey@3 2230 }
jlaskey@3 2231 }
jlaskey@3 2232 sb.append('}');
jlaskey@3 2233 sb.append(' ');
jlaskey@3 2234 }
jlaskey@3 2235
jlaskey@3 2236 pad = padConstant - sb.length();
jlaskey@3 2237 while (pad > 0) {
jlaskey@3 2238 sb.append(' ');
jlaskey@3 2239 pad--;
jlaskey@3 2240 }
jlaskey@3 2241
jlaskey@3 2242 for (final Object arg : args) {
jlaskey@3 2243 sb.append(arg);
jlaskey@3 2244 sb.append(' ');
jlaskey@3 2245 }
jlaskey@3 2246
jlaskey@3 2247 if (context != null) { //early bootstrap code doesn't have inited context yet
jlaskey@3 2248 LOG.info(sb.toString());
jlaskey@3 2249 if (DEBUG_TRACE_LINE == linePrefix) {
jlaskey@3 2250 new Throwable().printStackTrace(LOG.getOutputStream());
jlaskey@3 2251 }
jlaskey@3 2252 }
jlaskey@3 2253
jlaskey@3 2254 }
jlaskey@3 2255 }
jlaskey@3 2256
jlaskey@3 2257
jlaskey@3 2258 /**
jlaskey@3 2259 * Abstraction for labels, separating a label from the underlying
jlaskey@3 2260 * byte code emitter. Also augmenting label with e.g. a name
jlaskey@3 2261 * for easier debugging and reading code
jlaskey@3 2262 *
jlaskey@3 2263 * see -Dnashorn.codegen.debug, --log=codegen
jlaskey@3 2264 */
jlaskey@3 2265 public static class Label extends jdk.internal.org.objectweb.asm.Label {
jlaskey@3 2266 /** Name of this label */
jlaskey@3 2267 private final String name;
jlaskey@3 2268
jlaskey@3 2269 /** Type stack at this label */
jlaskey@3 2270 private ArrayDeque<Type> stack;
jlaskey@3 2271
jlaskey@3 2272 /**
jlaskey@3 2273 * Constructor
jlaskey@3 2274 *
jlaskey@3 2275 * @param name name of this label
jlaskey@3 2276 */
jlaskey@3 2277 public Label(final String name) {
jlaskey@3 2278 super();
jlaskey@3 2279 this.name = name;
jlaskey@3 2280 }
jlaskey@3 2281
jlaskey@3 2282 /**
jlaskey@3 2283 * Copy constructor
jlaskey@3 2284 *
jlaskey@3 2285 * @param label a label to clone
jlaskey@3 2286 */
jlaskey@3 2287 public Label(final Label label) {
jlaskey@3 2288 super();
jlaskey@3 2289 name = label.name;
jlaskey@3 2290 }
jlaskey@3 2291
jlaskey@3 2292 ArrayDeque<Type> getStack() {
jlaskey@3 2293 return stack;
jlaskey@3 2294 }
jlaskey@3 2295
jlaskey@3 2296 void setStack(final ArrayDeque<Type> stack) {
jlaskey@3 2297 this.stack = stack;
jlaskey@3 2298 }
jlaskey@3 2299
jlaskey@3 2300 @Override
jlaskey@3 2301 public String toString() {
jlaskey@3 2302 final StringBuilder sb = new StringBuilder();
jlaskey@3 2303 String s = super.toString();
jlaskey@3 2304 s = s.substring(1, s.length());
jlaskey@3 2305 sb.append(name).append('_').append(Long.toHexString(Long.parseLong(s)));
jlaskey@3 2306
jlaskey@3 2307 return sb.toString();
jlaskey@3 2308 }
jlaskey@3 2309 }
jlaskey@3 2310
jlaskey@3 2311 /**
jlaskey@3 2312 * Condition enum used for all kinds of jumps, regardless of type
jlaskey@3 2313 */
jlaskey@3 2314 static enum Condition {
jlaskey@3 2315 EQ,
jlaskey@3 2316 NE,
jlaskey@3 2317 LE,
jlaskey@3 2318 LT,
jlaskey@3 2319 GE,
jlaskey@3 2320 GT;
jlaskey@3 2321
jlaskey@3 2322 public static Condition forRuntimeRequest(final RuntimeNode.Request request) {
jlaskey@3 2323 try {
jlaskey@3 2324 final String reqString = request.toString().replace("_STRICT", "");
jlaskey@3 2325 return Condition.valueOf(reqString);
jlaskey@3 2326 } catch (final IllegalArgumentException e) {
jlaskey@3 2327 return null;
jlaskey@3 2328 }
jlaskey@3 2329 }
jlaskey@3 2330
jlaskey@3 2331 public static int toUnary(final Condition c) {
jlaskey@3 2332 switch (c) {
jlaskey@3 2333 case EQ:
jlaskey@3 2334 return IFEQ;
jlaskey@3 2335 case NE:
jlaskey@3 2336 return IFNE;
jlaskey@3 2337 case LE:
jlaskey@3 2338 return IFLE;
jlaskey@3 2339 case LT:
jlaskey@3 2340 return IFLT;
jlaskey@3 2341 case GE:
jlaskey@3 2342 return IFGE;
jlaskey@3 2343 case GT:
jlaskey@3 2344 return IFGT;
jlaskey@3 2345 default:
jlaskey@3 2346 assert false;
jlaskey@3 2347 return -1;
jlaskey@3 2348 }
jlaskey@3 2349 }
jlaskey@3 2350
jlaskey@3 2351 public static int toBinary(final Condition c) {
jlaskey@3 2352 return toBinary(c, false);
jlaskey@3 2353 }
jlaskey@3 2354
jlaskey@3 2355 public static int toBinary(final Condition c, final boolean isObject) {
jlaskey@3 2356 switch (c) {
jlaskey@3 2357 case EQ:
jlaskey@3 2358 return isObject ? IF_ACMPEQ : IF_ICMPEQ;
jlaskey@3 2359 case NE:
jlaskey@3 2360 return isObject ? IF_ACMPNE : IF_ICMPNE;
jlaskey@3 2361 case LE:
jlaskey@3 2362 return IF_ICMPLE;
jlaskey@3 2363 case LT:
jlaskey@3 2364 return IF_ICMPLT;
jlaskey@3 2365 case GE:
jlaskey@3 2366 return IF_ICMPGE;
jlaskey@3 2367 case GT:
jlaskey@3 2368 return IF_ICMPGT;
jlaskey@3 2369 default:
jlaskey@3 2370 assert false;
jlaskey@3 2371 return -1;
jlaskey@3 2372 }
jlaskey@3 2373 }
jlaskey@3 2374 }
jlaskey@3 2375
jlaskey@3 2376 /**
jlaskey@3 2377 * Set the current function node being emitted
jlaskey@3 2378 * @param functionNode the function node
jlaskey@3 2379 */
jlaskey@3 2380 public void setFunctionNode(final FunctionNode functionNode) {
jlaskey@3 2381 this.functionNode = functionNode;
jlaskey@3 2382 }
jlaskey@3 2383
jlaskey@3 2384 /**
jlaskey@3 2385 * Get the split node for this method emitter, if this is code
jlaskey@3 2386 * generation due to splitting large methods
jlaskey@3 2387 *
jlaskey@3 2388 * @return split node
jlaskey@3 2389 */
jlaskey@3 2390 public SplitNode getSplitNode() {
jlaskey@3 2391 return splitNode;
jlaskey@3 2392 }
jlaskey@3 2393
jlaskey@3 2394 /**
jlaskey@3 2395 * Set the split node for this method emitter
jlaskey@3 2396 * @param splitNode split node
jlaskey@3 2397 */
jlaskey@3 2398 public void setSplitNode(final SplitNode splitNode) {
jlaskey@3 2399 this.splitNode = splitNode;
jlaskey@3 2400 }
jlaskey@3 2401 }

mercurial