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