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