1/*
2 * Copyright (c) 2012, 2013, 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
26package java.lang.invoke;
27
28import jdk.internal.org.objectweb.asm.*;
29import sun.invoke.util.BytecodeDescriptor;
30import jdk.internal.misc.Unsafe;
31import sun.security.action.GetPropertyAction;
32
33import java.io.FilePermission;
34import java.io.Serializable;
35import java.lang.reflect.Constructor;
36import java.security.AccessController;
37import java.security.PrivilegedAction;
38import java.util.LinkedHashSet;
39import java.util.concurrent.atomic.AtomicInteger;
40import java.util.PropertyPermission;
41import java.util.Set;
42
43import static jdk.internal.org.objectweb.asm.Opcodes.*;
44
45/**
46 * Lambda metafactory implementation which dynamically creates an
47 * inner-class-like class per lambda callsite.
48 *
49 * @see LambdaMetafactory
50 */
51/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
52    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
53
54    private static final int CLASSFILE_VERSION = 52;
55    private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
56    private static final String JAVA_LANG_OBJECT = "java/lang/Object";
57    private static final String NAME_CTOR = "<init>";
58    private static final String NAME_FACTORY = "get$Lambda";
59
60    //Serialization support
61    private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
62    private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException";
63    private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
64    private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
65    private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
66    private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
67    private static final String NAME_METHOD_READ_OBJECT = "readObject";
68    private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
69
70    private static final String DESCR_CLASS = "Ljava/lang/Class;";
71    private static final String DESCR_STRING = "Ljava/lang/String;";
72    private static final String DESCR_OBJECT = "Ljava/lang/Object;";
73    private static final String DESCR_CTOR_SERIALIZED_LAMBDA
74            = "(" + DESCR_CLASS + DESCR_STRING + DESCR_STRING + DESCR_STRING + "I"
75            + DESCR_STRING + DESCR_STRING + DESCR_STRING + DESCR_STRING + "[" + DESCR_OBJECT + ")V";
76
77    private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION = "(Ljava/lang/String;)V";
78    private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
79
80
81    private static final String[] EMPTY_STRING_ARRAY = new String[0];
82
83    // Used to ensure that each spun class name is unique
84    private static final AtomicInteger counter = new AtomicInteger(0);
85
86    // For dumping generated classes to disk, for debugging purposes
87    private static final ProxyClassesDumper dumper;
88
89    static {
90        final String key = "jdk.internal.lambda.dumpProxyClasses";
91        String path = GetPropertyAction.privilegedGetProperty(key);
92        dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path);
93    }
94
95    // See context values in AbstractValidatingLambdaMetafactory
96    private final String implMethodClassName;        // Name of type containing implementation "CC"
97    private final String implMethodName;             // Name of implementation method "impl"
98    private final String implMethodDesc;             // Type descriptor for implementation methods "(I)Ljava/lang/String;"
99    private final MethodType constructorType;        // Generated class constructor type "(CC)void"
100    private final ClassWriter cw;                    // ASM class writer
101    private final String[] argNames;                 // Generated names for the constructor arguments
102    private final String[] argDescs;                 // Type descriptors for the constructor arguments
103    private final String lambdaClassName;            // Generated name for the generated class "X$$Lambda$1"
104
105    /**
106     * General meta-factory constructor, supporting both standard cases and
107     * allowing for uncommon options such as serialization or bridging.
108     *
109     * @param caller Stacked automatically by VM; represents a lookup context
110     *               with the accessibility privileges of the caller.
111     * @param invokedType Stacked automatically by VM; the signature of the
112     *                    invoked method, which includes the expected static
113     *                    type of the returned lambda object, and the static
114     *                    types of the captured arguments for the lambda.  In
115     *                    the event that the implementation method is an
116     *                    instance method, the first argument in the invocation
117     *                    signature will correspond to the receiver.
118     * @param samMethodName Name of the method in the functional interface to
119     *                      which the lambda or method reference is being
120     *                      converted, represented as a String.
121     * @param samMethodType Type of the method in the functional interface to
122     *                      which the lambda or method reference is being
123     *                      converted, represented as a MethodType.
124     * @param implMethod The implementation method which should be called (with
125     *                   suitable adaptation of argument types, return types,
126     *                   and adjustment for captured arguments) when methods of
127     *                   the resulting functional interface instance are invoked.
128     * @param instantiatedMethodType The signature of the primary functional
129     *                               interface method after type variables are
130     *                               substituted with their instantiation from
131     *                               the capture site
132     * @param isSerializable Should the lambda be made serializable?  If set,
133     *                       either the target type or one of the additional SAM
134     *                       types must extend {@code Serializable}.
135     * @param markerInterfaces Additional interfaces which the lambda object
136     *                       should implement.
137     * @param additionalBridges Method types for additional signatures to be
138     *                          bridged to the implementation method
139     * @throws LambdaConversionException If any of the meta-factory protocol
140     * invariants are violated
141     */
142    public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
143                                       MethodType invokedType,
144                                       String samMethodName,
145                                       MethodType samMethodType,
146                                       MethodHandle implMethod,
147                                       MethodType instantiatedMethodType,
148                                       boolean isSerializable,
149                                       Class<?>[] markerInterfaces,
150                                       MethodType[] additionalBridges)
151            throws LambdaConversionException {
152        super(caller, invokedType, samMethodName, samMethodType,
153              implMethod, instantiatedMethodType,
154              isSerializable, markerInterfaces, additionalBridges);
155        implMethodClassName = implClass.getName().replace('.', '/');
156        implMethodName = implInfo.getName();
157        implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
158        constructorType = invokedType.changeReturnType(Void.TYPE);
159        lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
160        cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
161        int parameterCount = invokedType.parameterCount();
162        if (parameterCount > 0) {
163            argNames = new String[parameterCount];
164            argDescs = new String[parameterCount];
165            for (int i = 0; i < parameterCount; i++) {
166                argNames[i] = "arg$" + (i + 1);
167                argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));
168            }
169        } else {
170            argNames = argDescs = EMPTY_STRING_ARRAY;
171        }
172    }
173
174    /**
175     * Build the CallSite. Generate a class file which implements the functional
176     * interface, define the class, if there are no parameters create an instance
177     * of the class which the CallSite will return, otherwise, generate handles
178     * which will call the class' constructor.
179     *
180     * @return a CallSite, which, when invoked, will return an instance of the
181     * functional interface
182     * @throws ReflectiveOperationException
183     * @throws LambdaConversionException If properly formed functional interface
184     * is not found
185     */
186    @Override
187    CallSite buildCallSite() throws LambdaConversionException {
188        final Class<?> innerClass = spinInnerClass();
189        if (invokedType.parameterCount() == 0) {
190            final Constructor<?>[] ctrs = AccessController.doPrivileged(
191                    new PrivilegedAction<>() {
192                @Override
193                public Constructor<?>[] run() {
194                    Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
195                    if (ctrs.length == 1) {
196                        // The lambda implementing inner class constructor is private, set
197                        // it accessible (by us) before creating the constant sole instance
198                        ctrs[0].setAccessible(true);
199                    }
200                    return ctrs;
201                }
202                    });
203            if (ctrs.length != 1) {
204                throw new LambdaConversionException("Expected one lambda constructor for "
205                        + innerClass.getCanonicalName() + ", got " + ctrs.length);
206            }
207
208            try {
209                Object inst = ctrs[0].newInstance();
210                return new ConstantCallSite(MethodHandles.constant(samBase, inst));
211            }
212            catch (ReflectiveOperationException e) {
213                throw new LambdaConversionException("Exception instantiating lambda object", e);
214            }
215        } else {
216            try {
217                UNSAFE.ensureClassInitialized(innerClass);
218                return new ConstantCallSite(
219                        MethodHandles.Lookup.IMPL_LOOKUP
220                             .findStatic(innerClass, NAME_FACTORY, invokedType));
221            }
222            catch (ReflectiveOperationException e) {
223                throw new LambdaConversionException("Exception finding constructor", e);
224            }
225        }
226    }
227
228    /**
229     * Generate a class file which implements the functional
230     * interface, define and return the class.
231     *
232     * @implNote The class that is generated does not include signature
233     * information for exceptions that may be present on the SAM method.
234     * This is to reduce classfile size, and is harmless as checked exceptions
235     * are erased anyway, no one will ever compile against this classfile,
236     * and we make no guarantees about the reflective properties of lambda
237     * objects.
238     *
239     * @return a Class which implements the functional interface
240     * @throws LambdaConversionException If properly formed functional interface
241     * is not found
242     */
243    private Class<?> spinInnerClass() throws LambdaConversionException {
244        String[] interfaces;
245        String samIntf = samBase.getName().replace('.', '/');
246        boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase);
247        if (markerInterfaces.length == 0) {
248            interfaces = new String[]{samIntf};
249        } else {
250            // Assure no duplicate interfaces (ClassFormatError)
251            Set<String> itfs = new LinkedHashSet<>(markerInterfaces.length + 1);
252            itfs.add(samIntf);
253            for (Class<?> markerInterface : markerInterfaces) {
254                itfs.add(markerInterface.getName().replace('.', '/'));
255                accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);
256            }
257            interfaces = itfs.toArray(new String[itfs.size()]);
258        }
259
260        cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
261                 lambdaClassName, null,
262                 JAVA_LANG_OBJECT, interfaces);
263
264        // Generate final fields to be filled in by constructor
265        for (int i = 0; i < argDescs.length; i++) {
266            FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
267                                            argNames[i],
268                                            argDescs[i],
269                                            null, null);
270            fv.visitEnd();
271        }
272
273        generateConstructor();
274
275        if (invokedType.parameterCount() != 0) {
276            generateFactory();
277        }
278
279        // Forward the SAM method
280        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
281                                          samMethodType.toMethodDescriptorString(), null, null);
282        mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
283        new ForwardingMethodGenerator(mv).generate(samMethodType);
284
285        // Forward the bridges
286        if (additionalBridges != null) {
287            for (MethodType mt : additionalBridges) {
288                mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
289                                    mt.toMethodDescriptorString(), null, null);
290                mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
291                new ForwardingMethodGenerator(mv).generate(mt);
292            }
293        }
294
295        if (isSerializable)
296            generateSerializationFriendlyMethods();
297        else if (accidentallySerializable)
298            generateSerializationHostileMethods();
299
300        cw.visitEnd();
301
302        // Define the generated class in this VM.
303
304        final byte[] classBytes = cw.toByteArray();
305
306        // If requested, dump out to a file for debugging purposes
307        if (dumper != null) {
308            AccessController.doPrivileged(new PrivilegedAction<>() {
309                @Override
310                public Void run() {
311                    dumper.dumpClass(lambdaClassName, classBytes);
312                    return null;
313                }
314            }, null,
315            new FilePermission("<<ALL FILES>>", "read, write"),
316            // createDirectories may need it
317            new PropertyPermission("user.dir", "read"));
318        }
319
320        return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
321    }
322
323    /**
324     * Generate the factory method for the class
325     */
326    private void generateFactory() {
327        MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null);
328        m.visitCode();
329        m.visitTypeInsn(NEW, lambdaClassName);
330        m.visitInsn(Opcodes.DUP);
331        int parameterCount = invokedType.parameterCount();
332        for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) {
333            Class<?> argType = invokedType.parameterType(typeIndex);
334            m.visitVarInsn(getLoadOpcode(argType), varIndex);
335            varIndex += getParameterSize(argType);
336        }
337        m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false);
338        m.visitInsn(ARETURN);
339        m.visitMaxs(-1, -1);
340        m.visitEnd();
341    }
342
343    /**
344     * Generate the constructor for the class
345     */
346    private void generateConstructor() {
347        // Generate constructor
348        MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
349                                            constructorType.toMethodDescriptorString(), null, null);
350        ctor.visitCode();
351        ctor.visitVarInsn(ALOAD, 0);
352        ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
353                             METHOD_DESCRIPTOR_VOID, false);
354        int parameterCount = invokedType.parameterCount();
355        for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
356            ctor.visitVarInsn(ALOAD, 0);
357            Class<?> argType = invokedType.parameterType(i);
358            ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
359            lvIndex += getParameterSize(argType);
360            ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);
361        }
362        ctor.visitInsn(RETURN);
363        // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
364        ctor.visitMaxs(-1, -1);
365        ctor.visitEnd();
366    }
367
368    /**
369     * Generate a writeReplace method that supports serialization
370     */
371    private void generateSerializationFriendlyMethods() {
372        TypeConvertingMethodAdapter mv
373                = new TypeConvertingMethodAdapter(
374                    cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
375                    NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
376                    null, null));
377
378        mv.visitCode();
379        mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
380        mv.visitInsn(DUP);
381        mv.visitLdcInsn(Type.getType(targetClass));
382        mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/'));
383        mv.visitLdcInsn(samMethodName);
384        mv.visitLdcInsn(samMethodType.toMethodDescriptorString());
385        mv.visitLdcInsn(implInfo.getReferenceKind());
386        mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/'));
387        mv.visitLdcInsn(implInfo.getName());
388        mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString());
389        mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString());
390        mv.iconst(argDescs.length);
391        mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT);
392        for (int i = 0; i < argDescs.length; i++) {
393            mv.visitInsn(DUP);
394            mv.iconst(i);
395            mv.visitVarInsn(ALOAD, 0);
396            mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
397            mv.boxIfTypePrimitive(Type.getType(argDescs[i]));
398            mv.visitInsn(AASTORE);
399        }
400        mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
401                DESCR_CTOR_SERIALIZED_LAMBDA, false);
402        mv.visitInsn(ARETURN);
403        // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
404        mv.visitMaxs(-1, -1);
405        mv.visitEnd();
406    }
407
408    /**
409     * Generate a readObject/writeObject method that is hostile to serialization
410     */
411    private void generateSerializationHostileMethods() {
412        MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
413                                          NAME_METHOD_WRITE_OBJECT, DESCR_METHOD_WRITE_OBJECT,
414                                          null, SER_HOSTILE_EXCEPTIONS);
415        mv.visitCode();
416        mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
417        mv.visitInsn(DUP);
418        mv.visitLdcInsn("Non-serializable lambda");
419        mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
420                           DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false);
421        mv.visitInsn(ATHROW);
422        mv.visitMaxs(-1, -1);
423        mv.visitEnd();
424
425        mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
426                            NAME_METHOD_READ_OBJECT, DESCR_METHOD_READ_OBJECT,
427                            null, SER_HOSTILE_EXCEPTIONS);
428        mv.visitCode();
429        mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
430        mv.visitInsn(DUP);
431        mv.visitLdcInsn("Non-serializable lambda");
432        mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
433                           DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false);
434        mv.visitInsn(ATHROW);
435        mv.visitMaxs(-1, -1);
436        mv.visitEnd();
437    }
438
439    /**
440     * This class generates a method body which calls the lambda implementation
441     * method, converting arguments, as needed.
442     */
443    private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
444
445        ForwardingMethodGenerator(MethodVisitor mv) {
446            super(mv);
447        }
448
449        void generate(MethodType methodType) {
450            visitCode();
451
452            if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
453                visitTypeInsn(NEW, implMethodClassName);
454                visitInsn(DUP);
455            }
456            for (int i = 0; i < argNames.length; i++) {
457                visitVarInsn(ALOAD, 0);
458                visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
459            }
460
461            convertArgumentTypes(methodType);
462
463            // Invoke the method we want to forward to
464            visitMethodInsn(invocationOpcode(), implMethodClassName,
465                            implMethodName, implMethodDesc,
466                            implClass.isInterface());
467
468            // Convert the return value (if any) and return it
469            // Note: if adapting from non-void to void, the 'return'
470            // instruction will pop the unneeded result
471            Class<?> implReturnClass = implMethodType.returnType();
472            Class<?> samReturnClass = methodType.returnType();
473            convertType(implReturnClass, samReturnClass, samReturnClass);
474            visitInsn(getReturnOpcode(samReturnClass));
475            // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
476            visitMaxs(-1, -1);
477            visitEnd();
478        }
479
480        private void convertArgumentTypes(MethodType samType) {
481            int lvIndex = 0;
482            int samParametersLength = samType.parameterCount();
483            int captureArity = invokedType.parameterCount();
484            for (int i = 0; i < samParametersLength; i++) {
485                Class<?> argType = samType.parameterType(i);
486                visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
487                lvIndex += getParameterSize(argType);
488                convertType(argType, implMethodType.parameterType(captureArity + i), instantiatedMethodType.parameterType(i));
489            }
490        }
491
492        private int invocationOpcode() throws InternalError {
493            switch (implKind) {
494                case MethodHandleInfo.REF_invokeStatic:
495                    return INVOKESTATIC;
496                case MethodHandleInfo.REF_newInvokeSpecial:
497                    return INVOKESPECIAL;
498                 case MethodHandleInfo.REF_invokeVirtual:
499                    return INVOKEVIRTUAL;
500                case MethodHandleInfo.REF_invokeInterface:
501                    return INVOKEINTERFACE;
502                case MethodHandleInfo.REF_invokeSpecial:
503                    return INVOKESPECIAL;
504                default:
505                    throw new InternalError("Unexpected invocation kind: " + implKind);
506            }
507        }
508    }
509
510    static int getParameterSize(Class<?> c) {
511        if (c == Void.TYPE) {
512            return 0;
513        } else if (c == Long.TYPE || c == Double.TYPE) {
514            return 2;
515        }
516        return 1;
517    }
518
519    static int getLoadOpcode(Class<?> c) {
520        if(c == Void.TYPE) {
521            throw new InternalError("Unexpected void type of load opcode");
522        }
523        return ILOAD + getOpcodeOffset(c);
524    }
525
526    static int getReturnOpcode(Class<?> c) {
527        if(c == Void.TYPE) {
528            return RETURN;
529        }
530        return IRETURN + getOpcodeOffset(c);
531    }
532
533    private static int getOpcodeOffset(Class<?> c) {
534        if (c.isPrimitive()) {
535            if (c == Long.TYPE) {
536                return 1;
537            } else if (c == Float.TYPE) {
538                return 2;
539            } else if (c == Double.TYPE) {
540                return 3;
541            }
542            return 0;
543        } else {
544            return 4;
545        }
546    }
547
548}
549