1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang.invoke; 27 28 import jdk.experimental.bytecode.*; 29 import jdk.experimental.bytecode.MacroCodeBuilder.FieldAccessKind; 30 import jdk.experimental.bytecode.MacroCodeBuilder.InvocationKind; 31 import jdk.experimental.value.MethodHandleBuilder; 32 import sun.invoke.util.VerifyType; 33 import sun.invoke.util.Wrapper; 34 35 import java.lang.invoke.LambdaForm.BasicType; 36 import java.lang.invoke.LambdaForm.Name; 37 import java.lang.invoke.MethodHandles.Lookup; 38 import java.util.Arrays; 39 import java.util.stream.Stream; 40 41 import static java.lang.invoke.LambdaForm.BasicType.L_TYPE; 42 import static java.lang.invoke.LambdaForm.BasicType.V_TYPE; 43 import static java.lang.invoke.LambdaForm.BasicType.basicType; 44 import static java.lang.invoke.MethodHandleNatives.Constants.REF_getField; 45 import static java.lang.invoke.MethodHandleNatives.Constants.REF_getStatic; 46 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeInterface; 47 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeSpecial; 48 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic; 49 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeVirtual; 50 import static java.lang.invoke.MethodHandleNatives.Constants.REF_putField; 51 import static java.lang.invoke.MethodHandleNatives.Constants.REF_putStatic; 52 import static java.lang.invoke.MethodHandleStatics.PROFILE_GWT; 53 import static java.lang.invoke.MethodHandleStatics.PROFILE_LEVEL; 54 import static java.lang.invoke.MethodHandleStatics.newInternalError; 55 import static jdk.experimental.bytecode.MacroCodeBuilder.CondKind.EQ; 56 import static jdk.experimental.bytecode.MacroCodeBuilder.CondKind.NE; 57 58 /** 59 * Utility class for spinning classfiles associated with lambda forms. 60 */ 61 class LambdaFormBuilder extends MethodHandleBuilder { 62 63 private static final String OBJ = "java/lang/Object"; 64 private static final String CLASS_PREFIX = "java/lang/invoke/LambdaForm$Value$"; 65 private static final String DEFAULT_CLASS = "MH"; 66 private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";"; 67 private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V"; 68 69 private static final String MH = "java/lang/invoke/MethodHandle"; 70 private static final String MHARY2 = "[[L" + MH + ";"; 71 72 static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) { 73 String invokerName = form.lambdaName(); 74 int p = invokerName.indexOf('.'); 75 boolean overrideNames = p != -1; 76 String methodName = overrideNames ? invokerName.substring(p + 1) : invokerName; 77 String className = overrideNames ? 78 CLASS_PREFIX + invokerName.substring(0, p) : 79 CLASS_PREFIX + DEFAULT_CLASS; 80 return MethodHandleBuilder.loadCode(Lookup.IMPL_LOOKUP.in(LambdaForm.class), className, methodName, invokerType.toMethodDescriptorString(), 81 M -> new LambdaFormCodeBuilder(form, invokerType, M), clazz -> InvokerBytecodeGenerator.resolveInvokerMember(clazz, methodName, invokerType), 82 C -> new LambdaFormBuilder(C, form).generateLambdaFormBody()); 83 } 84 85 LambdaFormCodeBuilder builder; 86 LambdaForm lambdaForm; 87 88 LambdaFormBuilder(LambdaFormCodeBuilder builder, LambdaForm lambdaForm) { 89 this.builder = builder; 90 this.lambdaForm = lambdaForm; 91 } 92 93 /** Generates code to check that actual receiver and LambdaForm matches */ 94 private boolean checkActualReceiver() { 95 // Expects MethodHandle on the stack and actual receiver MethodHandle in slot #0 96 builder.dup().load(0).invokestatic(MethodHandleImpl.class, "assertSame", LLV_SIG, false); 97 return true; 98 } 99 100 void generateLambdaFormBody() { 101 if (lambdaForm.customized != null && MethodHandleBuilder.ENABLE_POOL_PATCHES) { 102 // Since LambdaForm is customized for a particular MethodHandle, it's safe to substitute 103 // receiver MethodHandle (at slot #0) with an embedded constant and use it instead. 104 // It enables more efficient code generation in some situations, since embedded constants 105 // are compile-time constants for JIT compiler. 106 builder.ldc(lambdaForm.customized) 107 .checkcast(MethodHandle.class); 108 assert(checkActualReceiver()); // generates runtime check 109 builder.store(0); 110 } 111 112 // iterate over the form's names, generating bytecode instructions for each 113 // start iterating at the first name following the arguments 114 Name onStack = null; 115 for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) { 116 Name name = lambdaForm.names[i]; 117 118 if (onStack != null && onStack.type != V_TYPE) { 119 // non-void: actually assign 120 builder.store(fromBasicType(onStack.type), onStack.index()); 121 } 122 onStack = name; // unless otherwise modified below 123 MemberName member = name.function.member(); 124 MethodHandleImpl.Intrinsic intr = name.function.intrinsicName(); 125 switch (intr) { 126 case SELECT_ALTERNATIVE: { 127 assert lambdaForm.isSelectAlternative(i); 128 if (PROFILE_GWT) { 129 assert(name.arguments[0] instanceof Name && 130 ((Name)name.arguments[0]).refersTo(MethodHandleImpl.class, "profileBoolean")); 131 builder.method().withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, InvokerBytecodeGenerator.INJECTEDPROFILE_SIG); 132 } 133 onStack = emitSelectAlternative(name, lambdaForm.names[i+1]); 134 i++; // skip MH.invokeBasic of the selectAlternative result 135 continue; 136 137 } 138 139 case LOOP: { 140 assert lambdaForm.isLoop(i); 141 onStack = emitLoop(i); 142 i += 2; // jump to the end of the LOOP idiom 143 continue; 144 } 145 case IDENTITY: { 146 assert (name.arguments.length == 1); 147 builder.pushArguments(name, 0); 148 continue; 149 } 150 case ZERO: { 151 assert (name.arguments.length == 0); 152 builder.ldc(name.type.basicTypeWrapper().zero()); 153 continue; 154 } 155 // TODO: case GUARD_WITH_CATCH: 156 // TODO: case TRY_FINALLY: 157 // TODO: case NEW_ARRAY: 158 // TODO: case ARRAY_LOAD: 159 // TODO: case ARRAY_STORE: 160 // TODO: case ARRAY_LENGTH: 161 } 162 if (InvokerBytecodeGenerator.isStaticallyInvocable(member)) { 163 builder.invokeStaticName(member, name); 164 } else { 165 builder.invokeName(name); 166 } 167 } 168 builder.return_(onStack); 169 } 170 171 private Name emitLoop(int pos) { 172 Name args = lambdaForm.names[pos]; 173 Name invoker = lambdaForm.names[pos+1]; 174 Name result = lambdaForm.names[pos+2]; 175 176 // extract clause and loop-local state types 177 // find the type info in the loop invocation 178 BasicType[] loopClauseTypes = (BasicType[]) invoker.arguments[0]; 179 Class<?>[] loopLocalStateTypes = Stream.of(loopClauseTypes). 180 filter(bt -> bt != BasicType.V_TYPE).map(BasicType::basicTypeClass).toArray(Class<?>[]::new); 181 182 Class<?>[] localTypes = new Class<?>[loopLocalStateTypes.length + 1]; 183 localTypes[0] = MethodHandleImpl.LoopClauses.class; 184 System.arraycopy(loopLocalStateTypes, 0, localTypes, 1, loopLocalStateTypes.length); 185 186 final int clauseDataIndex = builder.extendLocalsMap(localTypes); 187 final int firstLoopStateIndex = clauseDataIndex + 1; 188 189 Class<?> returnType = result.function.resolvedHandle().type().returnType(); 190 MethodType loopType = args.function.resolvedHandle().type() 191 .dropParameterTypes(0,1) 192 .changeReturnType(returnType); 193 MethodType loopHandleType = loopType.insertParameterTypes(0, loopLocalStateTypes); 194 MethodType predType = loopHandleType.changeReturnType(boolean.class); 195 MethodType finiType = loopHandleType; 196 197 final int nClauses = loopClauseTypes.length; 198 199 // indices to invoker arguments to load method handle arrays 200 final int inits = 1; 201 final int steps = 2; 202 final int preds = 3; 203 final int finis = 4; 204 205 // PREINIT: 206 builder.pushArgument(MethodHandleImpl.LoopClauses.class, invoker.arguments[1]) 207 .getfield(MethodHandleImpl.LoopClauses.class, "clauses", MHARY2) 208 .store(TypeTag.A, clauseDataIndex); 209 210 // INIT: 211 for (int c = 0, state = 0; c < nClauses; ++c) { 212 MethodType cInitType = loopType.changeReturnType(loopClauseTypes[c].basicTypeClass()); 213 builder.invokeLoopHandle(inits, c, args, false, cInitType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex); 214 if (cInitType.returnType() != void.class) { 215 builder.store(fromClass(cInitType.returnType()), firstLoopStateIndex + state); 216 ++state; 217 } 218 } 219 220 // LOOP: 221 String loopLabel = builder.label(); 222 String doneLabel = builder.label(); 223 builder.label(loopLabel); 224 225 String val = null; 226 for (int c = 0, s = 0; c < nClauses; ++c) { 227 MethodType stepType = loopHandleType.changeReturnType(loopClauseTypes[c].basicTypeClass()); 228 boolean isVoid = (stepType.returnType() == void.class); 229 230 // invoke loop step 231 builder.invokeLoopHandle(steps, c, args, true, stepType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex); 232 if (!isVoid) { 233 builder.store(fromClass(stepType.returnType()), firstLoopStateIndex + s); 234 ++s; 235 } 236 237 String nextLabel = builder.label(); 238 239 // invoke loop predicate 240 builder.invokeLoopHandle(preds, c, args, true, predType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex) 241 .emitCondJump(Opcode.IFEQ, NE, nextLabel) 242 .invokeLoopHandle(finis, c, args, true, finiType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex) 243 .goto_(doneLabel) 244 .label(nextLabel); 245 } 246 builder.goto_(loopLabel) 247 .label(doneLabel); 248 249 return result; 250 } 251 252 /** 253 * Emit bytecode for the selectAlternative idiom. 254 * 255 * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest): 256 * <blockquote><pre>{@code 257 * Lambda(a0:L,a1:I)=>{ 258 * t2:I=foo.test(a1:I); 259 * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int)); 260 * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I} 261 * }</pre></blockquote> 262 */ 263 private Name emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) { 264 assert InvokerBytecodeGenerator.isStaticallyInvocable(invokeBasicName); 265 266 Name receiver = (Name) invokeBasicName.arguments[0]; 267 268 String fallbackLabel = builder.label(); 269 String doneLabel = builder.label(); 270 271 builder.pushArgument(selectAlternativeName, 0) // load test result 272 .emitCondJump(Opcode.IFEQ, EQ, fallbackLabel) // if_icmp L_fallback 273 .pushArgument(selectAlternativeName, 1) // get 2nd argument of selectAlternative 274 .store(TypeTag.A, receiver.index()) // store the MH in the receiver slot 275 .invokeStaticName(invokeBasicName) // invoke selectAlternativeName.arguments[1] 276 .goto_(doneLabel) 277 .label(fallbackLabel) 278 .pushArgument(selectAlternativeName, 2) // get 3rd argument of selectAlternative 279 .store(TypeTag.A, receiver.index()) // store the MH in the receiver slot 280 .invokeStaticName(invokeBasicName) // invoke selectAlternativeName.arguments[2] 281 .label(doneLabel); 282 283 return invokeBasicName; // return what's on stack 284 } 285 286 static class LambdaFormCodeBuilder extends MethodHandleCodeBuilder<LambdaFormCodeBuilder> { 287 288 LambdaForm lambdaForm; 289 MethodType invokerType; 290 int maxLocals; 291 int[] localsMap; 292 private MethodBuilder<Class<?>, String, byte[]> methodBuilder; 293 private int labelCount = 0; 294 295 public LambdaFormCodeBuilder(LambdaForm form, MethodType invokerType, MethodBuilder<Class<?>, String, byte[]> methodBuilder) { 296 super(methodBuilder); 297 if (form.forceInline) { 298 methodBuilder.withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljdk/internal/vm/annotation/ForceInline;"); 299 } 300 methodBuilder.withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljava/lang/invoke/LambdaForm$Hidden;") 301 .withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljava/lang/invoke/LambdaForm$Compiled;"); 302 this.lambdaForm = form; 303 this.invokerType = invokerType; 304 this.maxLocals = form.names.length; 305 this.localsMap = computeLocalsMap(form); 306 this.methodBuilder = methodBuilder; 307 } 308 309 static int[] computeLocalsMap(LambdaForm lform) { 310 int localsMapSize = lform.names.length; 311 int[] localsMap = new int[localsMapSize+1]; // last entry of localsMap is count of allocated local slots 312 for (int i = 0, index = 0; i < localsMap.length; i++) { 313 localsMap[i] = index; 314 if (i < lform.names.length) { 315 BasicType type = lform.names[i].type(); 316 index += type.basicTypeSlots(); 317 } 318 } 319 return localsMap; 320 } 321 322 @Override 323 public LambdaFormCodeBuilder load(int index) { 324 return load(tagOfLocal(index), index); 325 } 326 327 @Override 328 public LambdaFormCodeBuilder store(int index) { 329 return store(tagOfLocal(index), index); 330 } 331 332 @Override 333 public LambdaFormCodeBuilder load(TypeTag type, int n) { 334 return super.load(type, localsMap[n]); 335 } 336 337 @Override 338 public LambdaFormCodeBuilder store(TypeTag type, int n) { 339 return super.store(type, localsMap[n]); 340 } 341 342 /** 343 * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type. 344 */ 345 private void return_(Name onStack) { 346 // return statement 347 Class<?> rclass = invokerType.returnType(); 348 BasicType rtype = lambdaForm.returnType(); 349 assert(rtype == basicType(rclass)); // must agree 350 if (rtype == V_TYPE) { 351 // void 352 return_(); 353 // it doesn't matter what rclass is; the JVM will discard any value 354 } else { 355 LambdaForm.Name rn = lambdaForm.names[lambdaForm.result]; 356 357 // put return value on the stack if it is not already there 358 if (rn != onStack) { 359 load(lambdaForm.result); 360 } 361 362 coerce(rtype, rclass, rn); 363 364 // generate actual return statement 365 return_(fromBasicType(rtype)); 366 } 367 } 368 369 /** 370 * Emit an implicit conversion for an argument which must be of the given pclass. 371 * This is usually a no-op, except when pclass is a subword type or a reference other than Object or an interface. 372 * 373 * @param ptype type of value present on stack 374 * @param pclass type of value required on stack 375 * @param arg compile-time representation of value on stack (Node, constant) or null if none 376 */ 377 private LambdaFormCodeBuilder coerce(BasicType ptype, Class<?> pclass, Object arg) { 378 assert(basicType(pclass) == ptype); // boxing/unboxing handled by caller 379 if (pclass == ptype.basicTypeClass() && ptype != L_TYPE) 380 return this; // nothing to do 381 switch (ptype) { 382 case L_TYPE: 383 if (VerifyType.isNullConversion(Object.class, pclass, false)) { 384 if (PROFILE_LEVEL > 0) 385 coerce(Object.class, arg); 386 return this; 387 } 388 coerce(pclass, arg); 389 return this; 390 case I_TYPE: 391 if (!VerifyType.isNullConversion(int.class, pclass, false)) 392 conv(fromBasicType(ptype), fromBasicType(BasicType.basicType(pclass))); 393 return this; 394 } 395 throw newInternalError("bad implicit conversion: tc="+ptype+": "+pclass); 396 } 397 398 private LambdaFormCodeBuilder coerce(Class<?> cls, Object arg) { 399 Name writeBack = null; // local to write back result 400 if (arg instanceof Name) { 401 Name n = (Name) arg; 402 if (cls.isAssignableFrom(typeOfLocal(n.index()))) 403 return this; // this cast was already performed 404 if (lambdaForm.useCount(n) > 1) { 405 // This guy gets used more than once. 406 writeBack = n; 407 } 408 } 409 if (InvokerBytecodeGenerator.isStaticallyNameable(cls)) { 410 if (cls.isInlineClass()) { 411 checkvaluecast(cls); 412 } else { 413 checkcast(cls); 414 } 415 } else { 416 ldc(cls) 417 .checkcast(Class.class) 418 .swap() 419 .invokevirtual(Class.class, "cast", LL_SIG, false); 420 if (Object[].class.isAssignableFrom(cls)) 421 checkcast(Object[].class); 422 else if (PROFILE_LEVEL > 0) 423 checkcast(Object.class); 424 } 425 if (writeBack != null) { 426 dup().store(TypeTag.A, writeBack.index()); 427 } 428 return this; 429 } 430 431 LambdaFormCodeBuilder invokeStaticName(Name name) { 432 return invokeStaticName(name.function.member(), name); 433 } 434 435 /** 436 * Emit an invoke for the given name, using the MemberName directly. 437 */ 438 LambdaFormCodeBuilder invokeStaticName(MemberName member, Name name) { 439 assert(member.equals(name.function.member())); 440 Class<?> defc = member.getDeclaringClass(); 441 String mname = member.getName(); 442 String mtype; 443 byte refKind = member.getReferenceKind(); 444 if (refKind == REF_invokeSpecial) { 445 // in order to pass the verifier, we need to convert this to invokevirtual in all cases 446 assert(member.canBeStaticallyBound()) : member; 447 refKind = REF_invokeVirtual; 448 } 449 450 assert(!(member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual)); 451 452 // push arguments 453 pushArguments(name); 454 455 // invocation 456 if (member.isMethod()) { 457 mtype = member.getMethodType().toMethodDescriptorString(); 458 invoke(invKindFromRefKind(refKind), defc, mname, mtype, 459 member.getDeclaringClass().isInterface()); 460 } else { 461 mtype = MethodType.toFieldDescriptorString(member.getFieldType()); 462 getfield(fieldKindFromRefKind(refKind), defc, mname, mtype); 463 } 464 // Issue a type assertion for the result, so we can avoid casts later. 465 if (name.type == L_TYPE) { 466 Class<?> rtype = member.getInvocationType().returnType(); 467 assert(!rtype.isPrimitive()); 468 } 469 return this; 470 } 471 472 /** 473 * Emit an invoke for the given name. 474 */ 475 LambdaFormCodeBuilder invokeName(Name name) { 476 //assert(!isLinkerMethodInvoke(name)); // should use the static path for these 477 if (true) { 478 // push receiver 479 MethodHandle target = name.function.resolvedHandle(); 480 assert(target != null) : name.exprString(); 481 ldc(target); 482 coerce(MethodHandle.class, target); 483 } 484 485 // push arguments 486 pushArguments(name); 487 488 // invocation 489 MethodType type = name.function.methodType(); 490 invokevirtual(MethodHandle.class, "invokeBasic", type.basicType().toMethodDescriptorString(), false); 491 return this; 492 } 493 494 private LambdaFormCodeBuilder pushArguments(Name args) { 495 return pushArguments(args, 0); 496 } 497 498 private LambdaFormCodeBuilder pushArguments(Name args, int start) { 499 for (int i = start; i < args.arguments.length; i++) { 500 pushArgument(args, i); 501 } 502 return this; 503 } 504 505 private LambdaFormCodeBuilder pushArgument(Name name, int paramIndex) { 506 Object arg = name.arguments[paramIndex]; 507 Class<?> ptype = name.function.methodType().parameterType(paramIndex); 508 return pushArgument(ptype, arg); 509 } 510 511 private LambdaFormCodeBuilder pushArgument(Class<?> ptype, Object arg) { 512 BasicType bptype = basicType(ptype); 513 if (arg instanceof Name) { 514 Name n = (Name) arg; 515 load(fromBasicType(n.type), n.index()); 516 coerce(n.type, ptype, n); 517 } else if ((arg == null || arg instanceof String) && bptype == L_TYPE) { 518 ldc(arg); 519 } else { 520 if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) { 521 ldc(arg); 522 } else { 523 ldc(arg); 524 coerce(L_TYPE, ptype, arg); 525 } 526 } 527 return this; 528 } 529 530 private LambdaFormCodeBuilder invokeLoopHandle(int handles, int clause, Name args, boolean pushLocalState, 531 MethodType type, Class<?>[] loopLocalStateTypes, int clauseDataSlot, 532 int firstLoopStateSlot) { 533 // load handle for clause 534 load(TypeTag.A, clauseDataSlot) 535 .ldc(handles - 1) 536 .aaload() 537 .ldc(clause) 538 .aaload(); 539 540 // load loop state (preceding the other arguments) 541 if (pushLocalState) { 542 for (int s = 0; s < loopLocalStateTypes.length; ++s) { 543 load(fromClass(loopLocalStateTypes[s]), firstLoopStateSlot + s); 544 } 545 } 546 // load loop args (skip 0: method handle) 547 return pushArguments(args, 1) 548 .invokevirtual(MethodHandle.class, "invokeBasic", type.toMethodDescriptorString(), false); 549 } 550 551 MethodBuilder<Class<?>, String, byte[]> method() { 552 return methodBuilder; 553 } 554 555 String label() { 556 return "label" + labelCount++; 557 } 558 559 private int extendLocalsMap(Class<?>[] types) { 560 int firstSlot = localsMap.length - 1; 561 localsMap = Arrays.copyOf(localsMap, localsMap.length + types.length); 562 int index = localsMap[firstSlot - 1] + 1; 563 int lastSlots = 0; 564 for (int i = 0; i < types.length; ++i) { 565 localsMap[firstSlot + i] = index; 566 lastSlots = BasicType.basicType(types[i]).basicTypeSlots(); 567 index += lastSlots; 568 } 569 localsMap[localsMap.length - 1] = index - lastSlots; 570 maxLocals = types.length; 571 return firstSlot; 572 } 573 574 Class<?> typeOfLocal(int index) { 575 return typeHelper.symbol(descOfLocal(index)); 576 } 577 578 TypeTag tagOfLocal(int index) { 579 return typeHelper.tag(descOfLocal(index)); 580 } 581 582 String descOfLocal(int index) { 583 return state.locals.get(localsMap[index]); 584 } 585 } 586 587 /*** Utility methods ***/ 588 589 static TypeTag fromBasicType(BasicType type) { 590 switch (type) { 591 case I_TYPE: return TypeTag.I; 592 case J_TYPE: return TypeTag.J; 593 case F_TYPE: return TypeTag.F; 594 case D_TYPE: return TypeTag.D; 595 case L_TYPE: return TypeTag.A; 596 case V_TYPE: return TypeTag.V; 597 default: 598 throw new InternalError("unknown type: " + type); 599 } 600 } 601 602 static InvocationKind invKindFromRefKind(int refKind) { 603 switch (refKind) { 604 case REF_invokeVirtual: return InvocationKind.INVOKEVIRTUAL; 605 case REF_invokeStatic: return InvocationKind.INVOKESTATIC; 606 case REF_invokeSpecial: return InvocationKind.INVOKESPECIAL; 607 case REF_invokeInterface: return InvocationKind.INVOKEINTERFACE; 608 } 609 throw new InternalError("refKind="+refKind); 610 } 611 612 static FieldAccessKind fieldKindFromRefKind(int refKind) { 613 switch (refKind) { 614 case REF_getField: 615 case REF_putField: return FieldAccessKind.INSTANCE; 616 case REF_getStatic: 617 case REF_putStatic: return FieldAccessKind.STATIC; 618 } 619 throw new InternalError("refKind="+refKind); 620 } 621 622 static TypeTag fromClass(Class<?> cls) { 623 return fromBasicType(BasicType.basicType(cls)); 624 } 625 }