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

Tue, 08 Jan 2013 08:51:00 +0530

author
sundar
date
Tue, 08 Jan 2013 08:51:00 +0530
changeset 10
d14da0d0c577
parent 7
5a1b0714df0e
child 57
59970b70ebb5
permissions
-rw-r--r--

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

mercurial