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