| /* |
| * 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); |
| } |
| } |
| |
| } |