| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| |
| package libcore.java.lang.invoke; |
| |
| import java.lang.Thread; |
| import java.lang.invoke.MethodHandle; |
| import java.lang.invoke.MethodHandles; |
| import java.lang.invoke.MethodType; |
| import java.lang.invoke.WrongMethodTypeException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| |
| import junit.framework.TestCase; |
| |
| public class MethodHandleCombinersTest extends TestCase { |
| |
| static final int TEST_THREAD_ITERATIONS = 1000; |
| |
| public static void testThrowException() throws Throwable { |
| MethodHandle handle = MethodHandles.throwException(String.class, |
| IllegalArgumentException.class); |
| |
| if (handle.type().returnType() != String.class) { |
| fail("Unexpected return type for handle: " + handle + |
| " [ " + handle.type() + "]"); |
| } |
| |
| final IllegalArgumentException iae = new IllegalArgumentException("boo!"); |
| try { |
| handle.invoke(iae); |
| fail("Expected an exception of type: java.lang.IllegalArgumentException"); |
| } catch (IllegalArgumentException expected) { |
| if (expected != iae) { |
| fail("Wrong exception: expected " + iae + " but was " + expected); |
| } |
| } |
| } |
| |
| public static void dropArguments_delegate(String message, long message2) { |
| assertEquals("foo", message); |
| assertEquals(42l, message2); |
| } |
| |
| public static void testDropArguments() throws Throwable { |
| MethodHandle delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "dropArguments_delegate", |
| MethodType.methodType(void.class, new Class<?>[]{String.class, long.class})); |
| |
| MethodHandle transform = MethodHandles.dropArguments( |
| delegate, 0, int.class, Object.class); |
| |
| // The transformer will accept two additional arguments at position zero. |
| try { |
| transform.invokeExact("foo", 42l); |
| fail(); |
| } catch (WrongMethodTypeException expected) { |
| } |
| |
| transform.invokeExact(45, new Object(), "foo", 42l); |
| transform.invoke(45, new Object(), "foo", 42l); |
| |
| // Additional arguments at position 1. |
| transform = MethodHandles.dropArguments(delegate, 1, int.class, Object.class); |
| transform.invokeExact("foo", 45, new Object(), 42l); |
| transform.invoke("foo", 45, new Object(), 42l); |
| |
| // Additional arguments at position 2. |
| transform = MethodHandles.dropArguments(delegate, 2, int.class, Object.class); |
| transform.invokeExact("foo", 42l, 45, new Object()); |
| transform.invoke("foo", 42l, 45, new Object()); |
| |
| // Note that we still perform argument conversions even for the arguments that |
| // are subsequently dropped. |
| try { |
| transform.invoke("foo", 42l, 45l, new Object()); |
| fail(); |
| } catch (WrongMethodTypeException expected) { |
| } catch (IllegalArgumentException expected) { |
| // TODO(narayan): We currently throw the wrong type of exception here, |
| // it's IAE and should be WMTE instead. |
| } |
| |
| // Check that asType works as expected. |
| transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class); |
| transform = transform.asType(MethodType.methodType(void.class, |
| new Class<?>[]{short.class, Object.class, String.class, long.class})); |
| transform.invokeExact((short) 45, new Object(), "foo", 42l); |
| |
| // Invalid argument location, should not be allowed. |
| try { |
| MethodHandles.dropArguments(delegate, -1, int.class, Object.class); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| |
| // Invalid argument location, should not be allowed. |
| try { |
| MethodHandles.dropArguments(delegate, 3, int.class, Object.class); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| |
| try { |
| MethodHandles.dropArguments(delegate, 1, void.class); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| public static void testDropArguments_List() throws Throwable { |
| MethodHandle delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "dropArguments_delegate", |
| MethodType.methodType(void.class, new Class<?>[]{String.class, long.class})); |
| |
| MethodHandle transform = MethodHandles.dropArguments( |
| delegate, 0, Arrays.asList(int.class, Object.class)); |
| |
| transform.invokeExact(45, new Object(), "foo", 42l); |
| transform.invoke(45, new Object(), "foo", 42l); |
| |
| // Check that asType works as expected. |
| transform = transform.asType(MethodType.methodType(void.class, |
| new Class<?>[]{short.class, Object.class, String.class, long.class})); |
| transform.invokeExact((short) 45, new Object(), "foo", 42l); |
| } |
| |
| public static String testCatchException_target(String arg1, long arg2, String exceptionMessage) |
| throws Throwable { |
| if (exceptionMessage != null) { |
| throw new IllegalArgumentException(exceptionMessage); |
| } |
| |
| assertEquals(null, exceptionMessage); |
| assertEquals(42l, arg2); |
| return "target"; |
| } |
| |
| public static String testCatchException_handler(IllegalArgumentException iae, String arg1, |
| long arg2, |
| String exMsg) { |
| // Check that the thrown exception has the right message. |
| assertEquals("exceptionMessage", iae.getMessage()); |
| // Check the other arguments. |
| assertEquals("foo", arg1); |
| assertEquals(42, arg2); |
| assertEquals("exceptionMessage", exMsg); |
| |
| return "handler1"; |
| } |
| |
| public static String testCatchException_handler2(IllegalArgumentException iae, String arg1) { |
| assertEquals("exceptionMessage", iae.getMessage()); |
| assertEquals("foo", arg1); |
| |
| return "handler2"; |
| } |
| |
| public static void testCatchException() throws Throwable { |
| MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "testCatchException_target", |
| MethodType |
| .methodType(String.class, new Class<?>[]{String.class, long.class, String.class})); |
| |
| MethodHandle handler = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "testCatchException_handler", |
| MethodType.methodType(String.class, new Class<?>[]{IllegalArgumentException.class, |
| String.class, long.class, String.class})); |
| |
| MethodHandle adapter = MethodHandles.catchException(target, IllegalArgumentException.class, |
| handler); |
| |
| String returnVal = null; |
| |
| // These two should end up calling the target always. We're passing a null exception |
| // message here, which means the target will not throw. |
| returnVal = (String) adapter.invoke("foo", 42, null); |
| assertEquals("target", returnVal); |
| returnVal = (String) adapter.invokeExact("foo", 42l, (String) null); |
| assertEquals("target", returnVal); |
| |
| // We're passing a non-null exception message here, which means the target will throw, |
| // which in turn means that the handler must be called for the next two invokes. |
| returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage"); |
| assertEquals("handler1", returnVal); |
| returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage"); |
| assertEquals("handler1", returnVal); |
| |
| handler = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "testCatchException_handler2", |
| MethodType.methodType(String.class, new Class<?>[]{IllegalArgumentException.class, |
| String.class})); |
| adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler); |
| |
| returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage"); |
| assertEquals("handler2", returnVal); |
| returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage"); |
| assertEquals("handler2", returnVal); |
| |
| // Test that the type of the invoke doesn't matter. Here we call |
| // IllegalArgumentException.toString() on the exception that was thrown by |
| // the target. |
| handler = MethodHandles.lookup().findVirtual(IllegalArgumentException.class, |
| "toString", MethodType.methodType(String.class)); |
| adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler); |
| |
| returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage"); |
| assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal); |
| returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage"); |
| assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal); |
| |
| // Check that asType works as expected. |
| adapter = MethodHandles.catchException(target, IllegalArgumentException.class, |
| handler); |
| adapter = adapter.asType(MethodType.methodType(String.class, |
| new Class<?>[]{String.class, int.class, String.class})); |
| returnVal = (String) adapter.invokeExact("foo", 42, "exceptionMessage"); |
| assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal); |
| } |
| |
| public static boolean testGuardWithTest_test(String arg1, long arg2) { |
| return "target".equals(arg1) && 42 == arg2; |
| } |
| |
| public static String testGuardWithTest_target(String arg1, long arg2, int arg3) { |
| // Make sure that the test passed. |
| assertTrue(testGuardWithTest_test(arg1, arg2)); |
| // Make sure remaining arguments were passed through unmodified. |
| assertEquals(56, arg3); |
| |
| return "target"; |
| } |
| |
| public static String testGuardWithTest_fallback(String arg1, long arg2, int arg3) { |
| // Make sure that the test failed. |
| assertTrue(!testGuardWithTest_test(arg1, arg2)); |
| // Make sure remaining arguments were passed through unmodified. |
| assertEquals(56, arg3); |
| |
| return "fallback"; |
| } |
| |
| public static void testGuardWithTest() throws Throwable { |
| MethodHandle test = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "testGuardWithTest_test", |
| MethodType.methodType(boolean.class, new Class<?>[]{String.class, long.class})); |
| |
| final MethodType type = MethodType.methodType(String.class, |
| new Class<?>[]{String.class, long.class, int.class}); |
| |
| final MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "testGuardWithTest_target", type); |
| final MethodHandle fallback = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "testGuardWithTest_fallback", type); |
| |
| MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback); |
| |
| String returnVal = null; |
| |
| returnVal = (String) adapter.invoke("target", 42, 56); |
| assertEquals("target", returnVal); |
| returnVal = (String) adapter.invokeExact("target", 42l, 56); |
| assertEquals("target", returnVal); |
| |
| returnVal = (String) adapter.invoke("fallback", 42l, 56); |
| assertEquals("fallback", returnVal); |
| returnVal = (String) adapter.invoke("target", 46l, 56); |
| assertEquals("fallback", returnVal); |
| returnVal = (String) adapter.invokeExact("target", 42l, 56); |
| assertEquals("target", returnVal); |
| |
| // Check that asType works as expected. |
| adapter = adapter.asType(MethodType.methodType(String.class, |
| new Class<?>[]{String.class, int.class, int.class})); |
| returnVal = (String) adapter.invokeExact("target", 42, 56); |
| assertEquals("target", returnVal); |
| } |
| |
| public static void testArrayElementGetter() throws Throwable { |
| MethodHandle getter = MethodHandles.arrayElementGetter(int[].class); |
| |
| { |
| int[] array = new int[1]; |
| array[0] = 42; |
| int value = (int) getter.invoke(array, 0); |
| assertEquals(42, value); |
| |
| try { |
| value = (int) getter.invoke(array, -1); |
| fail(); |
| } catch (ArrayIndexOutOfBoundsException expected) { |
| } |
| |
| try { |
| value = (int) getter.invoke(null, -1); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| } |
| |
| { |
| getter = MethodHandles.arrayElementGetter(long[].class); |
| long[] array = new long[1]; |
| array[0] = 42; |
| long value = (long) getter.invoke(array, 0); |
| assertEquals(42l, value); |
| } |
| |
| { |
| getter = MethodHandles.arrayElementGetter(short[].class); |
| short[] array = new short[1]; |
| array[0] = 42; |
| short value = (short) getter.invoke(array, 0); |
| assertEquals((short) 42, value); |
| } |
| |
| { |
| getter = MethodHandles.arrayElementGetter(char[].class); |
| char[] array = new char[1]; |
| array[0] = 42; |
| char value = (char) getter.invoke(array, 0); |
| assertEquals((char) 42, value); |
| } |
| |
| { |
| getter = MethodHandles.arrayElementGetter(byte[].class); |
| byte[] array = new byte[1]; |
| array[0] = (byte) 0x8; |
| byte value = (byte) getter.invoke(array, 0); |
| assertEquals((byte) 0x8, value); |
| } |
| |
| { |
| getter = MethodHandles.arrayElementGetter(boolean[].class); |
| boolean[] array = new boolean[1]; |
| array[0] = true; |
| boolean value = (boolean) getter.invoke(array, 0); |
| assertTrue(value); |
| } |
| |
| { |
| getter = MethodHandles.arrayElementGetter(float[].class); |
| float[] array = new float[1]; |
| array[0] = 42.0f; |
| float value = (float) getter.invoke(array, 0); |
| assertEquals(42.0f, value); |
| } |
| |
| { |
| getter = MethodHandles.arrayElementGetter(double[].class); |
| double[] array = new double[1]; |
| array[0] = 42.0; |
| double value = (double) getter.invoke(array, 0); |
| assertEquals(42.0, value); |
| } |
| |
| { |
| getter = MethodHandles.arrayElementGetter(String[].class); |
| String[] array = new String[3]; |
| array[0] = "42"; |
| array[1] = "48"; |
| array[2] = "54"; |
| String value = (String) getter.invoke(array, 0); |
| assertEquals("42", value); |
| value = (String) getter.invoke(array, 1); |
| assertEquals("48", value); |
| value = (String) getter.invoke(array, 2); |
| assertEquals("54", value); |
| } |
| } |
| |
| public static void testArrayElementSetter() throws Throwable { |
| MethodHandle setter = MethodHandles.arrayElementSetter(int[].class); |
| |
| { |
| int[] array = new int[2]; |
| setter.invoke(array, 0, 42); |
| setter.invoke(array, 1, 43); |
| |
| assertEquals(42, array[0]); |
| assertEquals(43, array[1]); |
| |
| try { |
| setter.invoke(array, -1, 42); |
| fail(); |
| } catch (ArrayIndexOutOfBoundsException expected) { |
| } |
| |
| try { |
| setter.invoke(null, 0, 42); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| } |
| |
| { |
| setter = MethodHandles.arrayElementSetter(long[].class); |
| long[] array = new long[1]; |
| setter.invoke(array, 0, 42l); |
| assertEquals(42l, array[0]); |
| } |
| |
| { |
| setter = MethodHandles.arrayElementSetter(short[].class); |
| short[] array = new short[1]; |
| setter.invoke(array, 0, (short) 42); |
| assertEquals((short) 42, array[0]); |
| } |
| |
| { |
| setter = MethodHandles.arrayElementSetter(char[].class); |
| char[] array = new char[1]; |
| setter.invoke(array, 0, (char) 42); |
| assertEquals((char) 42, array[0]); |
| } |
| |
| { |
| setter = MethodHandles.arrayElementSetter(byte[].class); |
| byte[] array = new byte[1]; |
| setter.invoke(array, 0, (byte) 0x8); |
| assertEquals((byte) 0x8, array[0]); |
| } |
| |
| { |
| setter = MethodHandles.arrayElementSetter(boolean[].class); |
| boolean[] array = new boolean[1]; |
| setter.invoke(array, 0, true); |
| assertTrue(array[0]); |
| } |
| |
| { |
| setter = MethodHandles.arrayElementSetter(float[].class); |
| float[] array = new float[1]; |
| setter.invoke(array, 0, 42.0f); |
| assertEquals(42.0f, array[0]); |
| } |
| |
| { |
| setter = MethodHandles.arrayElementSetter(double[].class); |
| double[] array = new double[1]; |
| setter.invoke(array, 0, 42.0); |
| assertEquals(42.0, array[0]); |
| } |
| |
| { |
| setter = MethodHandles.arrayElementSetter(String[].class); |
| String[] array = new String[3]; |
| setter.invoke(array, 0, "42"); |
| setter.invoke(array, 1, "48"); |
| setter.invoke(array, 2, "54"); |
| assertEquals("42", array[0]); |
| assertEquals("48", array[1]); |
| assertEquals("54", array[2]); |
| } |
| } |
| |
| public static void testIdentity() throws Throwable { |
| { |
| MethodHandle identity = MethodHandles.identity(boolean.class); |
| boolean value = (boolean) identity.invoke(false); |
| assertFalse(value); |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(byte.class); |
| byte value = (byte) identity.invoke((byte) 0x8); |
| assertEquals((byte) 0x8, value); |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(char.class); |
| char value = (char) identity.invoke((char) -56); |
| assertEquals((char) -56, value); |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(short.class); |
| short value = (short) identity.invoke((short) -59); |
| assertEquals((short) -59, value); |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(int.class); |
| int value = (int) identity.invoke(52); |
| assertEquals((int) 52, value); |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(long.class); |
| long value = (long) identity.invoke(-76l); |
| assertEquals(-76l, value); |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(float.class); |
| float value = (float) identity.invoke(56.0f); |
| assertEquals(56.0f, value); |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(double.class); |
| double value = (double) identity.invoke((double) 72.0); |
| assertEquals(72.0, value); |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(String.class); |
| String value = (String) identity.invoke("bazman"); |
| assertEquals("bazman", value); |
| } |
| } |
| |
| public static void testConstant() throws Throwable { |
| // int constants. |
| { |
| MethodHandle constant = MethodHandles.constant(int.class, 56); |
| assertEquals(56, (int) constant.invoke()); |
| |
| // short constant values are converted to int. |
| constant = MethodHandles.constant(int.class, (short) 52); |
| assertEquals(52, (int) constant.invoke()); |
| |
| // char constant values are converted to int. |
| constant = MethodHandles.constant(int.class, (char) 'b'); |
| assertEquals('b', (int) constant.invoke()); |
| |
| // int constant values are converted to int. |
| constant = MethodHandles.constant(int.class, (byte) 0x1); |
| assertEquals(0x1, (int) constant.invoke()); |
| |
| // boolean, float, double and long primitive constants are not convertible |
| // to int, so the handle creation must fail with a CCE. |
| try { |
| MethodHandles.constant(int.class, false); |
| fail(); |
| } catch (ClassCastException expected) { |
| } |
| |
| try { |
| MethodHandles.constant(int.class, 0.1f); |
| fail(); |
| } catch (ClassCastException expected) { |
| } |
| |
| try { |
| MethodHandles.constant(int.class, 0.2); |
| fail(); |
| } catch (ClassCastException expected) { |
| } |
| |
| try { |
| MethodHandles.constant(int.class, 73l); |
| fail(); |
| } catch (ClassCastException expected) { |
| } |
| } |
| |
| // long constants. |
| { |
| MethodHandle constant = MethodHandles.constant(long.class, 56l); |
| assertEquals(56l, (long) constant.invoke()); |
| |
| constant = MethodHandles.constant(long.class, (int) 56); |
| assertEquals(56l, (long) constant.invoke()); |
| } |
| |
| // byte constants. |
| { |
| MethodHandle constant = MethodHandles.constant(byte.class, (byte) 0x12); |
| assertEquals((byte) 0x12, (byte) constant.invoke()); |
| } |
| |
| // boolean constants. |
| { |
| MethodHandle constant = MethodHandles.constant(boolean.class, true); |
| assertTrue((boolean) constant.invoke()); |
| } |
| |
| // char constants. |
| { |
| MethodHandle constant = MethodHandles.constant(char.class, 'f'); |
| assertEquals('f', (char) constant.invoke()); |
| } |
| |
| // short constants. |
| { |
| MethodHandle constant = MethodHandles.constant(short.class, (short) 123); |
| assertEquals((short) 123, (short) constant.invoke()); |
| } |
| |
| // float constants. |
| { |
| MethodHandle constant = MethodHandles.constant(float.class, 56.0f); |
| assertEquals(56.0f, (float) constant.invoke()); |
| } |
| |
| // double constants. |
| { |
| MethodHandle constant = MethodHandles.constant(double.class, 256.0); |
| assertEquals(256.0, (double) constant.invoke()); |
| } |
| |
| // reference constants. |
| { |
| MethodHandle constant = MethodHandles.constant(String.class, "256.0"); |
| assertEquals("256.0", (String) constant.invoke()); |
| } |
| } |
| |
| public static void testBindTo() throws Throwable { |
| MethodHandle stringCharAt = MethodHandles.lookup().findVirtual( |
| String.class, "charAt", MethodType.methodType(char.class, int.class)); |
| |
| char value = (char) stringCharAt.invoke("foo", 0); |
| if (value != 'f') { |
| fail("Unexpected value: " + value); |
| } |
| |
| MethodHandle bound = stringCharAt.bindTo("foo"); |
| value = (char) bound.invoke(0); |
| if (value != 'f') { |
| fail("Unexpected value: " + value); |
| } |
| |
| try { |
| stringCharAt.bindTo(new Object()); |
| fail(); |
| } catch (ClassCastException expected) { |
| } |
| |
| bound = stringCharAt.bindTo(null); |
| try { |
| bound.invoke(0); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| |
| MethodHandle integerParseInt = MethodHandles.lookup().findStatic( |
| Integer.class, "parseInt", MethodType.methodType(int.class, String.class)); |
| |
| bound = integerParseInt.bindTo("78452"); |
| int intValue = (int) bound.invoke(); |
| if (intValue != 78452) { |
| fail("Unexpected value: " + intValue); |
| } |
| } |
| |
| public static String filterReturnValue_target(int a) { |
| return "ReturnValue" + a; |
| } |
| |
| public static boolean filterReturnValue_filter(String value) { |
| return value.indexOf("42") != -1; |
| } |
| |
| public static int filterReturnValue_intTarget(String a) { |
| return Integer.parseInt(a); |
| } |
| |
| public static int filterReturnValue_intFilter(int b) { |
| return b + 1; |
| } |
| |
| public static void filterReturnValue_voidTarget() { |
| } |
| |
| public static int filterReturnValue_voidFilter() { |
| return 42; |
| } |
| |
| public static void testFilterReturnValue() throws Throwable { |
| // A target that returns a reference. |
| { |
| final MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "filterReturnValue_target", MethodType.methodType(String.class, int.class)); |
| final MethodHandle filter = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "filterReturnValue_filter", MethodType.methodType(boolean.class, String.class)); |
| |
| MethodHandle adapter = MethodHandles.filterReturnValue(target, filter); |
| |
| boolean value = (boolean) adapter.invoke((int) 42); |
| if (!value) { |
| fail("Unexpected value: " + value); |
| } |
| value = (boolean) adapter.invoke((int) 43); |
| if (value) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| // A target that returns a primitive. |
| { |
| final MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "filterReturnValue_intTarget", MethodType.methodType(int.class, String.class)); |
| final MethodHandle filter = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "filterReturnValue_intFilter", MethodType.methodType(int.class, int.class)); |
| |
| MethodHandle adapter = MethodHandles.filterReturnValue(target, filter); |
| |
| int value = (int) adapter.invoke("56"); |
| if (value != 57) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| // A target that returns void. |
| { |
| final MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "filterReturnValue_voidTarget", MethodType.methodType(void.class)); |
| final MethodHandle filter = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "filterReturnValue_voidFilter", MethodType.methodType(int.class)); |
| |
| MethodHandle adapter = MethodHandles.filterReturnValue(target, filter); |
| |
| int value = (int) adapter.invoke(); |
| if (value != 42) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| } |
| |
| public static void permuteArguments_callee(boolean a, byte b, char c, |
| short d, int e, long f, float g, double h) { |
| assertTrue(a); |
| assertEquals((byte) 'b', b); |
| assertEquals('c', c); |
| assertEquals((short) 56, d); |
| assertEquals(78, e); |
| assertEquals(97l, f); |
| assertEquals(98.0f, g); |
| assertEquals(97.0, h); |
| } |
| |
| public static void permuteArguments_boxingCallee(boolean a, Integer b) { |
| assertTrue(a); |
| assertEquals(Integer.valueOf(42), b); |
| } |
| |
| public static void testPermuteArguments() throws Throwable { |
| { |
| final MethodHandle target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "permuteArguments_callee", |
| MethodType.methodType(void.class, new Class<?>[]{ |
| boolean.class, byte.class, char.class, short.class, int.class, |
| long.class, float.class, double.class})); |
| |
| final MethodType newType = MethodType.methodType(void.class, new Class<?>[]{ |
| double.class, float.class, long.class, int.class, short.class, char.class, |
| byte.class, boolean.class}); |
| |
| final MethodHandle permutation = MethodHandles.permuteArguments( |
| target, newType, new int[]{7, 6, 5, 4, 3, 2, 1, 0}); |
| |
| permutation.invoke((double) 97.0, (float) 98.0f, (long) 97, 78, |
| (short) 56, 'c', (byte) 'b', (boolean) true); |
| |
| // The permutation array was not of the right length. |
| try { |
| MethodHandles.permuteArguments(target, newType, |
| new int[]{7}); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| |
| // The permutation array has an element that's out of bounds |
| // (there's no argument with idx == 8). |
| try { |
| MethodHandles.permuteArguments(target, newType, |
| new int[]{8, 6, 5, 4, 3, 2, 1, 0}); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| |
| // The permutation array maps to an incorrect type. |
| try { |
| MethodHandles.permuteArguments(target, newType, |
| new int[]{7, 7, 5, 4, 3, 2, 1, 0}); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| // Tests for reference arguments as well as permutations that |
| // repeat arguments. |
| { |
| final MethodHandle target = MethodHandles.lookup().findVirtual( |
| String.class, "concat", MethodType.methodType(String.class, String.class)); |
| |
| final MethodType newType = MethodType.methodType(String.class, String.class, |
| String.class); |
| |
| assertEquals("foobar", (String) target.invoke("foo", "bar")); |
| |
| MethodHandle permutation = MethodHandles.permuteArguments(target, |
| newType, new int[]{1, 0}); |
| assertEquals("barfoo", (String) permutation.invoke("foo", "bar")); |
| |
| permutation = MethodHandles.permuteArguments(target, newType, new int[]{0, 0}); |
| assertEquals("foofoo", (String) permutation.invoke("foo", "bar")); |
| |
| permutation = MethodHandles.permuteArguments(target, newType, new int[]{1, 1}); |
| assertEquals("barbar", (String) permutation.invoke("foo", "bar")); |
| } |
| |
| // Tests for boxing and unboxing. |
| { |
| final MethodHandle target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "permuteArguments_boxingCallee", |
| MethodType.methodType(void.class, new Class<?>[]{boolean.class, Integer.class})); |
| |
| final MethodType newType = MethodType.methodType(void.class, |
| new Class<?>[]{Integer.class, boolean.class}); |
| |
| MethodHandle permutation = MethodHandles.permuteArguments(target, |
| newType, new int[]{1, 0}); |
| |
| permutation.invoke(42, true); |
| permutation.invoke(42, Boolean.TRUE); |
| permutation.invoke(Integer.valueOf(42), true); |
| permutation.invoke(Integer.valueOf(42), Boolean.TRUE); |
| } |
| } |
| |
| private static Object returnBar() { |
| return "bar"; |
| } |
| |
| public static void testInvokers() throws Throwable { |
| final MethodType targetType = MethodType.methodType(String.class, String.class); |
| final MethodHandle target = MethodHandles.lookup().findVirtual( |
| String.class, "concat", targetType); |
| |
| MethodHandle invoker = MethodHandles.invoker(target.type()); |
| assertEquals("barbar", (String) invoker.invoke(target, "bar", "bar")); |
| assertEquals("barbar", (String) invoker.invoke(target, (Object) returnBar(), "bar")); |
| try { |
| String foo = (String) invoker.invoke(target, "bar", "bar", 24); |
| fail(); |
| } catch (WrongMethodTypeException expected) { |
| } |
| |
| MethodHandle exactInvoker = MethodHandles.exactInvoker(target.type()); |
| assertEquals("barbar", (String) exactInvoker.invoke(target, "bar", "bar")); |
| try { |
| String foo = (String) exactInvoker.invoke(target, (Object) returnBar(), "bar"); |
| fail(); |
| } catch (WrongMethodTypeException expected) { |
| } |
| try { |
| String foo = (String) exactInvoker.invoke(target, "bar", "bar", 24); |
| fail(); |
| } catch (WrongMethodTypeException expected) { |
| } |
| } |
| |
| public static int spreadReferences(String a, String b, String c) { |
| assertEquals("a", a); |
| assertEquals("b", b); |
| assertEquals("c", c); |
| return 42; |
| } |
| |
| public static int spreadReferences_Unbox(String a, int b) { |
| assertEquals("a", a); |
| assertEquals(43, b); |
| return 43; |
| } |
| |
| public static void testSpreaders_reference() throws Throwable { |
| MethodType methodType = MethodType.methodType(int.class, |
| new Class<?>[]{String.class, String.class, String.class}); |
| MethodHandle delegate = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "spreadReferences", methodType); |
| |
| // Basic checks on array lengths. |
| // |
| // Array size = 0 |
| MethodHandle mhAsSpreader = delegate.asSpreader(String[].class, 0); |
| int ret = (int) mhAsSpreader.invoke("a", "b", "c", new String[]{}); |
| assertEquals(42, ret); |
| // Array size = 1 |
| mhAsSpreader = delegate.asSpreader(String[].class, 1); |
| ret = (int) mhAsSpreader.invoke("a", "b", new String[]{"c"}); |
| assertEquals(42, ret); |
| // Array size = 2 |
| mhAsSpreader = delegate.asSpreader(String[].class, 2); |
| ret = (int) mhAsSpreader.invoke("a", new String[]{"b", "c"}); |
| assertEquals(42, ret); |
| // Array size = 3 |
| mhAsSpreader = delegate.asSpreader(String[].class, 3); |
| ret = (int) mhAsSpreader.invoke(new String[]{"a", "b", "c"}); |
| assertEquals(42, ret); |
| |
| // Exception case, array size = 4 is illegal. |
| try { |
| delegate.asSpreader(String[].class, 4); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| |
| // Exception case, calling with an arg of the wrong size. |
| // Array size = 3 |
| mhAsSpreader = delegate.asSpreader(String[].class, 3); |
| try { |
| ret = (int) mhAsSpreader.invoke(new String[]{"a", "b"}); |
| } catch (IllegalArgumentException expected) { |
| } |
| |
| // Various other hijinks, pass as Object[] arrays, Object etc. |
| mhAsSpreader = delegate.asSpreader(Object[].class, 2); |
| ret = (int) mhAsSpreader.invoke("a", new String[]{"b", "c"}); |
| assertEquals(42, ret); |
| |
| mhAsSpreader = delegate.asSpreader(Object[].class, 2); |
| ret = (int) mhAsSpreader.invoke("a", new Object[]{"b", "c"}); |
| assertEquals(42, ret); |
| |
| mhAsSpreader = delegate.asSpreader(Object[].class, 2); |
| ret = (int) mhAsSpreader.invoke("a", (Object) new Object[]{"b", "c"}); |
| assertEquals(42, ret); |
| |
| // Test implicit unboxing. |
| MethodType methodType2 = MethodType.methodType(int.class, |
| new Class<?>[]{String.class, int.class}); |
| MethodHandle delegate2 = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "spreadReferences_Unbox", methodType2); |
| |
| // .. with an Integer[] array. |
| mhAsSpreader = delegate2.asSpreader(Integer[].class, 1); |
| ret = (int) mhAsSpreader.invoke("a", new Integer[]{43}); |
| assertEquals(43, ret); |
| |
| // .. with an Integer[] array declared as an Object[] argument type. |
| mhAsSpreader = delegate2.asSpreader(Object[].class, 1); |
| ret = (int) mhAsSpreader.invoke("a", new Integer[]{43}); |
| assertEquals(43, ret); |
| |
| // .. with an Object[] array. |
| mhAsSpreader = delegate2.asSpreader(Object[].class, 1); |
| ret = (int) mhAsSpreader.invoke("a", new Object[]{Integer.valueOf(43)}); |
| assertEquals(43, ret); |
| |
| // -- Part 2-- |
| // Run a subset of these tests on MethodHandles.spreadInvoker, which only accepts |
| // a trailing argument type of Object[]. |
| MethodHandle spreadInvoker = MethodHandles.spreadInvoker(methodType2, 1); |
| ret = (int) spreadInvoker.invoke(delegate2, "a", new Object[]{Integer.valueOf(43)}); |
| assertEquals(43, ret); |
| |
| ret = (int) spreadInvoker.invoke(delegate2, "a", new Integer[]{43}); |
| assertEquals(43, ret); |
| |
| // NOTE: Annoyingly, the second argument here is leadingArgCount and not |
| // arrayLength. |
| spreadInvoker = MethodHandles.spreadInvoker(methodType, 3); |
| ret = (int) spreadInvoker.invoke(delegate, "a", "b", "c", new String[]{}); |
| assertEquals(42, ret); |
| |
| spreadInvoker = MethodHandles.spreadInvoker(methodType, 0); |
| ret = (int) spreadInvoker.invoke(delegate, new String[]{"a", "b", "c"}); |
| assertEquals(42, ret); |
| |
| // Exact invokes: Double check that the expected parameter type is |
| // Object[] and not T[]. |
| try { |
| spreadInvoker.invokeExact(delegate, new String[]{"a", "b", "c"}); |
| fail(); |
| } catch (WrongMethodTypeException expected) { |
| } |
| |
| ret = (int) spreadInvoker.invoke(delegate, new Object[]{"a", "b", "c"}); |
| assertEquals(42, ret); |
| } |
| |
| public static int spreadBoolean(String a, Boolean b, boolean c) { |
| assertEquals("a", a); |
| assertSame(Boolean.TRUE, b); |
| assertFalse(c); |
| |
| return 44; |
| } |
| |
| public static int spreadByte(String a, Byte b, byte c, |
| short d, int e, long f, float g, double h) { |
| assertEquals("a", a); |
| assertEquals(Byte.valueOf((byte) 1), b); |
| assertEquals((byte) 2, c); |
| assertEquals((short) 3, d); |
| assertEquals(4, e); |
| assertEquals(5l, f); |
| assertEquals(6.0f, g); |
| assertEquals(7.0, h); |
| |
| return 45; |
| } |
| |
| public static int spreadChar(String a, Character b, char c, |
| int d, long e, float f, double g) { |
| assertEquals("a", a); |
| assertEquals(Character.valueOf('1'), b); |
| assertEquals('2', c); |
| assertEquals((short) '3', d); |
| assertEquals('4', e); |
| assertEquals((float) '5', f); |
| assertEquals((double) '6', g); |
| |
| return 46; |
| } |
| |
| public static int spreadShort(String a, Short b, short c, |
| int d, long e, float f, double g) { |
| assertEquals("a", a); |
| assertEquals(Short.valueOf((short) 1), b); |
| assertEquals(2, c); |
| assertEquals(3, d); |
| assertEquals(4l, e); |
| assertEquals(5.0f, f); |
| assertEquals(6.0, g); |
| |
| return 47; |
| } |
| |
| public static int spreadInt(String a, Integer b, int c, |
| long d, float e, double f) { |
| assertEquals("a", a); |
| assertEquals(Integer.valueOf(1), b); |
| assertEquals(2, c); |
| assertEquals(3l, d); |
| assertEquals(4.0f, e); |
| assertEquals(5.0, f); |
| |
| return 48; |
| } |
| |
| public static int spreadLong(String a, Long b, long c, float d, double e) { |
| assertEquals("a", a); |
| assertEquals(Long.valueOf(1), b); |
| assertEquals(2l, c); |
| assertEquals(3.0f, d); |
| assertEquals(4.0, e); |
| |
| return 49; |
| } |
| |
| public static int spreadFloat(String a, Float b, float c, double d) { |
| assertEquals("a", a); |
| assertEquals(Float.valueOf(1.0f), b); |
| assertEquals(2.0f, c); |
| assertEquals(3.0, d); |
| |
| return 50; |
| } |
| |
| public static int spreadDouble(String a, Double b, double c) { |
| assertEquals("a", a); |
| assertEquals(Double.valueOf(1.0), b); |
| assertEquals(2.0, c); |
| |
| return 51; |
| } |
| |
| public static void testSpreaders_primitive() throws Throwable { |
| // boolean[] |
| // --------------------- |
| MethodType type = MethodType.methodType(int.class, |
| new Class<?>[]{String.class, Boolean.class, boolean.class}); |
| MethodHandle delegate = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "spreadBoolean", type); |
| |
| MethodHandle spreader = delegate.asSpreader(boolean[].class, 2); |
| int ret = (int) spreader.invokeExact("a", new boolean[]{true, false}); |
| assertEquals(44, ret); |
| ret = (int) spreader.invoke("a", new boolean[]{true, false}); |
| assertEquals(44, ret); |
| |
| // boolean can't be cast to String (the first argument to the method). |
| try { |
| delegate.asSpreader(boolean[].class, 3); |
| fail(); |
| } catch (WrongMethodTypeException expected) { |
| } |
| |
| // int can't be cast to boolean to supply the last argument to the method. |
| try { |
| delegate.asSpreader(int[].class, 1); |
| fail(); |
| } catch (WrongMethodTypeException expected) { |
| } |
| |
| // byte[] |
| // --------------------- |
| type = MethodType.methodType(int.class, |
| new Class<?>[]{ |
| String.class, Byte.class, byte.class, |
| short.class, int.class, long.class, |
| float.class, double.class}); |
| delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadByte", type); |
| |
| spreader = delegate.asSpreader(byte[].class, 7); |
| ret = (int) spreader.invokeExact("a", |
| new byte[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}); |
| assertEquals(45, ret); |
| ret = (int) spreader.invoke("a", |
| new byte[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}); |
| assertEquals(45, ret); |
| |
| // char[] |
| // --------------------- |
| type = MethodType.methodType(int.class, |
| new Class<?>[]{ |
| String.class, Character.class, char.class, |
| int.class, long.class, float.class, double.class}); |
| delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadChar", type); |
| |
| spreader = delegate.asSpreader(char[].class, 6); |
| ret = (int) spreader.invokeExact("a", |
| new char[]{'1', '2', '3', '4', '5', '6'}); |
| assertEquals(46, ret); |
| ret = (int) spreader.invokeExact("a", |
| new char[]{'1', '2', '3', '4', '5', '6'}); |
| assertEquals(46, ret); |
| |
| // short[] |
| // --------------------- |
| type = MethodType.methodType(int.class, |
| new Class<?>[]{ |
| String.class, Short.class, short.class, |
| int.class, long.class, float.class, double.class}); |
| delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadShort", type); |
| |
| spreader = delegate.asSpreader(short[].class, 6); |
| ret = (int) spreader.invokeExact("a", |
| new short[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6}); |
| assertEquals(47, ret); |
| ret = (int) spreader.invoke("a", |
| new short[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6}); |
| assertEquals(47, ret); |
| |
| // int[] |
| // --------------------- |
| type = MethodType.methodType(int.class, |
| new Class<?>[]{ |
| String.class, Integer.class, int.class, |
| long.class, float.class, double.class}); |
| delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadInt", type); |
| |
| spreader = delegate.asSpreader(int[].class, 5); |
| ret = (int) spreader.invokeExact("a", new int[]{1, 2, 3, 4, 5}); |
| assertEquals(48, ret); |
| ret = (int) spreader.invokeExact("a", new int[]{1, 2, 3, 4, 5}); |
| assertEquals(48, ret); |
| |
| // long[] |
| // --------------------- |
| type = MethodType.methodType(int.class, |
| new Class<?>[]{ |
| String.class, Long.class, long.class, float.class, double.class}); |
| delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadLong", type); |
| |
| spreader = delegate.asSpreader(long[].class, 4); |
| ret = (int) spreader.invokeExact("a", |
| new long[]{0x1, 0x2, 0x3, 0x4}); |
| assertEquals(49, ret); |
| ret = (int) spreader.invoke("a", |
| new long[]{0x1, 0x2, 0x3, 0x4}); |
| assertEquals(49, ret); |
| |
| // float[] |
| // --------------------- |
| type = MethodType.methodType(int.class, |
| new Class<?>[]{ |
| String.class, Float.class, float.class, double.class}); |
| delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadFloat", type); |
| |
| spreader = delegate.asSpreader(float[].class, 3); |
| ret = (int) spreader.invokeExact("a", |
| new float[]{1.0f, 2.0f, 3.0f}); |
| assertEquals(50, ret); |
| ret = (int) spreader.invokeExact("a", |
| new float[]{1.0f, 2.0f, 3.0f}); |
| assertEquals(50, ret); |
| |
| // double[] |
| // --------------------- |
| type = MethodType.methodType(int.class, |
| new Class<?>[]{String.class, Double.class, double.class}); |
| delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadDouble", type); |
| |
| spreader = delegate.asSpreader(double[].class, 2); |
| ret = (int) spreader.invokeExact("a", new double[]{1.0, 2.0}); |
| assertEquals(51, ret); |
| ret = (int) spreader.invokeExact("a", new double[]{1.0, 2.0}); |
| assertEquals(51, ret); |
| } |
| |
| public static void testInvokeWithArguments() throws Throwable { |
| MethodType methodType = MethodType.methodType(int.class, |
| new Class<?>[]{String.class, String.class, String.class}); |
| MethodHandle handle = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "spreadReferences", methodType); |
| |
| Object ret = handle.invokeWithArguments(new Object[]{"a", "b", "c"}); |
| assertEquals(42, (int) ret); |
| ret = handle.invokeWithArguments(new String[]{"a", "b", "c"}); |
| assertEquals(42, (int) ret); |
| |
| // Also test the versions that take a List<?> instead of an array. |
| ret = handle.invokeWithArguments(Arrays.asList(new Object[] {"a", "b", "c"})); |
| assertEquals(42, (int) ret); |
| ret = handle.invokeWithArguments(Arrays.asList(new String[]{"a", "b", "c"})); |
| assertEquals(42, (int) ret); |
| |
| // Pass in an array that's too small. Should throw an IAE. |
| try { |
| handle.invokeWithArguments(new Object[]{"a", "b"}); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } catch (WrongMethodTypeException expected) { |
| } |
| |
| try { |
| handle.invokeWithArguments(Arrays.asList(new Object[]{"a", "b"})); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } catch (WrongMethodTypeException expected) { |
| } |
| |
| |
| // Test implicit unboxing. |
| MethodType methodType2 = MethodType.methodType(int.class, |
| new Class<?>[]{String.class, int.class}); |
| MethodHandle handle2 = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "spreadReferences_Unbox", methodType2); |
| |
| ret = (int) handle2.invokeWithArguments(new Object[]{"a", 43}); |
| assertEquals(43, (int) ret); |
| } |
| |
| public static int collectBoolean(String a, boolean[] b) { |
| assertEquals("a", a); |
| assertTrue(b[0]); |
| assertFalse(b[1]); |
| |
| return 44; |
| } |
| |
| public static int collectByte(String a, byte[] b) { |
| assertEquals("a", a); |
| assertEquals((byte) 1, b[0]); |
| assertEquals((byte) 2, b[1]); |
| return 45; |
| } |
| |
| public static int collectChar(String a, char[] b) { |
| assertEquals("a", a); |
| assertEquals('a', b[0]); |
| assertEquals('b', b[1]); |
| return 46; |
| } |
| |
| public static int collectShort(String a, short[] b) { |
| assertEquals("a", a); |
| assertEquals((short) 3, b[0]); |
| assertEquals((short) 4, b[1]); |
| |
| return 47; |
| } |
| |
| public static int collectInt(String a, int[] b) { |
| assertEquals("a", a); |
| assertEquals(42, b[0]); |
| assertEquals(43, b[1]); |
| |
| return 48; |
| } |
| |
| public static int collectLong(String a, long[] b) { |
| assertEquals("a", a); |
| assertEquals(100l, b[0]); |
| assertEquals(99l, b[1]); |
| |
| return 49; |
| } |
| |
| public static int collectFloat(String a, float[] b) { |
| assertEquals("a", a); |
| assertEquals(8.9f, b[0]); |
| assertEquals(9.1f, b[1]); |
| |
| return 50; |
| } |
| |
| public static int collectDouble(String a, double[] b) { |
| assertEquals("a", a); |
| assertEquals(6.7, b[0]); |
| assertEquals(7.8, b[1]); |
| |
| return 51; |
| } |
| |
| public static int collectCharSequence(String a, CharSequence[] b) { |
| assertEquals("a", a); |
| assertEquals("b", b[0]); |
| assertEquals("c", b[1]); |
| return 99; |
| } |
| |
| public static void testAsCollector() throws Throwable { |
| // Reference arrays. |
| // ------------------- |
| MethodHandle trailingRef = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "collectCharSequence", |
| MethodType.methodType(int.class, String.class, CharSequence[].class)); |
| |
| // int[] is not convertible to CharSequence[].class. |
| try { |
| trailingRef.asCollector(int[].class, 1); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| |
| // Object[] is not convertible to CharSequence[].class. |
| try { |
| trailingRef.asCollector(Object[].class, 1); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| |
| // String[].class is convertible to CharSequence.class |
| MethodHandle collector = trailingRef.asCollector(String[].class, 2); |
| assertEquals(99, (int) collector.invoke("a", "b", "c")); |
| |
| // Too few arguments should fail with a WMTE. |
| try { |
| collector.invoke("a", "b"); |
| fail(); |
| } catch (WrongMethodTypeException expected) { |
| } |
| |
| // Too many arguments should fail with a WMTE. |
| try { |
| collector.invoke("a", "b", "c", "d"); |
| fail(); |
| } catch (WrongMethodTypeException expected) { |
| } |
| |
| // Sanity checks on other array types. |
| |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "collectBoolean", |
| MethodType.methodType(int.class, String.class, boolean[].class)); |
| assertEquals(44, (int) target.asCollector(boolean[].class, 2).invoke("a", true, false)); |
| |
| target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectByte", |
| MethodType.methodType(int.class, String.class, byte[].class)); |
| assertEquals(45, (int) target.asCollector(byte[].class, 2).invoke("a", (byte) 1, (byte) 2)); |
| |
| target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectChar", |
| MethodType.methodType(int.class, String.class, char[].class)); |
| assertEquals(46, (int) target.asCollector(char[].class, 2).invoke("a", 'a', 'b')); |
| |
| target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectShort", |
| MethodType.methodType(int.class, String.class, short[].class)); |
| assertEquals(47, (int) target.asCollector(short[].class, 2).invoke("a", (short) 3, (short) 4)); |
| |
| target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectInt", |
| MethodType.methodType(int.class, String.class, int[].class)); |
| assertEquals(48, (int) target.asCollector(int[].class, 2).invoke("a", 42, 43)); |
| |
| target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectLong", |
| MethodType.methodType(int.class, String.class, long[].class)); |
| assertEquals(49, (int) target.asCollector(long[].class, 2).invoke("a", 100, 99)); |
| |
| target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectFloat", |
| MethodType.methodType(int.class, String.class, float[].class)); |
| assertEquals(50, (int) target.asCollector(float[].class, 2).invoke("a", 8.9f, 9.1f)); |
| |
| target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectDouble", |
| MethodType.methodType(int.class, String.class, double[].class)); |
| assertEquals(51, (int) target.asCollector(double[].class, 2).invoke("a", 6.7, 7.8)); |
| } |
| |
| public static String filter1(char a) { |
| return String.valueOf(a); |
| } |
| |
| public static char filter2(String b) { |
| return b.charAt(0); |
| } |
| |
| public static String badFilter1(char a, char b) { |
| return "bad"; |
| } |
| |
| public static int filterTarget(String a, char b, String c, char d) { |
| assertEquals("a", a); |
| assertEquals('b', b); |
| assertEquals("c", c); |
| assertEquals('d', d); |
| return 56; |
| } |
| |
| public static void testFilterArguments() throws Throwable { |
| MethodHandle filter1 = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "filter1", MethodType.methodType(String.class, char.class)); |
| MethodHandle filter2 = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "filter2", MethodType.methodType(char.class, String.class)); |
| |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "filterTarget", MethodType.methodType(int.class, |
| String.class, char.class, String.class, char.class)); |
| |
| // In all the cases below, the values printed will be 'a', 'b', 'c', 'd'. |
| |
| // Filter arguments [0, 1] - all other arguments are passed through |
| // as is. |
| MethodHandle adapter = MethodHandles.filterArguments( |
| target, 0, filter1, filter2); |
| assertEquals(56, (int) adapter.invokeExact('a', "bXXXX", "c", 'd')); |
| |
| // Filter arguments [1, 2]. |
| adapter = MethodHandles.filterArguments(target, 1, filter2, filter1); |
| assertEquals(56, (int) adapter.invokeExact("a", "bXXXX", 'c', 'd')); |
| |
| // Filter arguments [2, 3]. |
| adapter = MethodHandles.filterArguments(target, 2, filter1, filter2); |
| assertEquals(56, (int) adapter.invokeExact("a", 'b', 'c', "dXXXXX")); |
| |
| // Try out a few error cases : |
| |
| // The return types of the filter doesn't align with the expected argument |
| // type of the target. |
| try { |
| adapter = MethodHandles.filterArguments(target, 2, filter2, filter1); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| |
| // There are more filters than arguments. |
| try { |
| adapter = MethodHandles.filterArguments(target, 3, filter2, filter1); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| |
| // We pass in an obviously bogus position. |
| try { |
| adapter = MethodHandles.filterArguments(target, -1, filter2, filter1); |
| fail(); |
| } catch (ArrayIndexOutOfBoundsException expected) { |
| } |
| |
| // We pass in a function that has more than one argument. |
| MethodHandle badFilter1 = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "badFilter1", |
| MethodType.methodType(String.class, char.class, char.class)); |
| |
| try { |
| adapter = MethodHandles.filterArguments(target, 0, badFilter1, filter2); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| static void voidFilter(char a, char b) { |
| } |
| |
| static String filter(char a, char b) { |
| return String.valueOf(a) + "+" + b; |
| } |
| |
| static char badFilter(char a, char b) { |
| return 0; |
| } |
| |
| static String target(String a, String b, String c) { |
| return ("a: " + a + ", b: " + b + ", c: " + c); |
| } |
| |
| public static void testCollectArguments() throws Throwable { |
| // Test non-void filters. |
| MethodHandle filter = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "filter", |
| MethodType.methodType(String.class, char.class, char.class)); |
| |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "target", |
| MethodType.methodType(String.class, String.class, String.class, String.class)); |
| |
| // Filter at position 0. |
| MethodHandle adapter = MethodHandles.collectArguments(target, 0, filter); |
| assertEquals("a: a+b, b: c, c: d", |
| (String) adapter.invokeExact('a', 'b', "c", "d")); |
| |
| // Filter at position 1. |
| adapter = MethodHandles.collectArguments(target, 1, filter); |
| assertEquals("a: a, b: b+c, c: d", |
| (String) adapter.invokeExact("a", 'b', 'c', "d")); |
| |
| // Filter at position 2. |
| adapter = MethodHandles.collectArguments(target, 2, filter); |
| assertEquals("a: a, b: b, c: c+d", |
| (String) adapter.invokeExact("a", "b", 'c', 'd')); |
| |
| // Test void filters. Note that we're passing in one more argument |
| // than usual because the filter returns nothing - we have to invoke with |
| // the full set of filter args and the full set of target args. |
| filter = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "voidFilter", |
| MethodType.methodType(void.class, char.class, char.class)); |
| adapter = MethodHandles.collectArguments(target, 0, filter); |
| assertEquals("a: a, b: b, c: c", |
| (String) adapter.invokeExact('a', 'b', "a", "b", "c")); |
| |
| adapter = MethodHandles.collectArguments(target, 1, filter); |
| assertEquals("a: a, b: b, c: c", |
| (String) adapter.invokeExact("a", 'a', 'b', "b", "c")); |
| |
| // Test out a few failure cases. |
| filter = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "filter", |
| MethodType.methodType(String.class, char.class, char.class)); |
| |
| // Bogus filter position. |
| try { |
| adapter = MethodHandles.collectArguments(target, 3, filter); |
| fail(); |
| } catch (IndexOutOfBoundsException expected) { |
| } |
| |
| // Mismatch in filter return type. |
| filter = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "badFilter", |
| MethodType.methodType(char.class, char.class, char.class)); |
| try { |
| adapter = MethodHandles.collectArguments(target, 0, filter); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| static int insertReceiver(String a, int b, Integer c, String d) { |
| assertEquals("foo", a); |
| assertEquals(56, b); |
| assertEquals(Integer.valueOf(57), c); |
| assertEquals("bar", d); |
| |
| return 73; |
| } |
| |
| public static void testInsertArguments() throws Throwable { |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "insertReceiver", |
| MethodType.methodType(int.class, |
| String.class, int.class, Integer.class, String.class)); |
| |
| // Basic single element array inserted at position 0. |
| MethodHandle adapter = MethodHandles.insertArguments( |
| target, 0, new Object[]{"foo"}); |
| assertEquals(73, (int) adapter.invokeExact(56, Integer.valueOf(57), "bar")); |
| |
| // Exercise unboxing. |
| adapter = MethodHandles.insertArguments( |
| target, 1, new Object[]{Integer.valueOf(56), 57}); |
| assertEquals(73, (int) adapter.invokeExact("foo", "bar")); |
| |
| // Exercise a widening conversion. |
| adapter = MethodHandles.insertArguments( |
| target, 1, new Object[]{(short) 56, Integer.valueOf(57)}); |
| assertEquals(73, (int) adapter.invokeExact("foo", "bar")); |
| |
| // Insert an argument at the last position. |
| adapter = MethodHandles.insertArguments( |
| target, 3, new Object[]{"bar"}); |
| assertEquals(73, (int) adapter.invokeExact("foo", 56, Integer.valueOf(57))); |
| |
| // Exercise a few error cases. |
| |
| // A reference type that can't be cast to another reference type. |
| try { |
| MethodHandles.insertArguments(target, 3, new Object[]{new Object()}); |
| fail(); |
| } catch (ClassCastException expected) { |
| } |
| |
| // A boxed type that can't be unboxed correctly. |
| try { |
| MethodHandles.insertArguments(target, 1, new Object[]{Long.valueOf(56)}); |
| fail(); |
| } catch (ClassCastException expected) { |
| } |
| } |
| |
| public static String foldFilter(char a, char b) { |
| return String.valueOf(a) + "+" + b; |
| } |
| |
| public static void voidFoldFilter(String e, char a, char b) { |
| assertEquals("a", e); |
| assertEquals('c', a); |
| assertEquals('d', b); |
| } |
| |
| public static String foldTarget(String a, char b, char c, String d) { |
| return ("a: " + a + " ,b:" + b + " ,c:" + c + " ,d:" + d); |
| } |
| |
| public static void mismatchedVoidFilter(Integer a) { |
| } |
| |
| public static Integer mismatchedNonVoidFilter(char a, char b) { |
| return null; |
| } |
| |
| public static void testFoldArguments() throws Throwable { |
| // Test non-void filters. |
| MethodHandle filter = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "foldFilter", |
| MethodType.methodType(String.class, char.class, char.class)); |
| |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "foldTarget", |
| MethodType.methodType(String.class, String.class, |
| char.class, char.class, String.class)); |
| |
| // Folder with a non-void type. |
| MethodHandle adapter = MethodHandles.foldArguments(target, filter); |
| assertEquals("a: c+d ,b:c ,c:d ,d:e", |
| (String) adapter.invokeExact('c', 'd', "e")); |
| |
| // Folder with a void type. |
| filter = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "voidFoldFilter", |
| MethodType.methodType(void.class, String.class, char.class, char.class)); |
| adapter = MethodHandles.foldArguments(target, filter); |
| assertEquals("a: a ,b:c ,c:d ,d:e", |
| (String) adapter.invokeExact("a", 'c', 'd', "e")); |
| |
| // Test a few erroneous cases. |
| |
| filter = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "mismatchedVoidFilter", |
| MethodType.methodType(void.class, Integer.class)); |
| try { |
| adapter = MethodHandles.foldArguments(target, filter); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| |
| filter = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "mismatchedNonVoidFilter", |
| MethodType.methodType(Integer.class, char.class, char.class)); |
| try { |
| adapter = MethodHandles.foldArguments(target, filter); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| // An exception thrown on worker threads and re-thrown on the main thread. |
| static Throwable workerException = null; |
| |
| private static void invokeMultiThreaded(final MethodHandle mh) throws Throwable { |
| // Create enough worker threads to be oversubscribed in bid to force some parallelism. |
| final int threadCount = Runtime.getRuntime().availableProcessors() + 1; |
| final Thread threads [] = new Thread [threadCount]; |
| |
| // Launch worker threads and iterate invoking method handle. |
| for (int i = 0; i < threadCount; ++i) { |
| threads[i] = new Thread(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| for (int j = 0; j < TEST_THREAD_ITERATIONS; ++j) { |
| mh.invoke(); |
| } |
| } catch (Throwable t) { |
| workerException = t; |
| fail("Unexpected exception " + workerException); |
| } |
| }}); |
| threads[i].start(); |
| } |
| |
| // Wait for completion |
| for (int i = 0; i < threadCount; ++i) { |
| threads[i].join(); |
| } |
| |
| // Fail on main thread to avoid test appearing to complete successfully. |
| Throwable t = workerException; |
| workerException = null; |
| if (t != null) { |
| throw t; |
| } |
| } |
| |
| public static void testDropInsertArgumentsMultithreaded() throws Throwable { |
| MethodHandle delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "dropArguments_delegate", |
| MethodType.methodType(void.class, new Class<?>[]{String.class, long.class})); |
| MethodHandle mh = MethodHandles.dropArguments(delegate, 0, int.class, Object.class); |
| mh = MethodHandles.insertArguments(mh, 0, 3333, "bogon", "foo", 42); |
| invokeMultiThreaded(mh); |
| } |
| |
| private static void exceptionHandler_delegate(NumberFormatException e, int x, int y, long z) |
| throws Throwable { |
| assertEquals(e.getClass(), NumberFormatException.class); |
| assertEquals(e.getMessage(), "fake"); |
| assertEquals(x, 66); |
| assertEquals(y, 51); |
| assertEquals(z, 20000000000l); |
| } |
| |
| public static void testThrowCatchExceptionMultiThreaded() throws Throwable { |
| MethodHandle thrower = MethodHandles.throwException(void.class, |
| NumberFormatException.class); |
| thrower = MethodHandles.dropArguments(thrower, 0, int.class, int.class, long.class); |
| MethodHandle handler = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "exceptionHandler_delegate", |
| MethodType.methodType(void.class, NumberFormatException.class, |
| int.class, int.class, long.class)); |
| MethodHandle catcher = |
| MethodHandles.catchException(thrower, NumberFormatException.class, handler); |
| MethodHandle caller = MethodHandles.insertArguments(catcher, 0, 66, 51, 20000000000l, |
| new NumberFormatException("fake")); |
| invokeMultiThreaded(caller); |
| } |
| |
| private static void testTargetAndFallback_delegate(MethodHandle mh) throws Throwable { |
| String actual = (String) mh.invoke("target", 42, 56); |
| assertEquals("target", actual); |
| actual = (String) mh.invoke("blah", 41, 56); |
| assertEquals("fallback", actual); |
| } |
| |
| public static void testGuardWithTestMultiThreaded() throws Throwable { |
| MethodHandle test = |
| MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "testGuardWithTest_test", |
| MethodType.methodType(boolean.class, |
| new Class<?>[]{String.class, |
| long.class})); |
| final MethodType type = MethodType.methodType(String.class, |
| new Class<?>[]{String.class, long.class, int.class}); |
| final MethodHandle target = |
| MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "testGuardWithTest_target", type); |
| final MethodHandle fallback = |
| MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, |
| "testGuardWithTest_fallback", type); |
| MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback); |
| MethodHandle tester = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, |
| "testTargetAndFallback_delegate", |
| MethodType.methodType(void.class, MethodHandle.class)); |
| invokeMultiThreaded(MethodHandles.insertArguments(tester, 0, adapter)); |
| } |
| |
| private static void arrayElementSetterGetter_delegate(MethodHandle getter, |
| MethodHandle setter, |
| int [] values) |
| throws Throwable{ |
| for (int i = 0; i < values.length; ++i) { |
| int value = i * 13; |
| setter.invoke(values, i, value); |
| assertEquals(values[i], value); |
| assertEquals(getter.invoke(values, i), values[i]); |
| } |
| } |
| |
| public static void testReferenceArrayGetterMultiThreaded() throws Throwable { |
| MethodHandle getter = MethodHandles.arrayElementGetter(int[].class); |
| MethodHandle setter = MethodHandles.arrayElementSetter(int[].class); |
| MethodHandle mh = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, |
| "arrayElementSetterGetter_delegate", |
| MethodType.methodType(void.class, MethodHandle.class, MethodHandle.class, int[].class)); |
| mh = MethodHandles.insertArguments(mh, 0, getter, setter, |
| new int[] { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23 }); |
| invokeMultiThreaded(mh); |
| } |
| |
| private static void checkConstant_delegate(MethodHandle mh, double value) throws Throwable { |
| assertEquals(mh.invoke(), value); |
| } |
| |
| public static void testConstantMultithreaded() throws Throwable { |
| final double value = 7.77e77; |
| MethodHandle constant = MethodHandles.constant(double.class, value); |
| MethodHandle mh = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "checkConstant_delegate", |
| MethodType.methodType(void.class, MethodHandle.class, double.class)); |
| mh = MethodHandles.insertArguments(mh, 0, constant, value); |
| invokeMultiThreaded(mh); |
| } |
| |
| private static void checkIdentity_delegate(MethodHandle mh, char value) throws Throwable { |
| assertEquals(mh.invoke(value), value); |
| } |
| |
| public static void testIdentityMultiThreaded() throws Throwable { |
| final char value = 'z'; |
| MethodHandle identity = MethodHandles.identity(char.class); |
| MethodHandle mh = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "checkIdentity_delegate", |
| MethodType.methodType(void.class, MethodHandle.class, char.class)); |
| mh = MethodHandles.insertArguments(mh, 0, identity, value); |
| invokeMultiThreaded(mh); |
| } |
| |
| private static int multiplyByTwo(int x) { return x * 2; } |
| private static int divideByTwo(int x) { return x / 2; } |
| private static void assertMethodHandleInvokeEquals(MethodHandle mh, int value) throws Throwable{ |
| assertEquals(mh.invoke(value), value); |
| } |
| |
| public static void testFilterReturnValueMultiThreaded() throws Throwable { |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "multiplyByTwo", |
| MethodType.methodType(int.class, int.class)); |
| MethodHandle filter = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "divideByTwo", |
| MethodType.methodType(int.class, int.class)); |
| MethodHandle filtered = MethodHandles.filterReturnValue(target, filter); |
| assertEquals(filtered.invoke(33), 33); |
| MethodHandle mh = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "assertMethodHandleInvokeEquals", |
| MethodType.methodType(void.class, MethodHandle.class, int.class)); |
| invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, filtered, 77)); |
| } |
| |
| public static void compareStringAndFloat(String s, float f) { |
| assertEquals(s, Float.toString(f)); |
| } |
| |
| public static void testPermuteArgumentsMultiThreaded() throws Throwable { |
| MethodHandle mh = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "compareStringAndFloat", |
| MethodType.methodType(void.class, String.class, float.class)); |
| mh = MethodHandles.permuteArguments( |
| mh, MethodType.methodType(void.class, float.class, String.class), 1, 0); |
| invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, 2.22f, "2.22")); |
| } |
| |
| public static void testSpreadInvokerMultiThreaded() throws Throwable { |
| MethodType methodType = MethodType.methodType( |
| int.class, new Class<?>[]{String.class, String.class, String.class}); |
| MethodHandle delegate = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "spreadReferences", methodType); |
| MethodHandle mh = delegate.asSpreader(String[].class, 3); |
| mh = MethodHandles.insertArguments(mh, 0, new Object[] { new String [] { "a", "b", "c" }}); |
| invokeMultiThreaded(mh); |
| } |
| |
| public static void testCollectorMultiThreaded() throws Throwable { |
| MethodHandle trailingRef = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "collectCharSequence", |
| MethodType.methodType(int.class, String.class, CharSequence[].class)); |
| MethodHandle mh = trailingRef.asCollector(String[].class, 2); |
| mh = MethodHandles.insertArguments(mh, 0, "a", "b", "c"); |
| invokeMultiThreaded(mh); |
| } |
| |
| public static void testFilterArgumentsMultiThreaded() throws Throwable { |
| MethodHandle filter1 = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "filter1", |
| MethodType.methodType(String.class, char.class)); |
| MethodHandle filter2 = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "filter2", |
| MethodType.methodType(char.class, String.class)); |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "filterTarget", |
| MethodType.methodType(int.class, String.class, char.class, String.class, char.class)); |
| MethodHandle adapter = MethodHandles.filterArguments(target, 2, filter1, filter2); |
| invokeMultiThreaded(MethodHandles.insertArguments(adapter, 0, "a", 'b', 'c', "dXXXXX")); |
| } |
| |
| private static void checkStringResult_delegate(MethodHandle mh, |
| String expected) throws Throwable { |
| assertEquals(mh.invoke(), expected); |
| } |
| |
| public static void testCollectArgumentsMultiThreaded() throws Throwable { |
| MethodHandle filter = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "filter", |
| MethodType.methodType(String.class, char.class, char.class)); |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "target", |
| MethodType.methodType(String.class, String.class, String.class, String.class)); |
| MethodHandle collect = MethodHandles.collectArguments(target, 2, filter); |
| collect = MethodHandles.insertArguments(collect, 0, "a", "b", 'c', 'd'); |
| MethodHandle mh = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "checkStringResult_delegate", |
| MethodType.methodType(void.class, MethodHandle.class, String.class)); |
| invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, collect, "a: a, b: b, c: c+d")); |
| } |
| |
| public static void testFoldArgumentsMultiThreaded() throws Throwable { |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "foldTarget", |
| MethodType.methodType(String.class, String.class, |
| char.class, char.class, String.class)); |
| MethodHandle filter = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "foldFilter", |
| MethodType.methodType(String.class, char.class, char.class)); |
| MethodHandle adapter = MethodHandles.foldArguments(target, filter); |
| adapter = MethodHandles.insertArguments(adapter, 0, 'c', 'd', "e"); |
| MethodHandle mh = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "checkStringResult_delegate", |
| MethodType.methodType(void.class, MethodHandle.class, String.class)); |
| invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, adapter, "a: c+d ,b:c ,c:d ,d:e")); |
| } |
| |
| private static void checkBooleanCast_delegate(boolean expected, boolean z, boolean b, |
| boolean c, boolean s, boolean i, boolean j, |
| boolean f, boolean d, boolean l) { |
| assertEquals(expected, z); |
| assertEquals(expected, b); |
| assertEquals(expected, c); |
| assertEquals(expected, s); |
| assertEquals(expected, i); |
| assertEquals(expected, j); |
| assertEquals(expected, f); |
| assertEquals(expected, d); |
| assertEquals(expected, l); |
| } |
| |
| private static void checkByteCast_delegate(byte expected, byte z, byte b, byte c, byte s, |
| byte i, byte j, byte f, byte d, byte l) { |
| int mask = 0xff; |
| assertEquals(expected & 1, z); |
| assertEquals(expected, b); |
| assertEquals(expected, c & mask); |
| assertEquals(expected, s & mask); |
| assertEquals(expected, i & mask); |
| assertEquals(expected, j & mask); |
| assertEquals(expected, f & mask); |
| assertEquals(expected, d & mask); |
| assertEquals(expected, l); |
| } |
| |
| private static void checkCharCast_delegate(char expected, char z, char b, char c, char s, |
| char i, char j, char f, char d, char l) { |
| int mask = 0xffff; |
| assertEquals(expected & 1, z); |
| assertEquals(expected & 0xff, b); |
| assertEquals(expected, c); |
| assertEquals(expected, s & mask); |
| assertEquals(expected, i & mask); |
| assertEquals(expected, j & mask); |
| assertEquals(expected, f & mask); |
| assertEquals(expected, d & mask); |
| assertEquals(expected, l); |
| } |
| |
| private static void checkShortCast_delegate(short expected, short z, short b, short c, short s, |
| short i, short j, short f, short d, short l) { |
| int mask = 0xffff; |
| assertEquals(expected & 1, z); |
| assertEquals(expected & 0xff, b); |
| assertEquals(expected, c & mask); |
| assertEquals(expected, s); |
| assertEquals(expected, i & mask); |
| assertEquals(expected, j & mask); |
| assertEquals(expected, f & mask); |
| assertEquals(expected, d & mask); |
| assertEquals(expected, l); |
| } |
| |
| private static void checkIntCast_delegate(int expected, int z, int b, int c, int s, int i, |
| int j, int f, int d, int l) { |
| int mask = 0xffffffff; |
| assertEquals(expected & 1, z); |
| assertEquals(expected & 0xff, b); |
| assertEquals(expected & 0xffff, c); |
| assertEquals(expected & 0xffff, s); |
| assertEquals(expected, i & mask); |
| assertEquals(expected, j & mask); |
| assertEquals(expected, f & mask); |
| assertEquals(expected, d & mask); |
| assertEquals(expected, l); |
| } |
| |
| private static void checkLongCast_delegate(long expected, long z, long b, long c, long s, |
| long i, long j, long f, long d, long l) { |
| long mask = 0xffffffffl; |
| assertEquals(expected & 1, z); |
| assertEquals(expected & 0xff, b); |
| assertEquals(expected & 0xffff, c); |
| assertEquals(expected & 0xffff, s); |
| assertEquals(expected & mask, i & mask); |
| assertEquals(expected, j); |
| assertEquals(expected & mask, f & mask); |
| assertEquals(expected, d); |
| assertEquals(expected, l); |
| } |
| |
| private static void checkFloatCast_delegate(float expected, float z, float b, float c, float s, |
| float i, float j, float f, float d, float l) { |
| assertEquals((byte) expected & 1, (int) z); |
| assertEquals((byte) expected, (int) b & 0xff); |
| assertEquals((char) expected & 0xffff, (int) c& 0xffff); |
| assertEquals((short) expected & 0xffff, (int) s & 0xffff); |
| assertEquals((int) expected, (int) i); |
| assertEquals((long) expected, (long) j); |
| assertEquals(expected, f); |
| assertEquals(expected, d); |
| assertEquals(expected, l); |
| } |
| |
| private static void checkDoubleCast_delegate(double expected, double z, double b, double c, |
| double s, double i, double j, double f, double d, |
| double l) { |
| assertEquals((byte) expected & 1, (int) z); |
| assertEquals((byte) expected & 0xff, (int) b & 0xff); |
| assertEquals((int) expected & 0xffff, (int) c & 0xffff); |
| assertEquals((int) expected & 0xffff, (int) s & 0xffff); |
| assertEquals((int) expected, (int) i); |
| assertEquals((long) expected, (long) j); |
| assertEquals((float) expected, (float) f); |
| assertEquals(expected, d); |
| assertEquals(expected, l); |
| } |
| |
| private static void checkBoxingCasts_delegate(boolean expected, Boolean z, Byte b, Character c, |
| Short s, Integer i, Long j, Float f, Double d) { |
| int v = expected ? 1 : 0; |
| assertEquals(Boolean.valueOf(expected ? true : false), z); |
| assertEquals(Byte.valueOf((byte) v), b); |
| assertEquals(Character.valueOf((char) v), c); |
| assertEquals(Short.valueOf((short) v), s); |
| assertEquals(Integer.valueOf(v), i); |
| assertEquals(Long.valueOf(v), j); |
| assertEquals(Float.valueOf(v), f); |
| assertEquals(Double.valueOf(v), d); |
| } |
| |
| public static void testExplicitCastArguments() throws Throwable { |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "checkBooleanCast_delegate", |
| MethodType.methodType(void.class, boolean.class, boolean.class, boolean.class, |
| boolean.class, boolean.class, boolean.class, boolean.class, |
| boolean.class, boolean.class, boolean.class)); |
| MethodHandle mh = MethodHandles.explicitCastArguments( |
| target, MethodType.methodType(void.class, boolean.class, boolean.class, byte.class, |
| char.class, short.class, int.class, long.class, |
| float.class, double.class, Boolean.class)); |
| mh.invokeExact(false, false, (byte) 0, (char) 0, (short) 0, 0, 0l, 0.0f, 0.0, |
| Boolean.valueOf(false)); |
| mh.invokeExact(false, false, (byte) 2, (char) 2, (short) 2, 2, 2l, 2.2f, 2.2, |
| Boolean.valueOf(false)); |
| mh.invokeExact(true, true, (byte) 1, (char) 1, (short) 1, 1, 1l, 1.0f, 1.0, |
| Boolean.valueOf(true)); |
| mh.invokeExact(true, true, (byte) 51, (char) 51, (short) 51, 51, 51l, 51.0f, 51.0, |
| Boolean.valueOf(true)); |
| MethodHandles.explicitCastArguments( |
| target, MethodType.methodType(void.class, boolean.class, boolean.class, byte.class, |
| char.class, short.class, int.class, long.class, |
| float.class, double.class, String.class)); |
| try { |
| mh.invoke(true, true, (byte) 51, (char) 51, (short) 51, 51, 51l, 51.0f, 51.0, |
| "ClassCastException here!"); |
| fail(); |
| } catch (ClassCastException e) { |
| } |
| |
| target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "checkByteCast_delegate", |
| MethodType.methodType(void.class, byte.class, byte.class, byte.class, |
| byte.class, byte.class, byte.class, byte.class, |
| byte.class, byte.class, byte.class)); |
| mh = MethodHandles.explicitCastArguments( |
| target, MethodType.methodType(void.class, byte.class, boolean.class, byte.class, |
| char.class, short.class, int.class, long.class, |
| float.class, double.class, Byte.class)); |
| mh.invokeExact((byte) 0x5a, false, (byte) 0x5a, (char) 0x5a5a, (short) 0x5a5a, (int) 0x5a5a, |
| (long) 0x5a5a, (float) 0x5a5a, (double) 0x5a5a, Byte.valueOf((byte) 0x5a)); |
| try { |
| mh.invoke((byte) 0x5a, false, (byte) 0x5a, (char) 0x5a5a, (short) 0x5a5a, (int) 0x5a5a, |
| (long) 0x5a5a, (float) 0x5a5a, (double) 0x5a5a, |
| Short.valueOf((short) 0x5a5a)); |
| fail(); |
| } catch (ClassCastException e) { |
| } |
| |
| target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "checkCharCast_delegate", |
| MethodType.methodType(void.class, char.class, char.class, char.class, |
| char.class, char.class, char.class, char.class, |
| char.class, char.class, char.class)); |
| mh = MethodHandles.explicitCastArguments( |
| target, MethodType.methodType(void.class, char.class, boolean.class, byte.class, |
| char.class, short.class, int.class, long.class, |
| float.class, double.class, Character.class)); |
| mh.invokeExact((char) 0x5555, true, (byte) 0x5555, (char) 0x5555, (short) 0x5555, |
| (int) 0x5555, (long) 0x5555, (float) 0x5555, (double) 0x5555, |
| Character.valueOf((char) 0x5555)); |
| try { |
| mh.invoke((char) 0x5555, false, (byte) 0x5555, (char) 0x5555, (short) 0x5555, |
| (int) 0x5555, (long) 0x5555, (float) 0x5555, (double) 0x5555, |
| Integer.valueOf((int) 0x5555)); |
| fail(); |
| } catch (ClassCastException e) { |
| } |
| |
| target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "checkShortCast_delegate", |
| MethodType.methodType(void.class, short.class, short.class, short.class, |
| short.class, short.class, short.class, short.class, |
| short.class, short.class, short.class)); |
| mh = MethodHandles.explicitCastArguments( |
| target, MethodType.methodType(void.class, short.class, boolean.class, byte.class, |
| char.class, short.class, int.class, long.class, |
| float.class, double.class, Short.class)); |
| mh.invokeExact((short) 0x3773, true, (byte) 0x3773, (char) 0x3773, (short) 0x3773, |
| (int) 0x3773, (long) 0x3773, (float) 0x3773, (double) 0x3773, |
| Short.valueOf((short) 0x3773)); |
| try { |
| mh.invoke((short) 0x3773, true, (byte) 0x3773, (char) 0x3773, (short) 0x3773, |
| (int) 0x3773, (long) 0x3773, (float) 0x3773, (double) 0x3773, |
| Long.valueOf((long) 0x3773)); |
| fail(); |
| } catch (ClassCastException e) { |
| } |
| |
| target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "checkIntCast_delegate", |
| MethodType.methodType(void.class, int.class, int.class, int.class, |
| int.class, int.class, int.class, int.class, |
| int.class, int.class, int.class)); |
| mh = MethodHandles.explicitCastArguments( |
| target, MethodType.methodType(void.class, int.class, boolean.class, byte.class, |
| char.class, short.class, int.class, long.class, |
| float.class, double.class, Integer.class)); |
| mh.invokeExact((int) 0x3773470, false, (byte) 0x3773470, (char) 0x3773470, |
| (short) 0x3773470, (int) 0x3773470, (long) 0x3773470, (float) 0x3773470, |
| (double) 0x3773470, Integer.valueOf(0x3773470)); |
| try { |
| mh.invoke((int) 0x3773470, false, (byte) 0x3773470, (char) 0x3773470, |
| (short) 0x3773470, (int) 0x3773470, (long) 0x3773470, (float) 0x3773470, |
| (double) 0x3773470, Long.valueOf((long) 0x3773470)); |
| fail(); |
| } catch (ClassCastException e) { |
| } |
| |
| target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "checkLongCast_delegate", |
| MethodType.methodType(void.class, long.class, long.class, long.class, |
| long.class, long.class, long.class, long.class, |
| long.class, long.class, long.class)); |
| mh = MethodHandles.explicitCastArguments( |
| target, MethodType.methodType(void.class, long.class, boolean.class, byte.class, |
| char.class, short.class, int.class, long.class, |
| float.class, double.class, Long.class)); |
| long longValue = 0x770000000l; |
| mh.invokeExact((long) longValue, false, (byte) longValue, (char) longValue, |
| (short) longValue, (int) longValue, (long) longValue, (float) longValue, |
| (double) longValue, Long.valueOf(longValue)); |
| try { |
| mh.invoke((long) longValue, false, (byte) longValue, (char) longValue, |
| (short) longValue, (int) longValue, (long) longValue, (float) longValue, |
| (double) longValue, Integer.valueOf(3)); |
| fail(); |
| } catch (ClassCastException e) { |
| } |
| |
| target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "checkFloatCast_delegate", |
| MethodType.methodType(void.class, float.class, float.class, float.class, |
| float.class, float.class, float.class, float.class, |
| float.class, float.class, float.class)); |
| mh = MethodHandles.explicitCastArguments( |
| target, MethodType.methodType(void.class, float.class, boolean.class, byte.class, |
| char.class, short.class, int.class, long.class, |
| float.class, double.class, Float.class)); |
| float floatValue = 33333.141f; |
| mh.invokeExact(floatValue, true, (byte) floatValue, (char) floatValue, (short) floatValue, |
| (int) floatValue, (long) floatValue, floatValue, (double) floatValue, |
| Float.valueOf(floatValue)); |
| try { |
| mh.invoke(floatValue, true, (byte) floatValue, (char) floatValue, |
| (short) floatValue, (int) floatValue, (long) floatValue, floatValue, |
| (double) floatValue, Integer.valueOf((int) floatValue)); |
| fail(); |
| } catch (ClassCastException e) { |
| } |
| |
| target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "checkDoubleCast_delegate", |
| MethodType.methodType(void.class, double.class, double.class, double.class, |
| double.class, double.class, double.class, double.class, |
| double.class, double.class, double.class)); |
| mh = MethodHandles.explicitCastArguments( |
| target, MethodType.methodType(void.class, double.class, boolean.class, byte.class, |
| char.class, short.class, int.class, long.class, |
| float.class, double.class, Double.class)); |
| double doubleValue = 33333333333.141; |
| mh.invokeExact(doubleValue, true, (byte) doubleValue, (char) doubleValue, |
| (short) doubleValue, (int) doubleValue, (long) doubleValue, |
| (float) doubleValue, doubleValue, Double.valueOf(doubleValue)); |
| try { |
| mh.invoke(doubleValue, true, (byte) doubleValue, (char) doubleValue, |
| (short) doubleValue, (int) doubleValue, (long) doubleValue, |
| (float) doubleValue, (double) doubleValue, |
| Integer.valueOf((int) doubleValue)); |
| fail(); |
| } catch (ClassCastException e) { |
| } |
| |
| target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "checkBoxingCasts_delegate", |
| MethodType.methodType(void.class, boolean.class, Boolean.class, Byte.class, |
| Character.class, Short.class, Integer.class, Long.class, |
| Float.class, Double.class)); |
| mh = MethodHandles.explicitCastArguments( |
| target, MethodType.methodType(void.class, boolean.class, boolean.class, byte.class, |
| char.class, short.class, int.class, long.class, |
| float.class, double.class)); |
| mh.invokeExact(false, false, (byte) 0, (char) 0, (short) 0, 0, 0l, 0.0f, 0.0); |
| mh.invokeExact(true, true, (byte) 1, (char) 1, (short) 1, 1, 1l, 1.0f, 1.0); |
| mh.invoke(Boolean.valueOf(false), Boolean.valueOf(false), Byte.valueOf((byte) 0), |
| Character.valueOf((char) 0), Short.valueOf((short) 0), Integer.valueOf(0), |
| Long.valueOf(0l), Float.valueOf(0.0f), Double.valueOf(0.0)); |
| mh.invoke(Boolean.valueOf(true), Boolean.valueOf(true), Byte.valueOf((byte) 1), |
| Character.valueOf((char) 1), Short.valueOf((short) 1), Integer.valueOf(1), |
| Long.valueOf(1l), Float.valueOf(1.0f), Double.valueOf(1.0)); |
| mh = MethodHandles.explicitCastArguments( |
| target, MethodType.methodType(void.class, double.class, boolean.class, byte.class, |
| char.class, short.class, int.class, long.class, |
| float.class, double.class)); |
| mh.invokeExact(0.0, false, (byte) 0, (char) 0, (short) 0, 0, 0l, 0.0f, 0.0); |
| } |
| |
| static void returnVoid() {} |
| |
| static boolean returnBoolean(boolean b) { return b; } |
| |
| static Boolean returnBooleanObject(boolean b) { return b; } |
| |
| public static void testExplicitCastReturnValues() throws Throwable { |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "returnVoid", MethodType.methodType(void.class)); |
| assertEquals(false, |
| MethodHandles |
| .explicitCastArguments(target, MethodType.methodType(boolean.class)) |
| .invoke()); |
| assertEquals(null, |
| MethodHandles |
| .explicitCastArguments(target, MethodType.methodType(Boolean.class)) |
| .invoke()); |
| assertEquals(0l, |
| MethodHandles |
| .explicitCastArguments(target, MethodType.methodType(long.class)) |
| .invoke()); |
| assertEquals(null, |
| MethodHandles |
| .explicitCastArguments(target, MethodType.methodType(Long.class)) |
| .invoke()); |
| |
| target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "returnBoolean", |
| MethodType.methodType(boolean.class, boolean.class)); |
| assertEquals(false, |
| MethodHandles |
| .explicitCastArguments(target, |
| MethodType.methodType(boolean.class, boolean.class)) |
| .invoke(false)); |
| assertEquals(true, |
| MethodHandles |
| .explicitCastArguments(target, |
| MethodType.methodType(boolean.class, boolean.class)) |
| .invoke(true)); |
| assertEquals(Boolean.valueOf(false), |
| MethodHandles |
| .explicitCastArguments(target, |
| MethodType.methodType(Boolean.class, boolean.class)) |
| .invoke(false)); |
| assertEquals(Boolean.valueOf(true), |
| MethodHandles |
| .explicitCastArguments(target, |
| MethodType.methodType(Boolean.class, boolean.class)) |
| .invoke(true)); |
| assertEquals((byte) 0, |
| MethodHandles |
| .explicitCastArguments(target, |
| MethodType.methodType(byte.class, boolean.class)) |
| .invoke(false)); |
| assertEquals((byte) 1, |
| MethodHandles |
| .explicitCastArguments(target, |
| MethodType.methodType(byte.class, boolean.class)) |
| .invoke(true)); |
| try { |
| assertEquals(Byte.valueOf((byte) 0), |
| MethodHandles |
| .explicitCastArguments(target, |
| MethodType.methodType(Byte.class, boolean.class)) |
| .invoke(false)); |
| fail(); |
| } catch (ClassCastException e) { |
| } |
| |
| try { |
| assertEquals(Byte.valueOf((byte) 1), |
| MethodHandles |
| .explicitCastArguments(target, |
| MethodType.methodType(Byte.class, boolean.class)) |
| .invoke(true)); |
| } catch (ClassCastException e) { |
| } |
| |
| target = MethodHandles.lookup().findStatic( |
| MethodHandleCombinersTest.class, "returnBooleanObject", |
| MethodType.methodType(Boolean.class, boolean.class)); |
| assertEquals(false, |
| (boolean) MethodHandles |
| .explicitCastArguments(target, |
| MethodType.methodType(boolean.class, boolean.class)) |
| .invokeExact(false)); |
| assertEquals(true, |
| (boolean) MethodHandles |
| .explicitCastArguments(target, |
| MethodType.methodType(boolean.class, boolean.class)) |
| .invokeExact(true)); |
| } |
| } |