| /* | |
| * Copyright (c) 2021, 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 8174222 | |
| * @summary Validation of LambdaMetafactory arguments | |
| */ | |
| import java.lang.invoke.*; | |
| import java.util.*; | |
| public class MetafactoryArgValidationTest { | |
| public static void main(String... args) { | |
| testNPE(); | |
| testIAE(); | |
| testLCE(); | |
| } | |
| public static void testNPE() { | |
| MethodType toI = mt(I.class); | |
| MethodType toVoid = mt(void.class); | |
| MethodType toC = mt(C.class); | |
| MethodHandle impl = C.invokeStaticMH(); | |
| MethodHandle impl2 = C.newInvokeSpecialMH(); | |
| int flagSer = LambdaMetafactory.FLAG_SERIALIZABLE; | |
| int flagMark = LambdaMetafactory.FLAG_MARKERS; | |
| int flagBridge = LambdaMetafactory.FLAG_BRIDGES; | |
| mfFail(NullPointerException.class, null, "m", toI, toVoid, impl, toVoid); | |
| mfFail(NullPointerException.class, C.lookup, null, toI, toVoid, impl, toVoid); | |
| mfFail(NullPointerException.class, C.lookup, "m", null, toVoid, impl, toVoid); | |
| mfFail(NullPointerException.class, C.lookup, "m", toI, null, impl, toVoid); | |
| mfFail(NullPointerException.class, C.lookup, "m", toI, toVoid, null, toVoid); | |
| mfFail(NullPointerException.class, C.lookup, "m", toI, toVoid, impl, null); | |
| amfFail(NullPointerException.class, null, "m", toI, arr(toVoid, impl, toVoid, flagSer)); | |
| amfFail(NullPointerException.class, C.lookup, null, toI, arr(toVoid, impl, toVoid, flagSer)); | |
| amfFail(NullPointerException.class, C.lookup, "m", null, arr(toVoid, impl, toVoid, flagSer)); | |
| amfFail(NullPointerException.class, C.lookup, "m", toI, null); | |
| amfFail(NullPointerException.class, C.lookup, "m", toI, arr(null, impl, toVoid, flagSer)); | |
| amfFail(NullPointerException.class, C.lookup, "m", toI, arr(toVoid, null, toVoid, flagSer)); | |
| amfFail(NullPointerException.class, C.lookup, "m", toI, arr(toVoid, impl, null, flagSer)); | |
| amfFail(NullPointerException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, null)); | |
| amfFail(NullPointerException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, flagMark, null)); | |
| amfFail(NullPointerException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, flagMark, 1, null)); | |
| amfFail(NullPointerException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, flagMark, 2, Runnable.class, null)); | |
| amfFail(NullPointerException.class, C.lookup, "m", toI, arr(toC, impl2, toC, flagBridge, null)); | |
| amfFail(NullPointerException.class, C.lookup, "m", toI, arr(toC, impl2, toC, flagBridge, 1, null)); | |
| amfFail(NullPointerException.class, C.lookup, "m", toI, arr(toC, impl2, toC, flagBridge, 1, null)); | |
| amfFail(NullPointerException.class, C.lookup, "m", toI, arr(toC, impl2, toC, flagBridge, 2, mt(Object.class), null)); | |
| amfFail(NullPointerException.class, C.lookup, "m", toI, arr(toC, impl2, toC, flagBridge|flagMark|flagSer, 1, Runnable.class, 2, mt(Object.class), null)); | |
| } | |
| public static void testIAE() { | |
| MethodType toI = mt(I.class); | |
| MethodType toVoid = mt(void.class); | |
| MethodType toC = mt(C.class); | |
| MethodHandle impl = C.invokeStaticMH(); | |
| MethodHandle impl2 = C.newInvokeSpecialMH(); | |
| int flagSer = LambdaMetafactory.FLAG_SERIALIZABLE; | |
| int flagMark = LambdaMetafactory.FLAG_MARKERS; | |
| int flagBridge = LambdaMetafactory.FLAG_BRIDGES; | |
| // missing arguments | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr()); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid)); | |
| amfSucceed(C.lookup, "m", toI, arr(toVoid, impl, toVoid, 0)); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, flagMark)); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, flagMark, 1)); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, flagMark, 2, Runnable.class)); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toC, impl2, toC, flagBridge)); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toC, impl2, toC, flagBridge, 1)); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toC, impl2, toC, flagBridge, 2, mt(Object.class))); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toC, impl2, toC, flagBridge|flagMark|flagSer, 1, Runnable.class, 2, mt(Object.class))); | |
| // too many arguments | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, 0, 1)); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toC, impl2, toC, flagMark|flagSer, 1, Runnable.class, 1, mt(Object.class))); | |
| // wrong argument types | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(impl, impl, toVoid, flagSer)); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toVoid, toVoid, toVoid, flagSer)); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toVoid, impl, impl, flagSer)); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, "hi")); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, flagMark, Runnable.class)); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, flagBridge, 1, Runnable.class)); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, flagMark, 1, toVoid)); | |
| // negative count | |
| amfSucceed(C.lookup, "m", toI, arr(toC, impl2, toC, flagBridge|flagMark|flagSer, 0, 0)); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toC, impl2, toC, flagBridge|flagMark|flagSer, -1, 0)); | |
| amfFail(IllegalArgumentException.class, C.lookup, "m", toI, arr(toC, impl2, toC, flagBridge|flagMark|flagSer, 0, -1)); | |
| } | |
| public static void testLCE() { | |
| MethodType toI = mt(I.class); | |
| MethodType toC = mt(C.class); | |
| MethodType toVoid = mt(void.class); | |
| MethodType cToVoid = mt(void.class, C.class); | |
| MethodType ccToVoid = mt(void.class, C.class, C.class); | |
| MethodType cToC = mt(C.class, C.class); | |
| MethodType cToString = mt(String.class, C.class); | |
| MethodHandle impl = C.invokeStaticMH(); | |
| int flagSer = LambdaMetafactory.FLAG_SERIALIZABLE; | |
| int flagMark = LambdaMetafactory.FLAG_MARKERS; | |
| // non-interface | |
| mfFail(LambdaConversionException.class, C.lookup, "m", mt(Object.class), toVoid, impl, toVoid); | |
| amfFail(LambdaConversionException.class, C.lookup, "m", mt(Object.class), arr(toVoid, impl, toVoid, flagSer)); | |
| mfFail(LambdaConversionException.class, C.lookup, "m", mt(int.class), toVoid, impl, toVoid); | |
| amfFail(LambdaConversionException.class, C.lookup, "m", mt(int.class), arr(toVoid, impl, toVoid, flagSer)); | |
| amfFail(LambdaConversionException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, flagMark, 1, Object.class)); | |
| amfFail(LambdaConversionException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, flagMark, 1, int.class)); | |
| amfFail(LambdaConversionException.class, C.lookup, "m", toI, arr(toVoid, impl, toVoid, flagMark, 2, Runnable.class, Object.class)); | |
| // lookup without private access | |
| mfFail(LambdaConversionException.class, MethodHandles.publicLookup(), "m", toI, toVoid, impl, toVoid); | |
| amfFail(LambdaConversionException.class, MethodHandles.publicLookup(), "m", toI, arr(toVoid, impl, toVoid, flagSer)); | |
| mfFail(LambdaConversionException.class, C.lookup.dropLookupMode(MethodHandles.Lookup.PRIVATE), "m", toI, toVoid, impl, toVoid); | |
| amfFail(LambdaConversionException.class, C.lookup.dropLookupMode(MethodHandles.Lookup.PRIVATE), "m", toI, arr(toVoid, impl, toVoid, flagSer)); | |
| // unsupported MethodHandle | |
| mfFail(LambdaConversionException.class, C.lookup, "m", toI, cToC, C.getFieldMH(), cToC); | |
| amfFail(LambdaConversionException.class, C.lookup, "m", toI, arr(cToC, C.getFieldMH(), cToC, flagSer)); | |
| mfFail(LambdaConversionException.class, C.lookup, "m", toI, toC, C.getStaticMH(), toC); | |
| amfFail(LambdaConversionException.class, C.lookup, "m", toI, arr(toC, C.getStaticMH(), toC, flagSer)); | |
| mfFail(LambdaConversionException.class, C.lookup, "m", toI, ccToVoid, C.putFieldMH(), ccToVoid); | |
| amfFail(LambdaConversionException.class, C.lookup, "m", toI, arr(ccToVoid, C.putFieldMH(), ccToVoid, flagSer)); | |
| mfFail(LambdaConversionException.class, C.lookup, "m", toI, cToVoid, C.putStaticMH(), cToVoid); | |
| amfFail(LambdaConversionException.class, C.lookup, "m", toI, arr(cToVoid, C.putStaticMH(), cToVoid, flagSer)); | |
| mfSucceed(C.lookup, "m", toI, cToVoid, C.invokeVirtualMH(), cToVoid); | |
| amfSucceed(C.lookup, "m", toI, arr(cToVoid, C.invokeVirtualMH(), cToVoid, flagSer)); | |
| mfSucceed(C.lookup, "m", toI, toVoid, C.invokeStaticMH(), toVoid); | |
| amfSucceed(C.lookup, "m", toI, arr(toVoid, C.invokeStaticMH(), toVoid, flagSer)); | |
| mfSucceed(C.lookup, "m", toI, cToString, C.invokeSpecialMH(), cToString); | |
| amfSucceed(C.lookup, "m", toI, arr(cToString, C.invokeSpecialMH(), cToString, flagSer)); | |
| mfSucceed(C.lookup, "m", toI, toC, C.newInvokeSpecialMH(), toC); | |
| amfSucceed(C.lookup, "m", toI, arr(toC, C.newInvokeSpecialMH(), toC, flagSer)); | |
| mfSucceed(C.lookup, "m", toI, cToVoid, C.invokeInterfaceMH(), cToVoid); | |
| amfSucceed(C.lookup, "m", toI, arr(cToVoid, C.invokeInterfaceMH(), cToVoid, flagSer)); | |
| mfFail(LambdaConversionException.class, C.lookup, "m", toI, toVoid, MethodHandles.empty(toVoid), toVoid); | |
| amfFail(LambdaConversionException.class, C.lookup, "m", toI, arr(toVoid, MethodHandles.empty(toVoid), toVoid, flagSer)); | |
| } | |
| static MethodType mt(Class<?> ret, Class<?>... params) { | |
| return MethodType.methodType(ret, params); | |
| } | |
| static Object[] arr(Object... args) { | |
| return args; | |
| } | |
| public static class C implements Runnable { | |
| public static MethodHandles.Lookup lookup = MethodHandles.lookup(); | |
| public static MethodHandle getFieldMH() { | |
| try { return lookup.findGetter(C.class, "iv", C.class); } | |
| catch (ReflectiveOperationException e) { throw new RuntimeException(e); } | |
| } | |
| public static MethodHandle getStaticMH() { | |
| try { return lookup.findStaticGetter(C.class, "sv", C.class); } | |
| catch (ReflectiveOperationException e) { throw new RuntimeException(e); } | |
| } | |
| public static MethodHandle putFieldMH() { | |
| try { return lookup.findSetter(C.class, "iv", C.class); } | |
| catch (ReflectiveOperationException e) { throw new RuntimeException(e); } | |
| } | |
| public static MethodHandle putStaticMH() { | |
| try { return lookup.findStaticSetter(C.class, "sv", C.class); } | |
| catch (ReflectiveOperationException e) { throw new RuntimeException(e); } | |
| } | |
| public static MethodHandle invokeVirtualMH() { | |
| try { return lookup.findVirtual(C.class, "im", mt(void.class)); } | |
| catch (ReflectiveOperationException e) { throw new RuntimeException(e); } | |
| } | |
| public static MethodHandle invokeStaticMH() { | |
| try { return lookup.findStatic(C.class, "sm", mt(void.class)); } | |
| catch (ReflectiveOperationException e) { throw new RuntimeException(e); } | |
| } | |
| public static MethodHandle invokeSpecialMH() { | |
| try { return lookup.findSpecial(Object.class, "toString", mt(String.class), C.class); } | |
| catch (ReflectiveOperationException e) { throw new RuntimeException(e); } | |
| } | |
| public static MethodHandle newInvokeSpecialMH() { | |
| try { return lookup.findConstructor(C.class, mt(void.class)); } | |
| catch (ReflectiveOperationException e) { throw new RuntimeException(e); } | |
| } | |
| public static MethodHandle invokeInterfaceMH() { | |
| try { return lookup.findVirtual(Runnable.class, "run", mt(void.class)); } | |
| catch (ReflectiveOperationException e) { throw new RuntimeException(e); } | |
| } | |
| public static C sv; | |
| public C iv; | |
| public static void sm() {} | |
| public void im() {} | |
| public C() {} | |
| public void run() {} | |
| } | |
| public interface I {} | |
| static CallSite mfSucceed(MethodHandles.Lookup lookup, | |
| String name, | |
| MethodType capType, | |
| MethodType desc, | |
| MethodHandle impl, | |
| MethodType checked) { | |
| try { | |
| return LambdaMetafactory.metafactory(lookup, name, capType, desc, impl, checked); | |
| } catch (Throwable t) { | |
| String msg = String.format("Unexpected exception during linkage for metafactory(%s, %s, %s, %s, %s, %s)", | |
| lookup, name, capType, desc, impl, checked); | |
| throw new AssertionError(msg, t); | |
| } | |
| } | |
| static void mfFail(Class<?> exceptionType, | |
| MethodHandles.Lookup lookup, | |
| String name, | |
| MethodType capType, | |
| MethodType desc, | |
| MethodHandle impl, | |
| MethodType checked) { | |
| try { | |
| LambdaMetafactory.metafactory(lookup, name, capType, desc, impl, checked); | |
| } catch (Throwable t) { | |
| if (exceptionType.isInstance(t)) { | |
| return; | |
| } else { | |
| String msg = String.format("Unexpected exception: expected %s during linkage for metafactory(%s, %s, %s, %s, %s, %s)", | |
| exceptionType.getName(), lookup, name, capType, desc, impl, checked); | |
| throw new AssertionError(msg, t); | |
| } | |
| } | |
| String msg = String.format("Unexpected success: expected %s during linkage for metafactory(%s, %s, %s, %s, %s, %s)", | |
| exceptionType.getName(), lookup, name, capType, desc, impl, checked); | |
| throw new AssertionError(msg); | |
| } | |
| static CallSite amfSucceed(MethodHandles.Lookup lookup, | |
| String name, | |
| MethodType capType, | |
| Object[] args) { | |
| try { | |
| return LambdaMetafactory.altMetafactory(lookup, name, capType, args); | |
| } catch (Throwable t) { | |
| String msg = String.format("Unexpected exception during linkage for altMetafactory(%s, %s, %s, %s)", | |
| lookup, name, capType, Arrays.asList(args)); | |
| throw new AssertionError(msg, t); | |
| } | |
| } | |
| static void amfFail(Class<?> exceptionType, | |
| MethodHandles.Lookup lookup, | |
| String name, | |
| MethodType capType, | |
| Object[] args) { | |
| try { | |
| LambdaMetafactory.altMetafactory(lookup, name, capType, args); | |
| } catch (Throwable t) { | |
| if (exceptionType.isInstance(t)) { | |
| return; | |
| } else { | |
| String msg = String.format("Unexpected exception: expected %s during linkage for altMetafactory(%s, %s, %s, %s)", | |
| exceptionType.getName(), lookup, name, capType, Arrays.asList(args)); | |
| throw new AssertionError(msg, t); | |
| } | |
| } | |
| String msg = String.format("Unexpected success: expected %s during linkage for altMetafactory(%s, %s, %s, %s)", | |
| exceptionType.getName(), lookup, name, capType, Arrays.asList(args)); | |
| throw new AssertionError(msg); | |
| } | |
| } |