# HG changeset patch # User lana # Date 1393617064 28800 # Node ID e291ac47c9a90366c3c0787a6f7ce547a2bda308 # Parent b07a8059dc08626ab135f0fb951bfaff1e5f7694# Parent 183a8c520b4ad4e185e9251d77475edecbbcbd2f Merge diff -r b07a8059dc08 -r e291ac47c9a9 src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java --- a/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java Fri Feb 28 10:07:21 2014 -0800 +++ b/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java Fri Feb 28 11:51:04 2014 -0800 @@ -200,6 +200,13 @@ implIsInstanceMethod ? "instance" : "static", implInfo, instantiatedArity, samArity)); } + for (MethodType bridgeMT : additionalBridges) { + if (bridgeMT.parameterCount() != samArity) { + throw new LambdaConversionException( + String.format("Incorrect number of parameters for bridge signature %s; incompatible with %s", + bridgeMT, samMethodType)); + } + } // If instance: first captured arg (receiver) must be subtype of class where impl method is defined final int capturedStart; @@ -232,7 +239,7 @@ throw new LambdaConversionException( String.format("Invalid receiver type %s; not a subtype of implementation receiver type %s", receiverClass, implReceiverClass)); - } + } } else { // no receiver capturedStart = 0; @@ -274,11 +281,18 @@ String.format("Type mismatch for lambda return: %s is not convertible to %s", actualReturnType, expectedType)); } - if (!isAdaptableToAsReturn(expectedType, samReturnType)) { + if (!isAdaptableToAsReturnStrict(expectedType, samReturnType)) { throw new LambdaConversionException( String.format("Type mismatch for lambda expected return: %s is not convertible to %s", expectedType, samReturnType)); } + for (MethodType bridgeMT : additionalBridges) { + if (!isAdaptableToAsReturnStrict(expectedType, bridgeMT.returnType())) { + throw new LambdaConversionException( + String.format("Type mismatch for lambda expected return: %s is not convertible to %s", + expectedType, bridgeMT.returnType())); + } + } } /** @@ -330,6 +344,10 @@ return toType.equals(void.class) || !fromType.equals(void.class) && isAdaptableTo(fromType, toType, false); } + private boolean isAdaptableToAsReturnStrict(Class fromType, Class toType) { + if (fromType.equals(void.class)) return toType.equals(void.class); + return isAdaptableTo(fromType, toType, true); + } /*********** Logging support -- for debugging only, uncomment as needed diff -r b07a8059dc08 -r e291ac47c9a9 src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java --- a/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java Fri Feb 28 10:07:21 2014 -0800 +++ b/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java Fri Feb 28 11:51:04 2014 -0800 @@ -212,7 +212,7 @@ * @param functional */ void convertType(Class arg, Class target, Class functional) { - if (arg.equals(target)) { + if (arg.equals(target) && arg.equals(functional)) { return; } if (arg == Void.TYPE || target == Void.TYPE) { diff -r b07a8059dc08 -r e291ac47c9a9 test/java/lang/invoke/lambda/LambdaReceiver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/invoke/lambda/LambdaReceiver.java Fri Feb 28 11:51:04 2014 -0800 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014, 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. + * + * 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. + */ + +/* + * @test + * @bug 8035776 + * @summary Consistent Lambda construction + */ + +import java.lang.invoke.CallSite; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.LambdaConversionException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.List; + +import LambdaReceiver_anotherpkg.LambdaReceiver_A; + +public class LambdaReceiver extends LambdaReceiver_A { + + interface IA { + int m(LambdaReceiver_A x); + } + + static MethodHandles.Lookup l; + static MethodHandle h; + private static MethodType mt(Class k) { return MethodType.methodType(k); } + private static MethodType mt(Class k, Class k2) { return MethodType.methodType(k, k2); } + private static void mf(List errs, MethodType mts, MethodType mtf, boolean shouldWork) { + } + + public static void main(String[] args) throws Throwable { + l = MethodHandles.lookup(); + h = l.findVirtual(LambdaReceiver_A.class, "f", mt(int.class)); + MethodType X = mt(int.class, LambdaReceiver.class); + MethodType A = mt(int.class, LambdaReceiver_A.class); + MethodType mti = mt(IA.class); + CallSite cs = LambdaMetafactory.metafactory(l, "m", mti,A,h,X); + IA p = (IA)cs.dynamicInvoker().invoke(); + LambdaReceiver_A lra = new LambdaReceiver_A(); + try { + p.m(lra); + } catch (ClassCastException cce) { + return; + } + throw new AssertionError("CCE expected"); + } +} diff -r b07a8059dc08 -r e291ac47c9a9 test/java/lang/invoke/lambda/LambdaReceiverBridge.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/invoke/lambda/LambdaReceiverBridge.java Fri Feb 28 11:51:04 2014 -0800 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014, 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. + * + * 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. + */ + +/* + * @test + * @bug 8035776 + * @summary Consistent Lambda construction + */ + +import java.lang.invoke.CallSite; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.LambdaConversionException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.List; + +import LambdaReceiver_anotherpkg.LambdaReceiver_A; + +public class LambdaReceiverBridge extends LambdaReceiver_A { + + interface IA { + int m(LambdaReceiver_A x); + } + + static MethodHandles.Lookup l; + static MethodHandle h; + private static MethodType mt(Class k) { return MethodType.methodType(k); } + private static MethodType mt(Class k, Class k2) { return MethodType.methodType(k, k2); } + private static void mf(List errs, MethodType mts, MethodType mtf, boolean shouldWork) { + } + + public static void main(String[] args) throws Throwable { + l = MethodHandles.lookup(); + h = l.findVirtual(LambdaReceiver_A.class, "f", mt(int.class)); + MethodType X = mt(int.class, LambdaReceiverBridge.class); + MethodType A = mt(int.class, LambdaReceiver_A.class); + MethodType mti = mt(IA.class); + CallSite cs = LambdaMetafactory.altMetafactory(l, "m", mti,X,h,X, + LambdaMetafactory.FLAG_BRIDGES, 1, A); + IA p = (IA)cs.dynamicInvoker().invoke(); + LambdaReceiver_A lra = new LambdaReceiver_A(); + try { + p.m(lra); + } catch (ClassCastException cce) { + return; + } + throw new AssertionError("CCE expected"); + } +} diff -r b07a8059dc08 -r e291ac47c9a9 test/java/lang/invoke/lambda/LambdaReceiver_anotherpkg/LambdaReceiver_A.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/invoke/lambda/LambdaReceiver_anotherpkg/LambdaReceiver_A.java Fri Feb 28 11:51:04 2014 -0800 @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014, 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. + * + * 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 LambdaReceiver_anotherpkg; + +public class LambdaReceiver_A { + protected final int f() { return 2; } +} diff -r b07a8059dc08 -r e291ac47c9a9 test/java/lang/invoke/lambda/LambdaReturn.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/invoke/lambda/LambdaReturn.java Fri Feb 28 11:51:04 2014 -0800 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014, 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. + * + * 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. + */ + +/* + * @test + * @bug 8035776 + * @summary Consistent Lambda construction + */ + +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.LambdaConversionException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.List; + +public class LambdaReturn { + + interface I { + void m(); + } + + static void hereV() {} + static String hereS() { return "hi"; } + static MethodHandles.Lookup l; + private static MethodType mt(Class k) { return MethodType.methodType(k); } + private static MethodType mt(Class k, Class k2) { return MethodType.methodType(k, k2); } + private static void amf(List errs, MethodHandle h, MethodType mts, MethodType mtf, MethodType mtb, boolean shouldWork) { + MethodType mti = mt(I.class); + try { + LambdaMetafactory.altMetafactory(l, "m", mti, mts,h,mtf, + LambdaMetafactory.FLAG_BRIDGES, 1, mtb); + } catch(LambdaConversionException e) { + if (shouldWork) errs.add("Error: Should work h=" + h + " s=" + mts + " -- f=" + mtf + " / b=" + mtb + " got: " + e); + return; + } + if (!shouldWork) errs.add("Error: Should fail h=" + h + " s=" + mts + " -- f=" + mtf + " / b=" + mtb); + } + + public static void main(String[] args) throws Throwable { + l = MethodHandles.lookup(); + MethodHandle hV = l.findStatic(LambdaReturn.class, "hereV", mt(void.class)); + MethodHandle hS = l.findStatic(LambdaReturn.class, "hereS", mt(String.class)); + List errs = new ArrayList<>(); + MethodType V = mt(void.class); + MethodType S = mt(String.class); + MethodType O = mt(Object.class); + MethodType I = mt(int.class); + amf(errs, hS, S, S, O, true); + amf(errs, hS, S, S, V, false); + amf(errs, hS, S, S, I, false); + amf(errs, hS, O, S, S, true); + amf(errs, hS, V, S, S, false); + amf(errs, hS, I, S, S, false); + amf(errs, hS, O, O, S, false); + amf(errs, hS, S, O, O, false); + amf(errs, hV, V, V, O, false); + amf(errs, hV, V, V, I, false); + amf(errs, hV, V, V, S, false); + amf(errs, hV, O, V, V, false); + amf(errs, hV, I, V, V, false); + amf(errs, hV, S, V, V, false); + + if (errs.size() > 0) { + for (String err : errs) { + System.err.println(err); + } + throw new AssertionError("Errors: " + errs.size()); + } + } +} diff -r b07a8059dc08 -r e291ac47c9a9 test/java/lang/invoke/lambda/MetafactoryArityTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/invoke/lambda/MetafactoryArityTest.java Fri Feb 28 11:51:04 2014 -0800 @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2014, 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. + * + * 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. + */ + +/* + * @test + * @bug 8035776 + * @summary metafactory should fail if arities are mismatched + */ +import java.lang.invoke.*; +import java.util.Arrays; +import static java.lang.invoke.MethodType.methodType; + +public class MetafactoryArityTest { + + public interface I {} + public static class C { public static String m(int arg) { return ""; } } + + static final MethodHandles.Lookup lookup = MethodHandles.lookup(); + static final Class[] capInt = { int.class }; + static final MethodHandle C_m; + static { + try { C_m = lookup.findStatic(C.class, "m", methodType(String.class, int.class)); } + catch (NoSuchMethodException | IllegalAccessException e) { throw new RuntimeException(e); } + } + + public static void main(String... args) { + MethodType unary = methodType(String.class, int.class); + MethodType nullary = methodType(String.class); + MethodType binary = methodType(String.class, int.class, int.class); + MethodType unaryCS = methodType(CharSequence.class, int.class); + MethodType nullaryCS = methodType(CharSequence.class); + MethodType binaryCS = methodType(CharSequence.class, int.class, int.class); + MethodType unaryObj = methodType(Object.class, int.class); + MethodType nullaryObj = methodType(Object.class); + MethodType binaryObj = methodType(Object.class, int.class, int.class); + + test(true, C_m, unary, unary); + test(false, C_m, unary, nullary); + test(false, C_m, nullary, unary); + test(false, C_m, unary, binary); + test(false, C_m, binary, unary); + + testBridge(true, C_m, unary, unary, unaryCS); + testBridge(false, C_m, unary, unary, nullaryCS); + testBridge(false, C_m, unary, unary, binaryCS); + + testBridge(true, C_m, unary, unary, unaryCS, unaryObj); + testBridge(false, C_m, unary, unary, unaryCS, nullaryObj); + testBridge(false, C_m, unary, unary, unaryCS, binaryObj); + + testCapture(true, C_m, capInt, nullary, nullary); + testCapture(false, C_m, capInt, binary, binary); + testCapture(false, C_m, capInt, nullary, unary); + testCapture(false, C_m, capInt, nullary, binary); + testCapture(false, C_m, capInt, unary, nullary); + testCapture(false, C_m, capInt, unary, binary); + + testCaptureBridge(true, C_m, capInt, nullary, nullary, nullaryCS); + testCaptureBridge(false, C_m, capInt, unary, unary, unaryCS); + testCaptureBridge(false, C_m, capInt, nullary, nullary, unaryCS); + testCaptureBridge(false, C_m, capInt, nullary, nullary, binaryCS); + + testCaptureBridge(true, C_m, capInt, nullary, nullary, nullaryCS, nullaryObj); + testCaptureBridge(false, C_m, capInt, unary, unary, unaryCS, unaryObj); + testCaptureBridge(false, C_m, capInt, nullary, nullary, nullaryCS, unaryObj); + testCaptureBridge(false, C_m, capInt, nullary, nullary, nullaryCS, binaryObj); + } + + static void test(boolean correct, MethodHandle mh, MethodType instMT, MethodType samMT) { + tryMetafactory(correct, mh, new Class[]{}, instMT, samMT); + tryAltMetafactory(correct, mh, new Class[]{}, instMT, samMT); + } + + static void testBridge(boolean correct, MethodHandle mh, MethodType instMT, MethodType samMT, MethodType... bridgeMTs) { + tryAltMetafactory(correct, mh, new Class[]{}, instMT, samMT, bridgeMTs); + } + + static void testCapture(boolean correct, MethodHandle mh, Class[] captured, MethodType instMT, MethodType samMT) { + tryMetafactory(correct, mh, captured, instMT, samMT); + tryAltMetafactory(correct, mh, captured, instMT, samMT); + } + + static void testCaptureBridge(boolean correct, MethodHandle mh, Class[] captured, + MethodType instMT, MethodType samMT, MethodType... bridgeMTs) { + tryAltMetafactory(correct, mh, captured, instMT, samMT, bridgeMTs); + } + + static void tryMetafactory(boolean correct, MethodHandle mh, Class[] captured, + MethodType instMT, MethodType samMT) { + try { + LambdaMetafactory.metafactory(lookup, "run", methodType(I.class, captured), + samMT, mh, instMT); + if (!correct) { + throw new AssertionError("Uncaught linkage error:" + + " impl=" + mh + + ", captured=" + Arrays.toString(captured) + + ", inst=" + instMT + + ", sam=" + samMT); + } + } + catch (LambdaConversionException e) { + if (correct) { + throw new AssertionError("Unexpected linkage error:" + + " e=" + e + + ", impl=" + mh + + ", captured=" + Arrays.toString(captured) + + ", inst=" + instMT + + ", sam=" + samMT); + } + } + } + + static void tryAltMetafactory(boolean correct, MethodHandle mh, Class[] captured, + MethodType instMT, MethodType samMT, MethodType... bridgeMTs) { + boolean bridge = bridgeMTs.length > 0; + Object[] args = new Object[bridge ? 5+bridgeMTs.length : 4]; + args[0] = samMT; + args[1] = mh; + args[2] = instMT; + args[3] = bridge ? LambdaMetafactory.FLAG_BRIDGES : 0; + if (bridge) { + args[4] = bridgeMTs.length; + for (int i = 0; i < bridgeMTs.length; i++) args[5+i] = bridgeMTs[i]; + } + try { + LambdaMetafactory.altMetafactory(lookup, "run", methodType(I.class, captured), args); + if (!correct) { + throw new AssertionError("Uncaught linkage error:" + + " impl=" + mh + + ", captured=" + Arrays.toString(captured) + + ", inst=" + instMT + + ", sam=" + samMT + + ", bridges=" + Arrays.toString(bridgeMTs)); + } + } + catch (LambdaConversionException e) { + if (correct) { + throw new AssertionError("Unexpected linkage error:" + + " e=" + e + + ", impl=" + mh + + ", captured=" + Arrays.toString(captured) + + ", inst=" + instMT + + ", sam=" + samMT + + ", bridges=" + Arrays.toString(bridgeMTs)); + } + } + } + +} diff -r b07a8059dc08 -r e291ac47c9a9 test/java/lang/invoke/lambda/MetafactoryParameterCastTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/invoke/lambda/MetafactoryParameterCastTest.java Fri Feb 28 11:51:04 2014 -0800 @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2014, 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. + * + * 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. + */ + +/* + * @test + * @bug 8035776 + * @summary Ensure that invocation parameters are always cast to the instantiatedMethodType + */ +import java.lang.invoke.*; +import java.util.Arrays; +import static java.lang.invoke.MethodType.methodType; + +public class MetafactoryParameterCastTest { + + static final MethodHandles.Lookup lookup = MethodHandles.lookup(); + + public static class A { + } + + public static class B extends A { + void instance0() {} + void instance1(B arg) {} + static void static1(B arg) {} + static void static2(B arg1, B arg2) {} + } + + public static class C extends B {} + public static class NotC extends B {} + + public interface ASink { void take(A arg); } + public interface BSink { void take(B arg); } + + public static void main(String... args) throws Throwable { + new MetafactoryParameterCastTest().test(); + } + + void test() throws Throwable { + MethodType takeA = methodType(void.class, A.class); + MethodType takeB = methodType(void.class, B.class); + MethodType takeC = methodType(void.class, C.class); + + Class[] noCapture = {}; + Class[] captureB = { B.class }; + + MethodHandle[] oneBParam = { lookup.findVirtual(B.class, "instance0", methodType(void.class)), + lookup.findStatic(B.class, "static1", methodType(void.class, B.class)) }; + MethodHandle[] twoBParams = { lookup.findVirtual(B.class, "instance1", methodType(void.class, B.class)), + lookup.findStatic(B.class, "static2", methodType(void.class, B.class, B.class)) }; + + for (MethodHandle mh : oneBParam) { + // sam + tryASink(invokeMetafactory(mh, ASink.class, "take", noCapture, takeC, takeA)); + tryBSink(invokeMetafactory(mh, BSink.class, "take", noCapture, takeC, takeB)); + tryASink(invokeAltMetafactory(mh, ASink.class, "take", noCapture, takeC, takeA)); + tryBSink(invokeAltMetafactory(mh, BSink.class, "take", noCapture, takeC, takeB)); + + // bridge + tryASink(invokeAltMetafactory(mh, ASink.class, "take", noCapture, takeC, takeC, takeA)); + tryBSink(invokeAltMetafactory(mh, BSink.class, "take", noCapture, takeC, takeC, takeB)); + } + + for (MethodHandle mh : twoBParams) { + // sam + tryCapASink(invokeMetafactory(mh, ASink.class, "take", captureB, takeC, takeA)); + tryCapBSink(invokeMetafactory(mh, BSink.class, "take", captureB, takeC, takeB)); + tryCapASink(invokeAltMetafactory(mh, ASink.class, "take", captureB, takeC, takeA)); + tryCapBSink(invokeAltMetafactory(mh, BSink.class, "take", captureB, takeC, takeB)); + + // bridge + tryCapASink(invokeAltMetafactory(mh, ASink.class, "take", captureB, takeC, takeC, takeA)); + tryCapBSink(invokeAltMetafactory(mh, BSink.class, "take", captureB, takeC, takeC, takeB)); + } + } + + void tryASink(CallSite cs) throws Throwable { + ASink sink = (ASink) cs.dynamicInvoker().invoke(); + tryASink(sink); + } + + void tryCapASink(CallSite cs) throws Throwable { + ASink sink = (ASink) cs.dynamicInvoker().invoke(new B()); + tryASink(sink); + } + + void tryBSink(CallSite cs) throws Throwable { + BSink sink = (BSink) cs.dynamicInvoker().invoke(); + tryBSink(sink); + } + + void tryCapBSink(CallSite cs) throws Throwable { + BSink sink = (BSink) cs.dynamicInvoker().invoke(new B()); + tryBSink(sink); + } + + void tryASink(ASink sink) { + try { sink.take(new C()); } + catch (ClassCastException e) { + throw new AssertionError("Unexpected cast failure: " + e + " " + lastMFParams()); + } + + try { + sink.take(new B()); + throw new AssertionError("Missing cast from A to C: " + lastMFParams()); + } + catch (ClassCastException e) { /* expected */ } + + try { + sink.take(new NotC()); + throw new AssertionError("Missing cast from A to C: " + lastMFParams()); + } + catch (ClassCastException e) { /* expected */ } + } + + void tryBSink(BSink sink) { + try { sink.take(new C()); } + catch (ClassCastException e) { + throw new AssertionError("Unexpected cast failure: " + e + " " + lastMFParams()); + } + + try { + sink.take(new B()); + throw new AssertionError("Missing cast from B to C: " + lastMFParams()); + } + catch (ClassCastException e) { /* expected */ } + + try { + sink.take(new NotC()); + throw new AssertionError("Missing cast from B to C: " + lastMFParams()); + } + catch (ClassCastException e) { /* expected */ } + } + + MethodHandle lastMH; + Class[] lastCaptured; + MethodType lastInstMT; + MethodType lastSamMT; + MethodType[] lastBridgeMTs; + + String lastMFParams() { + return "mh=" + lastMH + + ", captured=" + Arrays.toString(lastCaptured) + + ", instMT=" + lastInstMT + + ", samMT=" + lastSamMT + + ", bridgeMTs=" + Arrays.toString(lastBridgeMTs); + } + + CallSite invokeMetafactory(MethodHandle mh, Class sam, String methodName, + Class[] captured, MethodType instMT, MethodType samMT) { + lastMH = mh; + lastCaptured = captured; + lastInstMT = instMT; + lastSamMT = samMT; + lastBridgeMTs = new MethodType[]{}; + try { + return LambdaMetafactory.metafactory(lookup, methodName, methodType(sam, captured), + samMT, mh, instMT); + } + catch (LambdaConversionException e) { + // unexpected linkage error + throw new RuntimeException(e); + } + } + + CallSite invokeAltMetafactory(MethodHandle mh, Class sam, String methodName, + Class[] captured, MethodType instMT, + MethodType samMT, MethodType... bridgeMTs) { + lastMH = mh; + lastCaptured = captured; + lastInstMT = instMT; + lastSamMT = samMT; + lastBridgeMTs = bridgeMTs; + try { + boolean bridge = bridgeMTs.length > 0; + Object[] args = new Object[bridge ? 5+bridgeMTs.length : 4]; + args[0] = samMT; + args[1] = mh; + args[2] = instMT; + args[3] = bridge ? LambdaMetafactory.FLAG_BRIDGES : 0; + if (bridge) { + args[4] = bridgeMTs.length; + for (int i = 0; i < bridgeMTs.length; i++) args[5+i] = bridgeMTs[i]; + } + return LambdaMetafactory.altMetafactory(lookup, methodName, methodType(sam, captured), args); + } + catch (LambdaConversionException e) { + // unexpected linkage error + throw new RuntimeException(e); + } + } + +} diff -r b07a8059dc08 -r e291ac47c9a9 test/java/lang/invoke/lambda/MetafactorySamReturnTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/invoke/lambda/MetafactorySamReturnTest.java Fri Feb 28 11:51:04 2014 -0800 @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2014, 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. + * + * 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. + */ + +/* + * @test + * @bug 8035776 + * @summary metafactory should fail if impl return does not match sam/bridge returns + */ +import java.lang.invoke.*; +import java.util.Arrays; +import static java.lang.invoke.MethodType.methodType; + +public class MetafactorySamReturnTest { + + static final MethodHandles.Lookup lookup = MethodHandles.lookup(); + + public interface I {} + + public static class C { + public static void m_void(String arg) {} + public static boolean m_boolean(String arg) { return true; } + public static char m_char(String arg) { return 'x'; } + public static byte m_byte(String arg) { return 12; } + public static short m_short(String arg) { return 12; } + public static int m_int(String arg) { return 12; } + public static long m_long(String arg) { return 12; } + public static float m_float(String arg) { return 12; } + public static double m_double(String arg) { return 12; } + public static String m_String(String arg) { return ""; } + public static Integer m_Integer(String arg) { return 23; } + public static Object m_Object(String arg) { return new Object(); } + + public static MethodHandle getMH(Class c) { + try { + return lookup.findStatic(C.class, "m_" + c.getSimpleName(), methodType(c, String.class)); + } + catch (NoSuchMethodException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + } + + public static void main(String... args) { + Class[] t = { void.class, boolean.class, char.class, + byte.class, short.class, int.class, long.class, float.class, double.class, + String.class, Integer.class, Object.class }; + + for (int i = 0; i < t.length; i++) { + MethodHandle mh = C.getMH(t[i]); + for (int j = 0; j < t.length; j++) { + // TEMPORARY EXCEPTIONS + if (t[j] == void.class) continue; + if (t[i].isPrimitive() && t[j] == Object.class) continue; + if (t[i] == char.class && (t[j] == int.class || t[j] == long.class || t[j] == float.class || t[j] == double.class)) continue; + if (t[i] == byte.class && (t[j] == short.class || t[j] == int.class || t[j] == long.class || t[j] == float.class || t[j] == double.class)) continue; + if (t[i] == short.class && (t[j] == int.class || t[j] == long.class || t[j] == float.class || t[j] == double.class)) continue; + if (t[i] == int.class && (t[j] == long.class || t[j] == float.class || t[j] == double.class)) continue; + if (t[i] == long.class && (t[j] == float.class || t[j] == double.class)) continue; + if (t[i] == float.class && t[j] == double.class) continue; + if (t[i] == int.class && t[j] == Integer.class) continue; + if (t[i] == Integer.class && (t[j] == int.class || t[j] == long.class || t[j] == float.class || t[j] == double.class)) continue; + // END TEMPORARY EXCEPTIONS + boolean correct = (t[i].isPrimitive() || t[j].isPrimitive()) + ? t[i] == t[j] + : t[j].isAssignableFrom(t[i]); + MethodType mti = methodType(t[i], String.class); + MethodType mtiCS = methodType(t[i], CharSequence.class); + MethodType mtj = methodType(t[j], String.class); + MethodType mtjObj = methodType(t[j], Object.class); + test(correct, mh, mti, mtj); + testBridge(correct, mh, mti, mti, mtjObj); + testBridge(correct, mh, mti, mti, mtiCS, mtjObj); + } + } + } + + static void test(boolean correct, MethodHandle mh, MethodType instMT, MethodType samMT) { + tryMetafactory(correct, mh, new Class[]{}, instMT, samMT); + tryAltMetafactory(correct, mh, new Class[]{}, instMT, samMT); + } + + static void testBridge(boolean correct, MethodHandle mh, MethodType instMT, MethodType samMT, MethodType... bridgeMTs) { + tryAltMetafactory(correct, mh, new Class[]{}, instMT, samMT, bridgeMTs); + } + + static void tryMetafactory(boolean correct, MethodHandle mh, Class[] captured, + MethodType instMT, MethodType samMT) { + try { + LambdaMetafactory.metafactory(lookup, "run", methodType(I.class, captured), + samMT, mh, instMT); + if (!correct) { + throw new AssertionError("Uncaught linkage error:" + + " impl=" + mh + + ", captured=" + Arrays.toString(captured) + + ", inst=" + instMT + + ", sam=" + samMT); + } + } + catch (LambdaConversionException e) { + if (correct) { + throw new AssertionError("Unexpected linkage error:" + + " e=" + e + + ", impl=" + mh + + ", captured=" + Arrays.toString(captured) + + ", inst=" + instMT + + ", sam=" + samMT); + } + } + } + + static void tryAltMetafactory(boolean correct, MethodHandle mh, Class[] captured, + MethodType instMT, MethodType samMT, MethodType... bridgeMTs) { + boolean bridge = bridgeMTs.length > 0; + Object[] args = new Object[bridge ? 5+bridgeMTs.length : 4]; + args[0] = samMT; + args[1] = mh; + args[2] = instMT; + args[3] = bridge ? LambdaMetafactory.FLAG_BRIDGES : 0; + if (bridge) { + args[4] = bridgeMTs.length; + for (int i = 0; i < bridgeMTs.length; i++) args[5+i] = bridgeMTs[i]; + } + try { + LambdaMetafactory.altMetafactory(lookup, "run", methodType(I.class, captured), args); + if (!correct) { + throw new AssertionError("Uncaught linkage error:" + + " impl=" + mh + + ", captured=" + Arrays.toString(captured) + + ", inst=" + instMT + + ", sam=" + samMT + + ", bridges=" + Arrays.toString(bridgeMTs)); + } + } + catch (LambdaConversionException e) { + if (correct) { + throw new AssertionError("Unexpected linkage error:" + + " e=" + e + + ", impl=" + mh + + ", captured=" + Arrays.toString(captured) + + ", inst=" + instMT + + ", sam=" + samMT + + ", bridges=" + Arrays.toString(bridgeMTs)); + } + } + } + +}