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 }