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