1 /*
   2  * Copyright (c) 2012, 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.misc.Unsafe;
  30 
  31 import java.lang.reflect.Constructor;
  32 import java.security.AccessController;
  33 import java.security.PrivilegedAction;
  34 import java.security.ProtectionDomain;
  35 import java.util.concurrent.atomic.AtomicInteger;
  36 
  37 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  38 
  39 /**
  40  * Lambda metafactory implementation which dynamically creates an
  41  * inner-class-like class per lambda callsite.
  42  *
  43  * @see LambdaMetafactory
  44  */
  45 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
  46     private static final Unsafe UNSAFE = Unsafe.getUnsafe();
  47 
  48     private static final int CLASSFILE_VERSION = 51;
  49     private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
  50     private static final String NAME_MAGIC_ACCESSOR_IMPL = "java/lang/invoke/MagicLambdaImpl";
  51     private static final String NAME_CTOR = "<init>";
  52 
  53     //Serialization support
  54     private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
  55     private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
  56     private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
  57     private static final String NAME_OBJECT = "java/lang/Object";
  58     private static final String DESCR_CTOR_SERIALIZED_LAMBDA
  59             = MethodType.methodType(void.class,
  60                                     Class.class,
  61                                     String.class, String.class, String.class,
  62                                     int.class, String.class, String.class, String.class,
  63                                     String.class,
  64                                     Object[].class).toMethodDescriptorString();
  65 
  66     // Used to ensure that each spun class name is unique
  67     private static final AtomicInteger counter = new AtomicInteger(0);
  68 
  69     // See context values in AbstractValidatingLambdaMetafactory
  70     private final String implMethodClassName;        // Name of type containing implementation "CC"
  71     private final String implMethodName;             // Name of implementation method "impl"
  72     private final String implMethodDesc;             // Type descriptor for implementation methods "(I)Ljava/lang/String;"
  73     private final Type[] implMethodArgumentTypes;    // ASM types for implementaion method parameters
  74     private final Type implMethodReturnType;         // ASM type for implementaion method return type "Ljava/lang/String;"
  75     private final MethodType constructorType;        // Generated class constructor type "(CC)void"
  76     private final String constructorDesc;            // Type descriptor for constructor "(LCC;)V"
  77     private final ClassWriter cw;                    // ASM class writer
  78     private final Type[] argTypes;                   // ASM types for the constructor arguments
  79     private final String[] argNames;                 // Generated names for the constructor arguments
  80     private final String lambdaClassName;            // Generated name for the generated class "X$$Lambda$1"
  81     private final Type[] instantiatedArgumentTypes;  // ASM types for the functional interface arguments
  82 
  83     /**
  84      * General meta-factory constructor, supporting both standard cases and
  85      * allowing for uncommon options such as serialization or bridging.
  86      *
  87      * @param caller Stacked automatically by VM; represents a lookup context
  88      *               with the accessibility privileges of the caller.
  89      * @param invokedType Stacked automatically by VM; the signature of the
  90      *                    invoked method, which includes the expected static
  91      *                    type of the returned lambda object, and the static
  92      *                    types of the captured arguments for the lambda.  In
  93      *                    the event that the implementation method is an
  94      *                    instance method, the first argument in the invocation
  95      *                    signature will correspond to the receiver.
  96      * @param samMethodName Name of the method in the functional interface to
  97      *                      which the lambda or method reference is being
  98      *                      converted, represented as a String.
  99      * @param samMethodType Type of the method in the functional interface to
 100      *                      which the lambda or method reference is being
 101      *                      converted, represented as a MethodType.
 102      * @param implMethod The implementation method which should be called (with
 103      *                   suitable adaptation of argument types, return types,
 104      *                   and adjustment for captured arguments) when methods of
 105      *                   the resulting functional interface instance are invoked.
 106      * @param instantiatedMethodType The signature of the primary functional
 107      *                               interface method after type variables are
 108      *                               substituted with their instantiation from
 109      *                               the capture site
 110      * @param isSerializable Should the lambda be made serializable?  If set,
 111      *                       either the target type or one of the additional SAM
 112      *                       types must extend {@code Serializable}.
 113      * @param markerInterfaces Additional interfaces which the lambda object
 114      *                       should implement.
 115      * @param additionalBridges Method types for additional signatures to be
 116      *                          bridged to the implementation method
 117      * @throws ReflectiveOperationException
 118      * @throws LambdaConversionException If any of the meta-factory protocol
 119      * invariants are violated
 120      */
 121     public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
 122                                        MethodType invokedType,
 123                                        String samMethodName,
 124                                        MethodType samMethodType,
 125                                        MethodHandle implMethod,
 126                                        MethodType instantiatedMethodType,
 127                                        boolean isSerializable,
 128                                        Class<?>[] markerInterfaces,
 129                                        MethodType[] additionalBridges)
 130             throws ReflectiveOperationException, LambdaConversionException {
 131         super(caller, invokedType, samMethodName, samMethodType,
 132               implMethod, instantiatedMethodType,
 133               isSerializable, markerInterfaces, additionalBridges);
 134         implMethodClassName = implDefiningClass.getName().replace('.', '/');
 135         implMethodName = implInfo.getName();
 136         implMethodDesc = implMethodType.toMethodDescriptorString();
 137         Type implMethodAsmType = Type.getMethodType(implMethodDesc);
 138         implMethodArgumentTypes = implMethodAsmType.getArgumentTypes();
 139         implMethodReturnType = (implKind == MethodHandleInfo.REF_newInvokeSpecial)
 140                 ? Type.getObjectType(implMethodClassName)
 141                 : implMethodAsmType.getReturnType();
 142         constructorType = invokedType.changeReturnType(Void.TYPE);
 143         constructorDesc = constructorType.toMethodDescriptorString();
 144         lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
 145         cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
 146         argTypes = Type.getArgumentTypes(constructorDesc);
 147         argNames = new String[argTypes.length];
 148         for (int i = 0; i < argTypes.length; i++) {
 149             argNames[i] = "arg$" + (i + 1);
 150         }
 151         instantiatedArgumentTypes = Type.getArgumentTypes(
 152                 instantiatedMethodType.toMethodDescriptorString());
 153     }
 154 
 155     /**
 156      * Build the CallSite. Generate a class file which implements the functional
 157      * interface, define the class, if there are no parameters create an instance
 158      * of the class which the CallSite will return, otherwise, generate handles
 159      * which will call the class' constructor.
 160      *
 161      * @return a CallSite, which, when invoked, will return an instance of the
 162      * functional interface
 163      * @throws ReflectiveOperationException
 164      * @throws LambdaConversionException If properly formed functional interface
 165      * is not found
 166      */
 167     @Override
 168     CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException {
 169         final Class<?> innerClass = spinInnerClass();
 170         if (invokedType.parameterCount() == 0) {
 171             final Constructor[] ctrs = AccessController.doPrivileged(
 172                     new PrivilegedAction<Constructor[]>() {
 173                 @Override
 174                 public Constructor[] run() {
 175                     return innerClass.getDeclaredConstructors();
 176                 }
 177             });
 178             if (ctrs.length != 1) {
 179                 throw new ReflectiveOperationException("Expected one lambda constructor for "
 180                         + innerClass.getCanonicalName() + ", got " + ctrs.length);
 181             }
 182             // The lambda implementing inner class constructor is private, set
 183             // it accessible (by us) before creating the constant sole instance
 184             AccessController.doPrivileged(new PrivilegedAction<Void>() {
 185                 @Override
 186                 public Void run() {
 187                     ctrs[0].setAccessible(true);
 188                     return null;
 189                 }
 190             });
 191             Object inst = ctrs[0].newInstance();
 192             return new ConstantCallSite(MethodHandles.constant(samBase, inst));
 193         } else {
 194             return new ConstantCallSite(
 195                     MethodHandles.Lookup.IMPL_LOOKUP
 196                          .findConstructor(innerClass, constructorType)
 197                          .asType(constructorType.changeReturnType(samBase)));
 198         }
 199     }
 200 
 201     /**
 202      * Generate a class file which implements the functional
 203      * interface, define and return the class.
 204      *
 205      * @implNote The class that is generated does not include signature
 206      * information for exceptions that may be present on the SAM method.
 207      * This is to reduce classfile size, and is harmless as checked exceptions
 208      * are erased anyway, no one will ever compile against this classfile,
 209      * and we make no guarantees about the reflective properties of lambda
 210      * objects.
 211      *
 212      * @return a Class which implements the functional interface
 213      * @throws LambdaConversionException If properly formed functional interface
 214      * is not found
 215      */
 216     private Class<?> spinInnerClass() throws LambdaConversionException {
 217         String[] interfaces = new String[markerInterfaces.length + 1];
 218         interfaces[0] = samBase.getName().replace('.', '/');
 219         for (int i=0; i<markerInterfaces.length; i++) {
 220             interfaces[i+1] = markerInterfaces[i].getName().replace('.', '/');
 221         }
 222         cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
 223                  lambdaClassName, null,
 224                  NAME_MAGIC_ACCESSOR_IMPL, interfaces);
 225 
 226         // Generate final fields to be filled in by constructor
 227         for (int i = 0; i < argTypes.length; i++) {
 228             FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
 229                                             argNames[i],
 230                                             argTypes[i].getDescriptor(),
 231                                             null, null);
 232             fv.visitEnd();
 233         }
 234 
 235         generateConstructor();
 236 
 237         // Forward the SAM method
 238         String methodDescriptor = samMethodType.toMethodDescriptorString();
 239         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
 240                                           methodDescriptor, null, null);
 241         new ForwardingMethodGenerator(mv).generate(methodDescriptor);
 242 
 243         // Forward the bridges
 244         if (additionalBridges != null) {
 245             for (MethodType mt : additionalBridges) {
 246                 methodDescriptor = mt.toMethodDescriptorString();
 247                 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
 248                                     methodDescriptor, null, null);
 249                 new ForwardingMethodGenerator(mv).generate(methodDescriptor);
 250             }
 251         }
 252 
 253         if (isSerializable)
 254             generateWriteReplace();
 255 
 256         cw.visitEnd();
 257 
 258         // Define the generated class in this VM.
 259 
 260         final byte[] classBytes = cw.toByteArray();
 261 
 262         /*** Uncomment to dump the generated file
 263             System.out.printf("Loaded: %s (%d bytes) %n", lambdaClassName,
 264                               classBytes.length);
 265             try (FileOutputStream fos = new FileOutputStream(lambdaClassName
 266                                             .replace('/', '.') + ".class")) {
 267                 fos.write(classBytes);
 268             } catch (IOException ex) {
 269                 PlatformLogger.getLogger(InnerClassLambdaMetafactory.class
 270                                       .getName()).severe(ex.getMessage(), ex);
 271             }
 272         ***/
 273 
 274         ClassLoader loader = targetClass.getClassLoader();
 275         ProtectionDomain pd = (loader == null)
 276             ? null
 277             : AccessController.doPrivileged(
 278             new PrivilegedAction<ProtectionDomain>() {
 279                 @Override
 280                 public ProtectionDomain run() {
 281                     return targetClass.getProtectionDomain();
 282                 }
 283             }
 284         );
 285 
 286         return UNSAFE.defineClass(lambdaClassName,
 287                                   classBytes, 0, classBytes.length,
 288                                   loader, pd);
 289     }
 290 
 291     /**
 292      * Generate the constructor for the class
 293      */
 294     private void generateConstructor() {
 295         // Generate constructor
 296         MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
 297                                             constructorDesc, null, null);
 298         ctor.visitCode();
 299         ctor.visitVarInsn(ALOAD, 0);
 300         ctor.visitMethodInsn(INVOKESPECIAL, NAME_MAGIC_ACCESSOR_IMPL, NAME_CTOR,
 301                              METHOD_DESCRIPTOR_VOID);
 302         int lvIndex = 0;
 303         for (int i = 0; i < argTypes.length; i++) {
 304             ctor.visitVarInsn(ALOAD, 0);
 305             ctor.visitVarInsn(argTypes[i].getOpcode(ILOAD), lvIndex + 1);
 306             lvIndex += argTypes[i].getSize();
 307             ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i],
 308                                 argTypes[i].getDescriptor());
 309         }
 310         ctor.visitInsn(RETURN);
 311         // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
 312         ctor.visitMaxs(-1, -1);
 313         ctor.visitEnd();
 314     }
 315 
 316     /**
 317      * Generate the writeReplace method (if needed for serialization)
 318      */
 319     private void generateWriteReplace() {
 320         TypeConvertingMethodAdapter mv
 321                 = new TypeConvertingMethodAdapter(
 322                     cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
 323                     NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
 324                     null, null));
 325 
 326         mv.visitCode();
 327         mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
 328         mv.visitInsn(DUP);
 329         mv.visitLdcInsn(Type.getType(targetClass));
 330         mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/'));
 331         mv.visitLdcInsn(samMethodName);
 332         mv.visitLdcInsn(samMethodType.toMethodDescriptorString());
 333         mv.visitLdcInsn(implInfo.getReferenceKind());
 334         mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/'));
 335         mv.visitLdcInsn(implInfo.getName());
 336         mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString());
 337         mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString());
 338 
 339         mv.iconst(argTypes.length);
 340         mv.visitTypeInsn(ANEWARRAY, NAME_OBJECT);
 341         for (int i = 0; i < argTypes.length; i++) {
 342             mv.visitInsn(DUP);
 343             mv.iconst(i);
 344             mv.visitVarInsn(ALOAD, 0);
 345             mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i],
 346                               argTypes[i].getDescriptor());
 347             mv.boxIfTypePrimitive(argTypes[i]);
 348             mv.visitInsn(AASTORE);
 349         }
 350         mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
 351                 DESCR_CTOR_SERIALIZED_LAMBDA);
 352         mv.visitInsn(ARETURN);
 353         // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
 354         mv.visitMaxs(-1, -1);
 355         mv.visitEnd();
 356     }
 357 
 358     /**
 359      * This class generates a method body which calls the lambda implementation
 360      * method, converting arguments, as needed.
 361      */
 362     private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
 363 
 364         ForwardingMethodGenerator(MethodVisitor mv) {
 365             super(mv);
 366         }
 367 
 368         void generate(String methodDescriptor) {
 369             visitCode();
 370 
 371             if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
 372                 visitTypeInsn(NEW, implMethodClassName);
 373                 visitInsn(DUP);
 374             }
 375             for (int i = 0; i < argTypes.length; i++) {
 376                 visitVarInsn(ALOAD, 0);
 377                 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i],
 378                                argTypes[i].getDescriptor());
 379             }
 380 
 381             convertArgumentTypes(Type.getArgumentTypes(methodDescriptor));
 382 
 383             // Invoke the method we want to forward to
 384             visitMethodInsn(invocationOpcode(), implMethodClassName, implMethodName, implMethodDesc);
 385 
 386             // Convert the return value (if any) and return it
 387             // Note: if adapting from non-void to void, the 'return'
 388             // instruction will pop the unneeded result
 389             Type samReturnType = Type.getReturnType(methodDescriptor);
 390             convertType(implMethodReturnType, samReturnType, samReturnType);
 391             visitInsn(samReturnType.getOpcode(Opcodes.IRETURN));
 392             // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
 393             visitMaxs(-1, -1);
 394             visitEnd();
 395         }
 396 
 397         private void convertArgumentTypes(Type[] samArgumentTypes) {
 398             int lvIndex = 0;
 399             boolean samIncludesReceiver = implIsInstanceMethod &&
 400                                                    argTypes.length == 0;
 401             int samReceiverLength = samIncludesReceiver ? 1 : 0;
 402             if (samIncludesReceiver) {
 403                 // push receiver
 404                 Type rcvrType = samArgumentTypes[0];
 405                 Type instantiatedRcvrType = instantiatedArgumentTypes[0];
 406 
 407                 visitVarInsn(rcvrType.getOpcode(ILOAD), lvIndex + 1);
 408                 lvIndex += rcvrType.getSize();
 409                 convertType(rcvrType, Type.getType(implDefiningClass), instantiatedRcvrType);
 410             }
 411             int argOffset = implMethodArgumentTypes.length - samArgumentTypes.length;
 412             for (int i = samReceiverLength; i < samArgumentTypes.length; i++) {
 413                 Type argType = samArgumentTypes[i];
 414                 Type targetType = implMethodArgumentTypes[argOffset + i];
 415                 Type instantiatedArgType = instantiatedArgumentTypes[i];
 416 
 417                 visitVarInsn(argType.getOpcode(ILOAD), lvIndex + 1);
 418                 lvIndex += argType.getSize();
 419                 convertType(argType, targetType, instantiatedArgType);
 420             }
 421         }
 422 
 423         private void convertType(Type argType, Type targetType, Type functionalType) {
 424             convertType(argType.getDescriptor(),
 425                         targetType.getDescriptor(),
 426                         functionalType.getDescriptor());
 427         }
 428 
 429         private int invocationOpcode() throws InternalError {
 430             switch (implKind) {
 431                 case MethodHandleInfo.REF_invokeStatic:
 432                     return INVOKESTATIC;
 433                 case MethodHandleInfo.REF_newInvokeSpecial:
 434                     return INVOKESPECIAL;
 435                  case MethodHandleInfo.REF_invokeVirtual:
 436                     return INVOKEVIRTUAL;
 437                 case MethodHandleInfo.REF_invokeInterface:
 438                     return INVOKEINTERFACE;
 439                 case MethodHandleInfo.REF_invokeSpecial:
 440                     return INVOKESPECIAL;
 441                 default:
 442                     throw new InternalError("Unexpected invocation kind: " + implKind);
 443             }
 444         }
 445     }
 446 }