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