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 java.io.FileOutputStream;
  29 import java.io.IOException;
  30 import java.lang.reflect.Method;
  31 import java.security.ProtectionDomain;
  32 import java.util.concurrent.atomic.AtomicInteger;
  33 import sun.util.logging.PlatformLogger;
  34 import jdk.internal.org.objectweb.asm.*;
  35 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  36 import sun.misc.Unsafe;
  37 
  38 /**
  39  * InnerClassLambdaMetafactory
  40  */
  41 /*non-public*/ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
  42     private static final int CLASSFILE_VERSION = 51;
  43     private static final Type TYPE_VOID = Type.getType(void.class);
  44     private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
  45     private static final String NAME_MAGIC_ACCESSOR_IMPL = "java/lang/invoke/MagicLambdaImpl";
  46     private static final String NAME_SERIALIZABLE = "java/io/Serializable";
  47     private static final String NAME_CTOR = "<init>";
  48 
  49     //Serialization support
  50     private static final String NAME_SERIALIZED_LAMBDA = "com/oracle/java/lang/invoke/SerializedLambdaImpl";
  51     private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
  52     private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
  53     private static final String NAME_OBJECT = "java/lang/Object";
  54 
  55     // Used to ensure that each spun class name is unique
  56     private static final AtomicInteger counter = new AtomicInteger(0);
  57 
  58     // See context values in AbstractValidatingLambdaMetafactory
  59     private final String implMethodClassName;        // Name of type containing implementation "CC"
  60     private final String implMethodName;             // Name of implementation method "impl"
  61     private final String implMethodDesc;             // Type descriptor for implementation methods "(I)Ljava/lang/String;"
  62     private final Type[] implMethodArgumentTypes;    // ASM types for implementaion method parameters
  63     private final Type implMethodReturnType;         // ASM type for implementaion method return type "Ljava/lang/String;"
  64     private final MethodType constructorType;        // Generated class constructor type "(CC)void"
  65     private final String constructorDesc;            // Type descriptor for constructor "(LCC;)V"
  66     private final ClassWriter cw;                    // ASM class writer
  67     private final Type[] argTypes;                   // ASM types for the constructor arguments
  68     private final String[] argNames;                 // Generated names for the constructor arguments
  69     private final String lambdaClassName;            // Generated name for the generated class "X$$Lambda$1"
  70     private final Type[] instantiatedArgumentTypes;  // ASM types for the functional interface arguments
  71 
  72     /**
  73      * Meta-factory constructor.
  74      *
  75      * @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges
  76      *               of the caller.
  77      * @param invokedType Stacked automatically by VM; the signature of the invoked method, which includes the
  78      *                    expected static type of the returned lambda object, and the static types of the captured
  79      *                    arguments for the lambda.  In the event that the implementation method is an instance method,
  80      *                    the first argument in the invocation signature will correspond to the receiver.
  81      * @param samMethod The primary method in the functional interface to which the lambda or method reference is
  82      *                  being converted, represented as a method handle.
  83      * @param implMethod The implementation method which should be called (with suitable adaptation of argument
  84      *                   types, return types, and adjustment for captured arguments) when methods of the resulting
  85      *                   functional interface instance are invoked.
  86      * @param instantiatedMethodType The signature of the SAM method from the functional interface's perspective
  87      * @throws ReflectiveOperationException
  88      */
  89     public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
  90                                        MethodType invokedType,
  91                                        MethodHandle samMethod,
  92                                        MethodHandle implMethod,
  93                                        MethodType instantiatedMethodType)
  94             throws ReflectiveOperationException {
  95         super(caller, invokedType, samMethod, implMethod, instantiatedMethodType);
  96         implMethodClassName = implDefiningClass.getName().replace('.', '/');
  97         implMethodName = implInfo.getName();
  98         implMethodDesc = implMethodType.toMethodDescriptorString();
  99         Type implMethodAsmType = Type.getMethodType(implMethodDesc);
 100         implMethodArgumentTypes = implMethodAsmType.getArgumentTypes();
 101         implMethodReturnType = implMethodAsmType.getReturnType();
 102         constructorType = invokedType.changeReturnType(Void.TYPE);
 103         constructorDesc = constructorType.toMethodDescriptorString();
 104         lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
 105         cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
 106         argTypes = Type.getArgumentTypes(constructorDesc);
 107         argNames = new String[argTypes.length];
 108         for (int i = 0; i < argTypes.length; i++) {
 109             argNames[i] = "arg$" + (i + 1);
 110         }
 111         instantiatedArgumentTypes = Type.getArgumentTypes(instantiatedMethodType.toMethodDescriptorString());
 112 
 113     }
 114 
 115     /**
 116      * Build the CallSite. Generate a class file which implements the functional
 117      * interface, define the class, if there are no parameters create an instance
 118      * of the class which the CallSite will return, otherwise, generate handles
 119      * which will call the class' constructor.
 120      *
 121      * @return a CallSite, which, when invoked, will return an instance of the
 122      * functional interface
 123      * @throws ReflectiveOperationException
 124      */
 125     @Override
 126     CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException {
 127         final Class<?> innerClass = spinInnerClass();
 128         if (invokedType.parameterCount() == 0) {
 129             return new ConstantCallSite(MethodHandles.constant(samBase, innerClass.newInstance()));
 130         } else {
 131             return new ConstantCallSite(
 132                     MethodHandles.Lookup.IMPL_LOOKUP
 133                     .findConstructor(innerClass, constructorType)
 134                     .asType(constructorType.changeReturnType(samBase)));
 135         }
 136     }
 137 
 138     /**
 139      * Generate a class file which implements the functional
 140      * interface, define and return the class.
 141      *
 142      * @return a Class which implements the functional interface
 143      */
 144     private <T> Class<? extends T> spinInnerClass() throws LambdaConversionException {
 145         String samName = samBase.getName().replace('.', '/');
 146 
 147         cw.visit(CLASSFILE_VERSION, ACC_PUBLIC + ACC_SUPER, lambdaClassName, null, NAME_MAGIC_ACCESSOR_IMPL,
 148                  isSerializable ? new String[]{samName, NAME_SERIALIZABLE} : new String[]{samName});
 149 
 150         // Generate final fields to be filled in by constructor
 151         for (int i = 0; i < argTypes.length; i++) {
 152             FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL, argNames[i], argTypes[i].getDescriptor(), null, null);
 153             fv.visitEnd();
 154         }
 155 
 156         generateConstructor();
 157 
 158         MethodAnalyzer ma = new MethodAnalyzer();
 159 
 160         // Forward the SAM method
 161         if (ma.getSamMethod() == null) {
 162             throw new LambdaConversionException(String.format("SAM method not found: %s", samMethodType));
 163         } else {
 164             generateForwardingMethod(ma.getSamMethod(), false);
 165         }
 166 
 167         // Forward the bridges
 168         // @@@ Once the VM can do fail-over, uncomment the default method test
 169         if (!ma.getMethodsToBridge().isEmpty() /* && !ma.wasDefaultMethodFound() */) {
 170             for (Method m : ma.getMethodsToBridge()) {
 171                 generateForwardingMethod(m, true);
 172             }
 173         }
 174 
 175         /***** Serialization not yet supported
 176         if (isSerializable) {
 177             String samMethodName = samInfo.getName();
 178             Type samType = Type.getType(samBase);
 179             generateSerializationMethod(samType, samMethodName);
 180         }
 181         ******/
 182 
 183         cw.visitEnd();
 184 
 185         // Define the generated class in this VM.
 186 
 187         final byte[] classBytes = cw.toByteArray();
 188 
 189         if (System.getProperty("debug.dump.generated") != null) {
 190             System.out.printf("Loaded: %s (%d bytes) %n", lambdaClassName, classBytes.length);
 191             try (FileOutputStream fos = new FileOutputStream(lambdaClassName.replace('/', '.') + ".class")) {
 192                 fos.write(classBytes);
 193             } catch (IOException ex) {
 194                 PlatformLogger.getLogger(InnerClassLambdaMetafactory.class.getName()).severe(ex.getMessage(), ex);
 195             }
 196         }
 197 
 198         ClassLoader loader = targetClass.getClassLoader();
 199         ProtectionDomain pd = (loader == null) ? null : targetClass.getProtectionDomain();
 200         return (Class<? extends T>) Unsafe.getUnsafe().defineClass(lambdaClassName, classBytes, 0, classBytes.length, loader, pd);
 201     }
 202 
 203     /**
 204      * Generate the constructor for the class
 205      */
 206     private void generateConstructor() {
 207         // Generate constructor
 208         MethodVisitor ctor = cw.visitMethod(ACC_PUBLIC, NAME_CTOR, constructorDesc, null, null);
 209         ctor.visitCode();
 210         ctor.visitVarInsn(ALOAD, 0);
 211         ctor.visitMethodInsn(INVOKESPECIAL, NAME_MAGIC_ACCESSOR_IMPL, NAME_CTOR, METHOD_DESCRIPTOR_VOID);
 212         int lvIndex = 0;
 213         for (int i = 0; i < argTypes.length; i++) {
 214             ctor.visitVarInsn(ALOAD, 0);
 215             ctor.visitVarInsn(argTypes[i].getOpcode(ILOAD), lvIndex + 1);
 216             lvIndex += argTypes[i].getSize();
 217             ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor());
 218         }
 219         ctor.visitInsn(RETURN);
 220         ctor.visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
 221         ctor.visitEnd();
 222     }
 223 
 224     /**
 225      * Generate the serialization method (if needed)
 226      */
 227     /****** This code is out of date -- known to be wrong -- and not currently used ******
 228     private void generateSerializationMethod(Type samType, String samMethodName) {
 229         String samMethodDesc = samMethodType.toMethodDescriptorString();
 230         TypeConvertingMethodAdapter mv = new TypeConvertingMethodAdapter(cw.visitMethod(ACC_PRIVATE + ACC_FINAL, NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, null, null));
 231 
 232         mv.visitCode();
 233         mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
 234         mv.dup();
 235         mv.visitLdcInsn(samType);
 236         mv.visitLdcInsn(samMethodName);
 237         mv.visitLdcInsn(samMethodDesc);
 238         mv.visitLdcInsn(Type.getType(implDefiningClass));
 239         mv.visitLdcInsn(implMethodName);
 240         mv.visitLdcInsn(implMethodDesc);
 241 
 242         mv.iconst(argTypes.length);
 243         mv.visitTypeInsn(ANEWARRAY, NAME_OBJECT);
 244         for (int i = 0; i < argTypes.length; i++) {
 245             mv.dup();
 246             mv.iconst(i);
 247             mv.visitVarInsn(ALOAD, 0);
 248             mv.getfield(lambdaClassName, argNames[i], argTypes[i].getDescriptor());
 249             mv.boxIfPrimitive(argTypes[i]);
 250             mv.visitInsn(AASTORE);
 251         }
 252         mv.invokespecial(NAME_SERIALIZED_LAMBDA, NAME_CTOR,
 253                            "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V");
 254         mv.visitInsn(ARETURN);
 255         mv.visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
 256         mv.visitEnd();
 257     }
 258     ********/
 259 
 260     /**
 261      * Generate a method which calls the lambda implementation method,
 262      * converting arguments, as needed.
 263      * @param m The method whose signature should be generated
 264      * @param isBridge True if this methods should be flagged as a bridge
 265      */
 266     private void generateForwardingMethod(Method m, boolean isBridge) {
 267         Class<?>[] exceptionTypes = m.getExceptionTypes();
 268         String[] exceptionNames = new String[exceptionTypes.length];
 269         for (int i = 0; i < exceptionTypes.length; i++) {
 270             exceptionNames[i] = exceptionTypes[i].getName().replace('.', '/');
 271         }
 272         String methodDescriptor = Type.getMethodDescriptor(m);
 273         int access = isBridge? ACC_PUBLIC | ACC_BRIDGE : ACC_PUBLIC;
 274         MethodVisitor mv = cw.visitMethod(access, m.getName(), methodDescriptor, null, exceptionNames);
 275         new ForwardingMethodGenerator(mv).generate(m);
 276     }
 277 
 278     /**
 279      * This class generates a method body which calls the lambda implementation
 280      * method, converting arguments, as needed.
 281      */
 282     private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
 283 
 284         ForwardingMethodGenerator(MethodVisitor mv) {
 285             super(mv);
 286         }
 287 
 288         void generate(Method m) throws InternalError {
 289             visitCode();
 290 
 291             if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
 292                 visitTypeInsn(NEW, implMethodClassName);
 293                 dup();
 294             }
 295             for (int i = 0; i < argTypes.length; i++) {
 296                 visitVarInsn(ALOAD, 0);
 297                 getfield(lambdaClassName, argNames[i], argTypes[i].getDescriptor());
 298             }
 299 
 300             convertArgumentTypes(Type.getArgumentTypes(m));
 301 
 302             // Invoke the method we want to forward to
 303             visitMethodInsn(invocationOpcode(), implMethodClassName, implMethodName, implMethodDesc);
 304 
 305             // Convert the return value (if any) and return it
 306             // Note: if adapting from non-void to void, the 'return' instruction will pop the unneeded result
 307             Type samReturnType = Type.getReturnType(m);
 308             convertType(implMethodReturnType, samReturnType, samReturnType);
 309             areturn(samReturnType);
 310 
 311             visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
 312             visitEnd();
 313         }
 314 
 315         private void convertArgumentTypes(Type[] samArgumentTypes) {
 316             int lvIndex = 0;
 317             boolean samIncludesReceiver = implIsInstanceMethod && argTypes.length == 0;
 318             int samReceiverLength = samIncludesReceiver ? 1 : 0;
 319             if (samIncludesReceiver) {
 320                 // push receiver
 321                 Type rcvrType = samArgumentTypes[0];
 322                 Type instantiatedRcvrType = instantiatedArgumentTypes[0];
 323 
 324                 load(lvIndex + 1, rcvrType);
 325                 lvIndex += rcvrType.getSize();
 326                 convertType(rcvrType, Type.getType(implDefiningClass), instantiatedRcvrType);
 327             }
 328             int argOffset = implMethodArgumentTypes.length - samArgumentTypes.length;
 329             for (int i = samReceiverLength; i < samArgumentTypes.length; i++) {
 330                 Type argType = samArgumentTypes[i];
 331                 Type targetType = implMethodArgumentTypes[argOffset + i];
 332                 Type instantiatedArgType = instantiatedArgumentTypes[i];
 333 
 334                 load(lvIndex + 1, argType);
 335                 lvIndex += argType.getSize();
 336                 convertType(argType, targetType, instantiatedArgType);
 337             }
 338         }
 339 
 340         private void convertType(Type argType, Type targetType, Type functionalType) {
 341             convertType(argType.getDescriptor(), targetType.getDescriptor(), functionalType.getDescriptor());
 342         }
 343 
 344         private int invocationOpcode() throws InternalError {
 345             switch (implKind) {
 346                 case MethodHandleInfo.REF_invokeStatic:
 347                     return INVOKESTATIC;
 348                 case MethodHandleInfo.REF_newInvokeSpecial:
 349                     return INVOKESPECIAL;
 350                  case MethodHandleInfo.REF_invokeVirtual:
 351                     return INVOKEVIRTUAL;
 352                 case MethodHandleInfo.REF_invokeInterface:
 353                     return INVOKEINTERFACE;
 354                 case MethodHandleInfo.REF_invokeSpecial:
 355                     return INVOKESPECIAL;
 356                 default:
 357                     throw new InternalError("Unexpected invocation kind: " + implKind);
 358             }
 359         }
 360 
 361         /**
 362          * The following methods are copied from
 363          * org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very
 364          * small and fast Java bytecode manipulation framework. Copyright (c)
 365          * 2000-2005 INRIA, France Telecom All rights reserved.
 366          *
 367          * Subclass with that (removing these methods) if that package/class is
 368          * ever added to the JDK.
 369          */
 370         private void iconst(final int cst) {
 371             if (cst >= -1 && cst <= 5) {
 372                 mv.visitInsn(Opcodes.ICONST_0 + cst);
 373             } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
 374                 mv.visitIntInsn(Opcodes.BIPUSH, cst);
 375             } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
 376                 mv.visitIntInsn(Opcodes.SIPUSH, cst);
 377             } else {
 378                 mv.visitLdcInsn(cst);
 379             }
 380         }
 381 
 382         private void load(final int var, final Type type) {
 383             mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), var);
 384         }
 385 
 386         private void dup() {
 387             mv.visitInsn(Opcodes.DUP);
 388         }
 389 
 390         private void areturn(final Type t) {
 391             mv.visitInsn(t.getOpcode(Opcodes.IRETURN));
 392         }
 393 
 394         private void getfield(
 395                 final String owner,
 396                 final String name,
 397                 final String desc) {
 398             mv.visitFieldInsn(Opcodes.GETFIELD, owner, name, desc);
 399         }
 400     }
 401 }