| /* |
| * Copyright (C) 2016 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. |
| */ |
| |
| import java.lang.invoke.MethodHandle; |
| import java.lang.invoke.MethodHandles; |
| import java.lang.invoke.MethodHandles.Lookup; |
| import java.lang.invoke.MethodType; |
| import java.lang.invoke.VarHandle; |
| import java.lang.invoke.WrongMethodTypeException; |
| |
| public class Main { |
| public static void main(String[] args) throws Throwable { |
| testThrowException(); |
| testDropArguments(); |
| testCatchException(); |
| testGuardWithTest(); |
| testArrayElementGetter(); |
| testArrayElementSetter(); |
| testIdentity(); |
| testConstant(); |
| testBindTo(); |
| testFilterReturnValue(); |
| testPermuteArguments(); |
| testInvokers(); |
| testSpreaders_reference(); |
| testSpreaders_primitive(); |
| testInvokeWithArguments(); |
| testAsCollector(); |
| testFilterArguments(); |
| testCollectArguments(); |
| testInsertArguments(); |
| testFoldArguments(); |
| } |
| |
| 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) { |
| System.out.println("Message: " + message + ", Message2: " + message2); |
| } |
| |
| public static void testDropArguments() throws Throwable { |
| MethodHandle delegate = MethodHandles.lookup().findStatic(Main.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 String testCatchException_target(String arg1, long arg2, String exceptionMessage) |
| throws Throwable { |
| if (exceptionMessage != null) { |
| throw new IllegalArgumentException(exceptionMessage); |
| } |
| |
| System.out.println("Target: Arg1: " + arg1 + ", Arg2: " + arg2); |
| return "target"; |
| } |
| |
| public static String testCatchException_handler(IllegalArgumentException iae, String arg1, long arg2, |
| String exMsg) { |
| System.out.println("Handler: " + iae + ", Arg1: " + arg1 + ", Arg2: " + arg2 + ", ExMsg: " + exMsg); |
| return "handler1"; |
| } |
| |
| public static String testCatchException_handler2(IllegalArgumentException iae, String arg1) { |
| System.out.println("Handler: " + iae + ", Arg1: " + arg1); |
| return "handler2"; |
| } |
| |
| public static void testCatchException() throws Throwable { |
| MethodHandle target = MethodHandles.lookup().findStatic(Main.class, |
| "testCatchException_target", |
| MethodType.methodType(String.class, new Class<?>[] { String.class, long.class, String.class })); |
| |
| MethodHandle handler = MethodHandles.lookup().findStatic(Main.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(Main.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, "exceptionMessage2"); |
| assertEquals("java.lang.IllegalArgumentException: exceptionMessage2", 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) { |
| System.out.println("target: " + arg1 + ", " + arg2 + ", " + arg3); |
| return "target"; |
| } |
| |
| public static String testGuardWithTest_fallback(String arg1, long arg2, int arg3) { |
| System.out.println("fallback: " + arg1 + ", " + arg2 + ", " + arg3); |
| return "fallback"; |
| } |
| |
| public static void testGuardWithTest() throws Throwable { |
| MethodHandle test = MethodHandles.lookup().findStatic(Main.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(Main.class, |
| "testGuardWithTest_target", type); |
| final MethodHandle fallback = MethodHandles.lookup().findStatic(Main.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.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); |
| if (value != 42) { |
| fail("Unexpected value: " + 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); |
| if (value != 42l) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| { |
| getter = MethodHandles.arrayElementGetter(short[].class); |
| short[] array = new short[1]; |
| array[0] = 42; |
| short value = (short) getter.invoke(array, 0); |
| if (value != 42l) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| { |
| getter = MethodHandles.arrayElementGetter(char[].class); |
| char[] array = new char[1]; |
| array[0] = 42; |
| char value = (char) getter.invoke(array, 0); |
| if (value != 42l) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| { |
| getter = MethodHandles.arrayElementGetter(byte[].class); |
| byte[] array = new byte[1]; |
| array[0] = (byte) 0x8; |
| byte value = (byte) getter.invoke(array, 0); |
| if (value != (byte) 0x8) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| { |
| getter = MethodHandles.arrayElementGetter(boolean[].class); |
| boolean[] array = new boolean[1]; |
| array[0] = true; |
| boolean value = (boolean) getter.invoke(array, 0); |
| if (!value) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| { |
| getter = MethodHandles.arrayElementGetter(float[].class); |
| float[] array = new float[1]; |
| array[0] = 42.0f; |
| float value = (float) getter.invoke(array, 0); |
| if (value != 42.0f) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| { |
| getter = MethodHandles.arrayElementGetter(double[].class); |
| double[] array = new double[1]; |
| array[0] = 42.0; |
| double value = (double) getter.invoke(array, 0); |
| if (value != 42.0) { |
| fail("Unexpected value: " + 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); |
| |
| if (array[0] != 42) { |
| fail("Unexpected value: " + array[0]); |
| } |
| if (array[1] != 43) { |
| fail("Unexpected value: " + 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); |
| if (array[0] != 42l) { |
| fail("Unexpected value: " + array[0]); |
| } |
| } |
| |
| { |
| setter = MethodHandles.arrayElementSetter(short[].class); |
| short[] array = new short[1]; |
| setter.invoke(array, 0, (short) 42); |
| if (array[0] != 42l) { |
| fail("Unexpected value: " + array[0]); |
| } |
| } |
| |
| { |
| setter = MethodHandles.arrayElementSetter(char[].class); |
| char[] array = new char[1]; |
| setter.invoke(array, 0, (char) 42); |
| if (array[0] != 42) { |
| fail("Unexpected value: " + array[0]); |
| } |
| } |
| |
| { |
| setter = MethodHandles.arrayElementSetter(byte[].class); |
| byte[] array = new byte[1]; |
| setter.invoke(array, 0, (byte) 0x8); |
| if (array[0] != (byte) 0x8) { |
| fail("Unexpected value: " + array[0]); |
| } |
| } |
| |
| { |
| setter = MethodHandles.arrayElementSetter(boolean[].class); |
| boolean[] array = new boolean[1]; |
| setter.invoke(array, 0, true); |
| if (!array[0]) { |
| fail("Unexpected value: " + array[0]); |
| } |
| } |
| |
| { |
| setter = MethodHandles.arrayElementSetter(float[].class); |
| float[] array = new float[1]; |
| setter.invoke(array, 0, 42.0f); |
| if (array[0] != 42.0f) { |
| fail("Unexpected value: " + array[0]); |
| } |
| } |
| |
| { |
| setter = MethodHandles.arrayElementSetter(double[].class); |
| double[] array = new double[1]; |
| setter.invoke(array, 0, 42.0); |
| if (array[0] != 42.0) { |
| fail("Unexpected value: " + 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); |
| if (value) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(byte.class); |
| byte value = (byte) identity.invoke((byte) 0x8); |
| if (value != (byte) 0x8) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(char.class); |
| char value = (char) identity.invoke((char) -56); |
| if (value != (char) -56) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(short.class); |
| short value = (short) identity.invoke((short) -59); |
| if (value != (short) -59) { |
| fail("Unexpected value: " + Short.toString(value)); |
| } |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(int.class); |
| int value = (int) identity.invoke(52); |
| if (value != 52) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(long.class); |
| long value = (long) identity.invoke(-76l); |
| if (value != (long) -76) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(float.class); |
| float value = (float) identity.invoke(56.0f); |
| if (value != (float) 56.0f) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| { |
| MethodHandle identity = MethodHandles.identity(double.class); |
| double value = (double) identity.invoke((double) 72.0); |
| if (value != (double) 72.0) { |
| fail("Unexpected value: " + 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); |
| int value = (int) constant.invoke(); |
| if (value != 56) { |
| fail("Unexpected value: " + value); |
| } |
| |
| // short constant values are converted to int. |
| constant = MethodHandles.constant(int.class, (short) 52); |
| value = (int) constant.invoke(); |
| if (value != 52) { |
| fail("Unexpected value: " + value); |
| } |
| |
| // char constant values are converted to int. |
| constant = MethodHandles.constant(int.class, (char) 'b'); |
| value = (int) constant.invoke(); |
| if (value != (int) 'b') { |
| fail("Unexpected value: " + value); |
| } |
| |
| // int constant values are converted to int. |
| constant = MethodHandles.constant(int.class, (byte) 0x1); |
| value = (int) constant.invoke(); |
| if (value != 1) { |
| fail("Unexpected value: " + value); |
| } |
| |
| // 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); |
| long value = (long) constant.invoke(); |
| if (value != 56l) { |
| fail("Unexpected value: " + value); |
| } |
| |
| constant = MethodHandles.constant(long.class, (int) 56); |
| value = (long) constant.invoke(); |
| if (value != 56l) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| // byte constants. |
| { |
| MethodHandle constant = MethodHandles.constant(byte.class, (byte) 0x12); |
| byte value = (byte) constant.invoke(); |
| if (value != (byte) 0x12) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| // boolean constants. |
| { |
| MethodHandle constant = MethodHandles.constant(boolean.class, true); |
| boolean value = (boolean) constant.invoke(); |
| if (!value) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| // char constants. |
| { |
| MethodHandle constant = MethodHandles.constant(char.class, 'f'); |
| char value = (char) constant.invoke(); |
| if (value != 'f') { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| // short constants. |
| { |
| MethodHandle constant = MethodHandles.constant(short.class, (short) 123); |
| short value = (short) constant.invoke(); |
| if (value != (short) 123) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| // float constants. |
| { |
| MethodHandle constant = MethodHandles.constant(float.class, 56.0f); |
| float value = (float) constant.invoke(); |
| if (value != 56.0f) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| // double constants. |
| { |
| MethodHandle constant = MethodHandles.constant(double.class, 256.0); |
| double value = (double) constant.invoke(); |
| if (value != 256.0) { |
| fail("Unexpected value: " + value); |
| } |
| } |
| |
| // reference constants. |
| { |
| MethodHandle constant = MethodHandles.constant(String.class, "256.0"); |
| String value = (String) constant.invoke(); |
| assertEquals("256.0", value); |
| } |
| } |
| |
| 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); |
| } |
| |
| class Locals { |
| public int intValue; |
| }; |
| |
| MethodHandle setter1 = MethodHandles.lookup().findSetter(Locals.class, "intValue", int.class); |
| Locals locals = new Locals(); |
| setter1.invoke(locals, 1); |
| assertEquals(1, locals.intValue); |
| |
| MethodHandle setter2 = |
| MethodHandles.insertArguments(setter1.bindTo(locals), 0, Integer.valueOf(2)); |
| setter2.invoke(); |
| assertEquals(2, locals.intValue); |
| |
| VarHandle vh = MethodHandles.lookup().findVarHandle(Locals.class, "intValue", int.class); |
| MethodHandle setter3 = vh.toMethodHandle(VarHandle.AccessMode.GET_AND_SET); |
| assertEquals(setter3.type().toString(), |
| MethodType.methodType(int.class, Locals.class, int.class).toString()); |
| assertEquals(2, (int) setter3.invoke(locals, 3)); |
| |
| MethodHandle setter4 = |
| MethodHandles.varHandleInvoker(VarHandle.AccessMode.GET_AND_SET, |
| MethodType.methodType(int.class, Locals.class, int.class)); |
| assertEquals(3, (int) setter4.invoke(vh, locals, 4)); |
| |
| MethodHandle setter5 = setter4.bindTo(vh); |
| assertEquals(4, (int) setter5.invoke(locals, 5)); |
| |
| MethodHandle setter6 = MethodHandles.insertArguments(setter5, 0, locals, 6); |
| assertEquals(5, (int) setter6.invoke()); |
| assertEquals(locals.intValue, 6); |
| } |
| |
| 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(Main.class, |
| "filterReturnValue_target", MethodType.methodType(String.class, int.class)); |
| final MethodHandle filter = MethodHandles.lookup().findStatic(Main.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(Main.class, |
| "filterReturnValue_intTarget", MethodType.methodType(int.class, String.class)); |
| final MethodHandle filter = MethodHandles.lookup().findStatic(Main.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(Main.class, |
| "filterReturnValue_voidTarget", MethodType.methodType(void.class)); |
| final MethodHandle filter = MethodHandles.lookup().findStatic(Main.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) { |
| if (a == true && b == (byte) 'b' && c == 'c' && d == (short) 56 && |
| e == 78 && f == (long) 97 && g == 98.0f && f == 97.0) { |
| return; |
| } |
| |
| fail("Unexpected arguments: " + a + ", " + b + ", " + c |
| + ", " + d + ", " + e + ", " + f + ", " + g + ", " + h); |
| } |
| |
| public static void permuteArguments_boxingCallee(boolean a, Integer b) { |
| if (a && b.intValue() == 42) { |
| return; |
| } |
| |
| fail("Unexpected arguments: " + a + ", " + b); |
| } |
| |
| public static void testPermuteArguments() throws Throwable { |
| { |
| final MethodHandle target = MethodHandles.lookup().findStatic( |
| Main.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( |
| Main.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")); |
| assertEquals("barbar", (String) exactInvoker.invoke(target, (Object) returnBar(), "bar")); |
| try { |
| assertEquals("barbar", (String) invoker.invoke(target, (Object) Integer.valueOf(7), "bar")); |
| fail(); |
| } catch (ClassCastException expected) { |
| } |
| try { |
| assertEquals("barbar", (String) invoker.invoke(target, (Object) null, "bar")); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| exactInvoker.invoke(target, (Object) returnBar(), "bar"); |
| |
| try { |
| String foo = (String) exactInvoker.invoke(target, "bar", "bar", 24); |
| fail(); |
| } catch (WrongMethodTypeException expected) { |
| } |
| } |
| |
| public static int spreadReferences(String a, String b, String c) { |
| System.out.println("a: " + a + ", b:" + b + ", c: " + c); |
| return 42; |
| } |
| |
| public static int spreadReferences_Unbox(String a, int b) { |
| System.out.println("a: " + a + ", b:" + 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( |
| Main.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( |
| Main.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) { |
| System.out.println("a: " + a + ", b:" + b + ", c: " + c); |
| return 44; |
| } |
| |
| public static int spreadByte(String a, Byte b, byte c, |
| short d, int e, long f, float g, double h) { |
| System.out.println("a: " + a + ", b:" + b + ", c: " + c + |
| ", d: " + d + ", e: " + e + ", f:" + f + ", g: " + g + |
| ", h: " + h); |
| return 45; |
| } |
| |
| public static int spreadChar(String a, Character b, char c, |
| int d, long e, float f, double g) { |
| System.out.println("a: " + a + ", b:" + b + ", c: " + c + |
| ", d: " + d + ", e: " + e + ", f:" + f + ", g: " + g); |
| return 46; |
| } |
| |
| public static int spreadShort(String a, Short b, short c, |
| int d, long e, float f, double g) { |
| System.out.println("a: " + a + ", b:" + b + ", c: " + c + |
| ", d: " + d + ", e: " + e + ", f:" + f + ", g:" + g); |
| return 47; |
| } |
| |
| public static int spreadInt(String a, Integer b, int c, |
| long d, float e, double f) { |
| System.out.println("a: " + a + ", b:" + b + ", c: " + c + |
| ", d: " + d + ", e: " + e + ", f:" + f); |
| return 48; |
| } |
| |
| public static int spreadLong(String a, Long b, long c, float d, double e) { |
| System.out.println("a: " + a + ", b:" + b + ", c: " + c + |
| ", d: " + d + ", e: " + e); |
| return 49; |
| } |
| |
| public static int spreadFloat(String a, Float b, float c, double d) { |
| System.out.println("a: " + a + ", b:" + b + ", c: " + c + ", d: " + d); |
| return 50; |
| } |
| |
| public static int spreadDouble(String a, Double b, double c) { |
| System.out.println("a: " + a + ", b:" + b + ", c: " + 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( |
| Main.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(Main.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(Main.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(Main.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(Main.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(Main.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(Main.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(Main.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( |
| Main.class, "spreadReferences", methodType); |
| |
| Object ret = handle.invokeWithArguments(new Object[] { "a", "b", "c"}); |
| assertEquals(42, (int) ret); |
| handle.invokeWithArguments(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) { |
| } |
| |
| // Test implicit unboxing. |
| MethodType methodType2 = MethodType.methodType(int.class, |
| new Class<?>[] { String.class, int.class }); |
| MethodHandle handle2 = MethodHandles.lookup().findStatic( |
| Main.class, "spreadReferences_Unbox", methodType2); |
| |
| ret = (int) handle2.invokeWithArguments(new Object[] { "a", 43 }); |
| assertEquals(43, (int) ret); |
| } |
| |
| public static int collectBoolean(String a, boolean[] b) { |
| System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); |
| return 44; |
| } |
| |
| public static int collectByte(String a, byte[] b) { |
| System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); |
| return 45; |
| } |
| |
| public static int collectChar(String a, char[] b) { |
| System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); |
| return 46; |
| } |
| |
| public static int collectShort(String a, short[] b) { |
| System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); |
| return 47; |
| } |
| |
| public static int collectInt(String a, int[] b) { |
| System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); |
| return 48; |
| } |
| |
| public static int collectLong(String a, long[] b) { |
| System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); |
| return 49; |
| } |
| |
| public static int collectFloat(String a, float[] b) { |
| System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); |
| return 50; |
| } |
| |
| public static int collectDouble(String a, double[] b) { |
| System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); |
| return 51; |
| } |
| |
| public static int collectCharSequence(String a, CharSequence[] b) { |
| System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); |
| return 99; |
| } |
| |
| public static void testAsCollector() throws Throwable { |
| // Reference arrays. |
| // ------------------- |
| MethodHandle trailingRef = MethodHandles.lookup().findStatic( |
| Main.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) { |
| } |
| |
| // Checks on other array types. |
| |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| Main.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(Main.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(Main.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(Main.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(Main.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(Main.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(Main.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(Main.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) { |
| System.out.println("a: " + a + ", b: " + b + ", c:" + c + ", d:" + d); |
| return 56; |
| } |
| |
| public static void testFilterArguments() throws Throwable { |
| MethodHandle filter1 = MethodHandles.lookup().findStatic( |
| Main.class, "filter1", MethodType.methodType(String.class, char.class)); |
| MethodHandle filter2 = MethodHandles.lookup().findStatic( |
| Main.class, "filter2", MethodType.methodType(char.class, String.class)); |
| |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| Main.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( |
| Main.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) { |
| System.out.println("voidFilter"); |
| } |
| |
| static String filter(char a, char b) { |
| return String.valueOf(a) + "+" + b; |
| } |
| |
| static char badFilter(char a, char b) { |
| return 0; |
| } |
| |
| static int target(String a, String b, String c) { |
| System.out.println("a: " + a + ", b: " + b + ", c: " + c); |
| return 57; |
| } |
| |
| public static void testCollectArguments() throws Throwable { |
| // Test non-void filters. |
| MethodHandle filter = MethodHandles.lookup().findStatic( |
| Main.class, "filter", |
| MethodType.methodType(String.class, char.class, char.class)); |
| |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| Main.class, "target", |
| MethodType.methodType(int.class, String.class, String.class, String.class)); |
| |
| // Filter at position 0. |
| MethodHandle adapter = MethodHandles.collectArguments(target, 0, filter); |
| assertEquals(57, (int) adapter.invokeExact('a', 'b', "c", "d")); |
| |
| // Filter at position 1. |
| adapter = MethodHandles.collectArguments(target, 1, filter); |
| assertEquals(57, (int) adapter.invokeExact("a", 'b', 'c', "d")); |
| |
| // Filter at position 2. |
| adapter = MethodHandles.collectArguments(target, 2, filter); |
| assertEquals(57, (int) 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(Main.class, "voidFilter", |
| MethodType.methodType(void.class, char.class, char.class)); |
| adapter = MethodHandles.collectArguments(target, 0, filter); |
| assertEquals(57, (int) adapter.invokeExact('a', 'b', "a", "b", "c")); |
| |
| adapter = MethodHandles.collectArguments(target, 1, filter); |
| assertEquals(57, (int) adapter.invokeExact("a", 'a', 'b', "b", "c")); |
| |
| // Test out a few failure cases. |
| filter = MethodHandles.lookup().findStatic( |
| Main.class, "filter", |
| MethodType.methodType(String.class, char.class, char.class)); |
| |
| // Bogus filter position. |
| try { |
| adapter = MethodHandles.collectArguments(target, 3, filter); |
| fail(); |
| } catch (IndexOutOfBoundsException | IllegalArgumentException expected) { |
| } |
| |
| // Mismatch in filter return type. |
| filter = MethodHandles.lookup().findStatic( |
| Main.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) { |
| System.out.println("a: " + a + ", b:" + b + ", c:" + c + ", d:" + d); |
| return 73; |
| } |
| |
| public static void testInsertArguments() throws Throwable { |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| Main.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(45, Integer.valueOf(56), "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", 45, Integer.valueOf(46))); |
| |
| // 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) { |
| System.out.println(String.valueOf(a) + "+" + b); |
| } |
| |
| public static int foldTarget(String a, char b, char c, String d) { |
| System.out.println("a: " + a + " ,b:" + b + " ,c:" + c + " ,d:" + d); |
| return 89; |
| } |
| |
| 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( |
| Main.class, "foldFilter", |
| MethodType.methodType(String.class, char.class, char.class)); |
| |
| MethodHandle target = MethodHandles.lookup().findStatic( |
| Main.class, "foldTarget", |
| MethodType.methodType(int.class, String.class, |
| char.class, char.class, String.class)); |
| |
| // Folder with a non-void type. |
| MethodHandle adapter = MethodHandles.foldArguments(target, filter); |
| assertEquals(89, (int) adapter.invokeExact('c', 'd', "e")); |
| |
| // Folder with a void type. |
| filter = MethodHandles.lookup().findStatic( |
| Main.class, "voidFoldFilter", |
| MethodType.methodType(void.class, String.class, char.class, char.class)); |
| adapter = MethodHandles.foldArguments(target, filter); |
| assertEquals(89, (int) adapter.invokeExact("a", 'c', 'd', "e")); |
| |
| // Test a few erroneous cases. |
| |
| filter = MethodHandles.lookup().findStatic( |
| Main.class, "mismatchedVoidFilter", |
| MethodType.methodType(void.class, Integer.class)); |
| try { |
| adapter = MethodHandles.foldArguments(target, filter); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| |
| filter = MethodHandles.lookup().findStatic( |
| Main.class, "mismatchedNonVoidFilter", |
| MethodType.methodType(Integer.class, char.class, char.class)); |
| try { |
| adapter = MethodHandles.foldArguments(target, filter); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| public static void fail() { |
| System.out.println("FAIL"); |
| Thread.dumpStack(); |
| } |
| |
| public static void fail(String message) { |
| System.out.println("fail: " + message); |
| Thread.dumpStack(); |
| } |
| |
| public static void assertEquals(int i1, int i2) { |
| if (i1 != i2) throw new AssertionError("Expected: " + i1 + " was " + i2); |
| } |
| |
| public static void assertEquals(String s1, String s2) { |
| if (s1 == s2) { |
| return; |
| } |
| |
| if (s1 != null && s2 != null && s1.equals(s2)) { |
| return; |
| } |
| |
| throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2); |
| } |
| } |