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