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