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