| /* |
| * 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. |
| */ |
| |
| // Android-added: package declaration. |
| package test.java.lang.invoke.MethodHandles; |
| |
| /* |
| * @test |
| * @run testng/othervm -Xverify:all TestTableSwitch |
| */ |
| |
| import org.testng.annotations.DataProvider; |
| import org.testng.annotations.Test; |
| |
| import java.lang.invoke.MethodHandle; |
| import java.lang.invoke.MethodHandles; |
| import java.lang.invoke.MethodType; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.function.IntConsumer; |
| import java.util.function.IntFunction; |
| |
| import static org.testng.Assert.assertEquals; |
| |
| public class TestTableSwitch { |
| |
| static final MethodHandle MH_IntConsumer_accept; |
| static final MethodHandle MH_check; |
| |
| static { |
| try { |
| MethodHandles.Lookup lookup = MethodHandles.lookup(); |
| MH_IntConsumer_accept = lookup.findVirtual(IntConsumer.class, "accept", |
| MethodType.methodType(void.class, int.class)); |
| MH_check = lookup.findStatic(TestTableSwitch.class, "check", |
| MethodType.methodType(void.class, List.class, Object[].class)); |
| } catch (ReflectiveOperationException e) { |
| throw new ExceptionInInitializerError(e); |
| } |
| } |
| |
| public static MethodHandle simpleTestCase(String value) { |
| return simpleTestCase(String.class, value); |
| } |
| |
| public static MethodHandle simpleTestCase(Class<?> type, Object value) { |
| return MethodHandles.dropArguments(MethodHandles.constant(type, value), 0, int.class); |
| } |
| |
| public static Object testValue(Class<?> type) { |
| if (type == String.class) { |
| return "X"; |
| } else if (type == byte.class) { |
| return (byte) 42; |
| } else if (type == short.class) { |
| return (short) 84; |
| } else if (type == char.class) { |
| return 'Y'; |
| } else if (type == int.class) { |
| return 168; |
| } else if (type == long.class) { |
| return 336L; |
| } else if (type == float.class) { |
| return 42F; |
| } else if (type == double.class) { |
| return 84D; |
| } else if (type == boolean.class) { |
| return true; |
| } |
| return null; |
| } |
| |
| static final Class<?>[] TEST_TYPES = { |
| Object.class, |
| String.class, |
| byte.class, |
| short.class, |
| char.class, |
| int.class, |
| long.class, |
| float.class, |
| double.class, |
| boolean.class |
| }; |
| |
| public static Object[] testArguments(int caseNum, List<Object> testValues) { |
| Object[] args = new Object[testValues.size() + 1]; |
| args[0] = caseNum; |
| int insertPos = 1; |
| for (Object testValue : testValues) { |
| args[insertPos++] = testValue; |
| } |
| return args; |
| } |
| |
| @DataProvider |
| public static Object[][] nonVoidCases() { |
| List<Object[]> tests = new ArrayList<>(); |
| |
| for (Class<?> returnType : TEST_TYPES) { |
| for (int numCases = 1; numCases < 5; numCases++) { |
| tests.add(new Object[] { returnType, numCases, List.of() }); |
| tests.add(new Object[] { returnType, numCases, List.of(TEST_TYPES) }); |
| } |
| } |
| |
| return tests.toArray(Object[][]::new); |
| } |
| |
| private static void check(List<Object> testValues, Object[] collectedValues) { |
| assertEquals(collectedValues, testValues.toArray()); |
| } |
| |
| @Test(dataProvider = "nonVoidCases") |
| public void testNonVoidHandles(Class<?> type, int numCases, List<Class<?>> additionalTypes) throws Throwable { |
| MethodHandle collector = MH_check; |
| List<Object> testArguments = new ArrayList<>(); |
| collector = MethodHandles.insertArguments(collector, 0, testArguments); |
| collector = collector.asCollector(Object[].class, additionalTypes.size()); |
| |
| Object defaultReturnValue = testValue(type); |
| MethodHandle defaultCase = simpleTestCase(type, defaultReturnValue); |
| defaultCase = MethodHandles.collectArguments(defaultCase, 1, collector); |
| Object[] returnValues = new Object[numCases]; |
| MethodHandle[] cases = new MethodHandle[numCases]; |
| for (int i = 0; i < cases.length; i++) { |
| Object returnValue = testValue(type); |
| returnValues[i] = returnValue; |
| MethodHandle theCase = simpleTestCase(type, returnValue); |
| theCase = MethodHandles.collectArguments(theCase, 1, collector); |
| cases[i] = theCase; |
| } |
| |
| MethodHandle mhSwitch = MethodHandles.tableSwitch( |
| defaultCase, |
| cases |
| ); |
| |
| for (Class<?> additionalType : additionalTypes) { |
| testArguments.add(testValue(additionalType)); |
| } |
| |
| assertEquals(mhSwitch.invokeWithArguments(testArguments(-1, testArguments)), defaultReturnValue); |
| |
| for (int i = 0; i < numCases; i++) { |
| assertEquals(mhSwitch.invokeWithArguments(testArguments(i, testArguments)), returnValues[i]); |
| } |
| |
| assertEquals(mhSwitch.invokeWithArguments(testArguments(numCases, testArguments)), defaultReturnValue); |
| } |
| |
| @Test |
| public void testVoidHandles() throws Throwable { |
| IntFunction<MethodHandle> makeTestCase = expectedIndex -> { |
| IntConsumer test = actualIndex -> assertEquals(actualIndex, expectedIndex); |
| return MH_IntConsumer_accept.bindTo(test); |
| }; |
| |
| MethodHandle mhSwitch = MethodHandles.tableSwitch( |
| /* default: */ makeTestCase.apply(-1), |
| /* case 0: */ makeTestCase.apply(0), |
| /* case 1: */ makeTestCase.apply(1), |
| /* case 2: */ makeTestCase.apply(2) |
| ); |
| |
| mhSwitch.invokeExact((int) -1); |
| mhSwitch.invokeExact((int) 0); |
| mhSwitch.invokeExact((int) 1); |
| mhSwitch.invokeExact((int) 2); |
| } |
| |
| @Test(expectedExceptions = NullPointerException.class) |
| public void testNullDefaultHandle() { |
| MethodHandles.tableSwitch(null, simpleTestCase("test")); |
| } |
| |
| @Test(expectedExceptions = NullPointerException.class) |
| public void testNullCases() { |
| MethodHandle[] cases = null; |
| MethodHandles.tableSwitch(simpleTestCase("default"), cases); |
| } |
| |
| @Test(expectedExceptions = NullPointerException.class) |
| public void testNullCase() { |
| MethodHandles.tableSwitch(simpleTestCase("default"), simpleTestCase("case"), null); |
| } |
| |
| @Test(expectedExceptions = IllegalArgumentException.class, |
| expectedExceptionsMessageRegExp = ".*Not enough cases.*") |
| public void testNotEnoughCases() { |
| MethodHandles.tableSwitch(simpleTestCase("default")); |
| } |
| |
| @Test(expectedExceptions = IllegalArgumentException.class, |
| expectedExceptionsMessageRegExp = ".*Case actions must have int as leading parameter.*") |
| public void testNotEnoughParameters() { |
| MethodHandle empty = MethodHandles.empty(MethodType.methodType(void.class)); |
| MethodHandles.tableSwitch(empty, empty, empty); |
| } |
| |
| @Test(expectedExceptions = IllegalArgumentException.class, |
| expectedExceptionsMessageRegExp = ".*Case actions must have int as leading parameter.*") |
| public void testNoLeadingIntParameter() { |
| MethodHandle empty = MethodHandles.empty(MethodType.methodType(void.class, double.class)); |
| MethodHandles.tableSwitch(empty, empty, empty); |
| } |
| |
| @Test(expectedExceptions = IllegalArgumentException.class, |
| expectedExceptionsMessageRegExp = ".*Case actions must have the same type.*") |
| public void testWrongCaseType() { |
| // doesn't return a String |
| MethodHandle wrongType = MethodHandles.empty(MethodType.methodType(void.class, int.class)); |
| MethodHandles.tableSwitch(simpleTestCase("default"), simpleTestCase("case"), wrongType); |
| } |
| |
| } |