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