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

Mon, 04 Feb 2013 15:59:44 +0100

author
attila
date
Mon, 04 Feb 2013 15:59:44 +0100
changeset 65
bb86bf840f9f
parent 62
f7825c1a11d3
child 66
bee7c8a45a04
permissions
-rw-r--r--

8007460: var assignment to a parameter in a varargs method causes compilation error
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@65 834 pushType(symbol.getSymbolType().load(method, slot));
attila@65 835 } else if (symbol.isParam()) {
attila@65 836 assert !symbol.isScope();
attila@65 837 assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
attila@65 838 final int index = symbol.getFieldIndex();
attila@65 839 if(functionNode.needsArguments()) {
attila@65 840 // ScriptObject.getArgument(int) on arguments
attila@65 841 debug("load symbol", symbol.getName(), " arguments index=", index);
attila@65 842 loadArguments();
attila@65 843 load(index);
attila@65 844 ScriptObject.GET_ARGUMENT.invoke(this);
attila@65 845 } else {
attila@65 846 // array load from __varargs__
attila@65 847 debug("load symbol", symbol.getName(), " array index=", index);
attila@65 848 loadVarArgs();
attila@65 849 load(symbol.getFieldIndex());
attila@65 850 arrayload();
attila@65 851 }
attila@62 852 }
attila@62 853 return this;
attila@62 854 }
attila@62 855
attila@62 856 /**
jlaskey@3 857 * Push a local variable to the stack, given an explicit bytecode slot
jlaskey@3 858 * This is used e.g. for stub generation where we know where items like
jlaskey@3 859 * "this" and "scope" reside.
jlaskey@3 860 *
jlaskey@3 861 * @param type the type of the variable
jlaskey@3 862 * @param slot the slot the variable is in
jlaskey@3 863 *
jlaskey@3 864 * @return the method emitter
jlaskey@3 865 */
jlaskey@3 866 public MethodEmitter load(final Type type, final int slot) {
jlaskey@3 867 debug("explicit load", type, slot);
jlaskey@3 868 pushType(type.load(method, slot));
jlaskey@3 869 return this;
jlaskey@3 870 }
jlaskey@3 871
jlaskey@3 872 /**
jlaskey@3 873 * Push the this object to the stack.
jlaskey@3 874 *
jlaskey@3 875 * @return the method emitter
jlaskey@3 876 */
jlaskey@3 877 public MethodEmitter loadThis() {
jlaskey@3 878 load(functionNode.getThisNode().getSymbol());
jlaskey@3 879 return this;
jlaskey@3 880 }
jlaskey@3 881
jlaskey@3 882 /**
jlaskey@3 883 * Push the scope object to the stack.
jlaskey@3 884 *
jlaskey@3 885 * @return the method emitter
jlaskey@3 886 */
jlaskey@3 887 public MethodEmitter loadScope() {
jlaskey@3 888 if (peekType() == Type.SCOPE) {
jlaskey@3 889 dup();
jlaskey@3 890 return this;
jlaskey@3 891 }
jlaskey@3 892 load(functionNode.getScopeNode().getSymbol());
jlaskey@3 893 return this;
jlaskey@3 894 }
jlaskey@3 895
jlaskey@3 896 /**
jlaskey@3 897 * Push the return object to the stack.
jlaskey@3 898 *
jlaskey@3 899 * @return the method emitter
jlaskey@3 900 */
jlaskey@3 901 public MethodEmitter loadResult() {
jlaskey@3 902 load(functionNode.getResultNode().getSymbol());
jlaskey@3 903 return this;
jlaskey@3 904 }
jlaskey@3 905
jlaskey@3 906
jlaskey@3 907 /**
jlaskey@3 908 * Push a method handle to the stack
jlaskey@3 909 *
jlaskey@3 910 * @param className class name
jlaskey@3 911 * @param methodName method name
jlaskey@3 912 * @param descName descriptor
jlaskey@3 913 * @param flags flags that describe this handle, e.g. invokespecial new, or invoke virtual
jlaskey@3 914 *
jlaskey@3 915 * @see ClassEmitter.Flag
jlaskey@3 916 *
jlaskey@3 917 * @return the method emitter
jlaskey@3 918 */
jlaskey@3 919 public MethodEmitter loadHandle(final String className, final String methodName, final String descName, final EnumSet<Flag> flags) {
jlaskey@3 920 debug("load handle ");
jlaskey@3 921 pushType(Type.OBJECT.ldc(method, new Handle(Flag.getValue(flags), className, methodName, descName)));
jlaskey@3 922 return this;
jlaskey@3 923 }
jlaskey@3 924
jlaskey@3 925 /**
jlaskey@3 926 * Push the varargs object to the stack
jlaskey@3 927 *
jlaskey@3 928 * @return the method emitter
jlaskey@3 929 */
jlaskey@3 930 public MethodEmitter loadVarArgs() {
jlaskey@3 931 debug("load var args " + functionNode.getVarArgsNode().getSymbol());
jlaskey@3 932 return load(functionNode.getVarArgsNode().getSymbol());
jlaskey@3 933 }
jlaskey@3 934
jlaskey@3 935 /**
jlaskey@3 936 * Push the arguments array to the stack
jlaskey@3 937 *
jlaskey@3 938 * @return the method emitter
jlaskey@3 939 */
jlaskey@3 940 public MethodEmitter loadArguments() {
attila@62 941 debug("load arguments ", functionNode.getArgumentsNode().getSymbol());
jlaskey@3 942 assert functionNode.getArgumentsNode().getSymbol().getSlot() != 0;
jlaskey@3 943 return load(functionNode.getArgumentsNode().getSymbol());
jlaskey@3 944 }
jlaskey@3 945
jlaskey@3 946 /**
jlaskey@3 947 * Push the callee object to the stack
jlaskey@3 948 *
jlaskey@3 949 * @return the method emitter
jlaskey@3 950 */
jlaskey@3 951 public MethodEmitter loadCallee() {
jlaskey@3 952 debug("load callee " + functionNode.getCalleeNode().getSymbol());
lagergren@57 953 assert functionNode.getCalleeNode().getSymbol().getSlot() != 0 : "callee has wrong slot " + functionNode.getCalleeNode().getSymbol().getSlot() + " in " + functionNode.getName();
jlaskey@3 954
jlaskey@3 955 return load(functionNode.getCalleeNode().getSymbol());
jlaskey@3 956 }
jlaskey@3 957
jlaskey@3 958 /**
jlaskey@3 959 * Pop the scope from the stack and store it in its predefined slot
jlaskey@3 960 */
jlaskey@3 961 public void storeScope() {
jlaskey@3 962 debug("store scope");
jlaskey@3 963 store(functionNode.getScopeNode().getSymbol());
jlaskey@3 964 }
jlaskey@3 965
jlaskey@3 966 /**
jlaskey@3 967 * Pop the return from the stack and store it in its predefined slot
jlaskey@3 968 */
jlaskey@3 969 public void storeResult() {
jlaskey@3 970 debug("store result");
jlaskey@3 971 store(functionNode.getResultNode().getSymbol());
jlaskey@3 972 }
jlaskey@3 973
jlaskey@3 974 /**
jlaskey@3 975 * Pop the arguments array from the stack and store it in its predefined slot
jlaskey@3 976 */
jlaskey@3 977 public void storeArguments() {
jlaskey@3 978 debug("store arguments");
jlaskey@3 979 store(functionNode.getArgumentsNode().getSymbol());
jlaskey@3 980 }
jlaskey@3 981
jlaskey@3 982 /**
jlaskey@3 983 * Load an element from an array, determining type automatically
jlaskey@3 984 * @return the method emitter
jlaskey@3 985 */
jlaskey@3 986 public MethodEmitter arrayload() {
jlaskey@3 987 debug("Xaload");
jlaskey@3 988 popType(Type.INT);
jlaskey@3 989 pushType(popArray().aload(method));
jlaskey@3 990 return this;
jlaskey@3 991 }
jlaskey@3 992
jlaskey@3 993 /**
jlaskey@3 994 * Pop a value, an index and an array from the stack and store
jlaskey@3 995 * the value at the given index in the array.
jlaskey@3 996 */
jlaskey@3 997 public void arraystore() {
jlaskey@3 998 debug("Xastore");
jlaskey@3 999 final Type value = popType();
jlaskey@3 1000 final Type index = popType(Type.INT);
jlaskey@3 1001 assert index.isInteger() : "array index is not integer, but " + index;
jlaskey@3 1002 final ArrayType array = popArray();
jlaskey@3 1003
jlaskey@3 1004 assert value.isEquivalentTo(array.getElementType()) : "Storing "+value+" into "+array;
jlaskey@3 1005 assert array.isObject();
jlaskey@3 1006 array.astore(method);
jlaskey@3 1007 }
jlaskey@3 1008
jlaskey@3 1009 /**
jlaskey@3 1010 * Pop a value from the stack and store it in a local variable represented
jlaskey@3 1011 * by the given symbol. If the symbol has no slot, this is a NOP
jlaskey@3 1012 *
jlaskey@3 1013 * @param symbol symbol to store stack to
jlaskey@3 1014 */
jlaskey@3 1015 public void store(final Symbol symbol) {
lagergren@57 1016 assert symbol != null : "No symbol to store";
jlaskey@3 1017 if (symbol.hasSlot()) {
attila@65 1018 final int slot = symbol.getSlot();
attila@65 1019 debug("store symbol", symbol.getName(), " slot=", slot);
attila@65 1020 popType(symbol.getSymbolType()).store(method, slot);
attila@65 1021 } else if (symbol.isParam()) {
attila@65 1022 assert !symbol.isScope();
attila@65 1023 assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
attila@65 1024 final int index = symbol.getFieldIndex();
attila@65 1025 if(functionNode.needsArguments()) {
attila@65 1026 debug("store symbol", symbol.getName(), " arguments index=", index);
attila@65 1027 loadArguments();
attila@65 1028 load(index);
attila@65 1029 ArgumentSetter.SET_ARGUMENT.invoke(this);
attila@65 1030 } else {
attila@65 1031 // varargs without arguments object - just do array store to __varargs__
attila@65 1032 debug("store symbol", symbol.getName(), " array index=", index);
attila@65 1033 loadVarArgs();
attila@65 1034 load(index);
attila@65 1035 ArgumentSetter.SET_ARRAY_ELEMENT.invoke(this);
attila@65 1036 }
jlaskey@3 1037 }
jlaskey@3 1038 }
jlaskey@3 1039
jlaskey@3 1040 /**
jlaskey@3 1041 * Pop a value from the stack and store it in a given local variable
jlaskey@3 1042 * slot.
jlaskey@3 1043 *
jlaskey@3 1044 * @param type the type to pop
jlaskey@3 1045 * @param slot the slot
jlaskey@3 1046 */
jlaskey@3 1047 public void store(final Type type, final int slot) {
jlaskey@3 1048 popType(type);
jlaskey@3 1049 type.store(method, slot);
jlaskey@3 1050 }
jlaskey@3 1051
jlaskey@3 1052 /**
jlaskey@3 1053 * Increment/Decrement a local integer by the given value.
jlaskey@3 1054 *
jlaskey@3 1055 * @param slot the int slot
jlaskey@3 1056 * @param increment the amount to increment
jlaskey@3 1057 */
jlaskey@3 1058 public void iinc(final int slot, final int increment) {
jlaskey@3 1059 debug("iinc");
jlaskey@3 1060 method.visitIincInsn(slot, increment);
jlaskey@3 1061 }
jlaskey@3 1062
jlaskey@3 1063 /**
jlaskey@3 1064 * Pop an exception object from the stack and generate code
jlaskey@3 1065 * for throwing it
jlaskey@3 1066 */
jlaskey@3 1067 public void athrow() {
jlaskey@3 1068 debug("athrow");
jlaskey@3 1069 final Type receiver = popType(Type.OBJECT);
jlaskey@3 1070 assert receiver.isObject();
jlaskey@3 1071 method.visitInsn(ATHROW);
jlaskey@3 1072 stack = null;
jlaskey@3 1073 }
jlaskey@3 1074
jlaskey@3 1075 /**
jlaskey@3 1076 * Pop an object from the stack and perform an instanceof
jlaskey@3 1077 * operation, given a classDescriptor to compare it to.
jlaskey@3 1078 * Push the boolean result 1/0 as an int to the stack
jlaskey@3 1079 *
jlaskey@3 1080 * @param classDescriptor descriptor of the class to type check against
jlaskey@3 1081 *
jlaskey@3 1082 * @return the method emitter
jlaskey@3 1083 */
jlaskey@3 1084 public MethodEmitter _instanceof(final String classDescriptor) {
jlaskey@3 1085 debug("instanceof", classDescriptor);
jlaskey@3 1086 popType(Type.OBJECT);
jlaskey@3 1087 method.visitTypeInsn(INSTANCEOF, classDescriptor);
jlaskey@3 1088 pushType(Type.INT);
jlaskey@3 1089 return this;
jlaskey@3 1090 }
jlaskey@3 1091
jlaskey@3 1092 /**
jlaskey@3 1093 * Pop an object from the stack and perform an instanceof
jlaskey@3 1094 * operation, given a classDescriptor to compare it to.
jlaskey@3 1095 * Push the boolean result 1/0 as an int to the stack
jlaskey@3 1096 *
jlaskey@3 1097 * @param clazz the type to check instanceof against
jlaskey@3 1098 *
jlaskey@3 1099 * @return the method emitter
jlaskey@3 1100 */
jlaskey@3 1101 public MethodEmitter _instanceof(final Class<?> clazz) {
jlaskey@3 1102 return _instanceof(CompilerConstants.className(clazz));
jlaskey@3 1103 }
jlaskey@3 1104
jlaskey@3 1105 /**
jlaskey@3 1106 * Perform a checkcast operation on the object at the top of the
jlaskey@3 1107 * stack.
jlaskey@3 1108 *
jlaskey@3 1109 * @param classDescriptor descriptor of the class to type check against
jlaskey@3 1110 *
jlaskey@3 1111 * @return the method emitter
jlaskey@3 1112 */
jlaskey@3 1113 public MethodEmitter checkcast(final String classDescriptor) {
jlaskey@3 1114 debug("checkcast", classDescriptor);
jlaskey@3 1115 assert peekType().isObject();
jlaskey@3 1116 method.visitTypeInsn(CHECKCAST, classDescriptor);
jlaskey@3 1117 return this;
jlaskey@3 1118 }
jlaskey@3 1119
jlaskey@3 1120 /**
jlaskey@3 1121 * Perform a checkcast operation on the object at the top of the
jlaskey@3 1122 * stack.
jlaskey@3 1123 *
jlaskey@3 1124 * @param clazz class to checkcast against
jlaskey@3 1125 *
jlaskey@3 1126 * @return the method emitter
jlaskey@3 1127 */
jlaskey@3 1128 public MethodEmitter checkcast(final Class<?> clazz) {
jlaskey@3 1129 return checkcast(CompilerConstants.className(clazz));
jlaskey@3 1130 }
jlaskey@3 1131
jlaskey@3 1132 /**
jlaskey@3 1133 * Instantiate a new array given a length that is popped
jlaskey@3 1134 * from the stack and the array type
jlaskey@3 1135 *
jlaskey@3 1136 * @param arrayType the type of the array
jlaskey@3 1137 *
jlaskey@3 1138 * @return the method emitter
jlaskey@3 1139 */
jlaskey@3 1140 public MethodEmitter newarray(final ArrayType arrayType) {
jlaskey@3 1141 debug("newarray ", "arrayType=" + arrayType);
jlaskey@3 1142 popType(Type.INT); //LENGTH
jlaskey@3 1143 pushType(arrayType.newarray(method));
jlaskey@3 1144 return this;
jlaskey@3 1145 }
jlaskey@3 1146
jlaskey@3 1147 /**
jlaskey@3 1148 * Instantiate a multidimensional array with a given number of dimensions.
jlaskey@3 1149 * On the stack are dim lengths of the sub arrays.
jlaskey@3 1150 *
jlaskey@3 1151 * @param arrayType type of the array
jlaskey@3 1152 * @param dims number of dimensions
jlaskey@3 1153 *
jlaskey@3 1154 * @return the method emitter
jlaskey@3 1155 */
jlaskey@3 1156 public MethodEmitter multinewarray(final ArrayType arrayType, final int dims) {
jlaskey@3 1157 debug("multianewarray ", arrayType, dims);
jlaskey@3 1158 for (int i = 0; i < dims; i++) {
jlaskey@3 1159 popType(Type.INT); //LENGTH
jlaskey@3 1160 }
jlaskey@3 1161 pushType(arrayType.newarray(method, dims));
jlaskey@3 1162 return this;
jlaskey@3 1163 }
jlaskey@3 1164
jlaskey@3 1165 /**
jlaskey@3 1166 * Helper function to pop and type check the appropriate arguments
jlaskey@3 1167 * from the stack given a method signature
jlaskey@3 1168 *
jlaskey@3 1169 * @param signature method signature
jlaskey@3 1170 *
jlaskey@3 1171 * @return return type of method
jlaskey@3 1172 */
jlaskey@3 1173 private Type fixParamStack(final String signature) {
jlaskey@3 1174 final Type[] params = Type.getMethodArguments(signature);
jlaskey@3 1175 for (int i = params.length - 1; i >= 0; i--) {
jlaskey@3 1176 popType(params[i]);
jlaskey@3 1177 }
jlaskey@3 1178 final Type returnType = Type.getMethodReturnType(signature);
jlaskey@3 1179 return returnType;
jlaskey@3 1180 }
jlaskey@3 1181
jlaskey@3 1182 /**
jlaskey@3 1183 * Generate an invocation to a Call structure
jlaskey@3 1184 * @see CompilerConstants
jlaskey@3 1185 *
jlaskey@3 1186 * @param call the call object
jlaskey@3 1187 *
jlaskey@3 1188 * @return the method emitter
jlaskey@3 1189 */
jlaskey@3 1190 public MethodEmitter invoke(final Call call) {
jlaskey@3 1191 return call.invoke(this);
jlaskey@3 1192 }
jlaskey@3 1193
jlaskey@3 1194 private MethodEmitter invoke(final int opcode, final String className, final String methodName, final String methodDescriptor, final boolean hasReceiver) {
jlaskey@3 1195 final Type returnType = fixParamStack(methodDescriptor);
jlaskey@3 1196
jlaskey@3 1197 if (hasReceiver) {
jlaskey@3 1198 popType(Type.OBJECT);
jlaskey@3 1199 }
jlaskey@3 1200
jlaskey@3 1201 method.visitMethodInsn(opcode, className, methodName, methodDescriptor);
jlaskey@3 1202
jlaskey@3 1203 if (returnType != null) {
jlaskey@3 1204 pushType(returnType);
jlaskey@3 1205 }
jlaskey@3 1206
jlaskey@3 1207 return this;
jlaskey@3 1208 }
jlaskey@3 1209
jlaskey@3 1210 /**
jlaskey@3 1211 * Pop receiver from stack, perform an invoke special
jlaskey@3 1212 *
jlaskey@3 1213 * @param className class name
jlaskey@3 1214 * @param methodName method name
jlaskey@3 1215 * @param methodDescriptor descriptor
jlaskey@3 1216 *
jlaskey@3 1217 * @return the method emitter
jlaskey@3 1218 */
jlaskey@3 1219 public MethodEmitter invokeSpecial(final String className, final String methodName, final String methodDescriptor) {
jlaskey@3 1220 debug("invokespecial", className + "." + methodName + methodDescriptor);
jlaskey@3 1221 return invoke(INVOKESPECIAL, className, methodName, methodDescriptor, true);
jlaskey@3 1222 }
jlaskey@3 1223
jlaskey@3 1224 /**
jlaskey@3 1225 * Pop receiver from stack, perform an invoke virtual, push return value if any
jlaskey@3 1226 *
jlaskey@3 1227 * @param className class name
jlaskey@3 1228 * @param methodName method name
jlaskey@3 1229 * @param methodDescriptor descriptor
jlaskey@3 1230 *
jlaskey@3 1231 * @return the method emitter
jlaskey@3 1232 */
jlaskey@3 1233 public MethodEmitter invokeVirtual(final String className, final String methodName, final String methodDescriptor) {
jlaskey@3 1234 debug("invokevirtual", className + "." + methodName + methodDescriptor + " " + stack);
jlaskey@3 1235 return invoke(INVOKEVIRTUAL, className, methodName, methodDescriptor, true);
jlaskey@3 1236 }
jlaskey@3 1237
jlaskey@3 1238 /**
jlaskey@3 1239 * Perform an invoke static and push the return value if any
jlaskey@3 1240 *
jlaskey@3 1241 * @param className class name
jlaskey@3 1242 * @param methodName method name
jlaskey@3 1243 * @param methodDescriptor descriptor
jlaskey@3 1244 *
jlaskey@3 1245 * @return the method emitter
jlaskey@3 1246 */
jlaskey@3 1247 public MethodEmitter invokeStatic(final String className, final String methodName, final String methodDescriptor) {
jlaskey@3 1248 debug("invokestatic", className + "." + methodName + methodDescriptor);
jlaskey@3 1249 invoke(INVOKESTATIC, className, methodName, methodDescriptor, false);
jlaskey@3 1250 return this;
jlaskey@3 1251 }
jlaskey@3 1252
jlaskey@3 1253 /**
jlaskey@3 1254 * Perform an invoke static and replace the return type if we know better, e.g. Global.allocate
jlaskey@3 1255 * that allocates an array should return an ObjectArray type as a NativeArray counts as that
jlaskey@3 1256 *
jlaskey@3 1257 * @param className class name
jlaskey@3 1258 * @param methodName method name
jlaskey@3 1259 * @param methodDescriptor descriptor
jlaskey@3 1260 * @param returnType return type override
jlaskey@3 1261 *
jlaskey@3 1262 * @return the method emitter
jlaskey@3 1263 */
jlaskey@3 1264 public MethodEmitter invokeStatic(final String className, final String methodName, final String methodDescriptor, final Type returnType) {
jlaskey@3 1265 invokeStatic(className, methodName, methodDescriptor);
jlaskey@3 1266 popType();
jlaskey@3 1267 pushType(returnType);
jlaskey@3 1268 return this;
jlaskey@3 1269 }
jlaskey@3 1270
jlaskey@3 1271 /**
jlaskey@3 1272 * Pop receiver from stack, perform an invoke interface and push return value if any
jlaskey@3 1273 *
jlaskey@3 1274 * @param className class name
jlaskey@3 1275 * @param methodName method name
jlaskey@3 1276 * @param methodDescriptor descriptor
jlaskey@3 1277 *
jlaskey@3 1278 * @return the method emitter
jlaskey@3 1279 */
jlaskey@3 1280 public MethodEmitter invokeInterface(final String className, final String methodName, final String methodDescriptor) {
jlaskey@3 1281 debug("invokeinterface", className + "." + methodName + methodDescriptor);
jlaskey@3 1282 return invoke(INVOKEINTERFACE, className, methodName, methodDescriptor, true);
jlaskey@3 1283 }
jlaskey@3 1284
jlaskey@3 1285 /**
jlaskey@3 1286 * Generate a lookup switch, popping the switch value from the stack
jlaskey@3 1287 *
jlaskey@3 1288 * @param defaultLabel default label
jlaskey@3 1289 * @param values case values for the table
jlaskey@3 1290 * @param table default label
jlaskey@3 1291 */
jlaskey@3 1292 public void lookupSwitch(final Label defaultLabel, final int[] values, final Label[] table) {
jlaskey@3 1293 debug("lookupswitch", peekType());
jlaskey@3 1294 popType(Type.INT);
jlaskey@3 1295 method.visitLookupSwitchInsn(defaultLabel, values, table);
jlaskey@3 1296 }
jlaskey@3 1297
jlaskey@3 1298 /**
jlaskey@3 1299 * Generate a table switch
jlaskey@3 1300 * @param lo low value
jlaskey@3 1301 * @param hi high value
jlaskey@3 1302 * @param defaultLabel default label
jlaskey@3 1303 * @param table label table
jlaskey@3 1304 */
jlaskey@3 1305 public void tableSwitch(final int lo, final int hi, final Label defaultLabel, final Label[] table) {
jlaskey@3 1306 debug("tableswitch", peekType());
jlaskey@3 1307 popType(Type.INT);
jlaskey@3 1308 method.visitTableSwitchInsn(lo, hi, defaultLabel, table);
jlaskey@3 1309 }
jlaskey@3 1310
jlaskey@3 1311 /**
jlaskey@3 1312 * Abstraction for performing a conditional jump of any type
jlaskey@3 1313 *
jlaskey@3 1314 * @see MethodEmitter.Condition
jlaskey@3 1315 *
jlaskey@3 1316 * @param cond the condition to test
jlaskey@3 1317 * @param trueLabel the destination label is condition is true
jlaskey@3 1318 */
jlaskey@3 1319 void conditionalJump(final Condition cond, final Label trueLabel) {
jlaskey@3 1320 conditionalJump(cond, cond != Condition.GT && cond != Condition.GE, trueLabel);
jlaskey@3 1321 }
jlaskey@3 1322
jlaskey@3 1323 /**
jlaskey@3 1324 * Abstraction for performing a conditional jump of any type,
jlaskey@3 1325 * including a dcmpg/dcmpl semantic for doubles.
jlaskey@3 1326 *
jlaskey@3 1327 * @param cond the condition to test
jlaskey@3 1328 * @param isCmpG is this a dcmpg for numbers, false if it's a dcmpl
jlaskey@3 1329 * @param trueLabel the destination label if condition is true
jlaskey@3 1330 */
jlaskey@3 1331 void conditionalJump(final Condition cond, final boolean isCmpG, final Label trueLabel) {
jlaskey@3 1332 if (peekType().isCategory2()) {
jlaskey@3 1333 debug("[ld]cmp isCmpG=" + isCmpG);
jlaskey@3 1334 pushType(get2n().cmp(method, isCmpG));
jlaskey@3 1335 jump(Condition.toUnary(cond), trueLabel, 1);
jlaskey@3 1336 } else {
jlaskey@3 1337 debug("if" + cond);
jlaskey@3 1338 jump(Condition.toBinary(cond, peekType().isObject()), trueLabel, 2);
jlaskey@3 1339 }
jlaskey@3 1340 }
jlaskey@3 1341
jlaskey@3 1342 /**
jlaskey@3 1343 * Perform a non void return, popping the type from the stack
jlaskey@3 1344 *
jlaskey@3 1345 * @param type the type for the return
jlaskey@3 1346 */
jlaskey@3 1347 public void _return(final Type type) {
jlaskey@3 1348 debug("return", type);
jlaskey@3 1349 assert stack.size() == 1 : "Only return value on stack allowed at return point - depth=" + stack.size() + " stack = " + stack;
jlaskey@3 1350 final Type stackType = peekType();
jlaskey@3 1351 if (!Type.areEquivalent(type, stackType)) {
jlaskey@3 1352 convert(type);
jlaskey@3 1353 }
jlaskey@3 1354 popType(type)._return(method);
jlaskey@3 1355 stack = null;
jlaskey@3 1356 }
jlaskey@3 1357
jlaskey@3 1358 /**
jlaskey@3 1359 * Perform a return using the stack top value as the guide for the type
jlaskey@3 1360 */
jlaskey@3 1361 public void _return() {
jlaskey@3 1362 _return(peekType());
jlaskey@3 1363 }
jlaskey@3 1364
jlaskey@3 1365 /**
jlaskey@3 1366 * Perform a void return.
jlaskey@3 1367 */
jlaskey@3 1368 public void returnVoid() {
jlaskey@3 1369 debug("return [void]");
jlaskey@3 1370 assert stack.isEmpty() : stack;
jlaskey@3 1371 method.visitInsn(RETURN);
jlaskey@3 1372 stack = null;
jlaskey@3 1373 }
jlaskey@3 1374
jlaskey@3 1375 /**
jlaskey@3 1376 * Goto, possibly when splitting is taking place. If
jlaskey@3 1377 * a splitNode exists, we need to handle the case that the
jlaskey@3 1378 * jump target is another method
jlaskey@3 1379 *
jlaskey@3 1380 * @param label destination label
jlaskey@3 1381 */
jlaskey@3 1382 public void splitAwareGoto(final Label label) {
jlaskey@3 1383
jlaskey@3 1384 if (splitNode != null) {
jlaskey@3 1385 final int index = splitNode.getExternalTargets().indexOf(label);
jlaskey@3 1386
jlaskey@3 1387 if (index > -1) {
jlaskey@3 1388 loadScope();
jlaskey@3 1389 checkcast(Scope.class);
jlaskey@3 1390 load(index + 1);
jlaskey@3 1391 invoke(Scope.SET_SPLIT_STATE);
jlaskey@3 1392 loadUndefined(Type.OBJECT);
jlaskey@3 1393 _return(functionNode.getReturnType());
jlaskey@3 1394 return;
jlaskey@3 1395 }
jlaskey@3 1396 }
jlaskey@3 1397
jlaskey@3 1398 _goto(label);
jlaskey@3 1399 }
jlaskey@3 1400
jlaskey@3 1401 /**
jlaskey@3 1402 * Perform a comparison of two number types that are popped from the stack
jlaskey@3 1403 *
jlaskey@3 1404 * @param isCmpG is this a dcmpg semantic, false if it's a dcmpl semantic
jlaskey@3 1405 *
jlaskey@3 1406 * @return the method emitter
jlaskey@3 1407 */
jlaskey@3 1408 public MethodEmitter cmp(final boolean isCmpG) {
jlaskey@3 1409 pushType(get2n().cmp(method, isCmpG));
jlaskey@3 1410 return this;
jlaskey@3 1411 }
jlaskey@3 1412
jlaskey@3 1413 /**
jlaskey@3 1414 * Helper function for jumps, conditional or not
jlaskey@3 1415 * @param opcode opcode for jump
jlaskey@3 1416 * @param label destination
jlaskey@3 1417 * @param n elements on stack to compare, 0-2
jlaskey@3 1418 */
jlaskey@3 1419 private void jump(final int opcode, final Label label, final int n) {
jlaskey@3 1420 for (int i = 0; i < n; i++) {
jlaskey@3 1421 assert peekType().isInteger() || peekType().isBoolean() || peekType().isObject() : "expecting integer type or object for jump, but found " + peekType();
jlaskey@3 1422 popType();
jlaskey@3 1423 }
jlaskey@3 1424 mergeStackTo(label);
jlaskey@3 1425 method.visitJumpInsn(opcode, label);
jlaskey@3 1426 }
jlaskey@3 1427
jlaskey@3 1428 /**
jlaskey@3 1429 * Generate an if_acmpeq
jlaskey@3 1430 *
jlaskey@3 1431 * @param label label to true case
jlaskey@3 1432 */
jlaskey@3 1433 public void if_acmpeq(final Label label) {
jlaskey@3 1434 debug("if_acmpeq", label);
jlaskey@3 1435 jump(IF_ACMPEQ, label, 2);
jlaskey@3 1436 }
jlaskey@3 1437
jlaskey@3 1438 /**
jlaskey@3 1439 * Generate an if_acmpne
jlaskey@3 1440 *
jlaskey@3 1441 * @param label label to true case
jlaskey@3 1442 */
jlaskey@3 1443 public void if_acmpne(final Label label) {
jlaskey@3 1444 debug("if_acmpne", label);
jlaskey@3 1445 jump(IF_ACMPNE, label, 2);
jlaskey@3 1446 }
jlaskey@3 1447
jlaskey@3 1448 /**
jlaskey@3 1449 * Generate an ifnull
jlaskey@3 1450 *
jlaskey@3 1451 * @param label label to true case
jlaskey@3 1452 */
jlaskey@3 1453 public void ifnull(final Label label) {
jlaskey@3 1454 debug("ifnull", label);
jlaskey@3 1455 jump(IFNULL, label, 1);
jlaskey@3 1456 }
jlaskey@3 1457
jlaskey@3 1458 /**
jlaskey@3 1459 * Generate an ifnonnull
jlaskey@3 1460 *
jlaskey@3 1461 * @param label label to true case
jlaskey@3 1462 */
jlaskey@3 1463 public void ifnonnull(final Label label) {
jlaskey@3 1464 debug("ifnonnull", label);
jlaskey@3 1465 jump(IFNONNULL, label, 1);
jlaskey@3 1466 }
jlaskey@3 1467
jlaskey@3 1468 /**
jlaskey@3 1469 * Generate an ifeq
jlaskey@3 1470 *
jlaskey@3 1471 * @param label label to true case
jlaskey@3 1472 */
jlaskey@3 1473 public void ifeq(final Label label) {
jlaskey@3 1474 debug("ifeq ", label);
jlaskey@3 1475 jump(IFEQ, label, 1);
jlaskey@3 1476 }
jlaskey@3 1477
jlaskey@3 1478 /**
jlaskey@3 1479 * Generate an if_icmpeq
jlaskey@3 1480 *
jlaskey@3 1481 * @param label label to true case
jlaskey@3 1482 */
jlaskey@3 1483 public void if_icmpeq(final Label label) {
jlaskey@3 1484 debug("if_icmpeq", label);
jlaskey@3 1485 jump(IF_ICMPEQ, label, 2);
jlaskey@3 1486 }
jlaskey@3 1487
jlaskey@3 1488 /**
jlaskey@3 1489 * Generate an if_ne
jlaskey@3 1490 *
jlaskey@3 1491 * @param label label to true case
jlaskey@3 1492 */
jlaskey@3 1493
jlaskey@3 1494 public void ifne(final Label label) {
jlaskey@3 1495 debug("ifne", label);
jlaskey@3 1496 jump(IFNE, label, 1);
jlaskey@3 1497 }
jlaskey@3 1498
jlaskey@3 1499 /**
jlaskey@3 1500 * Generate an if_icmpne
jlaskey@3 1501 *
jlaskey@3 1502 * @param label label to true case
jlaskey@3 1503 */
jlaskey@3 1504 public void if_icmpne(final Label label) {
jlaskey@3 1505 debug("if_icmpne", label);
jlaskey@3 1506 jump(IF_ICMPNE, label, 2);
jlaskey@3 1507 }
jlaskey@3 1508
jlaskey@3 1509 /**
jlaskey@3 1510 * Generate an iflt
jlaskey@3 1511 *
jlaskey@3 1512 * @param label label to true case
jlaskey@3 1513 */
jlaskey@3 1514 public void iflt(final Label label) {
jlaskey@3 1515 debug("iflt", label);
jlaskey@3 1516 jump(IFLT, label, 1);
jlaskey@3 1517 }
jlaskey@3 1518
jlaskey@3 1519 /**
jlaskey@3 1520 * Generate an ifle
jlaskey@3 1521 *
jlaskey@3 1522 * @param label label to true case
jlaskey@3 1523 */
jlaskey@3 1524 public void ifle(final Label label) {
jlaskey@3 1525 debug("ifle", label);
jlaskey@3 1526 jump(IFLE, label, 1);
jlaskey@3 1527 }
jlaskey@3 1528
jlaskey@3 1529 /**
jlaskey@3 1530 * Generate an ifgt
jlaskey@3 1531 *
jlaskey@3 1532 * @param label label to true case
jlaskey@3 1533 */
jlaskey@3 1534 public void ifgt(final Label label) {
jlaskey@3 1535 debug("ifgt", label);
jlaskey@3 1536 jump(IFGT, label, 1);
jlaskey@3 1537 }
jlaskey@3 1538
jlaskey@3 1539 /**
jlaskey@3 1540 * Generate an ifge
jlaskey@3 1541 *
jlaskey@3 1542 * @param label label to true case
jlaskey@3 1543 */
jlaskey@3 1544 public void ifge(final Label label) {
jlaskey@3 1545 debug("ifge", label);
jlaskey@3 1546 jump(IFGE, label, 1);
jlaskey@3 1547 }
jlaskey@3 1548
jlaskey@3 1549 /**
jlaskey@3 1550 * Unconditional jump to a label
jlaskey@3 1551 *
jlaskey@3 1552 * @param label destination label
jlaskey@3 1553 */
jlaskey@3 1554 public void _goto(final Label label) {
jlaskey@3 1555 debug("goto", label);
jlaskey@3 1556 jump(GOTO, label, 0);
jlaskey@3 1557 stack = null;
jlaskey@3 1558 }
jlaskey@3 1559
jlaskey@3 1560 /**
jlaskey@3 1561 * Examine two stacks and make sure they are of the same size and their
jlaskey@3 1562 * contents are equivalent to each other
jlaskey@3 1563 * @param s0 first stack
jlaskey@3 1564 * @param s1 second stack
jlaskey@3 1565 *
jlaskey@3 1566 * @return true if stacks are equivalent, false otherwise
jlaskey@3 1567 */
jlaskey@3 1568 private boolean stacksEquivalent(final ArrayDeque<Type> s0, final ArrayDeque<Type> s1) {
jlaskey@3 1569 if (s0.size() != s1.size()) {
jlaskey@3 1570 debug("different stack sizes", s0, s1);
jlaskey@3 1571 return false;
jlaskey@3 1572 }
jlaskey@3 1573
jlaskey@3 1574 final Type[] s0a = s0.toArray(new Type[s0.size()]);
jlaskey@3 1575 final Type[] s1a = s1.toArray(new Type[s1.size()]);
jlaskey@3 1576 for (int i = 0; i < s0.size(); i++) {
jlaskey@3 1577 if (!s0a[i].isEquivalentTo(s1a[i])) {
jlaskey@3 1578 debug("different stack element", s0a[i], s1a[i]);
jlaskey@3 1579 return false;
jlaskey@3 1580 }
jlaskey@3 1581 }
jlaskey@3 1582
jlaskey@3 1583 return true;
jlaskey@3 1584 }
jlaskey@3 1585
jlaskey@3 1586 /**
jlaskey@3 1587 * A join in control flow - helper function that makes sure all entry stacks
jlaskey@3 1588 * discovered for the join point so far are equivalent
jlaskey@3 1589 * @param label
jlaskey@3 1590 */
jlaskey@3 1591 private void mergeStackTo(final Label label) {
jlaskey@3 1592 final ArrayDeque<Type> labelStack = label.getStack();
jlaskey@3 1593 //debug(labelStack == null ? " >> Control flow - first visit " + label : " >> Control flow - JOIN with " + labelStack + " at " + label);
jlaskey@3 1594 if (labelStack == null) {
jlaskey@3 1595 assert stack != null;
jlaskey@3 1596 label.setStack(stack.clone());
jlaskey@3 1597 return;
jlaskey@3 1598 }
lagergren@57 1599 assert stacksEquivalent(stack, labelStack) : "stacks " + stack + " is not equivalent with " + labelStack + " at join point";
jlaskey@3 1600 }
jlaskey@3 1601
jlaskey@3 1602 /**
jlaskey@3 1603 * Register a new label, enter it here.
jlaskey@3 1604 *
jlaskey@3 1605 * @param label the label
jlaskey@3 1606 */
jlaskey@3 1607 public void label(final Label label) {
jlaskey@3 1608 /*
jlaskey@3 1609 * If stack == null, this means that we came here not through a fallthrough.
jlaskey@3 1610 * E.g. a label after an athrow. Then we create a new stack if one doesn't exist
jlaskey@3 1611 * for this location already.
jlaskey@3 1612 */
jlaskey@3 1613 if (stack == null) {
jlaskey@3 1614 stack = label.getStack();
jlaskey@3 1615 if (stack == null) {
jlaskey@3 1616 stack = new ArrayDeque<>(); //we don't have a stack at this point.
jlaskey@3 1617 }
jlaskey@3 1618 }
jlaskey@3 1619 debug_label(label);
jlaskey@3 1620
jlaskey@3 1621 mergeStackTo(label); //we have to merge our stack to whatever is in the label
jlaskey@3 1622
jlaskey@3 1623 method.visitLabel(label);
jlaskey@3 1624 }
jlaskey@3 1625
jlaskey@3 1626 /**
jlaskey@3 1627 * Pop element from stack, convert to given type
jlaskey@3 1628 *
jlaskey@3 1629 * @param to type to convert to
jlaskey@3 1630 *
jlaskey@3 1631 * @return the method emitter
jlaskey@3 1632 */
jlaskey@3 1633 public MethodEmitter convert(final Type to) {
jlaskey@3 1634 final Type type = peekType().convert(method, to);
jlaskey@3 1635 if (type != null) {
jlaskey@3 1636 if (peekType() != to) {
jlaskey@3 1637 debug("convert", peekType(), "->", to);
jlaskey@3 1638 }
jlaskey@3 1639 popType();
jlaskey@3 1640 pushType(type);
jlaskey@3 1641 }
jlaskey@3 1642 return this;
jlaskey@3 1643 }
jlaskey@3 1644
jlaskey@3 1645 /**
jlaskey@3 1646 * Helper function - expect two types that are equivalent
jlaskey@3 1647 *
jlaskey@3 1648 * @return common type
jlaskey@3 1649 */
jlaskey@3 1650 private Type get2() {
jlaskey@3 1651 final Type p0 = popType();
jlaskey@3 1652 final Type p1 = popType();
jlaskey@3 1653 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1;
jlaskey@3 1654 return p0;
jlaskey@3 1655 }
jlaskey@3 1656
jlaskey@3 1657 /**
jlaskey@3 1658 * Helper function - expect two types that are integer types and equivalent
jlaskey@3 1659 *
jlaskey@3 1660 * @return common type
jlaskey@3 1661 */
jlaskey@3 1662 private BitwiseType get2i() {
jlaskey@3 1663 final BitwiseType p0 = popInteger();
jlaskey@3 1664 final BitwiseType p1 = popInteger();
jlaskey@3 1665 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1;
jlaskey@3 1666 return p0;
jlaskey@3 1667 }
jlaskey@3 1668
jlaskey@3 1669 /**
jlaskey@3 1670 * Helper function - expect two types that are numbers and equivalent
jlaskey@3 1671 *
jlaskey@3 1672 * @return common type
jlaskey@3 1673 */
jlaskey@3 1674 private NumericType get2n() {
jlaskey@3 1675 final NumericType p0 = popNumeric();
jlaskey@3 1676 final NumericType p1 = popNumeric();
jlaskey@3 1677 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1;
jlaskey@3 1678 return p0;
jlaskey@3 1679 }
jlaskey@3 1680
jlaskey@3 1681 /**
jlaskey@3 1682 * Pop two numbers, perform addition and push result
jlaskey@3 1683 *
jlaskey@3 1684 * @return the method emitter
jlaskey@3 1685 */
jlaskey@3 1686 public MethodEmitter add() {
jlaskey@3 1687 debug("add");
jlaskey@3 1688 pushType(get2().add(method));
jlaskey@3 1689 return this;
jlaskey@3 1690 }
jlaskey@3 1691
jlaskey@3 1692 /**
jlaskey@3 1693 * Pop two numbers, perform subtraction and push result
jlaskey@3 1694 *
jlaskey@3 1695 * @return the method emitter
jlaskey@3 1696 */
jlaskey@3 1697 public MethodEmitter sub() {
jlaskey@3 1698 debug("sub");
jlaskey@3 1699 pushType(get2n().sub(method));
jlaskey@3 1700 return this;
jlaskey@3 1701 }
jlaskey@3 1702
jlaskey@3 1703 /**
jlaskey@3 1704 * Pop two numbers, perform multiplication and push result
jlaskey@3 1705 *
jlaskey@3 1706 * @return the method emitter
jlaskey@3 1707 */
jlaskey@3 1708 public MethodEmitter mul() {
jlaskey@3 1709 debug("mul ");
jlaskey@3 1710 pushType(get2n().mul(method));
jlaskey@3 1711 return this;
jlaskey@3 1712 }
jlaskey@3 1713
jlaskey@3 1714 /**
jlaskey@3 1715 * Pop two numbers, perform division and push result
jlaskey@3 1716 *
jlaskey@3 1717 * @return the method emitter
jlaskey@3 1718 */
jlaskey@3 1719 public MethodEmitter div() {
jlaskey@3 1720 debug("div");
jlaskey@3 1721 pushType(get2n().div(method));
jlaskey@3 1722 return this;
jlaskey@3 1723 }
jlaskey@3 1724
jlaskey@3 1725 /**
jlaskey@3 1726 * Pop two numbers, calculate remainder and push result
jlaskey@3 1727 *
jlaskey@3 1728 * @return the method emitter
jlaskey@3 1729 */
jlaskey@3 1730 public MethodEmitter rem() {
jlaskey@3 1731 debug("rem");
jlaskey@3 1732 pushType(get2n().rem(method));
jlaskey@3 1733 return this;
jlaskey@3 1734 }
jlaskey@3 1735
jlaskey@3 1736 /**
jlaskey@3 1737 * Retrieve the top <tt>count</tt> types on the stack without modifying it.
jlaskey@3 1738 *
jlaskey@3 1739 * @param count number of types to return
jlaskey@3 1740 * @return array of Types
jlaskey@3 1741 */
jlaskey@3 1742 protected Type[] getTypesFromStack(final int count) {
jlaskey@3 1743 final Iterator<Type> iter = stack.iterator();
jlaskey@3 1744 final Type[] types = new Type[count];
jlaskey@3 1745
jlaskey@3 1746 for (int i = count - 1; i >= 0; i--) {
jlaskey@3 1747 types[i] = iter.next();
jlaskey@3 1748 }
jlaskey@3 1749
jlaskey@3 1750 return types;
jlaskey@3 1751 }
jlaskey@3 1752
jlaskey@3 1753 /**
jlaskey@3 1754 * Helper function to generate a function signature based on stack contents
jlaskey@3 1755 * and argument count and return type
jlaskey@3 1756 *
jlaskey@3 1757 * @param returnType return type
jlaskey@3 1758 * @param argCount argument count
jlaskey@3 1759 *
jlaskey@3 1760 * @return function signature for stack contents
jlaskey@3 1761 */
jlaskey@3 1762 private String getDynamicSignature(final Type returnType, final int argCount, final boolean hasSelf) {
jlaskey@3 1763 final Iterator<Type> iter = stack.iterator();
jlaskey@3 1764 final Type[] argTypes = new Type[argCount];
jlaskey@3 1765
jlaskey@3 1766 for (int i = argCount - 1; i >= 0; i--) {
jlaskey@3 1767 argTypes[i] = iter.next();
jlaskey@3 1768 }
jlaskey@3 1769
jlaskey@3 1770 final FunctionSignature sig = new FunctionSignature(hasSelf, false, returnType, argTypes);
jlaskey@3 1771 for (int i = 0; i < argCount; i++) {
jlaskey@3 1772 popType(argTypes[argCount - i - 1]);
jlaskey@3 1773 }
jlaskey@3 1774 for (int i = 0 ; i < sig.size() - argTypes.length; i++) {
jlaskey@3 1775 popType(Type.OBJECT);
jlaskey@3 1776 }
jlaskey@3 1777
jlaskey@3 1778 return sig.toString();
jlaskey@3 1779 }
jlaskey@3 1780
jlaskey@3 1781 /**
jlaskey@3 1782 * Generate a dynamic new
jlaskey@3 1783 *
jlaskey@3 1784 * @param argCount number of arguments
jlaskey@3 1785 * @param flags callsite flags
jlaskey@3 1786 *
jlaskey@3 1787 * @return the method emitter
jlaskey@3 1788 */
jlaskey@3 1789 public MethodEmitter dynamicNew(final int argCount, final int flags) {
jlaskey@3 1790 debug("dynamic_new", "argcount=" + argCount);
jlaskey@3 1791 final String signature = getDynamicSignature(Type.OBJECT, argCount, true);
jlaskey@3 1792 method.visitInvokeDynamicInsn("dyn:new", signature, LINKERBOOTSTRAP, flags);
jlaskey@3 1793 pushType(Type.OBJECT); //TODO fix result type
jlaskey@3 1794 return this;
jlaskey@3 1795 }
jlaskey@3 1796
jlaskey@3 1797 /**
jlaskey@3 1798 * Generate a dynamic call
jlaskey@3 1799 *
jlaskey@3 1800 * @param returnType return type
jlaskey@3 1801 * @param argCount number of arguments
jlaskey@3 1802 * @param flags callsite flags
jlaskey@3 1803 *
jlaskey@3 1804 * @return the method emitter
jlaskey@3 1805 */
jlaskey@3 1806 public MethodEmitter dynamicCall(final Type returnType, final int argCount, final int flags) {
jlaskey@3 1807 debug("dynamic_call", "args=" + argCount, "returnType=" + returnType);
jlaskey@3 1808 final String signature = getDynamicSignature(returnType, argCount + 1, true);
jlaskey@3 1809 debug(" signature", signature);
jlaskey@3 1810 method.visitInvokeDynamicInsn("dyn:call", signature, LINKERBOOTSTRAP, flags);
jlaskey@3 1811 pushType(returnType);
jlaskey@3 1812
jlaskey@3 1813 return this;
jlaskey@3 1814 }
jlaskey@3 1815
jlaskey@3 1816 /**
jlaskey@3 1817 * Generate a dynamic call for a runtime node
jlaskey@3 1818 *
jlaskey@3 1819 * @param name tag for the invoke dynamic for this runtime node
jlaskey@3 1820 * @param returnType return type
jlaskey@3 1821 * @param request RuntimeNode request
jlaskey@3 1822 *
jlaskey@3 1823 * @return the method emitter
jlaskey@3 1824 */
jlaskey@3 1825 public MethodEmitter dynamicRuntimeCall(final String name, final Type returnType, final RuntimeNode.Request request) {
jlaskey@3 1826 debug("dynamic_runtime_call", name, "args=" + request.getArity(), "returnType=" + returnType);
jlaskey@3 1827 final String signature = getDynamicSignature(returnType, request.getArity(), false);
jlaskey@3 1828 debug(" signature", signature);
jlaskey@3 1829 method.visitInvokeDynamicInsn(name, signature, RUNTIMEBOOTSTRAP);
jlaskey@3 1830 pushType(returnType);
jlaskey@3 1831
jlaskey@3 1832 return this;
jlaskey@3 1833 }
jlaskey@3 1834
jlaskey@3 1835 /**
jlaskey@3 1836 * Generate dynamic getter. Pop scope from stack. Push result
jlaskey@3 1837 *
jlaskey@3 1838 * @param valueType type of the value to set
jlaskey@3 1839 * @param name name of property
jlaskey@3 1840 * @param flags call site flags
jlaskey@3 1841 * @param isMethod should it prefer retrieving methods
jlaskey@3 1842 *
jlaskey@3 1843 * @return the method emitter
jlaskey@3 1844 */
jlaskey@3 1845 public MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod) {
jlaskey@3 1846 debug("dynamic_get", name, valueType);
jlaskey@3 1847
jlaskey@3 1848 Type type = valueType;
jlaskey@3 1849 if (type.isObject() || type.isBoolean()) {
jlaskey@3 1850 type = Type.OBJECT; //promote e.g strings to object generic setter
jlaskey@3 1851 }
jlaskey@3 1852
jlaskey@3 1853 popType(Type.SCOPE);
jlaskey@3 1854 method.visitInvokeDynamicInsn((isMethod ? "dyn:getMethod|getProp|getElem:" : "dyn:getProp|getElem|getMethod:") +
jlaskey@3 1855 NameCodec.encode(name), Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags);
jlaskey@3 1856
jlaskey@3 1857 pushType(type);
jlaskey@3 1858
jlaskey@3 1859 convert(valueType); //most probably a nop
jlaskey@3 1860
jlaskey@3 1861 return this;
jlaskey@3 1862 }
jlaskey@3 1863
jlaskey@3 1864 /**
jlaskey@3 1865 * Generate dynamic setter. Pop receiver and property from stack.
jlaskey@3 1866 *
jlaskey@3 1867 * @param valueType the type of the value to set
jlaskey@3 1868 * @param name name of property
jlaskey@3 1869 * @param flags call site flags
jlaskey@3 1870 */
jlaskey@3 1871 public void dynamicSet(final Type valueType, final String name, final int flags) {
jlaskey@3 1872 debug("dynamic_set", name, peekType());
jlaskey@3 1873
jlaskey@3 1874 Type type = valueType;
jlaskey@3 1875 if (type.isObject() || type.isBoolean()) { //promote strings to objects etc
jlaskey@3 1876 type = Type.OBJECT;
jlaskey@3 1877 convert(Type.OBJECT); //TODO bad- until we specialize boolean setters,
jlaskey@3 1878 }
jlaskey@3 1879
jlaskey@3 1880 popType(type);
jlaskey@3 1881 popType(Type.SCOPE);
jlaskey@3 1882
jlaskey@3 1883 method.visitInvokeDynamicInsn("dyn:setProp|setElem:" + NameCodec.encode(name), methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags);
jlaskey@3 1884 }
jlaskey@3 1885
jlaskey@3 1886 /**
jlaskey@3 1887 * Dynamic getter for indexed structures. Pop index and receiver from stack,
jlaskey@3 1888 * generate appropriate signatures based on types
jlaskey@3 1889 *
jlaskey@3 1890 * @param result result type for getter
jlaskey@3 1891 * @param flags call site flags for getter
jlaskey@3 1892 * @param isMethod should it prefer retrieving methods
jlaskey@3 1893 *
jlaskey@3 1894 * @return the method emitter
jlaskey@3 1895 */
jlaskey@3 1896 public MethodEmitter dynamicGetIndex(final Type result, final int flags, final boolean isMethod) {
jlaskey@3 1897 debug("dynamic_get_index", peekType(1) + "[" + peekType() + "]");
jlaskey@3 1898
jlaskey@3 1899 Type resultType = result;
jlaskey@3 1900 if (result.isBoolean()) {
jlaskey@3 1901 resultType = Type.OBJECT; // INT->OBJECT to avoid another dimension of cross products in the getters. TODO
jlaskey@3 1902 }
jlaskey@3 1903
jlaskey@3 1904 Type index = peekType();
jlaskey@3 1905 if (index.isObject() || index.isBoolean()) {
jlaskey@3 1906 index = Type.OBJECT; //e.g. string->object
jlaskey@3 1907 convert(Type.OBJECT);
jlaskey@3 1908 }
jlaskey@3 1909 popType();
jlaskey@3 1910
jlaskey@3 1911 popType(Type.OBJECT);
jlaskey@3 1912
jlaskey@3 1913 final String signature = Type.getMethodDescriptor(resultType, Type.OBJECT /*e.g STRING->OBJECT*/, index);
jlaskey@3 1914
jlaskey@3 1915 method.visitInvokeDynamicInsn(isMethod ? "dyn:getMethod|getElem|getProp" : "dyn:getElem|getProp|getMethod",
jlaskey@3 1916 signature, LINKERBOOTSTRAP, flags);
jlaskey@3 1917 pushType(resultType);
jlaskey@3 1918
jlaskey@3 1919 if (result.isBoolean()) {
jlaskey@3 1920 convert(Type.BOOLEAN);
jlaskey@3 1921 }
jlaskey@3 1922
jlaskey@3 1923 return this;
jlaskey@3 1924 }
jlaskey@3 1925
jlaskey@3 1926 /**
jlaskey@3 1927 * Dynamic setter for indexed structures. Pop value, index and receiver from
jlaskey@3 1928 * stack, generate appropriate signature based on types
jlaskey@3 1929 *
jlaskey@3 1930 * @param flags call site flags for setter
jlaskey@3 1931 */
jlaskey@3 1932 public void dynamicSetIndex(final int flags) {
jlaskey@3 1933 debug("dynamic_set_index", peekType(2) + "[" + peekType(1) + "] =", peekType());
jlaskey@3 1934
jlaskey@3 1935 Type value = peekType();
jlaskey@3 1936 if (value.isObject() || value.isBoolean()) {
jlaskey@3 1937 value = Type.OBJECT; //e.g. STRING->OBJECT - one descriptor for all object types
jlaskey@3 1938 convert(Type.OBJECT);
jlaskey@3 1939 }
jlaskey@3 1940 popType();
jlaskey@3 1941
jlaskey@3 1942 Type index = peekType();
jlaskey@3 1943 if (index.isObject() || index.isBoolean()) {
jlaskey@3 1944 index = Type.OBJECT; //e.g. string->object
jlaskey@3 1945 convert(Type.OBJECT);
jlaskey@3 1946 }
jlaskey@3 1947 popType(index);
jlaskey@3 1948
jlaskey@3 1949 final Type receiver = popType(Type.OBJECT);
jlaskey@3 1950 assert receiver.isObject();
jlaskey@3 1951
jlaskey@3 1952 method.visitInvokeDynamicInsn("dyn:setElem|setProp", methodDescriptor(void.class, receiver.getTypeClass(), index.getTypeClass(), value.getTypeClass()), LINKERBOOTSTRAP, flags);
jlaskey@3 1953 }
jlaskey@3 1954
jlaskey@3 1955 /**
jlaskey@3 1956 * Load a key value in the proper form.
jlaskey@3 1957 *
jlaskey@3 1958 * @param key
jlaskey@3 1959 */
jlaskey@3 1960 //TODO move this and break it apart
jlaskey@3 1961 MethodEmitter loadKey(final Object key) {
jlaskey@3 1962 if (key instanceof IdentNode) {
jlaskey@3 1963 method.visitLdcInsn(((IdentNode) key).getName());
jlaskey@3 1964 } else if (key instanceof LiteralNode) {
jlaskey@3 1965 method.visitLdcInsn(((LiteralNode<?>)key).getString());
jlaskey@3 1966 } else {
jlaskey@3 1967 method.visitLdcInsn(JSType.toString(key));
jlaskey@3 1968 }
jlaskey@3 1969 pushType(Type.OBJECT); //STRING
jlaskey@3 1970 return this;
jlaskey@3 1971 }
jlaskey@3 1972
jlaskey@3 1973 @SuppressWarnings("fallthrough")
jlaskey@3 1974 private static Type fieldType(final String desc) {
jlaskey@3 1975 switch (desc) {
jlaskey@3 1976 case "Z":
jlaskey@3 1977 case "B":
jlaskey@3 1978 case "C":
jlaskey@3 1979 case "S":
jlaskey@3 1980 case "I":
jlaskey@3 1981 return Type.INT;
jlaskey@3 1982 case "F":
jlaskey@3 1983 assert false;
jlaskey@3 1984 case "D":
jlaskey@3 1985 return Type.NUMBER;
jlaskey@3 1986 case "J":
jlaskey@3 1987 return Type.LONG;
jlaskey@3 1988 default:
jlaskey@3 1989 assert desc.startsWith("[") || desc.startsWith("L") : desc + " is not an object type";
jlaskey@3 1990 switch (desc.charAt(0)) {
jlaskey@3 1991 case 'L':
jlaskey@3 1992 return Type.OBJECT;
jlaskey@3 1993 case '[':
jlaskey@3 1994 return Type.typeFor(Array.newInstance(fieldType(desc.substring(1)).getTypeClass(), 0).getClass());
jlaskey@3 1995 default:
jlaskey@3 1996 assert false;
jlaskey@3 1997 }
jlaskey@3 1998 return Type.OBJECT;
jlaskey@3 1999 }
jlaskey@3 2000 }
jlaskey@3 2001
jlaskey@3 2002 /**
jlaskey@3 2003 * Generate get for a field access
jlaskey@3 2004 *
jlaskey@3 2005 * @param fa the field access
jlaskey@3 2006 *
jlaskey@3 2007 * @return the method emitter
jlaskey@3 2008 */
jlaskey@3 2009 public MethodEmitter getField(final FieldAccess fa) {
jlaskey@3 2010 return fa.get(this);
jlaskey@3 2011 }
jlaskey@3 2012
jlaskey@3 2013 /**
jlaskey@3 2014 * Generate set for a field access
jlaskey@3 2015 *
jlaskey@3 2016 * @param fa the field access
jlaskey@3 2017 */
jlaskey@3 2018 public void putField(final FieldAccess fa) {
jlaskey@3 2019 fa.put(this);
jlaskey@3 2020 }
jlaskey@3 2021
jlaskey@3 2022 /**
jlaskey@3 2023 * Get the value of a non-static field, pop the receiver from the stack,
jlaskey@3 2024 * push value to the stack
jlaskey@3 2025 *
jlaskey@3 2026 * @param className class
jlaskey@3 2027 * @param fieldName field name
jlaskey@3 2028 * @param fieldDescriptor field descriptor
jlaskey@3 2029 *
jlaskey@3 2030 * @return the method emitter
jlaskey@3 2031 */
jlaskey@3 2032 public MethodEmitter getField(final String className, final String fieldName, final String fieldDescriptor) {
jlaskey@3 2033 debug("getfield", "receiver=" + peekType(), className + "." + fieldName + fieldDescriptor);
jlaskey@3 2034 final Type receiver = popType();
jlaskey@3 2035 assert receiver.isObject();
jlaskey@3 2036 method.visitFieldInsn(GETFIELD, className, fieldName, fieldDescriptor);
jlaskey@3 2037 pushType(fieldType(fieldDescriptor));
jlaskey@3 2038 return this;
jlaskey@3 2039 }
jlaskey@3 2040
jlaskey@3 2041 /**
jlaskey@3 2042 * Get the value of a static field, push it to the stack
jlaskey@3 2043 *
jlaskey@3 2044 * @param className class
jlaskey@3 2045 * @param fieldName field name
jlaskey@3 2046 * @param fieldDescriptor field descriptor
jlaskey@3 2047 *
jlaskey@3 2048 * @return the method emitter
jlaskey@3 2049 */
jlaskey@3 2050 public MethodEmitter getStatic(final String className, final String fieldName, final String fieldDescriptor) {
jlaskey@3 2051 debug("getstatic", className + "." + fieldName + "." + fieldDescriptor);
jlaskey@3 2052 method.visitFieldInsn(GETSTATIC, className, fieldName, fieldDescriptor);
jlaskey@3 2053 pushType(fieldType(fieldDescriptor));
jlaskey@3 2054 return this;
jlaskey@3 2055 }
jlaskey@3 2056
jlaskey@3 2057 /**
jlaskey@3 2058 * Pop value and field from stack and write to a non-static field
jlaskey@3 2059 *
jlaskey@3 2060 * @param className class
jlaskey@3 2061 * @param fieldName field name
jlaskey@3 2062 * @param fieldDescriptor field descriptor
jlaskey@3 2063 */
jlaskey@3 2064 public void putField(final String className, final String fieldName, final String fieldDescriptor) {
jlaskey@3 2065 debug("putfield", "receiver=" + peekType(1), "value=" + peekType());
jlaskey@3 2066 popType(fieldType(fieldDescriptor));
jlaskey@3 2067 popType(Type.OBJECT);
jlaskey@3 2068 method.visitFieldInsn(PUTFIELD, className, fieldName, fieldDescriptor);
jlaskey@3 2069 }
jlaskey@3 2070
jlaskey@3 2071 /**
jlaskey@3 2072 * Pop value from stack and write to a static field
jlaskey@3 2073 *
jlaskey@3 2074 * @param className class
jlaskey@3 2075 * @param fieldName field name
jlaskey@3 2076 * @param fieldDescriptor field descriptor
jlaskey@3 2077 */
jlaskey@3 2078 public void putStatic(final String className, final String fieldName, final String fieldDescriptor) {
jlaskey@3 2079 debug("putfield", "value=" + peekType());
jlaskey@3 2080 popType(fieldType(fieldDescriptor));
jlaskey@3 2081 method.visitFieldInsn(PUTSTATIC, className, fieldName, fieldDescriptor);
jlaskey@3 2082 }
jlaskey@3 2083
jlaskey@3 2084 /**
jlaskey@3 2085 * Register line number at a label
jlaskey@3 2086 *
jlaskey@3 2087 * @param line line number
jlaskey@3 2088 * @param label label
jlaskey@3 2089 */
jlaskey@3 2090 public void lineNumber(final int line, final Label label) {
jlaskey@3 2091 method.visitLineNumber(line, label);
jlaskey@3 2092 }
jlaskey@3 2093
jlaskey@3 2094 /*
jlaskey@3 2095 * Debugging below
jlaskey@3 2096 */
jlaskey@3 2097
jlaskey@3 2098 private final FieldAccess ERR_STREAM = staticField(System.class, "err", PrintStream.class);
jlaskey@3 2099 private final Call PRINT = virtualCallNoLookup(PrintStream.class, "print", void.class, Object.class);
jlaskey@3 2100 private final Call PRINTLN = virtualCallNoLookup(PrintStream.class, "println", void.class, Object.class);
jlaskey@3 2101 private final Call PRINT_STACKTRACE = virtualCallNoLookup(Throwable.class, "printStackTrace", void.class);
jlaskey@3 2102
jlaskey@3 2103 /**
jlaskey@3 2104 * Emit a System.err.print statement of whatever is on top of the bytecode stack
jlaskey@3 2105 */
jlaskey@3 2106 public void print() {
jlaskey@3 2107 getField(ERR_STREAM);
jlaskey@3 2108 swap();
jlaskey@3 2109 convert(Type.OBJECT);
jlaskey@3 2110 invoke(PRINT);
jlaskey@3 2111 }
jlaskey@3 2112
jlaskey@3 2113 /**
jlaskey@3 2114 * Emit a System.err.println statement of whatever is on top of the bytecode stack
jlaskey@3 2115 */
jlaskey@3 2116 public void println() {
jlaskey@3 2117 getField(ERR_STREAM);
jlaskey@3 2118 swap();
jlaskey@3 2119 convert(Type.OBJECT);
jlaskey@3 2120 invoke(PRINTLN);
jlaskey@3 2121 }
jlaskey@3 2122
jlaskey@3 2123 /**
jlaskey@3 2124 * Emit a System.err.print statement
jlaskey@3 2125 * @param string string to print
jlaskey@3 2126 */
jlaskey@3 2127 public void print(final String string) {
jlaskey@3 2128 getField(ERR_STREAM);
jlaskey@3 2129 load(string);
jlaskey@3 2130 invoke(PRINT);
jlaskey@3 2131 }
jlaskey@3 2132
jlaskey@3 2133 /**
jlaskey@3 2134 * Emit a System.err.println statement
jlaskey@3 2135 * @param string string to print
jlaskey@3 2136 */
jlaskey@3 2137 public void println(final String string) {
jlaskey@3 2138 getField(ERR_STREAM);
jlaskey@3 2139 load(string);
jlaskey@3 2140 invoke(PRINTLN);
jlaskey@3 2141 }
jlaskey@3 2142
jlaskey@3 2143 /**
jlaskey@3 2144 * Print a stacktrace to S
jlaskey@3 2145 */
jlaskey@3 2146 public void stacktrace() {
jlaskey@3 2147 _new(Throwable.class);
jlaskey@3 2148 dup();
jlaskey@3 2149 invoke(constructorNoLookup(Throwable.class));
jlaskey@3 2150 invoke(PRINT_STACKTRACE);
jlaskey@3 2151 }
jlaskey@3 2152
jlaskey@3 2153 private static int linePrefix = 0;
jlaskey@3 2154
jlaskey@3 2155 /**
jlaskey@3 2156 * Debug function that outputs generated bytecode and stack contents
jlaskey@3 2157 *
jlaskey@3 2158 * @param args debug information to print
jlaskey@3 2159 */
jlaskey@3 2160 private void debug(final Object... args) {
jlaskey@3 2161 debug(30, args);
jlaskey@3 2162 }
jlaskey@3 2163
jlaskey@3 2164 /**
jlaskey@3 2165 * Debug function that outputs generated bytecode and stack contents
jlaskey@3 2166 * for a label - indentation is currently the only thing that differs
jlaskey@3 2167 *
jlaskey@3 2168 * @param args debug information to print
jlaskey@3 2169 */
jlaskey@3 2170 private void debug_label(final Object... args) {
jlaskey@3 2171 debug(26, args);
jlaskey@3 2172 }
jlaskey@3 2173
jlaskey@3 2174 private void debug(final int padConstant, final Object... args) {
jlaskey@3 2175 if (DEBUG) {
jlaskey@3 2176 final StringBuilder sb = new StringBuilder();
jlaskey@3 2177 int pad;
jlaskey@3 2178
jlaskey@3 2179 sb.append('#');
jlaskey@3 2180 sb.append(++linePrefix);
jlaskey@3 2181
jlaskey@3 2182 pad = 5 - sb.length();
jlaskey@3 2183 while (pad > 0) {
jlaskey@3 2184 sb.append(' ');
jlaskey@3 2185 pad--;
jlaskey@3 2186 }
jlaskey@3 2187
jlaskey@3 2188 if (!stack.isEmpty()) {
jlaskey@3 2189 sb.append("{");
jlaskey@3 2190 sb.append(stack.size());
jlaskey@3 2191 sb.append(":");
jlaskey@3 2192 for (final Iterator<Type> iter = stack.iterator(); iter.hasNext();) {
jlaskey@3 2193 final Type t = iter.next();
jlaskey@3 2194
jlaskey@3 2195 if (t == Type.SCOPE) {
jlaskey@3 2196 sb.append("scope");
jlaskey@3 2197 } else if (t == Type.THIS) {
jlaskey@3 2198 sb.append("this");
jlaskey@3 2199 } else if (t.isObject()) {
jlaskey@3 2200 String desc = t.getDescriptor();
jlaskey@3 2201 final int slash = desc.lastIndexOf('/');
jlaskey@3 2202 if (slash != -1) {
jlaskey@3 2203 desc = desc.substring(slash + 1, desc.length() - 1);
jlaskey@3 2204 }
jlaskey@3 2205 if ("Object".equals(desc)) {
jlaskey@3 2206 sb.append('O');
jlaskey@3 2207 } else {
jlaskey@3 2208 sb.append(desc);
jlaskey@3 2209 }
jlaskey@3 2210 } else {
jlaskey@3 2211 sb.append(t.getDescriptor());
jlaskey@3 2212 }
jlaskey@3 2213
jlaskey@3 2214 if (iter.hasNext()) {
jlaskey@3 2215 sb.append(' ');
jlaskey@3 2216 }
jlaskey@3 2217 }
jlaskey@3 2218 sb.append('}');
jlaskey@3 2219 sb.append(' ');
jlaskey@3 2220 }
jlaskey@3 2221
jlaskey@3 2222 pad = padConstant - sb.length();
jlaskey@3 2223 while (pad > 0) {
jlaskey@3 2224 sb.append(' ');
jlaskey@3 2225 pad--;
jlaskey@3 2226 }
jlaskey@3 2227
jlaskey@3 2228 for (final Object arg : args) {
jlaskey@3 2229 sb.append(arg);
jlaskey@3 2230 sb.append(' ');
jlaskey@3 2231 }
jlaskey@3 2232
jlaskey@3 2233 if (context != null) { //early bootstrap code doesn't have inited context yet
jlaskey@3 2234 LOG.info(sb.toString());
jlaskey@3 2235 if (DEBUG_TRACE_LINE == linePrefix) {
jlaskey@3 2236 new Throwable().printStackTrace(LOG.getOutputStream());
jlaskey@3 2237 }
jlaskey@3 2238 }
jlaskey@3 2239
jlaskey@3 2240 }
jlaskey@3 2241 }
jlaskey@3 2242
jlaskey@3 2243
jlaskey@3 2244 /**
jlaskey@3 2245 * Abstraction for labels, separating a label from the underlying
jlaskey@3 2246 * byte code emitter. Also augmenting label with e.g. a name
jlaskey@3 2247 * for easier debugging and reading code
jlaskey@3 2248 *
jlaskey@3 2249 * see -Dnashorn.codegen.debug, --log=codegen
jlaskey@3 2250 */
jlaskey@3 2251 public static class Label extends jdk.internal.org.objectweb.asm.Label {
jlaskey@3 2252 /** Name of this label */
jlaskey@3 2253 private final String name;
jlaskey@3 2254
jlaskey@3 2255 /** Type stack at this label */
jlaskey@3 2256 private ArrayDeque<Type> stack;
jlaskey@3 2257
jlaskey@3 2258 /**
jlaskey@3 2259 * Constructor
jlaskey@3 2260 *
jlaskey@3 2261 * @param name name of this label
jlaskey@3 2262 */
jlaskey@3 2263 public Label(final String name) {
jlaskey@3 2264 super();
jlaskey@3 2265 this.name = name;
jlaskey@3 2266 }
jlaskey@3 2267
jlaskey@3 2268 /**
jlaskey@3 2269 * Copy constructor
jlaskey@3 2270 *
jlaskey@3 2271 * @param label a label to clone
jlaskey@3 2272 */
jlaskey@3 2273 public Label(final Label label) {
jlaskey@3 2274 super();
jlaskey@3 2275 name = label.name;
jlaskey@3 2276 }
jlaskey@3 2277
jlaskey@3 2278 ArrayDeque<Type> getStack() {
jlaskey@3 2279 return stack;
jlaskey@3 2280 }
jlaskey@3 2281
jlaskey@3 2282 void setStack(final ArrayDeque<Type> stack) {
jlaskey@3 2283 this.stack = stack;
jlaskey@3 2284 }
jlaskey@3 2285
jlaskey@3 2286 @Override
jlaskey@3 2287 public String toString() {
jlaskey@3 2288 final StringBuilder sb = new StringBuilder();
jlaskey@3 2289 String s = super.toString();
jlaskey@3 2290 s = s.substring(1, s.length());
jlaskey@3 2291 sb.append(name).append('_').append(Long.toHexString(Long.parseLong(s)));
jlaskey@3 2292
jlaskey@3 2293 return sb.toString();
jlaskey@3 2294 }
jlaskey@3 2295 }
jlaskey@3 2296
jlaskey@3 2297 /**
jlaskey@3 2298 * Condition enum used for all kinds of jumps, regardless of type
jlaskey@3 2299 */
jlaskey@3 2300 static enum Condition {
jlaskey@3 2301 EQ,
jlaskey@3 2302 NE,
jlaskey@3 2303 LE,
jlaskey@3 2304 LT,
jlaskey@3 2305 GE,
jlaskey@3 2306 GT;
jlaskey@3 2307
jlaskey@3 2308 public static Condition forRuntimeRequest(final RuntimeNode.Request request) {
jlaskey@3 2309 try {
jlaskey@3 2310 final String reqString = request.toString().replace("_STRICT", "");
jlaskey@3 2311 return Condition.valueOf(reqString);
jlaskey@3 2312 } catch (final IllegalArgumentException e) {
jlaskey@3 2313 return null;
jlaskey@3 2314 }
jlaskey@3 2315 }
jlaskey@3 2316
jlaskey@3 2317 public static int toUnary(final Condition c) {
jlaskey@3 2318 switch (c) {
jlaskey@3 2319 case EQ:
jlaskey@3 2320 return IFEQ;
jlaskey@3 2321 case NE:
jlaskey@3 2322 return IFNE;
jlaskey@3 2323 case LE:
jlaskey@3 2324 return IFLE;
jlaskey@3 2325 case LT:
jlaskey@3 2326 return IFLT;
jlaskey@3 2327 case GE:
jlaskey@3 2328 return IFGE;
jlaskey@3 2329 case GT:
jlaskey@3 2330 return IFGT;
jlaskey@3 2331 default:
jlaskey@3 2332 assert false;
jlaskey@3 2333 return -1;
jlaskey@3 2334 }
jlaskey@3 2335 }
jlaskey@3 2336
jlaskey@3 2337 public static int toBinary(final Condition c) {
jlaskey@3 2338 return toBinary(c, false);
jlaskey@3 2339 }
jlaskey@3 2340
jlaskey@3 2341 public static int toBinary(final Condition c, final boolean isObject) {
jlaskey@3 2342 switch (c) {
jlaskey@3 2343 case EQ:
jlaskey@3 2344 return isObject ? IF_ACMPEQ : IF_ICMPEQ;
jlaskey@3 2345 case NE:
jlaskey@3 2346 return isObject ? IF_ACMPNE : IF_ICMPNE;
jlaskey@3 2347 case LE:
jlaskey@3 2348 return IF_ICMPLE;
jlaskey@3 2349 case LT:
jlaskey@3 2350 return IF_ICMPLT;
jlaskey@3 2351 case GE:
jlaskey@3 2352 return IF_ICMPGE;
jlaskey@3 2353 case GT:
jlaskey@3 2354 return IF_ICMPGT;
jlaskey@3 2355 default:
jlaskey@3 2356 assert false;
jlaskey@3 2357 return -1;
jlaskey@3 2358 }
jlaskey@3 2359 }
jlaskey@3 2360 }
jlaskey@3 2361
jlaskey@3 2362 /**
jlaskey@3 2363 * Set the current function node being emitted
jlaskey@3 2364 * @param functionNode the function node
jlaskey@3 2365 */
jlaskey@3 2366 public void setFunctionNode(final FunctionNode functionNode) {
jlaskey@3 2367 this.functionNode = functionNode;
jlaskey@3 2368 }
jlaskey@3 2369
jlaskey@3 2370 /**
jlaskey@3 2371 * Get the split node for this method emitter, if this is code
jlaskey@3 2372 * generation due to splitting large methods
jlaskey@3 2373 *
jlaskey@3 2374 * @return split node
jlaskey@3 2375 */
jlaskey@3 2376 public SplitNode getSplitNode() {
jlaskey@3 2377 return splitNode;
jlaskey@3 2378 }
jlaskey@3 2379
jlaskey@3 2380 /**
jlaskey@3 2381 * Set the split node for this method emitter
jlaskey@3 2382 * @param splitNode split node
jlaskey@3 2383 */
jlaskey@3 2384 public void setSplitNode(final SplitNode splitNode) {
jlaskey@3 2385 this.splitNode = splitNode;
jlaskey@3 2386 }
jlaskey@3 2387 }

mercurial