OpenJDK / lambda / lambda / jdk
changeset 5893:a3394c993428
Restructure lambda metafactories to share data and implementation between validation, and the two implementations. Also, misc clean-up and documentation
author | Robert Field <Robert.Field@oracle.com> |
---|---|
date | Sun, 02 Sep 2012 22:05:39 -0700 |
parents | c2506fb31e41 |
children | 2da52ed16327 |
files | src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java src/share/classes/java/lang/invoke/InnerClassGenerator.java src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java src/share/classes/java/lang/invoke/LambdaMetafactory.java src/share/classes/java/lang/invoke/MethodHandleProxyLambdaMetafactory.java |
diffstat | 5 files changed, 633 insertions(+), 579 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java Sun Sep 02 22:05:39 2012 -0700 @@ -0,0 +1,240 @@ +/* + * 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.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Abstract implementation of a meta-factory which provides parameter unrolling + * and input validation. + * + * @author Robert Field + */ +abstract class AbstractValidatingLambdaMetafactory { + + final Class<?> targetClass; + final MethodType invokedType; + final Class<?> samBase; + final boolean isSerializable; + final MethodHandleInfo samInfo; + final Class<?> samClass; + final MethodType samMethodType; + final MethodHandleInfo implInfo; + final boolean implIsInstanceMethod; + final MethodType implMethodType; + final Class<?> implDefiningClass; + final MethodType functionDescType; + + AbstractValidatingLambdaMetafactory(MethodHandles.Lookup caller, + MethodType invokedType, + MethodHandle samMethod, + MethodHandle implMethod, + MethodType functionDescType) + 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.implIsInstanceMethod = + implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeVirtual || + implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeInterface; + this.implDefiningClass = implInfo.getDeclaringClass(); + this.implMethodType = implInfo.getMethodType(); + + this.functionDescType = functionDescType; + } + + abstract CallSite buildCallSite() throws ReflectiveOperationException; + + private static final Map<Class<?>, Set<Class<?>>> assignableTo = new HashMap<>(); + private static final Map<Class<?>, Class<?>> boxesTo = new HashMap<>(); + private static final Map<Class<?>, Class<?>> unboxesTo = new HashMap<>(); + static { + assignableTo.put(byte.class, new HashSet<>(Arrays.<Class<?>>asList(byte.class, short.class, int.class, + long.class, float.class, double.class))); + assignableTo.put(short.class, new HashSet<>(Arrays.<Class<?>>asList(short.class, int.class, long.class, + float.class, double.class))); + assignableTo.put(char.class, new HashSet<>(Arrays.<Class<?>>asList(char.class, short.class, int.class, long.class, + float.class, double.class))); + assignableTo.put(int.class, new HashSet<>(Arrays.<Class<?>>asList(int.class, long.class, float.class, double.class))); + assignableTo.put(long.class, new HashSet<>(Arrays.<Class<?>>asList(long.class, float.class, double.class))); + assignableTo.put(float.class, new HashSet<>(Arrays.<Class<?>>asList(float.class, double.class))); + assignableTo.put(double.class, new HashSet<>(Arrays.<Class<?>>asList(double.class))); + assignableTo.put(boolean.class, new HashSet<>(Arrays.<Class<?>>asList(boolean.class))); + assignableTo.put(void.class, new HashSet<>(Arrays.<Class<?>>asList(void.class))); + + boxesTo.put(byte.class, Byte.class); + boxesTo.put(short.class, Short.class); + boxesTo.put(char.class, Character.class); + boxesTo.put(int.class, Integer.class); + boxesTo.put(long.class, Long.class); + boxesTo.put(float.class, Float.class); + boxesTo.put(double.class, Double.class); + boxesTo.put(boolean.class, Boolean.class); + + for (Map.Entry<Class<?>, Class<?>> e : boxesTo.entrySet()) { + unboxesTo.put(e.getValue(), e.getKey()); + } + } + + void validateMetafactoryArgs() { + // @@@ Validate SAM-ness + + // 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())); + } + + // 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(); + if (implArity + receiverArity != capturedArity + samArity) { + throw new LambdaConversionException(String.format("Incorrect number of parameters for %s method; %d captured parameters, %d functional interface parameters, %d implementation parameters", + implIsInstanceMethod ? "instance" : "static", + capturedArity, samArity, implArity)); + } + + // 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 = functionDescType.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<?> samParamType = samMethodType.parameterType(i + samOffset); + if (!isAdaptableTo(samParamType, implParamType)) { + throw new LambdaConversionException( + String.format("Type mismatch for lambda argument %d: %s is not convertible to %s", i, samParamType, implParamType)); + } + } + + // Adaptation match: return type + Class<?> expectedType = samMethodType.returnType(); + Class<?> actualReturnType = + (implInfo.getReferenceKind() == 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)); + } + } + + private boolean isAdaptableTo(Class<?> fromType, Class<?> toType) { + if (fromType.equals(toType)) { + return true; + } + boolean fromIsPrimitive = fromType.isPrimitive(); + boolean toIsPrimitive = toType.isPrimitive(); + if (fromIsPrimitive) { + if (toIsPrimitive) { + // widening + Set<Class<?>> classes = assignableTo.get(fromType); + return classes != null && classes.contains(toType); + } + else { + // boxing + return toType.isAssignableFrom(boxesTo.get(fromType)); + } + } + else { + if (toIsPrimitive) { + // unboxing + Class<?> unboxedFrom = unboxesTo.get(fromType); + if (unboxedFrom != null) { + // fromType is a primitive wrapper; unbox+widen + Set<Class<?>> classes = assignableTo.get(unboxedFrom); + return classes != null && classes.contains(toType); + } + else { + // cast to wrapper for toType, and unbox + return true; + } + } + else { + // blind cast to toType + return true; + } + } + } + + private boolean isAdaptableToAsReturn(Class<?> fromType, Class<?> toType) { + return toType.equals(void.class) + || !fromType.equals(void.class) && isAdaptableTo(fromType, toType); + } + +}
--- a/src/share/classes/java/lang/invoke/InnerClassGenerator.java Sun Sep 02 09:52:16 2012 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,350 +0,0 @@ -/* - * 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.lang.reflect.Modifier; -import java.util.Arrays; -import java.security.ProtectionDomain; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.openjdk.org.objectweb.asm.*; -import static org.openjdk.org.objectweb.asm.Opcodes.*; -import sun.misc.Unsafe; - -/** - * InnerClassGenerator - */ -class InnerClassGenerator { - private static final int CLASSFILE_VERSION = 51; - private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;"; - private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace"; - private static final Type TYPE_VOID = Type.getType(void.class); - private static final String NAME_OBJECT = "java/lang/Object"; - 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_SERIALIZED_LAMBDA = "com/oracle/java/lang/invoke/SerializedLambdaImpl"; - private static final String NAME_CTOR = "<init>"; - - private static final Unsafe unsafe = Unsafe.getUnsafe(); - - // Used to ensure that each spun class name is unique - private static final AtomicInteger counter = new AtomicInteger(0); - - private final Class<?> targetClass; - private final int implKind; - private final Class<?> implMethodClass; - private final String implMethodClassName; - private final String implMethodName; - private final String implMethodDesc; - private final Type implMethodType; - private final boolean implIsInstanceMethod; - private final ClassWriter cw; - private final String[] argNames; - private final Type[] argTypes; - private final String lambdaClassName; - private final String constructorDesc; - - public InnerClassGenerator( MethodType constructorType, - MethodHandleInfo implInfo, - boolean implIsInstanceMethod, - Class<?> targetClass) { - this.targetClass = targetClass; - this.implIsInstanceMethod = implIsInstanceMethod; - implKind = implInfo.getReferenceKind(); - implMethodClass = implInfo.getDeclaringClass(); - implMethodClassName = implMethodClass.getName().replace('.', '/'); - implMethodName = implInfo.getName(); - implMethodDesc = implInfo.getMethodType().toMethodDescriptorString(); - implMethodType = Type.getMethodType(implMethodDesc); - 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); - } - } - - public <T> Class<? extends T> spinInnerClass(Class<T> samClass, - MethodHandleInfo samInfo, - MethodType functionDescType, - boolean serializable) { - String samMethodName = samInfo.getName(); - String functionalMethodDesc = functionDescType.toMethodDescriptorString(); - - Method[] methods = samClass.getMethods(); - String samName = samClass.getName().replace('.', '/'); - Type samType = Type.getType(samClass); - - cw.visit(CLASSFILE_VERSION, ACC_PUBLIC + ACC_SUPER, lambdaClassName, null, NAME_MAGIC_ACCESSOR_IMPL, - serializable ? new String[]{samName, NAME_SERIALIZABLE} : new String[]{samName}); - - // Generate fields - 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(constructorDesc); - - // Generate methods - MethodType samMethodType = samInfo.getMethodType(); - Class<?>[] samParamTypes = samMethodType.parameterArray(); - int samParamLength = samParamTypes.length; - Class<?> samReturnType = samMethodType.returnType(); - boolean defaultMethodFound = false; - Method samMethod = null; - List<Method> methodsFound = new ArrayList<>(methods.length); - List<Method> methodsToBridge = new ArrayList<>(methods.length); - Class<?> objectClass = Object.class; - - // 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 default methods (non-abstract), they should not be bridged-over and indicate a complex bridging situation. - 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 (!matchesAnyMethod(m, methodsFound)) { - methodsFound.add(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 - if (!matchesAnyMethod(m, methodsFound)) { - methodsFound.add(m); - } - } - } - } - } - - // Forward the SAM method - if (samMethod == null) { - throw new LambdaConversionException(String.format("SAM method not found: %s", samMethodType)); - } else { - generateForwardingMethod(samMethod, functionalMethodDesc, false); - } - - // Forward the bridges - // @@@ Once the VM can do fail-over, uncomment the default method test - if (!methodsToBridge.isEmpty() /* && !defaultMethodFound*/) { - for (Method m : methodsToBridge) { - generateForwardingMethod(m, functionalMethodDesc, true); - - } - } - - if (serializable) { - generateSerializationMethod(samInfo, samType, samMethodName); - } - - cw.visitEnd(); - - return defineGeneratedClass(cw.toByteArray()); - } - - private <T> Class<? extends T> defineGeneratedClass(final byte[] classBytes) { - 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(InnerClassGenerator.class.getName()).log(Level.SEVERE, null, ex); - } - } - - ClassLoader loader = targetClass.getClassLoader(); - ProtectionDomain pd = (loader == null)? null : targetClass.getProtectionDomain(); - return (Class <? extends T>) unsafe.defineClass(lambdaClassName, classBytes, 0, classBytes.length, loader, pd); - } - - private static boolean matchesAnyMethod(Method m, List<Method> methods) { - Class<?>[] ptypes = m.getParameterTypes(); - Class<?> rtype = m.getReturnType(); - for (Method md : methods) { - if (md.getReturnType().equals(rtype) && Arrays.equals(ptypes, md.getParameterTypes())) { - return true; - } - } - return false; - } - - private void generateConstructor(String constructorDesc) { - // 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, Type.getMethodDescriptor(Type.VOID_TYPE)); - 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(); - } - - private void generateSerializationMethod(MethodHandleInfo samInfo, Type samType, String samMethodName) { - String samMethodDesc = samInfo.getMethodType().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(implMethodClass)); - 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(); - } - - private void generateForwardingMethod(Method m, String functionalMethodDesc, boolean isBridge) throws InternalError { - 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, functionalMethodDesc); - } - - private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter { - - ForwardingMethodGenerator(MethodVisitor mv) { - super(mv); - } - - void generate(Method m, String functionalMethodDesc) 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), Type.getArgumentTypes(functionalMethodDesc)); - - // Invoke the method we want to forward to - visitMethodInsn(invocationOpcode(), implMethodClassName, implMethodName, implMethodDesc); - - // Convert the return value (if any) and return it - // @@@ if adapting from non-void to void, need to pop result off stack - Type samReturnType = Type.getReturnType(m); - if (!samReturnType.equals(TYPE_VOID)) { - convertType(implMethodType.getReturnType(), samReturnType, samReturnType); - } - areturn(samReturnType); - - visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored - visitEnd(); - } - - void convertArgumentTypes(Type[] samArgumentTypes, Type[] functionalArgumentTypes) { - int lvIndex = 0; - boolean samIncludesReceiver = implIsInstanceMethod && argTypes.length == 0; - int samReceiverLength = samIncludesReceiver ? 1 : 0; - if (samIncludesReceiver) { - // push receiver - Type rcvrType = samArgumentTypes[0]; - Type functionalType = functionalArgumentTypes[0]; - - load(lvIndex + 1, rcvrType); - lvIndex += rcvrType.getSize(); - convertType(rcvrType, Type.getType(implMethodClass), functionalType); - } - Type[] implArgumentTypes = implMethodType.getArgumentTypes(); - int argOffset = implArgumentTypes.length - samArgumentTypes.length; - for (int i = samReceiverLength; i < samArgumentTypes.length; i++) { - Type argType = samArgumentTypes[i]; - Type targetType = implArgumentTypes[argOffset + i]; - Type functionalType = functionalArgumentTypes[i]; - - load(lvIndex + 1, argType); - lvIndex += argType.getSize(); - convertType(argType, targetType, functionalType); - } - } - - 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; - default: - throw new InternalError("Unexpected invocation kind: " + implKind); - } - } - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Sun Sep 02 22:05:39 2012 -0700 @@ -0,0 +1,358 @@ +/* + * 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.lang.reflect.Modifier; +import java.util.Arrays; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.openjdk.org.objectweb.asm.*; +import static org.openjdk.org.objectweb.asm.Opcodes.*; +import sun.misc.Unsafe; + +/** + * InnerClassLambdaMetafactory + */ +class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { + private static final int CLASSFILE_VERSION = 51; + private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;"; + private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace"; + private static final Type TYPE_VOID = Type.getType(void.class); + private static final String NAME_OBJECT = "java/lang/Object"; + 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_SERIALIZED_LAMBDA = "com/oracle/java/lang/invoke/SerializedLambdaImpl"; + private static final String NAME_CTOR = "<init>"; + + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + // Used to ensure that each spun class name is unique + private static final AtomicInteger counter = new AtomicInteger(0); + + private final int implKind; + private final String implMethodClassName; + private final String implMethodName; + private final String implMethodDesc; + private final Type implMethodAsmType; + private final MethodType constructorType; + private final ClassWriter cw; + private final String[] argNames; + private final Type[] argTypes; + private final String lambdaClassName; + private final String constructorDesc; + + public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, + MethodType invokedType, + MethodHandle samMethod, + MethodHandle implMethod, + MethodType functionDescType) + throws ReflectiveOperationException { + super(caller, invokedType, samMethod, implMethod, functionDescType); + implKind = implInfo.getReferenceKind(); + implMethodClassName = implDefiningClass.getName().replace('.', '/'); + implMethodName = implInfo.getName(); + implMethodDesc = implMethodType.toMethodDescriptorString(); + implMethodAsmType = Type.getMethodType(implMethodDesc); + 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); + } + } + + @Override + CallSite buildCallSite() throws ReflectiveOperationException { + 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))); + } + } + + private <T> Class<? extends T> spinInnerClass() { + String samMethodName = samInfo.getName(); + String functionalMethodDesc = functionDescType.toMethodDescriptorString(); + + Method[] methods = samBase.getMethods(); + String samName = samBase.getName().replace('.', '/'); + Type samType = Type.getType(samBase); + + cw.visit(CLASSFILE_VERSION, ACC_PUBLIC + ACC_SUPER, lambdaClassName, null, NAME_MAGIC_ACCESSOR_IMPL, + isSerializable ? new String[]{samName, NAME_SERIALIZABLE} : new String[]{samName}); + + // Generate fields + 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(constructorDesc); + + // Generate methods + Class<?>[] samParamTypes = samMethodType.parameterArray(); + int samParamLength = samParamTypes.length; + Class<?> samReturnType = samMethodType.returnType(); + boolean defaultMethodFound = false; + Method samMethod = null; + List<Method> methodsFound = new ArrayList<>(methods.length); + List<Method> methodsToBridge = new ArrayList<>(methods.length); + Class<?> objectClass = Object.class; + + // 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 default methods (non-abstract), they should not be bridged-over and indicate a complex bridging situation. + 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 (!matchesAnyMethod(m, methodsFound)) { + methodsFound.add(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 + if (!matchesAnyMethod(m, methodsFound)) { + methodsFound.add(m); + } + } + } + } + } + + // Forward the SAM method + if (samMethod == null) { + throw new LambdaConversionException(String.format("SAM method not found: %s", samMethodType)); + } else { + generateForwardingMethod(samMethod, functionalMethodDesc, false); + } + + // Forward the bridges + // @@@ Once the VM can do fail-over, uncomment the default method test + if (!methodsToBridge.isEmpty() /* && !defaultMethodFound*/) { + for (Method m : methodsToBridge) { + generateForwardingMethod(m, functionalMethodDesc, true); + + } + } + + if (isSerializable) { + generateSerializationMethod(samInfo, samType, samMethodName); + } + + cw.visitEnd(); + + return defineGeneratedClass(cw.toByteArray()); + } + + private <T> Class<? extends T> defineGeneratedClass(final byte[] classBytes) { + 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.defineClass(lambdaClassName, classBytes, 0, classBytes.length, loader, pd); + } + + private static boolean matchesAnyMethod(Method m, List<Method> methods) { + Class<?>[] ptypes = m.getParameterTypes(); + Class<?> rtype = m.getReturnType(); + for (Method md : methods) { + if (md.getReturnType().equals(rtype) && Arrays.equals(ptypes, md.getParameterTypes())) { + return true; + } + } + return false; + } + + private void generateConstructor(String constructorDesc) { + // 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, Type.getMethodDescriptor(Type.VOID_TYPE)); + 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(); + } + + private void generateSerializationMethod(MethodHandleInfo samInfo, Type samType, String samMethodName) { + String samMethodDesc = samInfo.getMethodType().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(); + } + + private void generateForwardingMethod(Method m, String functionalMethodDesc, boolean isBridge) throws InternalError { + 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, functionalMethodDesc); + } + + private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter { + + ForwardingMethodGenerator(MethodVisitor mv) { + super(mv); + } + + void generate(Method m, String functionalMethodDesc) 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), Type.getArgumentTypes(functionalMethodDesc)); + + // Invoke the method we want to forward to + visitMethodInsn(invocationOpcode(), implMethodClassName, implMethodName, implMethodDesc); + + // Convert the return value (if any) and return it + // @@@ if adapting from non-void to void, need to pop result off stack + Type samReturnType = Type.getReturnType(m); + if (!samReturnType.equals(TYPE_VOID)) { + convertType(implMethodAsmType.getReturnType(), samReturnType, samReturnType); + } + areturn(samReturnType); + + visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored + visitEnd(); + } + + void convertArgumentTypes(Type[] samArgumentTypes, Type[] functionalArgumentTypes) { + int lvIndex = 0; + boolean samIncludesReceiver = implIsInstanceMethod && argTypes.length == 0; + int samReceiverLength = samIncludesReceiver ? 1 : 0; + if (samIncludesReceiver) { + // push receiver + Type rcvrType = samArgumentTypes[0]; + Type functionalType = functionalArgumentTypes[0]; + + load(lvIndex + 1, rcvrType); + lvIndex += rcvrType.getSize(); + convertType(rcvrType, Type.getType(implDefiningClass), functionalType); + } + Type[] implArgumentTypes = implMethodAsmType.getArgumentTypes(); + int argOffset = implArgumentTypes.length - samArgumentTypes.length; + for (int i = samReceiverLength; i < samArgumentTypes.length; i++) { + Type argType = samArgumentTypes[i]; + Type targetType = implArgumentTypes[argOffset + i]; + Type functionalType = functionalArgumentTypes[i]; + + load(lvIndex + 1, argType); + lvIndex += argType.getSize(); + convertType(argType, targetType, functionalType); + } + } + + 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; + default: + throw new InternalError("Unexpected invocation kind: " + implKind); + } + } + } +}
--- a/src/share/classes/java/lang/invoke/LambdaMetafactory.java Sun Sep 02 09:52:16 2012 -0700 +++ b/src/share/classes/java/lang/invoke/LambdaMetafactory.java Sun Sep 02 22:05:39 2012 -0700 @@ -25,9 +25,6 @@ package java.lang.invoke; -import java.io.Serializable; -import java.util.*; - /** * <p>Bootstrap methods for converting lambda expressions and method references to functional interface objects.</p> * @@ -40,7 +37,7 @@ * 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 - * metafactories. + * 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, @@ -51,7 +48,7 @@ * 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 accomodate arguments + * 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. * @@ -75,14 +72,14 @@ * <ul> * <li>K + N = M</li> * <li>For i=1..K, Di = Ai</li> - * <li>For i=1..N, Ti is adaptible to Aj, where j=i+k</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 adaptible to Aj, where j=i+k-1</li> + * <li>For i=1..N, Ti is adaptable to Aj, where j=i+k-1</li> * </ul></li> * <li>The return type Rf is void, or the return type Ri is not void and is adaptable to Rf</li> * </ul> @@ -102,7 +99,7 @@ * </tr> * <tr> * <td>Reference</td><td>Primitive</td> - * <td>If T is a preimitive wrapper, check that Primitive(T) can be widened to U</td> + * <td>If T is a primitive wrapper, check that Primitive(T) can be widened to U</td> * <td>If T is not a primitive wrapper, cast T to Wrapper(U)</td> * </tr> * <tr> @@ -116,36 +113,6 @@ */ public class LambdaMetafactory { - private static Map<Class<?>, Set<Class<?>>> assignableTo = new HashMap<>(); - private static Map<Class<?>, Class<?>> boxesTo = new HashMap<>(); - private static Map<Class<?>, Class<?>> unboxesTo = new HashMap<>(); - static { - assignableTo.put(byte.class, new HashSet<>(Arrays.<Class<?>>asList(byte.class, short.class, int.class, - long.class, float.class, double.class))); - assignableTo.put(short.class, new HashSet<>(Arrays.<Class<?>>asList(short.class, int.class, long.class, - float.class, double.class))); - assignableTo.put(char.class, new HashSet<>(Arrays.<Class<?>>asList(char.class, short.class, int.class, long.class, - float.class, double.class))); - assignableTo.put(int.class, new HashSet<>(Arrays.<Class<?>>asList(int.class, long.class, float.class, double.class))); - assignableTo.put(long.class, new HashSet<>(Arrays.<Class<?>>asList(long.class, float.class, double.class))); - assignableTo.put(float.class, new HashSet<>(Arrays.<Class<?>>asList(float.class, double.class))); - assignableTo.put(double.class, new HashSet<>(Arrays.<Class<?>>asList(double.class))); - assignableTo.put(boolean.class, new HashSet<>(Arrays.<Class<?>>asList(boolean.class))); - assignableTo.put(void.class, new HashSet<>(Arrays.<Class<?>>asList(void.class))); - - boxesTo.put(byte.class, Byte.class); - boxesTo.put(short.class, Short.class); - boxesTo.put(char.class, Character.class); - boxesTo.put(int.class, Integer.class); - boxesTo.put(long.class, Long.class); - boxesTo.put(float.class, Float.class); - boxesTo.put(double.class, Double.class); - boxesTo.put(boolean.class, Boolean.class); - - for (Map.Entry<Class<?>, Class<?>> e : boxesTo.entrySet()) - unboxesTo.put(e.getValue(), e.getKey()); - } - // @@@ For developement only -- remove for production private static final String whichMetafactory; static { @@ -154,11 +121,8 @@ } // -- dev-only -- - private static final MethodType MT_VOID = MethodType.fromMethodDescriptorString("()V", null); - /** - * Standard metafactory for conversion of lambda expressions or method references to functional interfaces. - * + * 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. @@ -174,9 +138,9 @@ * types, return types, and adjustment for captured arguments) when methods of the resulting * functional interface instance are invoked. * @param functionDescType The signature of the SAM method from the target type's perspective - * @return + * @return a CallSite, which, when invoked, will return an instance of the functional interface * @throws ReflectiveOperationException - * @throws LambdaConversionException If any of the metafactory protocol invariants are violated + * @throws LambdaConversionException If any of the meta-factory protocol invariants are violated */ public static CallSite metaFactory(MethodHandles.Lookup caller, String invokedName, @@ -185,190 +149,18 @@ MethodHandle implMethod, MethodType functionDescType) throws ReflectiveOperationException, LambdaConversionException { - final MethodHandleInfo samInfo = new MethodHandleInfo(samMethod); - final MethodHandleInfo implInfo = new MethodHandleInfo(implMethod); - final boolean implIsInstanceMethod = - implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeVirtual || - implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeInterface; - - validateMetafactoryArgs(implIsInstanceMethod, invokedType, samInfo, implInfo, functionDescType); - + AbstractValidatingLambdaMetafactory mf; switch (whichMetafactory) { case "innerClass": - return innerClassMetafactory(implIsInstanceMethod, caller, invokedType, samInfo, implInfo, functionDescType); + mf = new InnerClassLambdaMetafactory(caller, invokedType, samMethod, implMethod, functionDescType); + break; case "mhProxy": - return MethodHandleProxyLambdaMetafactory.mhProxyMetafactory(caller, invokedType, implMethod); + mf = new MethodHandleProxyLambdaMetafactory(caller, invokedType, samMethod, implMethod, functionDescType); + break; default: throw new Error("Unknown metafactory"); } - } - - private static void validateMetafactoryArgs(final boolean implIsInstanceMethod, - MethodType invokedType, - MethodHandleInfo samInfo, - MethodHandleInfo implInfo, - MethodType functionDescType) { - { - final Class<?> samClass = samInfo.getDeclaringClass(); - final Class<?> samBase = invokedType.returnType(); - - // @@@ Validate SAM-ness - - // 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())); - } - } - - final MethodType samType = samInfo.getMethodType(); - final MethodType implType = implInfo.getMethodType(); - final Class<?> implDefiningClass = implInfo.getDeclaringClass(); - - // Check arity: optional-receiver + captured + SAM == impl - final int implArity = implType.parameterCount(); - final int receiverArity = implIsInstanceMethod ? 1 : 0; - final int capturedArity = invokedType.parameterCount(); - final int samArity = samType.parameterCount(); - if (implArity + receiverArity != capturedArity + samArity) { - throw new LambdaConversionException(String.format("Incorrect number of parameters for %s method; %d captured parameters, %d functional interface parameters, %d implementation parameters", - implIsInstanceMethod ? "instance" : "static", - capturedArity, samArity, implArity)); - } - - // 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 = functionDescType.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 = implType.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 = implType.parameterType(i); - Class<?> samParamType = samType.parameterType(i + samOffset); - if (!isAdaptableTo(samParamType, implParamType)) { - throw new LambdaConversionException( - String.format("Type mismatch for lambda argument %d: %s is not convertible to %s", i, samParamType, implParamType)); - } - } - - // Adaptation match: return type - Class<?> expectedType = samType.returnType(); - Class<?> actualReturnType = - (implInfo.getReferenceKind() == MethodHandleInfo.REF_newInvokeSpecial) - ? implDefiningClass - : implType.returnType(); - if (!isAdaptableToAsReturn(actualReturnType, expectedType)) { - throw new LambdaConversionException( - String.format("Type mismatch for lambda return: %s is not convertible to %s", actualReturnType, expectedType)); - } - } - - /** - * Implementation of Lambda by dynamic inner-class generation - */ - private static CallSite innerClassMetafactory(final boolean implIsInstanceMethod, - final MethodHandles.Lookup caller, - final MethodType invokedType, - final MethodHandleInfo samInfo, - final MethodHandleInfo implInfo, - final MethodType functionDescType) - throws ReflectiveOperationException { - final MethodType constructorType = invokedType.changeReturnType(Void.TYPE); - - final Class<?> samBase = invokedType.returnType(); - - final InnerClassGenerator icg = new InnerClassGenerator(constructorType, - implInfo, - implIsInstanceMethod, - caller.lookupClass()); - final Class<?> innerClass = icg.spinInnerClass(samBase, samInfo, functionDescType, - Serializable.class.isAssignableFrom(samBase)); - 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))); - } - } - - private static boolean isAdaptableTo(Class<?> fromType, Class<?> toType) { - if (fromType.equals(toType)) - return true; - boolean fromIsPrimitive = fromType.isPrimitive(); - boolean toIsPrimitive = toType.isPrimitive(); - if (fromIsPrimitive) { - if (toIsPrimitive) { - // widening - Set<Class<?>> classes = assignableTo.get(fromType); - return classes != null && classes.contains(toType); - } - else { - // boxing - return toType.isAssignableFrom(boxesTo.get(fromType)); - } - } - else { - if (toIsPrimitive) { - // unboxing - Class<?> unboxedFrom = unboxesTo.get(fromType); - if (unboxedFrom != null) { - // fromType is a primitive wrapper; unbox+widen - Set<Class<?>> classes = assignableTo.get(unboxedFrom); - return classes != null && classes.contains(toType); - } - else { - // cast to wrapper for toType, and unbox - return true; - } - } - else { - // blind cast to toType - return true; - } - } - } - - private static boolean isAdaptableToAsReturn(Class<?> fromType, Class<?> toType) { - return toType.equals(void.class) - || !fromType.equals(void.class) && isAdaptableTo(fromType, toType); + mf.validateMetafactoryArgs(); + return mf.buildCallSite(); } } -
--- a/src/share/classes/java/lang/invoke/MethodHandleProxyLambdaMetafactory.java Sun Sep 02 09:52:16 2012 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandleProxyLambdaMetafactory.java Sun Sep 02 22:05:39 2012 -0700 @@ -30,12 +30,18 @@ /** * MethodHandleProxyLambdaMetafactory + * + * @author Brian Goetz */ -class MethodHandleProxyLambdaMetafactory { +class MethodHandleProxyLambdaMetafactory extends AbstractValidatingLambdaMetafactory { + + private final MethodHandle implMethod; + // @@@ For debugging only -- delete before shipping static Executor logPool = Executors.newSingleThreadExecutor(); private static void log(final String s) { logPool.execute(new Runnable() { + @Override public void run() { System.out.println(s); } @@ -43,6 +49,7 @@ } private static void log(final String s, final Throwable e) { logPool.execute(new Runnable() { + @Override public void run() { System.out.println(s); e.printStackTrace(System.out); @@ -51,20 +58,27 @@ } // -- dev-only -- - static CallSite mhProxyMetafactory(MethodHandles.Lookup caller, + MethodHandleProxyLambdaMetafactory(MethodHandles.Lookup caller, MethodType invokedType, - MethodHandle impl) { - Class<?> samBase = invokedType.returnType(); + MethodHandle samMethod, + MethodHandle implMethod, + MethodType functionDescType) + throws ReflectiveOperationException { + super(caller, invokedType, samMethod, implMethod, functionDescType); + this.implMethod = implMethod; + } + @Override + CallSite buildCallSite() { // @@@ Special bindTo case for count=1 && isInstance if (invokedType.parameterCount() == 0) { return new ConstantCallSite(MethodHandles.constant(samBase, - MethodHandleProxies.asInterfaceInstance(samBase, impl))); + MethodHandleProxies.asInterfaceInstance(samBase, implMethod))); } else { try { - return new MhMetafactoryCallSite(impl, invokedType, samBase); + return new MhMetafactoryCallSite(implMethod, invokedType, samBase); } catch (Throwable e) { log("Exception constructing Lambda factory callsite", e);