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