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