< prev index next >

src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java

Print this page
rev 58565 : 8238358: Implementation of JEP 371: Hidden Classes
Reviewed-by: duke
Contributed-by: mandy.chung@oracle.com, lois.foltan@oracle.com, david.holmes@oracle.com, harold.seigel@oracle.com, serguei.spitsyn@oracle.com, alex.buckley@oracle.com, jamsheed.c.m@oracle.com


  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 import sun.security.action.GetBooleanAction;
  33 
  34 import java.io.FilePermission;
  35 import java.io.Serializable;

  36 import java.lang.reflect.Constructor;

  37 import java.security.AccessController;
  38 import java.security.PrivilegedAction;
  39 import java.util.LinkedHashSet;
  40 import java.util.concurrent.atomic.AtomicInteger;
  41 import java.util.PropertyPermission;
  42 import java.util.Set;
  43 


  44 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  45 
  46 /**
  47  * Lambda metafactory implementation which dynamically creates an
  48  * inner-class-like class per lambda callsite.
  49  *
  50  * @see LambdaMetafactory
  51  */
  52 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
  53     private static final Unsafe UNSAFE = Unsafe.getUnsafe();
  54 
  55     private static final int CLASSFILE_VERSION = 52;
  56     private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
  57     private static final String JAVA_LANG_OBJECT = "java/lang/Object";
  58     private static final String NAME_CTOR = "<init>";
  59     private static final String NAME_FACTORY = "get$Lambda";
  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 NAME_METHOD_WRITE_REPLACE = "writeReplace";
  68     private static final String NAME_METHOD_READ_OBJECT = "readObject";
  69     private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";

  70 
  71     private static final String DESCR_CLASS = "Ljava/lang/Class;";
  72     private static final String DESCR_STRING = "Ljava/lang/String;";
  73     private static final String DESCR_OBJECT = "Ljava/lang/Object;";

  74     private static final String DESCR_CTOR_SERIALIZED_LAMBDA
  75             = "(" + DESCR_CLASS + DESCR_STRING + DESCR_STRING + DESCR_STRING + "I"
  76             + DESCR_STRING + DESCR_STRING + DESCR_STRING + DESCR_STRING + "[" + DESCR_OBJECT + ")V";
  77 
  78     private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION = "(Ljava/lang/String;)V";
  79     private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
  80 
  81     private static final String DESCR_HIDDEN = "Ljdk/internal/vm/annotation/Hidden;";
  82 
  83     private static final String[] EMPTY_STRING_ARRAY = new String[0];
  84 
  85     // Used to ensure that each spun class name is unique
  86     private static final AtomicInteger counter = new AtomicInteger(0);
  87 
  88     // For dumping generated classes to disk, for debugging purposes
  89     private static final ProxyClassesDumper dumper;
  90 
  91     private static final boolean disableEagerInitialization;
  92 
  93     static {
  94         final String dumpProxyClassesKey = "jdk.internal.lambda.dumpProxyClasses";
  95         String dumpPath = GetPropertyAction.privilegedGetProperty(dumpProxyClassesKey);
  96         dumper = (null == dumpPath) ? null : ProxyClassesDumper.getInstance(dumpPath);
  97 
  98         final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization";
  99         disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
 100     }
 101 
 102     // See context values in AbstractValidatingLambdaMetafactory
 103     private final String implMethodClassName;        // Name of type containing implementation "CC"
 104     private final String implMethodName;             // Name of implementation method "impl"
 105     private final String implMethodDesc;             // Type descriptor for implementation methods "(I)Ljava/lang/String;"
 106     private final MethodType constructorType;        // Generated class constructor type "(CC)void"
 107     private final ClassWriter cw;                    // ASM class writer
 108     private final String[] argNames;                 // Generated names for the constructor arguments
 109     private final String[] argDescs;                 // Type descriptors for the constructor arguments
 110     private final String lambdaClassName;            // Generated name for the generated class "X$$Lambda$1"

 111 
 112     /**
 113      * General meta-factory constructor, supporting both standard cases and
 114      * allowing for uncommon options such as serialization or bridging.
 115      *
 116      * @param caller Stacked automatically by VM; represents a lookup context
 117      *               with the accessibility privileges of the caller.
 118      * @param invokedType Stacked automatically by VM; the signature of the
 119      *                    invoked method, which includes the expected static
 120      *                    type of the returned lambda object, and the static
 121      *                    types of the captured arguments for the lambda.  In
 122      *                    the event that the implementation method is an
 123      *                    instance method, the first argument in the invocation
 124      *                    signature will correspond to the receiver.
 125      * @param samMethodName Name of the method in the functional interface to
 126      *                      which the lambda or method reference is being
 127      *                      converted, represented as a String.
 128      * @param samMethodType Type of the method in the functional interface to
 129      *                      which the lambda or method reference is being
 130      *                      converted, represented as a MethodType.


 146      * @throws LambdaConversionException If any of the meta-factory protocol
 147      * invariants are violated
 148      */
 149     public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
 150                                        MethodType invokedType,
 151                                        String samMethodName,
 152                                        MethodType samMethodType,
 153                                        MethodHandle implMethod,
 154                                        MethodType instantiatedMethodType,
 155                                        boolean isSerializable,
 156                                        Class<?>[] markerInterfaces,
 157                                        MethodType[] additionalBridges)
 158             throws LambdaConversionException {
 159         super(caller, invokedType, samMethodName, samMethodType,
 160               implMethod, instantiatedMethodType,
 161               isSerializable, markerInterfaces, additionalBridges);
 162         implMethodClassName = implClass.getName().replace('.', '/');
 163         implMethodName = implInfo.getName();
 164         implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
 165         constructorType = invokedType.changeReturnType(Void.TYPE);
 166         lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();


 167         cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
 168         int parameterCount = invokedType.parameterCount();
 169         if (parameterCount > 0) {
 170             argNames = new String[parameterCount];
 171             argDescs = new String[parameterCount];
 172             for (int i = 0; i < parameterCount; i++) {
 173                 argNames[i] = "arg$" + (i + 1);
 174                 argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));
 175             }
 176         } else {
 177             argNames = argDescs = EMPTY_STRING_ARRAY;
 178         }
 179     }
 180 









 181     /**
 182      * Build the CallSite. Generate a class file which implements the functional
 183      * interface, define the class, if there are no parameters create an instance
 184      * of the class which the CallSite will return, otherwise, generate handles
 185      * which will call the class' constructor.
 186      *
 187      * @return a CallSite, which, when invoked, will return an instance of the
 188      * functional interface
 189      * @throws ReflectiveOperationException
 190      * @throws LambdaConversionException If properly formed functional interface
 191      * is not found
 192      */
 193     @Override
 194     CallSite buildCallSite() throws LambdaConversionException {
 195         final Class<?> innerClass = spinInnerClass();
 196         if (invokedType.parameterCount() == 0 && !disableEagerInitialization) {
 197             // In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance,
 198             // unless we've suppressed eager initialization
 199             final Constructor<?>[] ctrs = AccessController.doPrivileged(
 200                     new PrivilegedAction<>() {
 201                 @Override
 202                 public Constructor<?>[] run() {
 203                     Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
 204                     if (ctrs.length == 1) {
 205                         // The lambda implementing inner class constructor is private, set
 206                         // it accessible (by us) before creating the constant sole instance
 207                         ctrs[0].setAccessible(true);
 208                     }
 209                     return ctrs;
 210                 }
 211                     });
 212             if (ctrs.length != 1) {
 213                 throw new LambdaConversionException("Expected one lambda constructor for "
 214                         + innerClass.getCanonicalName() + ", got " + ctrs.length);
 215             }
 216 
 217             try {
 218                 Object inst = ctrs[0].newInstance();
 219                 return new ConstantCallSite(MethodHandles.constant(samBase, inst));
 220             }
 221             catch (ReflectiveOperationException e) {
 222                 throw new LambdaConversionException("Exception instantiating lambda object", e);
 223             }
 224         } else {
 225             try {
 226                 if (!disableEagerInitialization) {
 227                     UNSAFE.ensureClassInitialized(innerClass);
 228                 }
 229                 return new ConstantCallSite(
 230                         MethodHandles.Lookup.IMPL_LOOKUP
 231                              .findStatic(innerClass, NAME_FACTORY, invokedType));
 232             }
 233             catch (ReflectiveOperationException e) {
 234                 throw new LambdaConversionException("Exception finding constructor", e);
 235             }
 236         }
 237     }
 238 
 239     /**
 240      * Generate a class file which implements the functional
 241      * interface, define and return the class.
 242      *
 243      * @implNote The class that is generated does not include signature
 244      * information for exceptions that may be present on the SAM method.
 245      * This is to reduce classfile size, and is harmless as checked exceptions
 246      * are erased anyway, no one will ever compile against this classfile,
 247      * and we make no guarantees about the reflective properties of lambda
 248      * objects.
 249      *
 250      * @return a Class which implements the functional interface
 251      * @throws LambdaConversionException If properly formed functional interface
 252      * is not found
 253      */


 266                 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);
 267             }
 268             interfaces = itfs.toArray(new String[itfs.size()]);
 269         }
 270 
 271         cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
 272                  lambdaClassName, null,
 273                  JAVA_LANG_OBJECT, interfaces);
 274 
 275         // Generate final fields to be filled in by constructor
 276         for (int i = 0; i < argDescs.length; i++) {
 277             FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
 278                                             argNames[i],
 279                                             argDescs[i],
 280                                             null, null);
 281             fv.visitEnd();
 282         }
 283 
 284         generateConstructor();
 285 
 286         if (invokedType.parameterCount() != 0 || disableEagerInitialization) {
 287             generateFactory();
 288         }
 289 
 290         // Forward the SAM method
 291         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
 292                                           samMethodType.toMethodDescriptorString(), null, null);
 293         mv.visitAnnotation(DESCR_HIDDEN, true);
 294         new ForwardingMethodGenerator(mv).generate(samMethodType);
 295 
 296         // Forward the bridges
 297         if (additionalBridges != null) {
 298             for (MethodType mt : additionalBridges) {
 299                 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
 300                                     mt.toMethodDescriptorString(), null, null);
 301                 mv.visitAnnotation(DESCR_HIDDEN, true);
 302                 new ForwardingMethodGenerator(mv).generate(mt);
 303             }
 304         }
 305 

















 306         if (isSerializable)
 307             generateSerializationFriendlyMethods();
 308         else if (accidentallySerializable)
 309             generateSerializationHostileMethods();
 310 
 311         cw.visitEnd();
 312 
 313         // Define the generated class in this VM.
 314 
 315         final byte[] classBytes = cw.toByteArray();
 316 
 317         // If requested, dump out to a file for debugging purposes
 318         if (dumper != null) {
 319             AccessController.doPrivileged(new PrivilegedAction<>() {
 320                 @Override
 321                 public Void run() {
 322                     dumper.dumpClass(lambdaClassName, classBytes);
 323                     return null;
 324                 }
 325             }, null,
 326             new FilePermission("<<ALL FILES>>", "read, write"),
 327             // createDirectories may need it
 328             new PropertyPermission("user.dir", "read"));
 329         }
 330 
 331         return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);

















 332     }
 333 
 334     /**
 335      * Generate the factory method for the class
 336      */
 337     private void generateFactory() {
 338         MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null);
 339         m.visitCode();
 340         m.visitTypeInsn(NEW, lambdaClassName);
 341         m.visitInsn(Opcodes.DUP);
 342         int parameterCount = invokedType.parameterCount();
 343         for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) {
 344             Class<?> argType = invokedType.parameterType(typeIndex);
 345             m.visitVarInsn(getLoadOpcode(argType), varIndex);
 346             varIndex += getParameterSize(argType);
 347         }
 348         m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false);
 349         m.visitInsn(ARETURN);
 350         m.visitMaxs(-1, -1);
 351         m.visitEnd();
 352     }
 353 
 354     /**
 355      * Generate the constructor for the class
 356      */
 357     private void generateConstructor() {
 358         // Generate constructor
 359         MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
 360                                             constructorType.toMethodDescriptorString(), null, null);
 361         ctor.visitCode();
 362         ctor.visitVarInsn(ALOAD, 0);
 363         ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
 364                              METHOD_DESCRIPTOR_VOID, false);
 365         int parameterCount = invokedType.parameterCount();
 366         for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
 367             ctor.visitVarInsn(ALOAD, 0);
 368             Class<?> argType = invokedType.parameterType(i);
 369             ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
 370             lvIndex += getParameterSize(argType);
 371             ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);


 447         mv.visitEnd();
 448     }
 449 
 450     /**
 451      * This class generates a method body which calls the lambda implementation
 452      * method, converting arguments, as needed.
 453      */
 454     private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
 455 
 456         ForwardingMethodGenerator(MethodVisitor mv) {
 457             super(mv);
 458         }
 459 
 460         void generate(MethodType methodType) {
 461             visitCode();
 462 
 463             if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
 464                 visitTypeInsn(NEW, implMethodClassName);
 465                 visitInsn(DUP);
 466             }




 467             for (int i = 0; i < argNames.length; i++) {
 468                 visitVarInsn(ALOAD, 0);
 469                 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
 470             }
 471 
 472             convertArgumentTypes(methodType);
 473 





 474             // Invoke the method we want to forward to
 475             visitMethodInsn(invocationOpcode(), implMethodClassName,
 476                             implMethodName, implMethodDesc,
 477                             implClass.isInterface());
 478 
 479             // Convert the return value (if any) and return it
 480             // Note: if adapting from non-void to void, the 'return'
 481             // instruction will pop the unneeded result
 482             Class<?> implReturnClass = implMethodType.returnType();
 483             Class<?> samReturnClass = methodType.returnType();
 484             convertType(implReturnClass, samReturnClass, samReturnClass);
 485             visitInsn(getReturnOpcode(samReturnClass));
 486             // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
 487             visitMaxs(-1, -1);
 488             visitEnd();
 489         }
 490 
 491         private void convertArgumentTypes(MethodType samType) {
 492             int lvIndex = 0;
 493             int samParametersLength = samType.parameterCount();
 494             int captureArity = invokedType.parameterCount();
 495             for (int i = 0; i < samParametersLength; i++) {
 496                 Class<?> argType = samType.parameterType(i);
 497                 visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
 498                 lvIndex += getParameterSize(argType);




  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.


 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.isHiddenClass()) {
 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     /**
 248      * Generate a class file which implements the functional
 249      * interface, define and return the class.
 250      *
 251      * @implNote The class that is generated does not include signature
 252      * information for exceptions that may be present on the SAM method.
 253      * This is to reduce classfile size, and is harmless as checked exceptions
 254      * are erased anyway, no one will ever compile against this classfile,
 255      * and we make no guarantees about the reflective properties of lambda
 256      * objects.
 257      *
 258      * @return a Class which implements the functional interface
 259      * @throws LambdaConversionException If properly formed functional interface
 260      * is not found
 261      */


 274                 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);
 275             }
 276             interfaces = itfs.toArray(new String[itfs.size()]);
 277         }
 278 
 279         cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
 280                  lambdaClassName, null,
 281                  JAVA_LANG_OBJECT, interfaces);
 282 
 283         // Generate final fields to be filled in by constructor
 284         for (int i = 0; i < argDescs.length; i++) {
 285             FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
 286                                             argNames[i],
 287                                             argDescs[i],
 288                                             null, null);
 289             fv.visitEnd();
 290         }
 291 
 292         generateConstructor();
 293 




 294         // Forward the SAM method
 295         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
 296                                           samMethodType.toMethodDescriptorString(), null, null);

 297         new ForwardingMethodGenerator(mv).generate(samMethodType);
 298 
 299         // Forward the bridges
 300         if (additionalBridges != null) {
 301             for (MethodType mt : additionalBridges) {
 302                 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
 303                                     mt.toMethodDescriptorString(), null, null);

 304                 new ForwardingMethodGenerator(mv).generate(mt);
 305             }
 306         }
 307 
 308         if (useImplMethodHandle) {
 309             FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_STATIC,
 310                                             NAME_FIELD_IMPL_METHOD,
 311                                             DESCR_METHOD_HANDLE,
 312                                             null, null);
 313             fv.visitEnd();
 314 
 315             mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC,
 316                                 "setImplMethod", DESCR_SET_IMPL_METHOD,
 317                                 null, null);
 318             mv.visitVarInsn(ALOAD, 0);
 319             mv.visitFieldInsn(PUTSTATIC, lambdaClassName, NAME_FIELD_IMPL_METHOD, DESCR_METHOD_HANDLE);
 320             mv.visitInsn(RETURN);
 321             mv.visitMaxs(-1, -1);
 322             mv.visitEnd();
 323         }
 324 
 325         if (isSerializable)
 326             generateSerializationFriendlyMethods();
 327         else if (accidentallySerializable)
 328             generateSerializationHostileMethods();
 329 
 330         cw.visitEnd();
 331 
 332         // Define the generated class in this VM.
 333 
 334         final byte[] classBytes = cw.toByteArray();

 335         // If requested, dump out to a file for debugging purposes
 336         if (dumper != null) {
 337             AccessController.doPrivileged(new PrivilegedAction<>() {
 338                 @Override
 339                 public Void run() {
 340                     dumper.dumpClass(lambdaClassName, classBytes);
 341                     return null;
 342                 }
 343             }, null,
 344             new FilePermission("<<ALL FILES>>", "read, write"),
 345             // createDirectories may need it
 346             new PropertyPermission("user.dir", "read"));
 347         }
 348         try {
 349             // this class is linked at the indy callsite; so define a hidden nestmate
 350             Lookup lookup = caller.defineHiddenClass(classBytes, !disableEagerInitialization, NESTMATE, STRONG);
 351             if (useImplMethodHandle) {
 352                 // If the target class invokes a method reference this::m which is
 353                 // resolved to a protected method inherited from a superclass in a different
 354                 // package, the target class does not have a bridge and this method reference
 355                 // has been changed from public to protected after the target class was compiled.
 356                 // This lambda proxy class has no access to the resolved method.
 357                 // So this workaround by passing the live implMethod method handle
 358                 // to the proxy class to invoke directly.
 359                 MethodHandle mh = lookup.findStaticSetter(lookup.lookupClass(), NAME_FIELD_IMPL_METHOD, MethodHandle.class);
 360                 mh.invokeExact(implMethod);
 361             }
 362             return lookup.lookupClass();
 363         } catch (IllegalAccessException e) {
 364             throw new LambdaConversionException("Exception defining lambda proxy class", e);
 365         } catch (Throwable t) {
 366             throw new InternalError(t);
 367         }



















 368     }
 369 
 370     /**
 371      * Generate the constructor for the class
 372      */
 373     private void generateConstructor() {
 374         // Generate constructor
 375         MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
 376                                             constructorType.toMethodDescriptorString(), null, null);
 377         ctor.visitCode();
 378         ctor.visitVarInsn(ALOAD, 0);
 379         ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
 380                              METHOD_DESCRIPTOR_VOID, false);
 381         int parameterCount = invokedType.parameterCount();
 382         for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
 383             ctor.visitVarInsn(ALOAD, 0);
 384             Class<?> argType = invokedType.parameterType(i);
 385             ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
 386             lvIndex += getParameterSize(argType);
 387             ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);


 463         mv.visitEnd();
 464     }
 465 
 466     /**
 467      * This class generates a method body which calls the lambda implementation
 468      * method, converting arguments, as needed.
 469      */
 470     private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
 471 
 472         ForwardingMethodGenerator(MethodVisitor mv) {
 473             super(mv);
 474         }
 475 
 476         void generate(MethodType methodType) {
 477             visitCode();
 478 
 479             if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
 480                 visitTypeInsn(NEW, implMethodClassName);
 481                 visitInsn(DUP);
 482             }
 483             if (useImplMethodHandle) {
 484                 visitVarInsn(ALOAD, 0);
 485                 visitFieldInsn(GETSTATIC, lambdaClassName, NAME_FIELD_IMPL_METHOD, DESCR_METHOD_HANDLE);
 486             }
 487             for (int i = 0; i < argNames.length; i++) {
 488                 visitVarInsn(ALOAD, 0);
 489                 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
 490             }
 491 
 492             convertArgumentTypes(methodType);
 493 
 494             if (useImplMethodHandle) {
 495                 MethodType mtype = implInfo.getMethodType().insertParameterTypes(0, implClass);
 496                 visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
 497                                 "invokeExact", mtype.descriptorString(), false);
 498             } else {
 499                 // Invoke the method we want to forward to
 500                 visitMethodInsn(invocationOpcode(), implMethodClassName,
 501                                 implMethodName, implMethodDesc,
 502                                 implClass.isInterface());
 503             }
 504             // Convert the return value (if any) and return it
 505             // Note: if adapting from non-void to void, the 'return'
 506             // instruction will pop the unneeded result
 507             Class<?> implReturnClass = implMethodType.returnType();
 508             Class<?> samReturnClass = methodType.returnType();
 509             convertType(implReturnClass, samReturnClass, samReturnClass);
 510             visitInsn(getReturnOpcode(samReturnClass));
 511             // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
 512             visitMaxs(-1, -1);
 513             visitEnd();
 514         }
 515 
 516         private void convertArgumentTypes(MethodType samType) {
 517             int lvIndex = 0;
 518             int samParametersLength = samType.parameterCount();
 519             int captureArity = invokedType.parameterCount();
 520             for (int i = 0; i < samParametersLength; i++) {
 521                 Class<?> argType = samType.parameterType(i);
 522                 visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
 523                 lvIndex += getParameterSize(argType);


< prev index next >