8000806: Implement runtime lambda metafactory
Summary: Implement lambda invokedynamic bootstrap by generating at runtime an inner class that implements the functional interface
Reviewed-by: twisti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java Thu Oct 25 17:34:24 2012 -0700
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.lang.invoke;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import sun.invoke.util.Wrapper;
+import static sun.invoke.util.Wrapper.*;
+
+/**
+ * Abstract implementation of a meta-factory which provides parameter unrolling and input validation.
+ *
+ * @author Robert Field
+ */
+/*non-public*/ abstract class AbstractValidatingLambdaMetafactory {
+
+ /*
+ * For context, the comments for the following fields are marked in quotes with their values, given this program:
+ * interface II<T> { Object foo(T x); }
+ * interface JJ<R extends Number> extends II<R> { }
+ * class CC { String impl(int i) { return "impl:"+i; }}
+ * class X {
+ * public static void main(String[] args) {
+ * JJ<Integer> iii = (new CC())::impl;
+ * System.out.printf(">>> %s\n", iii.foo(44));
+ * }}
+ */
+ final Class<?> targetClass; // The class calling the meta-factory via invokedynamic "class X"
+ final MethodType invokedType; // The type of the invoked method "(CC)II"
+ final Class<?> samBase; // The type of the returned instance "interface JJ"
+ final boolean isSerializable; // Should the returned instance be serializable
+ final MethodHandleInfo samInfo; // Info about the SAM method handle "MethodHandleInfo[9 II.foo(Object)Object]"
+ final Class<?> samClass; // Interface containing the SAM method "interface II"
+ final MethodType samMethodType; // Type of the SAM method "(Object)Object"
+ final MethodHandleInfo implInfo; // Info about the implementation method handle "MethodHandleInfo[5 CC.impl(int)String]"
+ final int implKind; // Invocation kind for implementation "5"=invokevirtual
+ final boolean implIsInstanceMethod; // Is the implementation an instance method "true"
+ final Class<?> implDefiningClass; // Type defining the implementation "class CC"
+ final MethodType implMethodType; // Type of the implementation method "(int)String"
+ final MethodType instantiatedMethodType; // Instantiated erased functional interface method type "(Integer)Object"
+
+
+ /**
+ * Meta-factory constructor.
+ *
+ * @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges
+ * of the caller.
+ * @param invokedType Stacked automatically by VM; the signature of the invoked method, which includes the
+ * expected static type of the returned lambda object, and the static types of the captured
+ * arguments for the lambda. In the event that the implementation method is an instance method,
+ * the first argument in the invocation signature will correspond to the receiver.
+ * @param samMethod The primary method in the functional interface to which the lambda or method reference is
+ * being converted, represented as a method handle.
+ * @param implMethod The implementation method which should be called (with suitable adaptation of argument
+ * types, return types, and adjustment for captured arguments) when methods of the resulting
+ * functional interface instance are invoked.
+ * @param instantiatedMethodType The signature of the SAM method from the functional interface's perspective
+ * @throws ReflectiveOperationException
+ */
+ AbstractValidatingLambdaMetafactory(MethodHandles.Lookup caller,
+ MethodType invokedType,
+ MethodHandle samMethod,
+ MethodHandle implMethod,
+ MethodType instantiatedMethodType)
+ throws ReflectiveOperationException {
+ this.targetClass = caller.lookupClass();
+ this.invokedType = invokedType;
+
+ this.samBase = invokedType.returnType();
+ this.isSerializable = Serializable.class.isAssignableFrom(samBase);
+
+ this.samInfo = new MethodHandleInfo(samMethod);
+ this.samClass = samInfo.getDeclaringClass();
+ this.samMethodType = samInfo.getMethodType();
+
+ this.implInfo = new MethodHandleInfo(implMethod);
+ this.implKind = implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeSpecial? MethodHandleInfo.REF_invokeVirtual : implInfo.getReferenceKind(); // @@@ Temp work-around to hotspot incorrectly converting to invokespecial
+ this.implIsInstanceMethod =
+ implKind == MethodHandleInfo.REF_invokeVirtual ||
+ implKind == MethodHandleInfo.REF_invokeSpecial ||
+ implKind == MethodHandleInfo.REF_invokeInterface;
+ this.implDefiningClass = implInfo.getDeclaringClass();
+ this.implMethodType = implInfo.getMethodType();
+
+ this.instantiatedMethodType = instantiatedMethodType;
+ }
+
+ /**
+ * Build the CallSite.
+ *
+ * @return a CallSite, which, when invoked, will return an instance of the
+ * functional interface
+ * @throws ReflectiveOperationException
+ */
+ abstract CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException;
+
+ /**
+ * Check the meta-factory arguments for errors
+ * @throws LambdaConversionException if there are improper conversions
+ */
+ void validateMetafactoryArgs() throws LambdaConversionException {
+ // Check target type is a subtype of class where SAM method is defined
+ if (!samClass.isAssignableFrom(samBase)) {
+ throw new LambdaConversionException(String.format("Invalid target type %s for lambda conversion; not a subtype of functional interface %s",
+ samBase.getName(), samClass.getName()));
+ }
+
+ switch (implKind) {
+ case MethodHandleInfo.REF_invokeInterface:
+ case MethodHandleInfo.REF_invokeVirtual:
+ case MethodHandleInfo.REF_invokeStatic:
+ case MethodHandleInfo.REF_newInvokeSpecial:
+ case MethodHandleInfo.REF_invokeSpecial:
+ break;
+ default:
+ throw new LambdaConversionException(String.format("Unsupported MethodHandle kind: %s", implInfo));
+ }
+
+ // Check arity: optional-receiver + captured + SAM == impl
+ final int implArity = implMethodType.parameterCount();
+ final int receiverArity = implIsInstanceMethod ? 1 : 0;
+ final int capturedArity = invokedType.parameterCount();
+ final int samArity = samMethodType.parameterCount();
+ final int instantiatedArity = instantiatedMethodType.parameterCount();
+ if (implArity + receiverArity != capturedArity + samArity) {
+ throw new LambdaConversionException(String.format("Incorrect number of parameters for %s method %s; %d captured parameters, %d functional interface parameters, %d implementation parameters",
+ implIsInstanceMethod ? "instance" : "static", implInfo,
+ capturedArity, samArity, implArity));
+ }
+ if (instantiatedArity != samArity) {
+ throw new LambdaConversionException(String.format("Incorrect number of parameters for %s method %s; %d functional interface parameters, %d SAM method parameters",
+ implIsInstanceMethod ? "instance" : "static", implInfo,
+ instantiatedArity, samArity));
+ }
+
+ // If instance: first captured arg (receiver) must be subtype of class where impl method is defined
+ final int capturedStart;
+ final int samStart;
+ if (implIsInstanceMethod) {
+ final Class<?> receiverClass;
+
+ // implementation is an instance method, adjust for receiver in captured variables / SAM arguments
+ if (capturedArity == 0) {
+ // receiver is function parameter
+ capturedStart = 0;
+ samStart = 1;
+ receiverClass = instantiatedMethodType.parameterType(0);
+ } else {
+ // receiver is a captured variable
+ capturedStart = 1;
+ samStart = 0;
+ receiverClass = invokedType.parameterType(0);
+ }
+
+ // check receiver type
+ if (!implDefiningClass.isAssignableFrom(receiverClass)) {
+ throw new LambdaConversionException(String.format("Invalid receiver type %s; not a subtype of implementation type %s",
+ receiverClass, implDefiningClass));
+ }
+ } else {
+ // no receiver
+ capturedStart = 0;
+ samStart = 0;
+ }
+
+ // Check for exact match on non-receiver captured arguments
+ final int implFromCaptured = capturedArity - capturedStart;
+ for (int i=0; i<implFromCaptured; i++) {
+ Class<?> implParamType = implMethodType.parameterType(i);
+ Class<?> capturedParamType = invokedType.parameterType(i + capturedStart);
+ if (!capturedParamType.equals(implParamType)) {
+ throw new LambdaConversionException(
+ String.format("Type mismatch in captured lambda parameter %d: expecting %s, found %s", i, capturedParamType, implParamType));
+ }
+ }
+ // Check for adaptation match on SAM arguments
+ final int samOffset = samStart - implFromCaptured;
+ for (int i=implFromCaptured; i<implArity; i++) {
+ Class<?> implParamType = implMethodType.parameterType(i);
+ Class<?> instantiatedParamType = instantiatedMethodType.parameterType(i + samOffset);
+ if (!isAdaptableTo(instantiatedParamType, implParamType, true)) {
+ throw new LambdaConversionException(
+ String.format("Type mismatch for lambda argument %d: %s is not convertible to %s", i, instantiatedParamType, implParamType));
+ }
+ }
+
+ // Adaptation match: return type
+ Class<?> expectedType = instantiatedMethodType.returnType();
+ Class<?> actualReturnType =
+ (implKind == MethodHandleInfo.REF_newInvokeSpecial)
+ ? implDefiningClass
+ : implMethodType.returnType();
+ if (!isAdaptableToAsReturn(actualReturnType, expectedType)) {
+ throw new LambdaConversionException(
+ String.format("Type mismatch for lambda return: %s is not convertible to %s", actualReturnType, expectedType));
+ }
+ }
+
+ /**
+ * Check type adaptability
+ * @param fromType
+ * @param toType
+ * @param strict If true, do strict checks, else allow that fromType may be parameterized
+ * @return True if 'fromType' can be passed to an argument of 'toType'
+ */
+ private boolean isAdaptableTo(Class<?> fromType, Class<?> toType, boolean strict) {
+ if (fromType.equals(toType)) {
+ return true;
+ }
+ if (fromType.isPrimitive()) {
+ Wrapper wfrom = forPrimitiveType(fromType);
+ if (toType.isPrimitive()) {
+ // both are primitive: widening
+ Wrapper wto = forPrimitiveType(toType);
+ return wto.isConvertibleFrom(wfrom);
+ } else {
+ // from primitive to reference: boxing
+ return toType.isAssignableFrom(wfrom.wrapperType());
+ }
+ } else {
+ if (toType.isPrimitive()) {
+ // from reference to primitive: unboxing
+ Wrapper wfrom;
+ if (isWrapperType(fromType) && (wfrom = forWrapperType(fromType)).primitiveType().isPrimitive()) {
+ // fromType is a primitive wrapper; unbox+widen
+ Wrapper wto = forPrimitiveType(toType);
+ return wto.isConvertibleFrom(wfrom);
+ } else {
+ // must be convertible to primitive
+ return !strict;
+ }
+ } else {
+ // both are reference types: fromType should be a superclass of toType.
+ return strict? toType.isAssignableFrom(fromType) : true;
+ }
+ }
+ }
+
+ /**
+ * Check type adaptability for return types -- special handling of void type) and parameterized fromType
+ * @param fromType
+ * @param toType
+ * @return True if 'fromType' can be converted to 'toType'
+ */
+ private boolean isAdaptableToAsReturn(Class<?> fromType, Class<?> toType) {
+ return toType.equals(void.class)
+ || !fromType.equals(void.class) && isAdaptableTo(fromType, toType, false);
+ }
+
+
+ /*********** Logging support -- for debugging only
+ static final Executor logPool = Executors.newSingleThreadExecutor(); // @@@ For debugging only
+ protected static void log(final String s) {
+ MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() {
+ @Override
+ public void run() {
+ System.out.println(s);
+ }
+ });
+ }
+
+ protected static void log(final String s, final Throwable e) {
+ MethodHandleProxyLambdaMetafactory.logPool.execute(new Runnable() {
+ @Override
+ public void run() {
+ System.out.println(s);
+ e.printStackTrace(System.out);
+ }
+ });
+ }
+ ***********************/
+
+ /**
+ * Find the SAM method and corresponding methods which should be bridged. SAM method and those to be bridged
+ * will have the same name and number of parameters. Check for matching default methods (non-abstract), they
+ * should not be bridged-over and indicate a complex bridging situation.
+ */
+ class MethodAnalyzer {
+ private final Method[] methods = samBase.getMethods();
+ private final List<Method> methodsFound = new ArrayList<>(methods.length);
+
+ private Method samMethod = null;
+ private final List<Method> methodsToBridge = new ArrayList<>(methods.length);
+ private boolean defaultMethodFound = false;
+
+ MethodAnalyzer() {
+ String samMethodName = samInfo.getName();
+ Class<?>[] samParamTypes = samMethodType.parameterArray();
+ int samParamLength = samParamTypes.length;
+ Class<?> samReturnType = samMethodType.returnType();
+ Class<?> objectClass = Object.class;
+
+ for (Method m : methods) {
+ if (m.getName().equals(samMethodName) && m.getDeclaringClass() != objectClass) {
+ Class<?>[] mParamTypes = m.getParameterTypes();
+ if (mParamTypes.length == samParamLength) {
+ if (Modifier.isAbstract(m.getModifiers())) {
+ // Exclude methods with duplicate signatures
+ if (methodUnique(m)) {
+ if (m.getReturnType().equals(samReturnType) && Arrays.equals(mParamTypes, samParamTypes)) {
+ // Exact match, this is the SAM method signature
+ samMethod = m;
+ } else {
+ methodsToBridge.add(m);
+ }
+ }
+ } else {
+ // This is a default method, flag for special processing
+ defaultMethodFound = true;
+ // Ignore future matching abstracts.
+ // Note, due to reabstraction, this is really a punt, hence pass-off to VM
+ methodUnique(m);
+ }
+ }
+ }
+ }
+ }
+
+ Method getSamMethod() {
+ return samMethod;
+ }
+
+ List<Method> getMethodsToBridge() {
+ return methodsToBridge;
+ }
+
+ boolean wasDefaultMethodFound() {
+ return defaultMethodFound;
+ }
+
+ /**
+ * Search the list of previously found methods to determine if there is a method with the same signature
+ * (return and parameter types) as the specified method. If it wasn't found before, add to the found list.
+ *
+ * @param m The method to match
+ * @return False if the method was found, True otherwise
+ */
+ private boolean methodUnique(Method m) {
+ Class<?>[] ptypes = m.getParameterTypes();
+ Class<?> rtype = m.getReturnType();
+ for (Method md : methodsFound) {
+ if (md.getReturnType().equals(rtype) && Arrays.equals(ptypes, md.getParameterTypes())) {
+ return false;
+ }
+ }
+ methodsFound.add(m);
+ return true;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Thu Oct 25 17:34:24 2012 -0700
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.security.ProtectionDomain;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import jdk.internal.org.objectweb.asm.*;
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+import sun.misc.Unsafe;
+
+/**
+ * InnerClassLambdaMetafactory
+ */
+/*non-public*/ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
+ private static final int CLASSFILE_VERSION = 51;
+ private static final Type TYPE_VOID = Type.getType(void.class);
+ private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
+ private static final String NAME_MAGIC_ACCESSOR_IMPL = "java/lang/invoke/MagicLambdaImpl";
+ private static final String NAME_SERIALIZABLE = "java/io/Serializable";
+ private static final String NAME_CTOR = "<init>";
+
+ //Serialization support
+ private static final String NAME_SERIALIZED_LAMBDA = "com/oracle/java/lang/invoke/SerializedLambdaImpl";
+ private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
+ private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
+ private static final String NAME_OBJECT = "java/lang/Object";
+
+ // Used to ensure that each spun class name is unique
+ private static final AtomicInteger counter = new AtomicInteger(0);
+
+ // See context values in AbstractValidatingLambdaMetafactory
+ private final String implMethodClassName; // Name of type containing implementation "CC"
+ private final String implMethodName; // Name of implementation method "impl"
+ private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;"
+ private final Type[] implMethodArgumentTypes; // ASM types for implementaion method parameters
+ private final Type implMethodReturnType; // ASM type for implementaion method return type "Ljava/lang/String;"
+ private final MethodType constructorType; // Generated class constructor type "(CC)void"
+ private final String constructorDesc; // Type descriptor for constructor "(LCC;)V"
+ private final ClassWriter cw; // ASM class writer
+ private final Type[] argTypes; // ASM types for the constructor arguments
+ private final String[] argNames; // Generated names for the constructor arguments
+ private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1"
+ private final Type[] instantiatedArgumentTypes; // ASM types for the functional interface arguments
+
+ /**
+ * Meta-factory constructor.
+ *
+ * @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges
+ * of the caller.
+ * @param invokedType Stacked automatically by VM; the signature of the invoked method, which includes the
+ * expected static type of the returned lambda object, and the static types of the captured
+ * arguments for the lambda. In the event that the implementation method is an instance method,
+ * the first argument in the invocation signature will correspond to the receiver.
+ * @param samMethod The primary method in the functional interface to which the lambda or method reference is
+ * being converted, represented as a method handle.
+ * @param implMethod The implementation method which should be called (with suitable adaptation of argument
+ * types, return types, and adjustment for captured arguments) when methods of the resulting
+ * functional interface instance are invoked.
+ * @param instantiatedMethodType The signature of the SAM method from the functional interface's perspective
+ * @throws ReflectiveOperationException
+ */
+ public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
+ MethodType invokedType,
+ MethodHandle samMethod,
+ MethodHandle implMethod,
+ MethodType instantiatedMethodType)
+ throws ReflectiveOperationException {
+ super(caller, invokedType, samMethod, implMethod, instantiatedMethodType);
+ implMethodClassName = implDefiningClass.getName().replace('.', '/');
+ implMethodName = implInfo.getName();
+ implMethodDesc = implMethodType.toMethodDescriptorString();
+ Type implMethodAsmType = Type.getMethodType(implMethodDesc);
+ implMethodArgumentTypes = implMethodAsmType.getArgumentTypes();
+ implMethodReturnType = implMethodAsmType.getReturnType();
+ constructorType = invokedType.changeReturnType(Void.TYPE);
+ constructorDesc = constructorType.toMethodDescriptorString();
+ lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
+ cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ argTypes = Type.getArgumentTypes(constructorDesc);
+ argNames = new String[argTypes.length];
+ for (int i = 0; i < argTypes.length; i++) {
+ argNames[i] = "arg$" + (i + 1);
+ }
+ instantiatedArgumentTypes = Type.getArgumentTypes(instantiatedMethodType.toMethodDescriptorString());
+
+ }
+
+ /**
+ * Build the CallSite. Generate a class file which implements the functional
+ * interface, define the class, if there are no parameters create an instance
+ * of the class which the CallSite will return, otherwise, generate handles
+ * which will call the class' constructor.
+ *
+ * @return a CallSite, which, when invoked, will return an instance of the
+ * functional interface
+ * @throws ReflectiveOperationException
+ */
+ @Override
+ CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException {
+ final Class<?> innerClass = spinInnerClass();
+ if (invokedType.parameterCount() == 0) {
+ return new ConstantCallSite(MethodHandles.constant(samBase, innerClass.newInstance()));
+ } else {
+ return new ConstantCallSite(
+ MethodHandles.Lookup.IMPL_LOOKUP
+ .findConstructor(innerClass, constructorType)
+ .asType(constructorType.changeReturnType(samBase)));
+ }
+ }
+
+ /**
+ * Generate a class file which implements the functional
+ * interface, define and return the class.
+ *
+ * @return a Class which implements the functional interface
+ */
+ private <T> Class<? extends T> spinInnerClass() throws LambdaConversionException {
+ String samName = samBase.getName().replace('.', '/');
+
+ cw.visit(CLASSFILE_VERSION, ACC_PUBLIC + ACC_SUPER, lambdaClassName, null, NAME_MAGIC_ACCESSOR_IMPL,
+ isSerializable ? new String[]{samName, NAME_SERIALIZABLE} : new String[]{samName});
+
+ // Generate final fields to be filled in by constructor
+ for (int i = 0; i < argTypes.length; i++) {
+ FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL, argNames[i], argTypes[i].getDescriptor(), null, null);
+ fv.visitEnd();
+ }
+
+ generateConstructor();
+
+ MethodAnalyzer ma = new MethodAnalyzer();
+
+ // Forward the SAM method
+ if (ma.getSamMethod() == null) {
+ throw new LambdaConversionException(String.format("SAM method not found: %s", samMethodType));
+ } else {
+ generateForwardingMethod(ma.getSamMethod(), false);
+ }
+
+ // Forward the bridges
+ // @@@ Once the VM can do fail-over, uncomment the default method test
+ if (!ma.getMethodsToBridge().isEmpty() /* && !ma.wasDefaultMethodFound() */) {
+ for (Method m : ma.getMethodsToBridge()) {
+ generateForwardingMethod(m, true);
+ }
+ }
+
+ /***** Serialization not yet supported
+ if (isSerializable) {
+ String samMethodName = samInfo.getName();
+ Type samType = Type.getType(samBase);
+ generateSerializationMethod(samType, samMethodName);
+ }
+ ******/
+
+ cw.visitEnd();
+
+ // Define the generated class in this VM.
+
+ final byte[] classBytes = cw.toByteArray();
+
+ if (System.getProperty("debug.dump.generated") != null) {
+ System.out.printf("Loaded: %s (%d bytes) %n", lambdaClassName, classBytes.length);
+ try (FileOutputStream fos = new FileOutputStream(lambdaClassName.replace('/', '.') + ".class")) {
+ fos.write(classBytes);
+ } catch (IOException ex) {
+ Logger.getLogger(InnerClassLambdaMetafactory.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+
+ ClassLoader loader = targetClass.getClassLoader();
+ ProtectionDomain pd = (loader == null) ? null : targetClass.getProtectionDomain();
+ return (Class<? extends T>) Unsafe.getUnsafe().defineClass(lambdaClassName, classBytes, 0, classBytes.length, loader, pd);
+ }
+
+ /**
+ * Generate the constructor for the class
+ */
+ private void generateConstructor() {
+ // Generate constructor
+ MethodVisitor ctor = cw.visitMethod(ACC_PUBLIC, NAME_CTOR, constructorDesc, null, null);
+ ctor.visitCode();
+ ctor.visitVarInsn(ALOAD, 0);
+ ctor.visitMethodInsn(INVOKESPECIAL, NAME_MAGIC_ACCESSOR_IMPL, NAME_CTOR, METHOD_DESCRIPTOR_VOID);
+ int lvIndex = 0;
+ for (int i = 0; i < argTypes.length; i++) {
+ ctor.visitVarInsn(ALOAD, 0);
+ ctor.visitVarInsn(argTypes[i].getOpcode(ILOAD), lvIndex + 1);
+ lvIndex += argTypes[i].getSize();
+ ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor());
+ }
+ ctor.visitInsn(RETURN);
+ ctor.visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
+ ctor.visitEnd();
+ }
+
+ /**
+ * Generate the serialization method (if needed)
+ */
+ /****** This code is out of date -- known to be wrong -- and not currently used ******
+ private void generateSerializationMethod(Type samType, String samMethodName) {
+ String samMethodDesc = samMethodType.toMethodDescriptorString();
+ TypeConvertingMethodAdapter mv = new TypeConvertingMethodAdapter(cw.visitMethod(ACC_PRIVATE + ACC_FINAL, NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, null, null));
+
+ mv.visitCode();
+ mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
+ mv.dup();
+ mv.visitLdcInsn(samType);
+ mv.visitLdcInsn(samMethodName);
+ mv.visitLdcInsn(samMethodDesc);
+ mv.visitLdcInsn(Type.getType(implDefiningClass));
+ mv.visitLdcInsn(implMethodName);
+ mv.visitLdcInsn(implMethodDesc);
+
+ mv.iconst(argTypes.length);
+ mv.visitTypeInsn(ANEWARRAY, NAME_OBJECT);
+ for (int i = 0; i < argTypes.length; i++) {
+ mv.dup();
+ mv.iconst(i);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.getfield(lambdaClassName, argNames[i], argTypes[i].getDescriptor());
+ mv.boxIfPrimitive(argTypes[i]);
+ mv.visitInsn(AASTORE);
+ }
+ mv.invokespecial(NAME_SERIALIZED_LAMBDA, NAME_CTOR,
+ "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V");
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
+ mv.visitEnd();
+ }
+ ********/
+
+ /**
+ * Generate a method which calls the lambda implementation method,
+ * converting arguments, as needed.
+ * @param m The method whose signature should be generated
+ * @param isBridge True if this methods should be flagged as a bridge
+ */
+ private void generateForwardingMethod(Method m, boolean isBridge) {
+ Class<?>[] exceptionTypes = m.getExceptionTypes();
+ String[] exceptionNames = new String[exceptionTypes.length];
+ for (int i = 0; i < exceptionTypes.length; i++) {
+ exceptionNames[i] = exceptionTypes[i].getName().replace('.', '/');
+ }
+ String methodDescriptor = Type.getMethodDescriptor(m);
+ int access = isBridge? ACC_PUBLIC | ACC_BRIDGE : ACC_PUBLIC;
+ MethodVisitor mv = cw.visitMethod(access, m.getName(), methodDescriptor, null, exceptionNames);
+ new ForwardingMethodGenerator(mv).generate(m);
+ }
+
+ /**
+ * This class generates a method body which calls the lambda implementation
+ * method, converting arguments, as needed.
+ */
+ private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
+
+ ForwardingMethodGenerator(MethodVisitor mv) {
+ super(mv);
+ }
+
+ void generate(Method m) throws InternalError {
+ visitCode();
+
+ if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
+ visitTypeInsn(NEW, implMethodClassName);
+ dup();
+ }
+ for (int i = 0; i < argTypes.length; i++) {
+ visitVarInsn(ALOAD, 0);
+ getfield(lambdaClassName, argNames[i], argTypes[i].getDescriptor());
+ }
+
+ convertArgumentTypes(Type.getArgumentTypes(m));
+
+ // Invoke the method we want to forward to
+ visitMethodInsn(invocationOpcode(), implMethodClassName, implMethodName, implMethodDesc);
+
+ // Convert the return value (if any) and return it
+ // Note: if adapting from non-void to void, the 'return' instruction will pop the unneeded result
+ Type samReturnType = Type.getReturnType(m);
+ convertType(implMethodReturnType, samReturnType, samReturnType);
+ areturn(samReturnType);
+
+ visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
+ visitEnd();
+ }
+
+ private void convertArgumentTypes(Type[] samArgumentTypes) {
+ int lvIndex = 0;
+ boolean samIncludesReceiver = implIsInstanceMethod && argTypes.length == 0;
+ int samReceiverLength = samIncludesReceiver ? 1 : 0;
+ if (samIncludesReceiver) {
+ // push receiver
+ Type rcvrType = samArgumentTypes[0];
+ Type instantiatedRcvrType = instantiatedArgumentTypes[0];
+
+ load(lvIndex + 1, rcvrType);
+ lvIndex += rcvrType.getSize();
+ convertType(rcvrType, Type.getType(implDefiningClass), instantiatedRcvrType);
+ }
+ int argOffset = implMethodArgumentTypes.length - samArgumentTypes.length;
+ for (int i = samReceiverLength; i < samArgumentTypes.length; i++) {
+ Type argType = samArgumentTypes[i];
+ Type targetType = implMethodArgumentTypes[argOffset + i];
+ Type instantiatedArgType = instantiatedArgumentTypes[i];
+
+ load(lvIndex + 1, argType);
+ lvIndex += argType.getSize();
+ convertType(argType, targetType, instantiatedArgType);
+ }
+ }
+
+ private void convertType(Type argType, Type targetType, Type functionalType) {
+ convertType(argType.getDescriptor(), targetType.getDescriptor(), functionalType.getDescriptor());
+ }
+
+ private int invocationOpcode() throws InternalError {
+ switch (implKind) {
+ case MethodHandleInfo.REF_invokeStatic:
+ return INVOKESTATIC;
+ case MethodHandleInfo.REF_newInvokeSpecial:
+ return INVOKESPECIAL;
+ case MethodHandleInfo.REF_invokeVirtual:
+ return INVOKEVIRTUAL;
+ case MethodHandleInfo.REF_invokeInterface:
+ return INVOKEINTERFACE;
+ case MethodHandleInfo.REF_invokeSpecial:
+ return INVOKESPECIAL;
+ default:
+ throw new InternalError("Unexpected invocation kind: " + implKind);
+ }
+ }
+
+ /**
+ * The following methods are copied from
+ * org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very
+ * small and fast Java bytecode manipulation framework. Copyright (c)
+ * 2000-2005 INRIA, France Telecom All rights reserved.
+ *
+ * Subclass with that (removing these methods) if that package/class is
+ * ever added to the JDK.
+ */
+ private void iconst(final int cst) {
+ if (cst >= -1 && cst <= 5) {
+ mv.visitInsn(Opcodes.ICONST_0 + cst);
+ } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
+ mv.visitIntInsn(Opcodes.BIPUSH, cst);
+ } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
+ mv.visitIntInsn(Opcodes.SIPUSH, cst);
+ } else {
+ mv.visitLdcInsn(cst);
+ }
+ }
+
+ private void load(final int var, final Type type) {
+ mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), var);
+ }
+
+ private void dup() {
+ mv.visitInsn(Opcodes.DUP);
+ }
+
+ private void areturn(final Type t) {
+ mv.visitInsn(t.getOpcode(Opcodes.IRETURN));
+ }
+
+ private void getfield(
+ final String owner,
+ final String name,
+ final String desc) {
+ mv.visitFieldInsn(Opcodes.GETFIELD, owner, name, desc);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/LambdaConversionException.java Thu Oct 25 17:34:24 2012 -0700
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+/**
+ * LambdaConversionException
+ */
+public class LambdaConversionException extends Exception {
+ public LambdaConversionException() {
+ }
+
+ public LambdaConversionException(String message) {
+ super(message);
+ }
+
+ public LambdaConversionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public LambdaConversionException(Throwable cause) {
+ super(cause);
+ }
+
+ public LambdaConversionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/LambdaMetafactory.java Thu Oct 25 17:34:24 2012 -0700
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+/**
+ * <p>Bootstrap methods for converting lambda expressions and method references to functional interface objects.</p>
+ *
+ * <p>For every lambda expressions or method reference in the source code, there is a target type which is a
+ * functional interface. Evaluating a lambda expression produces an object of its target type. The mechanism for
+ * evaluating lambda expressions is to invoke an invokedynamic call site, which takes arguments describing the sole
+ * method of the functional interface and the implementation method, and returns an object (the lambda object) that
+ * implements the target type. Methods of the lambda object invoke the implementation method. For method
+ * references, the implementation method is simply the referenced method; for lambda expressions, the
+ * implementation method is produced by the compiler based on the body of the lambda expression. The methods in
+ * this file are the bootstrap methods for those invokedynamic call sites, called lambda factories, and the
+ * bootstrap methods responsible for linking the lambda factories are called lambda meta-factories.
+ *
+ * <p>The bootstrap methods in this class take the information about the functional interface, the implementation
+ * method, and the static types of the captured lambda arguments, and link a call site which, when invoked,
+ * produces the lambda object.
+ *
+ * <p>Two pieces of information are needed about the functional interface: the SAM method and the type of the SAM
+ * method in the functional interface. The type can be different when parameterized types are used. For example,
+ * consider
+ * <code>interface I<T> { int m(T x); }</code> if this SAM type is used in a lambda
+ * <code>I<Byte> v = ...</code>, we need both the actual SAM method which has the signature
+ * <code>(Object)int</code> and the functional interface type of the method, which has signature
+ * <code>(Byte)int</code>. The latter is the instantiated erased functional interface method type, or
+ * simply <I>instantiated method type</I>.
+ *
+ * <p>While functional interfaces only have a single abstract method from the language perspective (concrete
+ * methods in Object are and default methods may be present), at the bytecode level they may actually have multiple
+ * methods because of the need for bridge methods. Invoking any of these methods on the lambda object will result
+ * in invoking the implementation method.
+ *
+ * <p>The argument list of the implementation method and the argument list of the functional interface method(s)
+ * may differ in several ways. The implementation methods may have additional arguments to accommodate arguments
+ * captured by the lambda expression; there may also be differences resulting from permitted adaptations of
+ * arguments, such as casting, boxing, unboxing, and primitive widening. They may also differ because of var-args,
+ * but this is expected to be handled by the compiler.
+ *
+ * <p>Invokedynamic call sites have two argument lists: a static argument list and a dynamic argument list. The
+ * static argument list lives in the constant pool; the dynamic argument list lives on the operand stack at
+ * invocation time. The bootstrap method has access to the entire static argument list (which in this case,
+ * contains method handles describing the implementation method and the canonical functional interface method),
+ * as well as a method signature describing the number and static types (but not the values) of the dynamic
+ * arguments, and the static return type of the invokedynamic site.
+ *
+ * <p>The implementation method is described with a method handle. In theory, any method handle could be used.
+ * Currently supported are method handles representing invocation of virtual, interface, constructor and static
+ * methods.
+ *
+ * <p>Assume:
+ * <ul>
+ * <li>the functional interface method has N arguments, of types (U1, U2, ... Un) and return type Ru</li>
+ * <li>then the instantiated method type also has N arguments, of types (T1, T2, ... Tn) and return type Rt</li>
+ * <li>the implementation method has M arguments, of types (A1..Am) and return type Ra,</li>
+ * <li>the dynamic argument list has K arguments of types (D1..Dk), and the invokedynamic return site has
+ * type Rd</li>
+ * <li>the functional interface type is F</li>
+ * </ul>
+ *
+ * <p>The following signature invariants must hold:
+ * <ul>
+ * <li>Rd is a subtype of F</li>
+ * <li>For i=1..N, Ti is a subtype of Ui</li>
+ * <li>Either Rt and Ru are primitive and are the same type, or both are reference types and
+ * Rt is a subtype of Ru</li>
+ * <li>If the implementation method is a static method:
+ * <ul>
+ * <li>K + N = M</li>
+ * <li>For i=1..K, Di = Ai</li>
+ * <li>For i=1..N, Ti is adaptable to Aj, where j=i+k</li>
+ * </ul></li>
+ * <li>If the implementation method is an instance method:
+ * <ul>
+ * <li>K + N = M + 1</li>
+ * <li>D1 must be a subtype of the enclosing class for the implementation method</li>
+ * <li>For i=2..K, Di = Aj, where j=i-1</li>
+ * <li>For i=1..N, Ti is adaptable to Aj, where j=i+k-1</li>
+ * </ul></li>
+ * <li>The return type Rt is void, or the return type Ra is not void and is adaptable to Rt</li>
+ * </ul>
+ *
+ * <p>Note that the potentially parameterized implementation return type provides the value for the SAM. Whereas
+ * the completely known instantiated return type is adapted to the implementation arguments. Because the
+ * instantiated type of the implementation method is not available, the adaptability of return types cannot be
+ * checked as precisely at link-time as the arguments can be checked. Thus a loose version of link-time checking is
+ * done on return type, while a strict version is applied to arguments.
+ *
+ * <p>A type Q is considered adaptable to S as follows:
+ * <table>
+ * <tr><th>Q</th><th>S</th><th>Link-time checks</th><th>Capture-time checks</th></tr>
+ * <tr>
+ * <td>Primitive</td><td>Primitive</td>
+ * <td>Q can be converted to S via a primitive widening conversion</td>
+ * <td>None</td>
+ * </tr>
+ * <tr>
+ * <td>Primitive</td><td>Reference</td>
+ * <td>S is a supertype of the Wrapper(Q)</td>
+ * <td>Cast from Wrapper(Q) to S</td>
+ * </tr>
+ * <tr>
+ * <td>Reference</td><td>Primitive</td>
+ * <td>strict: Q is a primitive wrapper and Primitive(Q) can be widened to S
+ * <br>loose: If Q is a primitive wrapper, check that Primitive(Q) can be widened to S</td>
+ * <td>If Q is not a primitive wrapper, cast Q to the base Wrapper(S); for example Number for numeric types</td>
+ * </tr>
+ * <tr>
+ * <td>Reference</td><td>Reference</td>
+ * <td>strict: S is a supertype of Q
+ * <br>loose: none</td>
+ * <td>Cast from Q to S</td>
+ * </tr>
+ * </table>
+ *
+ *
+ */
+public class LambdaMetafactory {
+
+ /**
+ * Standard meta-factory for conversion of lambda expressions or method references to functional interfaces.
+ *
+ * @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges
+ * of the caller.
+ * @param invokedName Stacked automatically by VM; the name of the invoked method as it appears at the call site.
+ * Currently unused.
+ * @param invokedType Stacked automatically by VM; the signature of the invoked method, which includes the
+ * expected static type of the returned lambda object, and the static types of the captured
+ * arguments for the lambda. In the event that the implementation method is an instance method,
+ * the first argument in the invocation signature will correspond to the receiver.
+ * @param samMethod The primary method in the functional interface to which the lambda or method reference is
+ * being converted, represented as a method handle.
+ * @param implMethod The implementation method which should be called (with suitable adaptation of argument
+ * types, return types, and adjustment for captured arguments) when methods of the resulting
+ * functional interface instance are invoked.
+ * @param instantiatedMethodType The signature of the SAM method from the functional interface's perspective
+ * @return a CallSite, which, when invoked, will return an instance of the functional interface
+ * @throws ReflectiveOperationException
+ * @throws LambdaConversionException If any of the meta-factory protocol invariants are violated
+ */
+ public static CallSite metaFactory(MethodHandles.Lookup caller,
+ String invokedName,
+ MethodType invokedType,
+ MethodHandle samMethod,
+ MethodHandle implMethod,
+ MethodType instantiatedMethodType)
+ throws ReflectiveOperationException, LambdaConversionException {
+ AbstractValidatingLambdaMetafactory mf;
+ mf = new InnerClassLambdaMetafactory(caller, invokedType, samMethod, implMethod, instantiatedMethodType);
+ mf.validateMetafactoryArgs();
+ return mf.buildCallSite();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/MagicLambdaImpl.java Thu Oct 25 17:34:24 2012 -0700
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.lang.invoke;
+
+/** <P> MagicLambdaImpl (named for similarity to MagicAccessorImpl and
+ others, not because it actually implements an interface) is a
+ marker class in the hierarchy. All subclasses of this class are
+ "magically" granted access by the VM to otherwise inaccessible
+ fields and methods of other classes. It is distinct from MagicAccessorImpl
+ because, while we want to bypass accessibility checks, we do not want to
+ bypass verification.</P>
+
+ <P> Do not change the name of this class without also changing the
+ VM's code. </P> */
+
+class MagicLambdaImpl {
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java Thu Oct 25 17:34:24 2012 -0700
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import sun.invoke.util.Wrapper;
+import static sun.invoke.util.Wrapper.*;
+
+class TypeConvertingMethodAdapter extends MethodVisitor {
+
+ TypeConvertingMethodAdapter(MethodVisitor mv) {
+ super(Opcodes.ASM4, mv);
+ }
+
+ private static final int NUM_WRAPPERS = Wrapper.values().length;
+
+ private static final String NAME_OBJECT = "java/lang/Object";
+ private static final String WRAPPER_PREFIX = "Ljava/lang/";
+
+ // Same for all primitives; name of the boxing method
+ private static final String NAME_BOX_METHOD = "valueOf";
+
+ // Table of opcodes for widening primitive conversions; NOP = no conversion
+ private static final int[][] wideningOpcodes = new int[NUM_WRAPPERS][NUM_WRAPPERS];
+
+ private static final Wrapper[] FROM_WRAPPER_NAME = new Wrapper[16];
+
+ static {
+ for (Wrapper w : Wrapper.values()) {
+ if (w.basicTypeChar() != 'L') {
+ int wi = hashWrapperName(w.wrapperSimpleName());
+ assert (FROM_WRAPPER_NAME[wi] == null);
+ FROM_WRAPPER_NAME[wi] = w;
+ }
+ }
+
+ for (int i = 0; i < NUM_WRAPPERS; i++) {
+ for (int j = 0; j < NUM_WRAPPERS; j++) {
+ wideningOpcodes[i][j] = Opcodes.NOP;
+ }
+ }
+
+ initWidening(LONG, Opcodes.I2L, BYTE, SHORT, INT, CHAR);
+ initWidening(LONG, Opcodes.F2L, FLOAT);
+ initWidening(FLOAT, Opcodes.I2F, BYTE, SHORT, INT, CHAR);
+ initWidening(FLOAT, Opcodes.L2F, LONG);
+ initWidening(DOUBLE, Opcodes.I2D, BYTE, SHORT, INT, CHAR);
+ initWidening(DOUBLE, Opcodes.F2D, FLOAT);
+ initWidening(DOUBLE, Opcodes.L2D, LONG);
+ }
+
+ private static void initWidening(Wrapper to, int opcode, Wrapper... from) {
+ for (Wrapper f : from) {
+ wideningOpcodes[f.ordinal()][to.ordinal()] = opcode;
+ }
+ }
+
+ /**
+ * Class name to Wrapper hash, derived from Wrapper.hashWrap()
+ * @param xn
+ * @return The hash code 0-15
+ */
+ private static int hashWrapperName(String xn) {
+ if (xn.length() < 3) {
+ return 0;
+ }
+ return (3 * xn.charAt(1) + xn.charAt(2)) % 16;
+ }
+
+ private Wrapper wrapperOrNullFromDescriptor(String desc) {
+ if (!desc.startsWith(WRAPPER_PREFIX)) {
+ // Not a class type (array or method), so not a boxed type
+ // or not in the right package
+ return null;
+ }
+ // Pare it down to the simple class name
+ String cname = desc.substring(WRAPPER_PREFIX.length(), desc.length() - 1);
+ // Hash to a Wrapper
+ Wrapper w = FROM_WRAPPER_NAME[hashWrapperName(cname)];
+ if (w == null || w.wrapperSimpleName().equals(cname)) {
+ return w;
+ } else {
+ return null;
+ }
+ }
+
+ private static String wrapperName(Wrapper w) {
+ return "java/lang/" + w.wrapperSimpleName();
+ }
+
+ private static String unboxMethod(Wrapper w) {
+ return w.primitiveSimpleName() + "Value";
+ }
+
+ private static String boxingDescriptor(Wrapper w) {
+ return String.format("(%s)L%s;", w.basicTypeChar(), wrapperName(w));
+ }
+
+ private static String unboxingDescriptor(Wrapper w) {
+ return "()" + w.basicTypeChar();
+ }
+
+ void boxIfPrimitive(Wrapper w) {
+ if (w.zero() != null) {
+ box(w);
+ }
+ }
+
+ void widen(Wrapper ws, Wrapper wt) {
+ if (ws != wt) {
+ int opcode = wideningOpcodes[ws.ordinal()][wt.ordinal()];
+ if (opcode != Opcodes.NOP) {
+ visitInsn(opcode);
+ }
+ }
+ }
+
+ void box(Wrapper w) {
+ visitMethodInsn(Opcodes.INVOKESTATIC,
+ wrapperName(w),
+ NAME_BOX_METHOD,
+ boxingDescriptor(w));
+ }
+
+ /**
+ * Convert types by unboxing. The source type is known to be a primitive wrapper.
+ * @param ws A primitive wrapper corresponding to wrapped reference source type
+ * @param wt A primitive wrapper being converted to
+ */
+ void unbox(String sname, Wrapper wt) {
+ visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+ sname,
+ unboxMethod(wt),
+ unboxingDescriptor(wt));
+ }
+
+ private String descriptorToName(String desc) {
+ int last = desc.length() - 1;
+ if (desc.charAt(0) == 'L' && desc.charAt(last) == ';') {
+ // In descriptor form
+ return desc.substring(1, last);
+ } else {
+ // Already in internal name form
+ return desc;
+ }
+ }
+
+ void cast(String ds, String dt) {
+ String ns = descriptorToName(ds);
+ String nt = descriptorToName(dt);
+ if (!nt.equals(ns) && !nt.equals(NAME_OBJECT)) {
+ visitTypeInsn(Opcodes.CHECKCAST, nt);
+ }
+ }
+
+ private boolean isPrimitive(Wrapper w) {
+ return w != OBJECT;
+ }
+
+ private Wrapper toWrapper(String desc) {
+ char first = desc.charAt(0);
+ if (first == '[' || first == '(') {
+ first = 'L';
+ }
+ return Wrapper.forBasicType(first);
+ }
+
+ /**
+ * Convert an argument of type 'argType' to be passed to 'targetType' assuring that it is 'functionalType'.
+ * Insert the needed conversion instructions in the method code.
+ * @param argType
+ * @param targetType
+ * @param functionalType
+ */
+ void convertType(String dArg, String dTarget, String dFunctional) {
+ if (dArg.equals(dTarget)) {
+ return;
+ }
+ Wrapper wArg = toWrapper(dArg);
+ Wrapper wTarget = toWrapper(dTarget);
+ if (wArg == VOID || wTarget == VOID) {
+ return;
+ }
+ if (isPrimitive(wArg)) {
+ if (isPrimitive(wTarget)) {
+ // Both primitives: widening
+ widen(wArg, wTarget);
+ } else {
+ // Primitive argument to reference target
+ Wrapper wPrimTarget = wrapperOrNullFromDescriptor(dTarget);
+ if (wPrimTarget != null) {
+ // The target is a boxed primitive type, widen to get there before boxing
+ widen(wArg, wPrimTarget);
+ box(wPrimTarget);
+ } else {
+ // Otherwise, box and cast
+ box(wArg);
+ cast(wrapperName(wArg), dTarget);
+ }
+ }
+ } else {
+ String dSrc;
+ Wrapper wFunctional = toWrapper(dFunctional);
+ if (isPrimitive(wFunctional)) {
+ dSrc = dArg;
+ } else {
+ // Cast to convert to possibly more specific type, and generate CCE for invalid arg
+ dSrc = dFunctional;
+ cast(dArg, dFunctional);
+ }
+ if (isPrimitive(wTarget)) {
+ // Reference argument to primitive target
+ Wrapper wps = wrapperOrNullFromDescriptor(dSrc);
+ if (wps != null) {
+ if (wps.isSigned() || wps.isFloating()) {
+ // Boxed number to primitive
+ unbox(wrapperName(wps), wTarget);
+ } else {
+ // Character or Boolean
+ unbox(wrapperName(wps), wps);
+ widen(wps, wTarget);
+ }
+ } else {
+ // Source type is reference type, but not boxed type,
+ // assume it is super type of target type
+ String intermediate;
+ if (wTarget.isSigned() || wTarget.isFloating()) {
+ // Boxed number to primitive
+ intermediate = "java/lang/Number";
+ } else {
+ // Character or Boolean
+ intermediate = wrapperName(wTarget);
+ }
+ cast(dSrc, intermediate);
+ unbox(intermediate, wTarget);
+ }
+ } else {
+ // Both reference types: just case to target type
+ cast(dSrc, dTarget);
+ }
+ }
+ }
+}