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