1 /*
   2  * Copyright (c) 2012, 2013, 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.internal.misc.JavaLangAccess;
  29 import jdk.internal.misc.SharedSecrets;
  30 import jdk.internal.org.objectweb.asm.*;
  31 import sun.invoke.util.BytecodeDescriptor;
  32 import jdk.internal.misc.Unsafe;
  33 import sun.security.action.GetPropertyAction;
  34 
  35 import java.io.FilePermission;
  36 import java.io.Serializable;
  37 import java.lang.reflect.Constructor;
  38 import java.security.AccessController;
  39 import java.security.PrivilegedAction;
  40 import java.util.HashSet;
  41 import java.util.LinkedHashSet;
  42 import java.util.concurrent.atomic.AtomicInteger;
  43 import java.util.PropertyPermission;
  44 import java.util.Set;
  45 
  46 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  47 
  48 /**
  49  * Lambda metafactory implementation which dynamically creates an
  50  * inner-class-like class per lambda callsite.
  51  *
  52  * @see LambdaMetafactory
  53  */
  54 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
  55     private static final Unsafe UNSAFE = Unsafe.getUnsafe();
  56     private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
  57 
  58     private static final int CLASSFILE_VERSION = 52;
  59     private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
  60     private static final String JAVA_LANG_OBJECT = "java/lang/Object";
  61     private static final String NAME_CTOR = "<init>";
  62     private static final String NAME_FACTORY = "get$Lambda";
  63 
  64     //Serialization support
  65     private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
  66     private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException";
  67     private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
  68     private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
  69     private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
  70     private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
  71     private static final String NAME_METHOD_READ_OBJECT = "readObject";
  72     private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
  73 
  74     private static final String DESCR_CLASS = "Ljava/lang/Class;";
  75     private static final String DESCR_STRING = "Ljava/lang/String;";
  76     private static final String DESCR_OBJECT = "Ljava/lang/Object;";
  77     private static final String DESCR_CTOR_SERIALIZED_LAMBDA
  78             = "(" + DESCR_CLASS + DESCR_STRING + DESCR_STRING + DESCR_STRING + "I"
  79             + DESCR_STRING + DESCR_STRING + DESCR_STRING + DESCR_STRING + "[" + DESCR_OBJECT + ")V";
  80 
  81     private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION = "(Ljava/lang/String;)V";
  82     private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
  83 
  84 
  85     private static final String[] EMPTY_STRING_ARRAY = new String[0];
  86 
  87     // Used to ensure that each spun class name is unique
  88     private static final AtomicInteger counter = new AtomicInteger(0);
  89 
  90     // For dumping generated classes to disk, for debugging purposes
  91     private static final ProxyClassesDumper dumper;
  92 
  93     static {
  94         final String key = "jdk.internal.lambda.dumpProxyClasses";
  95         String path = GetPropertyAction.privilegedGetProperty(key);
  96         dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path);
  97     }
  98 
  99     // See context values in AbstractValidatingLambdaMetafactory
 100     private final String implMethodClassName;        // Name of type containing implementation "CC"
 101     private final String implMethodName;             // Name of implementation method "impl"
 102     private final String implMethodDesc;             // Type descriptor for implementation methods "(I)Ljava/lang/String;"
 103     private final MethodType constructorType;        // Generated class constructor type "(CC)void"
 104     private final ClassWriter cw;                    // ASM class writer
 105     private final String[] argNames;                 // Generated names for the constructor arguments
 106     private final String[] argDescs;                 // Type descriptors for the constructor arguments
 107     private final String lambdaClassName;            // Generated name for the generated class "X$$Lambda$1"
 108 
 109     /**
 110      * General meta-factory constructor, supporting both standard cases and
 111      * allowing for uncommon options such as serialization or bridging.
 112      *
 113      * @param caller Stacked automatically by VM; represents a lookup context
 114      *               with the accessibility privileges of the caller.
 115      * @param invokedType Stacked automatically by VM; the signature of the
 116      *                    invoked method, which includes the expected static
 117      *                    type of the returned lambda object, and the static
 118      *                    types of the captured arguments for the lambda.  In
 119      *                    the event that the implementation method is an
 120      *                    instance method, the first argument in the invocation
 121      *                    signature will correspond to the receiver.
 122      * @param samMethodName Name of the method in the functional interface to
 123      *                      which the lambda or method reference is being
 124      *                      converted, represented as a String.
 125      * @param samMethodType Type of the method in the functional interface to
 126      *                      which the lambda or method reference is being
 127      *                      converted, represented as a MethodType.
 128      * @param implMethod The implementation method which should be called (with
 129      *                   suitable adaptation of argument types, return types,
 130      *                   and adjustment for captured arguments) when methods of
 131      *                   the resulting functional interface instance are invoked.
 132      * @param instantiatedMethodType The signature of the primary functional
 133      *                               interface method after type variables are
 134      *                               substituted with their instantiation from
 135      *                               the capture site
 136      * @param isSerializable Should the lambda be made serializable?  If set,
 137      *                       either the target type or one of the additional SAM
 138      *                       types must extend {@code Serializable}.
 139      * @param markerInterfaces Additional interfaces which the lambda object
 140      *                       should implement.
 141      * @param additionalBridges Method types for additional signatures to be
 142      *                          bridged to the implementation method
 143      * @throws LambdaConversionException If any of the meta-factory protocol
 144      * invariants are violated
 145      */
 146     public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
 147                                        MethodType invokedType,
 148                                        String samMethodName,
 149                                        MethodType samMethodType,
 150                                        MethodHandle implMethod,
 151                                        MethodType instantiatedMethodType,
 152                                        boolean isSerializable,
 153                                        Class<?>[] markerInterfaces,
 154                                        MethodType[] additionalBridges)
 155             throws LambdaConversionException {
 156         super(caller, invokedType, samMethodName, samMethodType,
 157             implMethod, instantiatedMethodType,
 158             isSerializable, markerInterfaces, additionalBridges);
 159         implMethodClassName = implClass.getName().replace('.', '/');
 160         implMethodName = implInfo.getName();
 161         implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
 162         constructorType = invokedType.changeReturnType(Void.TYPE);
 163         lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
 164         cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
 165         int parameterCount = invokedType.parameterCount();
 166         if (parameterCount > 0) {
 167             argNames = new String[parameterCount];
 168             argDescs = new String[parameterCount];
 169             for (int i = 0; i < parameterCount; i++) {
 170                 argNames[i] = "arg$" + (i + 1);
 171                 argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));
 172             }
 173         } else {
 174             argNames = argDescs = EMPTY_STRING_ARRAY;
 175         }
 176     }
 177 
 178     /**
 179      * Build the CallSite. Generate a class file which implements the functional
 180      * interface, define the class, if there are no parameters create an instance
 181      * of the class which the CallSite will return, otherwise, generate handles
 182      * which will call the class' constructor.
 183      *
 184      * @return a CallSite, which, when invoked, will return an instance of the
 185      * functional interface
 186      * @throws ReflectiveOperationException
 187      * @throws LambdaConversionException If properly formed functional interface
 188      * is not found
 189      */
 190     @Override
 191     CallSite buildCallSite() throws LambdaConversionException {
 192         final Class<?> innerClass = spinInnerClass();
 193         if (invokedType.parameterCount() == 0) {
 194             final Constructor<?>[] ctrs = AccessController.doPrivileged(
 195                     new PrivilegedAction<>() {
 196                 @Override
 197                 public Constructor<?>[] run() {
 198                     Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
 199                     if (ctrs.length == 1) {
 200                         // The lambda implementing inner class constructor is private, set
 201                         // it accessible (by us) before creating the constant sole instance
 202                         ctrs[0].setAccessible(true);
 203                     }
 204                     return ctrs;
 205                 }
 206                     });
 207             if (ctrs.length != 1) {
 208                 throw new LambdaConversionException("Expected one lambda constructor for "
 209                         + innerClass.getCanonicalName() + ", got " + ctrs.length);
 210             }
 211 
 212             try {
 213                 Object inst = ctrs[0].newInstance();
 214                 return new ConstantCallSite(MethodHandles.constant(samBase, inst));
 215             }
 216             catch (ReflectiveOperationException e) {
 217                 throw new LambdaConversionException("Exception instantiating lambda object", e);
 218             }
 219         } else {
 220             try {
 221                 UNSAFE.ensureClassInitialized(innerClass);
 222                 return new ConstantCallSite(
 223                         MethodHandles.Lookup.IMPL_LOOKUP
 224                              .findStatic(innerClass, NAME_FACTORY, invokedType));
 225             }
 226             catch (ReflectiveOperationException e) {
 227                 throw new LambdaConversionException("Exception finding constructor", e);
 228             }
 229         }
 230     }
 231 
 232     /**
 233      * Generate a class file which implements the functional
 234      * interface, define and return the class.
 235      *
 236      * @implNote The class that is generated does not include signature
 237      * information for exceptions that may be present on the SAM method.
 238      * This is to reduce classfile size, and is harmless as checked exceptions
 239      * are erased anyway, no one will ever compile against this classfile,
 240      * and we make no guarantees about the reflective properties of lambda
 241      * objects.
 242      *
 243      * @return a Class which implements the functional interface
 244      * @throws LambdaConversionException If properly formed functional interface
 245      * is not found
 246      */
 247     private Class<?> spinInnerClass() throws LambdaConversionException {
 248         String[] interfaces;
 249         String samIntf = samBase.getName().replace('.', '/');
 250         boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase);
 251         if (markerInterfaces.length == 0) {
 252             interfaces = new String[]{samIntf};
 253         } else {
 254             // Assure no duplicate interfaces (ClassFormatError)
 255             Set<String> itfs = new LinkedHashSet<>(markerInterfaces.length + 1);
 256             itfs.add(samIntf);
 257             for (Class<?> markerInterface : markerInterfaces) {
 258                 itfs.add(markerInterface.getName().replace('.', '/'));
 259                 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);
 260             }
 261             interfaces = itfs.toArray(new String[itfs.size()]);
 262         }
 263 
 264         cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
 265                  lambdaClassName, null,
 266                  JAVA_LANG_OBJECT, interfaces);
 267 
 268         // Generate final fields to be filled in by constructor
 269         for (int i = 0; i < argDescs.length; i++) {
 270             FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
 271                                             argNames[i],
 272                                             argDescs[i],
 273                                             null, null);
 274             fv.visitEnd();
 275         }
 276 
 277         generateConstructor();
 278 
 279         if (invokedType.parameterCount() != 0) {
 280             generateFactory();
 281         }
 282 
 283         // Forward the SAM method
 284         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
 285                                           samMethodType.toMethodDescriptorString(), null, null);
 286         mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
 287         new ForwardingMethodGenerator(mv).generate(samMethodType);
 288 
 289         // Forward the bridges
 290         if (additionalBridges != null) {
 291             for (MethodType mt : additionalBridges) {
 292                 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
 293                                     mt.toMethodDescriptorString(), null, null);
 294                 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
 295                 new ForwardingMethodGenerator(mv).generate(mt);
 296             }
 297         }
 298 
 299         if (isSerializable)
 300             generateSerializationFriendlyMethods();
 301         else if (accidentallySerializable)
 302             generateSerializationHostileMethods();
 303 
 304         // add ValueTypes attribute
 305         Set<String> valueTypeNames = JLA.getDeclaredValueTypeNames(targetClass);
 306         if (!valueTypeNames.isEmpty()) {
 307             ValueTypesAttributeBuilder builder = new ValueTypesAttributeBuilder(valueTypeNames);
 308             builder.add(invokedType)
 309                    .add(samMethodType)
 310                    .add(implMethodType)
 311                    .add(instantiatedMethodType)
 312                    .add(additionalBridges);
 313             if (!builder.isEmpty())
 314                 cw.visitAttribute(builder.build());
 315         }
 316         cw.visitEnd();
 317 
 318         // Define the generated class in this VM.
 319 
 320         final byte[] classBytes = cw.toByteArray();
 321 
 322         // If requested, dump out to a file for debugging purposes
 323         if (dumper != null) {
 324             AccessController.doPrivileged(new PrivilegedAction<>() {
 325                 @Override
 326                 public Void run() {
 327                     dumper.dumpClass(lambdaClassName, classBytes);
 328                     return null;
 329                 }
 330             }, null,
 331             new FilePermission("<<ALL FILES>>", "read, write"),
 332             // createDirectories may need it
 333             new PropertyPermission("user.dir", "read"));
 334         }
 335 
 336         return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
 337     }
 338 
 339     /**
 340      * Generate the factory method for the class
 341      */
 342     private void generateFactory() {
 343         MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null);
 344         m.visitCode();
 345         m.visitTypeInsn(NEW, lambdaClassName);
 346         m.visitInsn(Opcodes.DUP);
 347         int parameterCount = invokedType.parameterCount();
 348         for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) {
 349             Class<?> argType = invokedType.parameterType(typeIndex);
 350             m.visitVarInsn(getLoadOpcode(argType), varIndex);
 351             varIndex += getParameterSize(argType);
 352         }
 353         m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false);
 354         m.visitInsn(ARETURN);
 355         m.visitMaxs(-1, -1);
 356         m.visitEnd();
 357     }
 358 
 359     /**
 360      * Generate the constructor for the class
 361      */
 362     private void generateConstructor() {
 363         // Generate constructor
 364         MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
 365                                             constructorType.toMethodDescriptorString(), null, null);
 366         ctor.visitCode();
 367         ctor.visitVarInsn(ALOAD, 0);
 368         ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
 369                              METHOD_DESCRIPTOR_VOID, false);
 370         int parameterCount = invokedType.parameterCount();
 371         for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
 372             ctor.visitVarInsn(ALOAD, 0);
 373             Class<?> argType = invokedType.parameterType(i);
 374             ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
 375             lvIndex += getParameterSize(argType);
 376             ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);
 377         }
 378         ctor.visitInsn(RETURN);
 379         // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
 380         ctor.visitMaxs(-1, -1);
 381         ctor.visitEnd();
 382     }
 383 
 384     /**
 385      * Generate a writeReplace method that supports serialization
 386      */
 387     private void generateSerializationFriendlyMethods() {
 388         TypeConvertingMethodAdapter mv
 389                 = new TypeConvertingMethodAdapter(
 390                     cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
 391                     NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
 392                     null, null));
 393 
 394         mv.visitCode();
 395         mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
 396         mv.visitInsn(DUP);
 397         mv.visitLdcInsn(Type.getType(targetClass));
 398         mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/'));
 399         mv.visitLdcInsn(samMethodName);
 400         mv.visitLdcInsn(samMethodType.toMethodDescriptorString());
 401         mv.visitLdcInsn(implInfo.getReferenceKind());
 402         mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/'));
 403         mv.visitLdcInsn(implInfo.getName());
 404         mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString());
 405         mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString());
 406         mv.iconst(argDescs.length);
 407         mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT);
 408         for (int i = 0; i < argDescs.length; i++) {
 409             mv.visitInsn(DUP);
 410             mv.iconst(i);
 411             mv.visitVarInsn(ALOAD, 0);
 412             mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
 413             mv.boxIfTypePrimitive(Type.getType(argDescs[i]));
 414             mv.visitInsn(AASTORE);
 415         }
 416         mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
 417                 DESCR_CTOR_SERIALIZED_LAMBDA, false);
 418         mv.visitInsn(ARETURN);
 419         // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
 420         mv.visitMaxs(-1, -1);
 421         mv.visitEnd();
 422     }
 423 
 424     /**
 425      * Generate a readObject/writeObject method that is hostile to serialization
 426      */
 427     private void generateSerializationHostileMethods() {
 428         MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
 429                                           NAME_METHOD_WRITE_OBJECT, DESCR_METHOD_WRITE_OBJECT,
 430                                           null, SER_HOSTILE_EXCEPTIONS);
 431         mv.visitCode();
 432         mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
 433         mv.visitInsn(DUP);
 434         mv.visitLdcInsn("Non-serializable lambda");
 435         mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
 436                            DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false);
 437         mv.visitInsn(ATHROW);
 438         mv.visitMaxs(-1, -1);
 439         mv.visitEnd();
 440 
 441         mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
 442                             NAME_METHOD_READ_OBJECT, DESCR_METHOD_READ_OBJECT,
 443                             null, SER_HOSTILE_EXCEPTIONS);
 444         mv.visitCode();
 445         mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
 446         mv.visitInsn(DUP);
 447         mv.visitLdcInsn("Non-serializable lambda");
 448         mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
 449                            DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false);
 450         mv.visitInsn(ATHROW);
 451         mv.visitMaxs(-1, -1);
 452         mv.visitEnd();
 453     }
 454 
 455     /**
 456      * This class generates a method body which calls the lambda implementation
 457      * method, converting arguments, as needed.
 458      */
 459     private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
 460 
 461         ForwardingMethodGenerator(MethodVisitor mv) {
 462             super(mv);
 463         }
 464 
 465         void generate(MethodType methodType) {
 466             visitCode();
 467 
 468             if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
 469                 visitTypeInsn(NEW, implMethodClassName);
 470                 visitInsn(DUP);
 471             }
 472             for (int i = 0; i < argNames.length; i++) {
 473                 visitVarInsn(ALOAD, 0);
 474                 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
 475             }
 476 
 477             convertArgumentTypes(methodType);
 478 
 479             // Invoke the method we want to forward to
 480             visitMethodInsn(invocationOpcode(), implMethodClassName,
 481                             implMethodName, implMethodDesc,
 482                             implClass.isInterface());
 483 
 484             // Convert the return value (if any) and return it
 485             // Note: if adapting from non-void to void, the 'return'
 486             // instruction will pop the unneeded result
 487             Class<?> implReturnClass = implMethodType.returnType();
 488             Class<?> samReturnClass = methodType.returnType();
 489             convertType(implReturnClass, samReturnClass, samReturnClass);
 490             visitInsn(getReturnOpcode(samReturnClass));
 491             // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
 492             visitMaxs(-1, -1);
 493             visitEnd();
 494         }
 495 
 496         private void convertArgumentTypes(MethodType samType) {
 497             int lvIndex = 0;
 498             int samParametersLength = samType.parameterCount();
 499             int captureArity = invokedType.parameterCount();
 500             for (int i = 0; i < samParametersLength; i++) {
 501                 Class<?> argType = samType.parameterType(i);
 502                 visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
 503                 lvIndex += getParameterSize(argType);
 504                 convertType(argType, implMethodType.parameterType(captureArity + i), instantiatedMethodType.parameterType(i));
 505             }
 506         }
 507 
 508         private int invocationOpcode() throws InternalError {
 509             switch (implKind) {
 510                 case MethodHandleInfo.REF_invokeStatic:
 511                     return INVOKESTATIC;
 512                 case MethodHandleInfo.REF_newInvokeSpecial:
 513                     return INVOKESPECIAL;
 514                  case MethodHandleInfo.REF_invokeVirtual:
 515                     return INVOKEVIRTUAL;
 516                 case MethodHandleInfo.REF_invokeInterface:
 517                     return INVOKEINTERFACE;
 518                 case MethodHandleInfo.REF_invokeSpecial:
 519                     return INVOKESPECIAL;
 520                 default:
 521                     throw new InternalError("Unexpected invocation kind: " + implKind);
 522             }
 523         }
 524     }
 525 
 526     static int getParameterSize(Class<?> c) {
 527         if (c == Void.TYPE) {
 528             return 0;
 529         } else if (c == Long.TYPE || c == Double.TYPE) {
 530             return 2;
 531         }
 532         return 1;
 533     }
 534 
 535     static int getLoadOpcode(Class<?> c) {
 536         if(c == Void.TYPE) {
 537             throw new InternalError("Unexpected void type of load opcode");
 538         }
 539         return ILOAD + getOpcodeOffset(c);
 540     }
 541 
 542     static int getReturnOpcode(Class<?> c) {
 543         if(c == Void.TYPE) {
 544             return RETURN;
 545         }
 546         return IRETURN + getOpcodeOffset(c);
 547     }
 548 
 549     private static int getOpcodeOffset(Class<?> c) {
 550         if (c.isPrimitive()) {
 551             if (c == Long.TYPE) {
 552                 return 1;
 553             } else if (c == Float.TYPE) {
 554                 return 2;
 555             } else if (c == Double.TYPE) {
 556                 return 3;
 557             }
 558             return 0;
 559         } else {
 560             return 4;
 561         }
 562     }
 563 
 564     /*
 565      * Build ValueTypes attribute
 566      */
 567     static class ValueTypesAttributeBuilder {
 568         private final Set<String> declaredValueTypes;
 569         private final Set<String> valueTypes;
 570         ValueTypesAttributeBuilder(Set<String> valueTypeNames) {
 571             this.declaredValueTypes = valueTypeNames;
 572             this.valueTypes = new HashSet<>();
 573         }
 574 
 575         /*
 576          * Add the value types referenced in the given MethodType.
 577          */
 578         ValueTypesAttributeBuilder add(MethodType mt) {
 579             // parameter types
 580             for (Class<?> paramType : mt.ptypes()) {
 581                 if (isDeclaredValueType(paramType))
 582                     valueTypes.add(paramType.getName());
 583             }
 584             // return type
 585             if (isDeclaredValueType(mt.returnType()))
 586                 valueTypes.add(mt.returnType().getName());
 587             return this;
 588         }
 589 
 590         ValueTypesAttributeBuilder add(MethodType... mtypes) {
 591             for (MethodType mt : mtypes) {
 592                 add(mt);
 593             }
 594             return this;
 595         }
 596 
 597         private boolean isDeclaredValueType(Class<?> c) {
 598             while (c.isArray())
 599                 c = c.getComponentType();
 600             return declaredValueTypes.contains(c.getName());
 601         }
 602 
 603         boolean isEmpty() {
 604             return valueTypes.isEmpty();
 605         }
 606 
 607         Attribute build() {
 608             return new Attribute("ValueTypes") {
 609                 @Override
 610                 protected ByteVector write(ClassWriter cw,
 611                                            byte[] code,
 612                                            int len,
 613                                            int maxStack,
 614                                            int maxLocals) {
 615                     ByteVector attr = new ByteVector();
 616                     attr.putShort(valueTypes.size());
 617                     for (String cn : valueTypes) {
 618                         attr.putShort(cw.newClass(cn.replace('.', '/')));
 619                     }
 620                     return attr;
 621                 }
 622             };
 623         }
 624     }
 625 
 626 }