1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import java.lang.invoke.MethodHandle;
18 import java.lang.invoke.MethodHandles;
19 import java.lang.invoke.MethodHandles.Lookup;
20 import java.lang.invoke.MethodType;
21 import java.lang.invoke.WrongMethodTypeException;
22 
23 public class Main {
main(String[] args)24   public static void main(String[] args) throws Throwable {
25     testThrowException();
26     testDropArguments();
27     testCatchException();
28     testGuardWithTest();
29     testArrayElementGetter();
30     testArrayElementSetter();
31     testIdentity();
32     testConstant();
33     testBindTo();
34     testFilterReturnValue();
35     testPermuteArguments();
36     testInvokers();
37     testSpreaders_reference();
38     testSpreaders_primitive();
39     testInvokeWithArguments();
40     testAsCollector();
41     testFilterArguments();
42     testCollectArguments();
43     testInsertArguments();
44     testFoldArguments();
45   }
46 
testThrowException()47   public static void testThrowException() throws Throwable {
48     MethodHandle handle = MethodHandles.throwException(String.class,
49         IllegalArgumentException.class);
50 
51     if (handle.type().returnType() != String.class) {
52       fail("Unexpected return type for handle: " + handle +
53           " [ " + handle.type() + "]");
54     }
55 
56     final IllegalArgumentException iae = new IllegalArgumentException("boo!");
57     try {
58       handle.invoke(iae);
59       fail("Expected an exception of type: java.lang.IllegalArgumentException");
60     } catch (IllegalArgumentException expected) {
61       if (expected != iae) {
62         fail("Wrong exception: expected " + iae + " but was " + expected);
63       }
64     }
65   }
66 
dropArguments_delegate(String message, long message2)67   public static void dropArguments_delegate(String message, long message2) {
68     System.out.println("Message: " + message + ", Message2: " + message2);
69   }
70 
testDropArguments()71   public static void testDropArguments() throws Throwable {
72     MethodHandle delegate = MethodHandles.lookup().findStatic(Main.class,
73         "dropArguments_delegate",
74         MethodType.methodType(void.class, new Class<?>[] { String.class, long.class }));
75 
76     MethodHandle transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class);
77 
78     // The transformer will accept two additional arguments at position zero.
79     try {
80       transform.invokeExact("foo", 42l);
81       fail();
82     } catch (WrongMethodTypeException expected) {
83     }
84 
85     transform.invokeExact(45, new Object(), "foo", 42l);
86     transform.invoke(45, new Object(), "foo", 42l);
87 
88     // Additional arguments at position 1.
89     transform = MethodHandles.dropArguments(delegate, 1, int.class, Object.class);
90     transform.invokeExact("foo", 45, new Object(), 42l);
91     transform.invoke("foo", 45, new Object(), 42l);
92 
93     // Additional arguments at position 2.
94     transform = MethodHandles.dropArguments(delegate, 2, int.class, Object.class);
95     transform.invokeExact("foo", 42l, 45, new Object());
96     transform.invoke("foo", 42l, 45, new Object());
97 
98     // Note that we still perform argument conversions even for the arguments that
99     // are subsequently dropped.
100     try {
101       transform.invoke("foo", 42l, 45l, new Object());
102       fail();
103     } catch (WrongMethodTypeException expected) {
104     } catch (IllegalArgumentException expected) {
105       // TODO(narayan): We currently throw the wrong type of exception here,
106       // it's IAE and should be WMTE instead.
107     }
108 
109     // Check that asType works as expected.
110     transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class);
111     transform = transform.asType(MethodType.methodType(void.class,
112           new Class<?>[] { short.class, Object.class, String.class, long.class }));
113     transform.invokeExact((short) 45, new Object(), "foo", 42l);
114 
115     // Invalid argument location, should not be allowed.
116     try {
117       MethodHandles.dropArguments(delegate, -1, int.class, Object.class);
118       fail();
119     } catch (IllegalArgumentException expected) {
120     }
121 
122     // Invalid argument location, should not be allowed.
123     try {
124       MethodHandles.dropArguments(delegate, 3, int.class, Object.class);
125       fail();
126     } catch (IllegalArgumentException expected) {
127     }
128 
129     try {
130       MethodHandles.dropArguments(delegate, 1, void.class);
131       fail();
132     } catch (IllegalArgumentException expected) {
133     }
134   }
135 
testCatchException_target(String arg1, long arg2, String exceptionMessage)136   public static String testCatchException_target(String arg1, long arg2, String exceptionMessage)
137       throws Throwable {
138     if (exceptionMessage != null) {
139       throw new IllegalArgumentException(exceptionMessage);
140     }
141 
142     System.out.println("Target: Arg1: " + arg1 + ", Arg2: " + arg2);
143     return "target";
144   }
145 
testCatchException_handler(IllegalArgumentException iae, String arg1, long arg2, String exMsg)146   public static String testCatchException_handler(IllegalArgumentException iae, String arg1, long arg2,
147       String exMsg) {
148     System.out.println("Handler: " + iae + ", Arg1: " + arg1 + ", Arg2: " + arg2 + ", ExMsg: " + exMsg);
149     return "handler1";
150   }
151 
testCatchException_handler2(IllegalArgumentException iae, String arg1)152   public static String testCatchException_handler2(IllegalArgumentException iae, String arg1) {
153     System.out.println("Handler: " + iae + ", Arg1: " + arg1);
154     return "handler2";
155   }
156 
testCatchException()157   public static void testCatchException() throws Throwable {
158     MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
159         "testCatchException_target",
160         MethodType.methodType(String.class, new Class<?>[] { String.class, long.class, String.class }));
161 
162     MethodHandle handler = MethodHandles.lookup().findStatic(Main.class,
163         "testCatchException_handler",
164         MethodType.methodType(String.class, new Class<?>[] { IllegalArgumentException.class,
165             String.class, long.class, String.class }));
166 
167     MethodHandle adapter = MethodHandles.catchException(target, IllegalArgumentException.class,
168         handler);
169 
170     String returnVal = null;
171 
172     // These two should end up calling the target always. We're passing a null exception
173     // message here, which means the target will not throw.
174     returnVal = (String) adapter.invoke("foo", 42, null);
175     assertEquals("target", returnVal);
176     returnVal = (String) adapter.invokeExact("foo", 42l, (String) null);
177     assertEquals("target", returnVal);
178 
179     // We're passing a non-null exception message here, which means the target will throw,
180     // which in turn means that the handler must be called for the next two invokes.
181     returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
182     assertEquals("handler1", returnVal);
183     returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage");
184     assertEquals("handler1", returnVal);
185 
186     handler = MethodHandles.lookup().findStatic(Main.class,
187         "testCatchException_handler2",
188         MethodType.methodType(String.class, new Class<?>[] { IllegalArgumentException.class,
189             String.class }));
190     adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler);
191 
192     returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
193     assertEquals("handler2", returnVal);
194     returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage");
195     assertEquals("handler2", returnVal);
196 
197     // Test that the type of the invoke doesn't matter. Here we call
198     // IllegalArgumentException.toString() on the exception that was thrown by
199     // the target.
200     handler = MethodHandles.lookup().findVirtual(IllegalArgumentException.class,
201         "toString", MethodType.methodType(String.class));
202     adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler);
203 
204     returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
205     assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal);
206     returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage2");
207     assertEquals("java.lang.IllegalArgumentException: exceptionMessage2", returnVal);
208 
209     // Check that asType works as expected.
210     adapter = MethodHandles.catchException(target, IllegalArgumentException.class,
211         handler);
212     adapter = adapter.asType(MethodType.methodType(String.class,
213           new Class<?>[] { String.class, int.class, String.class }));
214     returnVal = (String) adapter.invokeExact("foo", 42, "exceptionMessage");
215     assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal);
216   }
217 
testGuardWithTest_test(String arg1, long arg2)218   public static boolean testGuardWithTest_test(String arg1, long arg2) {
219     return "target".equals(arg1) && 42 == arg2;
220   }
221 
testGuardWithTest_target(String arg1, long arg2, int arg3)222   public static String testGuardWithTest_target(String arg1, long arg2, int arg3) {
223     System.out.println("target: " + arg1 + ", " + arg2  + ", " + arg3);
224     return "target";
225   }
226 
testGuardWithTest_fallback(String arg1, long arg2, int arg3)227   public static String testGuardWithTest_fallback(String arg1, long arg2, int arg3) {
228     System.out.println("fallback: " + arg1 + ", " + arg2  + ", " + arg3);
229     return "fallback";
230   }
231 
testGuardWithTest()232   public static void testGuardWithTest() throws Throwable {
233     MethodHandle test = MethodHandles.lookup().findStatic(Main.class,
234         "testGuardWithTest_test",
235         MethodType.methodType(boolean.class, new Class<?>[] { String.class, long.class }));
236 
237     final MethodType type = MethodType.methodType(String.class,
238         new Class<?>[] { String.class, long.class, int.class });
239 
240     final MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
241         "testGuardWithTest_target", type);
242     final MethodHandle fallback = MethodHandles.lookup().findStatic(Main.class,
243         "testGuardWithTest_fallback", type);
244 
245     MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback);
246 
247     String returnVal = null;
248 
249     returnVal = (String) adapter.invoke("target", 42, 56);
250     assertEquals("target", returnVal);
251     returnVal = (String) adapter.invokeExact("target", 42l, 56);
252     assertEquals("target", returnVal);
253 
254     returnVal = (String) adapter.invoke("fallback", 42l, 56);
255     assertEquals("fallback", returnVal);
256     returnVal = (String) adapter.invokeExact("target", 42l, 56);
257     assertEquals("target", returnVal);
258 
259     // Check that asType works as expected.
260     adapter = adapter.asType(MethodType.methodType(String.class,
261           new Class<?>[] { String.class, int.class, int.class }));
262     returnVal = (String) adapter.invokeExact("target", 42, 56);
263     assertEquals("target", returnVal);
264   }
265 
testArrayElementGetter()266   public static void testArrayElementGetter() throws Throwable {
267     MethodHandle getter = MethodHandles.arrayElementGetter(int[].class);
268 
269     {
270       int[] array = new int[1];
271       array[0] = 42;
272       int value = (int) getter.invoke(array, 0);
273       if (value != 42) {
274         fail("Unexpected value: " + value);
275       }
276 
277       try {
278         value = (int) getter.invoke(array, -1);
279         fail();
280       } catch (ArrayIndexOutOfBoundsException expected) {
281       }
282 
283       try {
284         value = (int) getter.invoke(null, -1);
285         fail();
286       } catch (NullPointerException expected) {
287       }
288     }
289 
290     {
291       getter = MethodHandles.arrayElementGetter(long[].class);
292       long[] array = new long[1];
293       array[0] = 42;
294       long value = (long) getter.invoke(array, 0);
295       if (value != 42l) {
296         fail("Unexpected value: " + value);
297       }
298     }
299 
300     {
301       getter = MethodHandles.arrayElementGetter(short[].class);
302       short[] array = new short[1];
303       array[0] = 42;
304       short value = (short) getter.invoke(array, 0);
305       if (value != 42l) {
306         fail("Unexpected value: " + value);
307       }
308     }
309 
310     {
311       getter = MethodHandles.arrayElementGetter(char[].class);
312       char[] array = new char[1];
313       array[0] = 42;
314       char value = (char) getter.invoke(array, 0);
315       if (value != 42l) {
316         fail("Unexpected value: " + value);
317       }
318     }
319 
320     {
321       getter = MethodHandles.arrayElementGetter(byte[].class);
322       byte[] array = new byte[1];
323       array[0] = (byte) 0x8;
324       byte value = (byte) getter.invoke(array, 0);
325       if (value != (byte) 0x8) {
326         fail("Unexpected value: " + value);
327       }
328     }
329 
330     {
331       getter = MethodHandles.arrayElementGetter(boolean[].class);
332       boolean[] array = new boolean[1];
333       array[0] = true;
334       boolean value = (boolean) getter.invoke(array, 0);
335       if (!value) {
336         fail("Unexpected value: " + value);
337       }
338     }
339 
340     {
341       getter = MethodHandles.arrayElementGetter(float[].class);
342       float[] array = new float[1];
343       array[0] = 42.0f;
344       float value = (float) getter.invoke(array, 0);
345       if (value != 42.0f) {
346         fail("Unexpected value: " + value);
347       }
348     }
349 
350     {
351       getter = MethodHandles.arrayElementGetter(double[].class);
352       double[] array = new double[1];
353       array[0] = 42.0;
354       double value = (double) getter.invoke(array, 0);
355       if (value != 42.0) {
356         fail("Unexpected value: " + value);
357       }
358     }
359 
360     {
361       getter = MethodHandles.arrayElementGetter(String[].class);
362       String[] array = new String[3];
363       array[0] = "42";
364       array[1] = "48";
365       array[2] = "54";
366       String value = (String) getter.invoke(array, 0);
367       assertEquals("42", value);
368       value = (String) getter.invoke(array, 1);
369       assertEquals("48", value);
370       value = (String) getter.invoke(array, 2);
371       assertEquals("54", value);
372     }
373   }
374 
testArrayElementSetter()375   public static void testArrayElementSetter() throws Throwable {
376     MethodHandle setter = MethodHandles.arrayElementSetter(int[].class);
377 
378     {
379       int[] array = new int[2];
380       setter.invoke(array, 0, 42);
381       setter.invoke(array, 1, 43);
382 
383       if (array[0] != 42) {
384         fail("Unexpected value: " + array[0]);
385       }
386       if (array[1] != 43) {
387         fail("Unexpected value: " + array[1]);
388       }
389 
390       try {
391         setter.invoke(array, -1, 42);
392         fail();
393       } catch (ArrayIndexOutOfBoundsException expected) {
394       }
395 
396       try {
397         setter.invoke(null, 0, 42);
398         fail();
399       } catch (NullPointerException expected) {
400       }
401     }
402 
403     {
404       setter = MethodHandles.arrayElementSetter(long[].class);
405       long[] array = new long[1];
406       setter.invoke(array, 0, 42l);
407       if (array[0] != 42l) {
408         fail("Unexpected value: " + array[0]);
409       }
410     }
411 
412     {
413       setter = MethodHandles.arrayElementSetter(short[].class);
414       short[] array = new short[1];
415       setter.invoke(array, 0, (short) 42);
416       if (array[0] != 42l) {
417         fail("Unexpected value: " + array[0]);
418       }
419     }
420 
421     {
422       setter = MethodHandles.arrayElementSetter(char[].class);
423       char[] array = new char[1];
424       setter.invoke(array, 0, (char) 42);
425       if (array[0] != 42) {
426         fail("Unexpected value: " + array[0]);
427       }
428     }
429 
430     {
431       setter = MethodHandles.arrayElementSetter(byte[].class);
432       byte[] array = new byte[1];
433       setter.invoke(array, 0, (byte) 0x8);
434       if (array[0] != (byte) 0x8) {
435         fail("Unexpected value: " + array[0]);
436       }
437     }
438 
439     {
440       setter = MethodHandles.arrayElementSetter(boolean[].class);
441       boolean[] array = new boolean[1];
442       setter.invoke(array, 0, true);
443       if (!array[0]) {
444         fail("Unexpected value: " + array[0]);
445       }
446     }
447 
448     {
449       setter = MethodHandles.arrayElementSetter(float[].class);
450       float[] array = new float[1];
451       setter.invoke(array, 0, 42.0f);
452       if (array[0] != 42.0f) {
453         fail("Unexpected value: " + array[0]);
454       }
455     }
456 
457     {
458       setter = MethodHandles.arrayElementSetter(double[].class);
459       double[] array = new double[1];
460       setter.invoke(array, 0, 42.0);
461       if (array[0] != 42.0) {
462         fail("Unexpected value: " + array[0]);
463       }
464     }
465 
466     {
467       setter = MethodHandles.arrayElementSetter(String[].class);
468       String[] array = new String[3];
469       setter.invoke(array, 0, "42");
470       setter.invoke(array, 1, "48");
471       setter.invoke(array, 2, "54");
472       assertEquals("42", array[0]);
473       assertEquals("48", array[1]);
474       assertEquals("54", array[2]);
475     }
476   }
477 
testIdentity()478   public static void testIdentity() throws Throwable {
479     {
480       MethodHandle identity = MethodHandles.identity(boolean.class);
481       boolean value = (boolean) identity.invoke(false);
482       if (value) {
483         fail("Unexpected value: " + value);
484       }
485     }
486 
487     {
488       MethodHandle identity = MethodHandles.identity(byte.class);
489       byte value = (byte) identity.invoke((byte) 0x8);
490       if (value != (byte) 0x8) {
491         fail("Unexpected value: " + value);
492       }
493     }
494 
495     {
496       MethodHandle identity = MethodHandles.identity(char.class);
497       char value = (char) identity.invoke((char) -56);
498       if (value != (char) -56) {
499         fail("Unexpected value: " + value);
500       }
501     }
502 
503     {
504       MethodHandle identity = MethodHandles.identity(short.class);
505       short value = (short) identity.invoke((short) -59);
506       if (value != (short) -59) {
507         fail("Unexpected value: " + Short.toString(value));
508       }
509     }
510 
511     {
512       MethodHandle identity = MethodHandles.identity(int.class);
513       int value = (int) identity.invoke(52);
514       if (value != 52) {
515         fail("Unexpected value: " + value);
516       }
517     }
518 
519     {
520       MethodHandle identity = MethodHandles.identity(long.class);
521       long value = (long) identity.invoke(-76l);
522       if (value != (long) -76) {
523         fail("Unexpected value: " + value);
524       }
525     }
526 
527     {
528       MethodHandle identity = MethodHandles.identity(float.class);
529       float value = (float) identity.invoke(56.0f);
530       if (value != (float) 56.0f) {
531         fail("Unexpected value: " + value);
532       }
533     }
534 
535     {
536       MethodHandle identity = MethodHandles.identity(double.class);
537       double value = (double) identity.invoke((double) 72.0);
538       if (value != (double) 72.0) {
539         fail("Unexpected value: " + value);
540       }
541     }
542 
543     {
544       MethodHandle identity = MethodHandles.identity(String.class);
545       String value = (String) identity.invoke("bazman");
546       assertEquals("bazman", value);
547     }
548   }
549 
testConstant()550   public static void testConstant() throws Throwable {
551     // int constants.
552     {
553       MethodHandle constant = MethodHandles.constant(int.class, 56);
554       int value = (int) constant.invoke();
555       if (value != 56) {
556         fail("Unexpected value: " + value);
557       }
558 
559       // short constant values are converted to int.
560       constant = MethodHandles.constant(int.class, (short) 52);
561       value = (int) constant.invoke();
562       if (value != 52) {
563         fail("Unexpected value: " + value);
564       }
565 
566       // char constant values are converted to int.
567       constant = MethodHandles.constant(int.class, (char) 'b');
568       value = (int) constant.invoke();
569       if (value != (int) 'b') {
570         fail("Unexpected value: " + value);
571       }
572 
573       // int constant values are converted to int.
574       constant = MethodHandles.constant(int.class, (byte) 0x1);
575       value = (int) constant.invoke();
576       if (value != 1) {
577         fail("Unexpected value: " + value);
578       }
579 
580       // boolean, float, double and long primitive constants are not convertible
581       // to int, so the handle creation must fail with a CCE.
582       try {
583         MethodHandles.constant(int.class, false);
584         fail();
585       } catch (ClassCastException expected) {
586       }
587 
588       try {
589         MethodHandles.constant(int.class, 0.1f);
590         fail();
591       } catch (ClassCastException expected) {
592       }
593 
594       try {
595         MethodHandles.constant(int.class, 0.2);
596         fail();
597       } catch (ClassCastException expected) {
598       }
599 
600       try {
601         MethodHandles.constant(int.class, 73l);
602         fail();
603       } catch (ClassCastException expected) {
604       }
605     }
606 
607     // long constants.
608     {
609       MethodHandle constant = MethodHandles.constant(long.class, 56l);
610       long value = (long) constant.invoke();
611       if (value != 56l) {
612         fail("Unexpected value: " + value);
613       }
614 
615       constant = MethodHandles.constant(long.class, (int) 56);
616       value = (long) constant.invoke();
617       if (value != 56l) {
618         fail("Unexpected value: " + value);
619       }
620     }
621 
622     // byte constants.
623     {
624       MethodHandle constant = MethodHandles.constant(byte.class, (byte) 0x12);
625       byte value = (byte) constant.invoke();
626       if (value != (byte) 0x12) {
627         fail("Unexpected value: " + value);
628       }
629     }
630 
631     // boolean constants.
632     {
633       MethodHandle constant = MethodHandles.constant(boolean.class, true);
634       boolean value = (boolean) constant.invoke();
635       if (!value) {
636         fail("Unexpected value: " + value);
637       }
638     }
639 
640     // char constants.
641     {
642       MethodHandle constant = MethodHandles.constant(char.class, 'f');
643       char value = (char) constant.invoke();
644       if (value != 'f') {
645         fail("Unexpected value: " + value);
646       }
647     }
648 
649     // short constants.
650     {
651       MethodHandle constant = MethodHandles.constant(short.class, (short) 123);
652       short value = (short) constant.invoke();
653       if (value != (short) 123) {
654         fail("Unexpected value: " + value);
655       }
656     }
657 
658     // float constants.
659     {
660       MethodHandle constant = MethodHandles.constant(float.class, 56.0f);
661       float value = (float) constant.invoke();
662       if (value != 56.0f) {
663         fail("Unexpected value: " + value);
664       }
665     }
666 
667     // double constants.
668     {
669       MethodHandle constant = MethodHandles.constant(double.class, 256.0);
670       double value = (double) constant.invoke();
671       if (value != 256.0) {
672         fail("Unexpected value: " + value);
673       }
674     }
675 
676     // reference constants.
677     {
678       MethodHandle constant = MethodHandles.constant(String.class, "256.0");
679       String value = (String) constant.invoke();
680       assertEquals("256.0", value);
681     }
682   }
683 
testBindTo()684   public static void testBindTo() throws Throwable {
685     MethodHandle stringCharAt = MethodHandles.lookup().findVirtual(
686         String.class, "charAt", MethodType.methodType(char.class, int.class));
687 
688     char value = (char) stringCharAt.invoke("foo", 0);
689     if (value != 'f') {
690       fail("Unexpected value: " + value);
691     }
692 
693     MethodHandle bound = stringCharAt.bindTo("foo");
694     value = (char) bound.invoke(0);
695     if (value != 'f') {
696       fail("Unexpected value: " + value);
697     }
698 
699     try {
700       stringCharAt.bindTo(new Object());
701       fail();
702     } catch (ClassCastException expected) {
703     }
704 
705     bound = stringCharAt.bindTo(null);
706     try {
707       bound.invoke(0);
708       fail();
709     } catch (NullPointerException expected) {
710     }
711 
712     MethodHandle integerParseInt = MethodHandles.lookup().findStatic(
713         Integer.class, "parseInt", MethodType.methodType(int.class, String.class));
714 
715     bound = integerParseInt.bindTo("78452");
716     int intValue = (int) bound.invoke();
717     if (intValue != 78452) {
718       fail("Unexpected value: " + intValue);
719     }
720   }
721 
filterReturnValue_target(int a)722   public static String filterReturnValue_target(int a) {
723     return "ReturnValue" + a;
724   }
725 
filterReturnValue_filter(String value)726   public static boolean filterReturnValue_filter(String value) {
727     return value.indexOf("42") != -1;
728   }
729 
filterReturnValue_intTarget(String a)730   public static int filterReturnValue_intTarget(String a) {
731     return Integer.parseInt(a);
732   }
733 
filterReturnValue_intFilter(int b)734   public static int filterReturnValue_intFilter(int b) {
735     return b + 1;
736   }
737 
filterReturnValue_voidTarget()738   public static void filterReturnValue_voidTarget() {
739   }
740 
filterReturnValue_voidFilter()741   public static int filterReturnValue_voidFilter() {
742     return 42;
743   }
744 
testFilterReturnValue()745   public static void testFilterReturnValue() throws Throwable {
746     // A target that returns a reference.
747     {
748       final MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
749           "filterReturnValue_target", MethodType.methodType(String.class, int.class));
750       final MethodHandle filter = MethodHandles.lookup().findStatic(Main.class,
751           "filterReturnValue_filter", MethodType.methodType(boolean.class, String.class));
752 
753       MethodHandle adapter = MethodHandles.filterReturnValue(target, filter);
754 
755       boolean value = (boolean) adapter.invoke((int) 42);
756       if (!value) {
757         fail("Unexpected value: " + value);
758       }
759       value = (boolean) adapter.invoke((int) 43);
760       if (value) {
761         fail("Unexpected value: " + value);
762       }
763     }
764 
765     // A target that returns a primitive.
766     {
767       final MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
768           "filterReturnValue_intTarget", MethodType.methodType(int.class, String.class));
769       final MethodHandle filter = MethodHandles.lookup().findStatic(Main.class,
770           "filterReturnValue_intFilter", MethodType.methodType(int.class, int.class));
771 
772       MethodHandle adapter = MethodHandles.filterReturnValue(target, filter);
773 
774       int value = (int) adapter.invoke("56");
775       if (value != 57) {
776         fail("Unexpected value: " + value);
777       }
778     }
779 
780     // A target that returns void.
781     {
782       final MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
783           "filterReturnValue_voidTarget", MethodType.methodType(void.class));
784       final MethodHandle filter = MethodHandles.lookup().findStatic(Main.class,
785           "filterReturnValue_voidFilter", MethodType.methodType(int.class));
786 
787       MethodHandle adapter = MethodHandles.filterReturnValue(target, filter);
788 
789       int value = (int) adapter.invoke();
790       if (value != 42) {
791         fail("Unexpected value: " + value);
792       }
793     }
794   }
795 
permuteArguments_callee(boolean a, byte b, char c, short d, int e, long f, float g, double h)796   public static void permuteArguments_callee(boolean a, byte b, char c,
797       short d, int e, long f, float g, double h) {
798     if (a == true && b == (byte) 'b' && c == 'c' && d == (short) 56 &&
799         e == 78 && f == (long) 97 && g == 98.0f && f == 97.0) {
800       return;
801     }
802 
803     fail("Unexpected arguments: " + a + ", " + b + ", " + c
804         + ", " + d + ", " + e + ", " + f + ", " + g + ", " + h);
805   }
806 
permuteArguments_boxingCallee(boolean a, Integer b)807   public static void permuteArguments_boxingCallee(boolean a, Integer b) {
808     if (a && b.intValue() == 42) {
809       return;
810     }
811 
812     fail("Unexpected arguments: " + a + ", " + b);
813   }
814 
testPermuteArguments()815   public static void testPermuteArguments() throws Throwable {
816     {
817       final MethodHandle target = MethodHandles.lookup().findStatic(
818           Main.class, "permuteArguments_callee",
819           MethodType.methodType(void.class, new Class<?>[] {
820             boolean.class, byte.class, char.class, short.class, int.class,
821             long.class, float.class, double.class }));
822 
823       final MethodType newType = MethodType.methodType(void.class, new Class<?>[] {
824         double.class, float.class, long.class, int.class, short.class, char.class,
825         byte.class, boolean.class });
826 
827       final MethodHandle permutation = MethodHandles.permuteArguments(
828           target, newType, new int[] { 7, 6, 5, 4, 3, 2, 1, 0 });
829 
830       permutation.invoke((double) 97.0, (float) 98.0f, (long) 97, 78,
831           (short) 56, 'c', (byte) 'b', (boolean) true);
832 
833       // The permutation array was not of the right length.
834       try {
835         MethodHandles.permuteArguments(target, newType,
836             new int[] { 7 });
837         fail();
838       } catch (IllegalArgumentException expected) {
839       }
840 
841       // The permutation array has an element that's out of bounds
842       // (there's no argument with idx == 8).
843       try {
844         MethodHandles.permuteArguments(target, newType,
845             new int[] { 8, 6, 5, 4, 3, 2, 1, 0 });
846         fail();
847       } catch (IllegalArgumentException expected) {
848       }
849 
850       // The permutation array maps to an incorrect type.
851       try {
852         MethodHandles.permuteArguments(target, newType,
853             new int[] { 7, 7, 5, 4, 3, 2, 1, 0 });
854         fail();
855       } catch (IllegalArgumentException expected) {
856       }
857     }
858 
859     // Tests for reference arguments as well as permutations that
860     // repeat arguments.
861     {
862       final MethodHandle target = MethodHandles.lookup().findVirtual(
863           String.class, "concat", MethodType.methodType(String.class, String.class));
864 
865       final MethodType newType = MethodType.methodType(String.class, String.class,
866           String.class);
867 
868       assertEquals("foobar", (String) target.invoke("foo", "bar"));
869 
870       MethodHandle permutation = MethodHandles.permuteArguments(target,
871           newType, new int[] { 1, 0 });
872       assertEquals("barfoo", (String) permutation.invoke("foo", "bar"));
873 
874       permutation = MethodHandles.permuteArguments(target, newType, new int[] { 0, 0 });
875       assertEquals("foofoo", (String) permutation.invoke("foo", "bar"));
876 
877       permutation = MethodHandles.permuteArguments(target, newType, new int[] { 1, 1 });
878       assertEquals("barbar", (String) permutation.invoke("foo", "bar"));
879     }
880 
881     // Tests for boxing and unboxing.
882     {
883       final MethodHandle target = MethodHandles.lookup().findStatic(
884           Main.class, "permuteArguments_boxingCallee",
885           MethodType.methodType(void.class, new Class<?>[] { boolean.class, Integer.class }));
886 
887       final MethodType newType = MethodType.methodType(void.class,
888           new Class<?>[] { Integer.class, boolean.class });
889 
890       MethodHandle permutation = MethodHandles.permuteArguments(target,
891           newType, new int[] { 1, 0 });
892 
893       permutation.invoke(42, true);
894       permutation.invoke(42, Boolean.TRUE);
895       permutation.invoke(Integer.valueOf(42), true);
896       permutation.invoke(Integer.valueOf(42), Boolean.TRUE);
897     }
898   }
899 
returnBar()900   private static Object returnBar() {
901     return "bar";
902   }
903 
testInvokers()904   public static void testInvokers() throws Throwable {
905     final MethodType targetType = MethodType.methodType(String.class, String.class);
906     final MethodHandle target = MethodHandles.lookup().findVirtual(
907         String.class, "concat", targetType);
908 
909     MethodHandle invoker = MethodHandles.invoker(target.type());
910     assertEquals("barbar", (String) invoker.invoke(target, "bar", "bar"));
911     assertEquals("barbar", (String) invoker.invoke(target, (Object) returnBar(), "bar"));
912     try {
913       String foo = (String) invoker.invoke(target, "bar", "bar", 24);
914       fail();
915     } catch (WrongMethodTypeException expected) {
916     }
917 
918     MethodHandle exactInvoker = MethodHandles.exactInvoker(target.type());
919     assertEquals("barbar", (String) exactInvoker.invoke(target, "bar", "bar"));
920     try {
921       String foo = (String) exactInvoker.invoke(target, (Object) returnBar(), "bar");
922       fail();
923     } catch (WrongMethodTypeException expected) {
924     }
925     try {
926       String foo = (String) exactInvoker.invoke(target, "bar", "bar", 24);
927       fail();
928     } catch (WrongMethodTypeException expected) {
929     }
930   }
931 
spreadReferences(String a, String b, String c)932   public static int spreadReferences(String a, String b, String c) {
933     System.out.println("a: " + a + ", b:" + b + ", c: " + c);
934     return 42;
935   }
936 
spreadReferences_Unbox(String a, int b)937   public static int spreadReferences_Unbox(String a, int b) {
938     System.out.println("a: " + a + ", b:" + b);
939     return 43;
940   }
941 
testSpreaders_reference()942   public static void testSpreaders_reference() throws Throwable {
943     MethodType methodType = MethodType.methodType(int.class,
944         new Class<?>[] { String.class, String.class, String.class });
945     MethodHandle delegate = MethodHandles.lookup().findStatic(
946         Main.class, "spreadReferences", methodType);
947 
948     // Basic checks on array lengths.
949     //
950     // Array size = 0
951     MethodHandle mhAsSpreader = delegate.asSpreader(String[].class, 0);
952     int ret = (int) mhAsSpreader.invoke("a", "b", "c", new String[] {});
953     assertEquals(42, ret);
954     // Array size = 1
955     mhAsSpreader = delegate.asSpreader(String[].class, 1);
956     ret = (int) mhAsSpreader.invoke("a", "b", new String[] { "c" });
957     assertEquals(42, ret);
958     // Array size = 2
959     mhAsSpreader = delegate.asSpreader(String[].class, 2);
960     ret = (int) mhAsSpreader.invoke("a", new String[] { "b", "c" });
961     assertEquals(42, ret);
962     // Array size = 3
963     mhAsSpreader = delegate.asSpreader(String[].class, 3);
964     ret = (int) mhAsSpreader.invoke(new String[] { "a", "b", "c"});
965     assertEquals(42, ret);
966 
967     // Exception case, array size = 4 is illegal.
968     try {
969       delegate.asSpreader(String[].class, 4);
970       fail();
971     } catch (IllegalArgumentException expected) {
972     }
973 
974     // Exception case, calling with an arg of the wrong size.
975     // Array size = 3
976     mhAsSpreader = delegate.asSpreader(String[].class, 3);
977     try {
978       ret = (int) mhAsSpreader.invoke(new String[] { "a", "b"});
979     } catch (IllegalArgumentException expected) {
980     }
981 
982     // Various other hijinks, pass as Object[] arrays, Object etc.
983     mhAsSpreader = delegate.asSpreader(Object[].class, 2);
984     ret = (int) mhAsSpreader.invoke("a", new String[] { "b", "c" });
985     assertEquals(42, ret);
986 
987     mhAsSpreader = delegate.asSpreader(Object[].class, 2);
988     ret = (int) mhAsSpreader.invoke("a", new Object[] { "b", "c" });
989     assertEquals(42, ret);
990 
991     mhAsSpreader = delegate.asSpreader(Object[].class, 2);
992     ret = (int) mhAsSpreader.invoke("a", (Object) new Object[] { "b", "c" });
993     assertEquals(42, ret);
994 
995     // Test implicit unboxing.
996     MethodType methodType2 = MethodType.methodType(int.class,
997         new Class<?>[] { String.class, int.class });
998     MethodHandle delegate2 = MethodHandles.lookup().findStatic(
999         Main.class, "spreadReferences_Unbox", methodType2);
1000 
1001     // .. with an Integer[] array.
1002     mhAsSpreader = delegate2.asSpreader(Integer[].class, 1);
1003     ret = (int) mhAsSpreader.invoke("a", new Integer[] { 43 });
1004     assertEquals(43, ret);
1005 
1006     // .. with an Integer[] array declared as an Object[] argument type.
1007     mhAsSpreader = delegate2.asSpreader(Object[].class, 1);
1008     ret = (int) mhAsSpreader.invoke("a", new Integer[] { 43 });
1009     assertEquals(43, ret);
1010 
1011     // .. with an Object[] array.
1012     mhAsSpreader = delegate2.asSpreader(Object[].class, 1);
1013     ret = (int) mhAsSpreader.invoke("a", new Object[] { Integer.valueOf(43)});
1014     assertEquals(43, ret);
1015 
1016     // -- Part 2--
1017     // Run a subset of these tests on MethodHandles.spreadInvoker, which only accepts
1018     // a trailing argument type of Object[].
1019     MethodHandle spreadInvoker = MethodHandles.spreadInvoker(methodType2, 1);
1020     ret = (int) spreadInvoker.invoke(delegate2, "a", new Object[] { Integer.valueOf(43)});
1021     assertEquals(43, ret);
1022 
1023     ret = (int) spreadInvoker.invoke(delegate2, "a", new Integer[] { 43 });
1024     assertEquals(43, ret);
1025 
1026     // NOTE: Annoyingly, the second argument here is leadingArgCount and not
1027     // arrayLength.
1028     spreadInvoker = MethodHandles.spreadInvoker(methodType, 3);
1029     ret = (int) spreadInvoker.invoke(delegate, "a", "b", "c", new String[] {});
1030     assertEquals(42, ret);
1031 
1032     spreadInvoker = MethodHandles.spreadInvoker(methodType, 0);
1033     ret = (int) spreadInvoker.invoke(delegate, new String[] { "a", "b", "c" });
1034     assertEquals(42, ret);
1035 
1036     // Exact invokes: Double check that the expected parameter type is
1037     // Object[] and not T[].
1038     try {
1039       spreadInvoker.invokeExact(delegate, new String[] { "a", "b", "c" });
1040       fail();
1041     } catch (WrongMethodTypeException expected) {
1042     }
1043 
1044     ret = (int) spreadInvoker.invoke(delegate, new Object[] { "a", "b", "c" });
1045     assertEquals(42, ret);
1046   }
1047 
spreadBoolean(String a, Boolean b, boolean c)1048   public static int spreadBoolean(String a, Boolean b, boolean c) {
1049     System.out.println("a: " + a + ", b:" + b + ", c: " + c);
1050     return 44;
1051   }
1052 
spreadByte(String a, Byte b, byte c, short d, int e, long f, float g, double h)1053   public static int spreadByte(String a, Byte b, byte c,
1054       short d, int e, long f, float g, double h) {
1055     System.out.println("a: " + a + ", b:" + b + ", c: " + c +
1056         ", d: " + d + ", e: " + e + ", f:" + f + ", g: " + g +
1057         ", h: " + h);
1058     return 45;
1059   }
1060 
spreadChar(String a, Character b, char c, int d, long e, float f, double g)1061   public static int spreadChar(String a, Character b, char c,
1062       int d, long e, float f, double g) {
1063     System.out.println("a: " + a + ", b:" + b + ", c: " + c +
1064         ", d: " + d + ", e: " + e + ", f:" + f + ", g: " + g);
1065     return 46;
1066   }
1067 
spreadShort(String a, Short b, short c, int d, long e, float f, double g)1068   public static int spreadShort(String a, Short b, short c,
1069       int d, long e, float f, double g) {
1070     System.out.println("a: " + a + ", b:" + b + ", c: " + c +
1071         ", d: " + d + ", e: " + e + ", f:" + f + ", g:" + g);
1072     return 47;
1073   }
1074 
spreadInt(String a, Integer b, int c, long d, float e, double f)1075   public static int spreadInt(String a, Integer b, int c,
1076       long d, float e, double f) {
1077     System.out.println("a: " + a + ", b:" + b + ", c: " + c +
1078         ", d: " + d + ", e: " + e + ", f:" + f);
1079     return 48;
1080   }
1081 
spreadLong(String a, Long b, long c, float d, double e)1082   public static int spreadLong(String a, Long b, long c, float d, double e) {
1083     System.out.println("a: " + a + ", b:" + b + ", c: " + c +
1084         ", d: " + d + ", e: " + e);
1085     return 49;
1086   }
1087 
spreadFloat(String a, Float b, float c, double d)1088   public static int spreadFloat(String a, Float b, float c, double d) {
1089     System.out.println("a: " + a + ", b:" + b + ", c: " + c + ", d: " + d);
1090     return 50;
1091   }
1092 
spreadDouble(String a, Double b, double c)1093   public static int spreadDouble(String a, Double b, double c) {
1094     System.out.println("a: " + a + ", b:" + b + ", c: " + c);
1095     return 51;
1096   }
1097 
testSpreaders_primitive()1098   public static void testSpreaders_primitive() throws Throwable {
1099     // boolean[]
1100     // ---------------------
1101     MethodType type = MethodType.methodType(int.class,
1102         new Class<?>[] { String.class, Boolean.class, boolean.class });
1103     MethodHandle delegate = MethodHandles.lookup().findStatic(
1104         Main.class, "spreadBoolean", type);
1105 
1106     MethodHandle spreader = delegate.asSpreader(boolean[].class, 2);
1107     int ret = (int) spreader.invokeExact("a", new boolean[] { true, false });
1108     assertEquals(44, ret);
1109     ret = (int) spreader.invoke("a", new boolean[] { true, false });
1110     assertEquals(44, ret);
1111 
1112     // boolean can't be cast to String (the first argument to the method).
1113     try {
1114       delegate.asSpreader(boolean[].class, 3);
1115       fail();
1116     } catch (WrongMethodTypeException expected) {
1117     }
1118 
1119     // int can't be cast to boolean to supply the last argument to the method.
1120     try {
1121       delegate.asSpreader(int[].class, 1);
1122       fail();
1123     } catch (WrongMethodTypeException expected) {
1124     }
1125 
1126     // byte[]
1127     // ---------------------
1128     type = MethodType.methodType(int.class,
1129         new Class<?>[] {
1130           String.class, Byte.class, byte.class,
1131           short.class, int.class, long.class,
1132           float.class, double.class });
1133     delegate = MethodHandles.lookup().findStatic(Main.class, "spreadByte", type);
1134 
1135     spreader = delegate.asSpreader(byte[].class, 7);
1136     ret = (int) spreader.invokeExact("a",
1137         new byte[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 });
1138     assertEquals(45, ret);
1139     ret = (int) spreader.invoke("a",
1140         new byte[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 });
1141     assertEquals(45, ret);
1142 
1143     // char[]
1144     // ---------------------
1145     type = MethodType.methodType(int.class,
1146         new Class<?>[] {
1147           String.class, Character.class,char.class,
1148           int.class, long.class, float.class, double.class });
1149     delegate = MethodHandles.lookup().findStatic(Main.class, "spreadChar", type);
1150 
1151     spreader = delegate.asSpreader(char[].class, 6);
1152     ret = (int) spreader.invokeExact("a",
1153         new char[] { '1', '2', '3', '4', '5', '6' });
1154     assertEquals(46, ret);
1155     ret = (int) spreader.invokeExact("a",
1156         new char[] { '1', '2', '3', '4', '5', '6' });
1157     assertEquals(46, ret);
1158 
1159     // short[]
1160     // ---------------------
1161     type = MethodType.methodType(int.class,
1162         new Class<?>[] {
1163           String.class, Short.class, short.class,
1164           int.class, long.class, float.class, double.class });
1165     delegate = MethodHandles.lookup().findStatic(Main.class, "spreadShort", type);
1166 
1167     spreader = delegate.asSpreader(short[].class, 6);
1168     ret = (int) spreader.invokeExact("a",
1169         new short[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 });
1170     assertEquals(47, ret);
1171     ret = (int) spreader.invoke("a",
1172         new short[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 });
1173     assertEquals(47, ret);
1174 
1175     // int[]
1176     // ---------------------
1177     type = MethodType.methodType(int.class,
1178         new Class<?>[] {
1179           String.class, Integer.class, int.class,
1180           long.class, float.class, double.class });
1181     delegate = MethodHandles.lookup().findStatic(Main.class, "spreadInt", type);
1182 
1183     spreader = delegate.asSpreader(int[].class, 5);
1184     ret = (int) spreader.invokeExact("a", new int[] { 1, 2, 3, 4, 5 });
1185     assertEquals(48, ret);
1186     ret = (int) spreader.invokeExact("a", new int[] { 1, 2, 3, 4, 5 });
1187     assertEquals(48, ret);
1188 
1189     // long[]
1190     // ---------------------
1191     type = MethodType.methodType(int.class,
1192         new Class<?>[] {
1193           String.class, Long.class, long.class, float.class, double.class });
1194     delegate = MethodHandles.lookup().findStatic(Main.class, "spreadLong", type);
1195 
1196     spreader = delegate.asSpreader(long[].class, 4);
1197     ret = (int) spreader.invokeExact("a",
1198         new long[] { 0x1, 0x2, 0x3, 0x4 });
1199     assertEquals(49, ret);
1200     ret = (int) spreader.invoke("a",
1201         new long[] { 0x1, 0x2, 0x3, 0x4 });
1202     assertEquals(49, ret);
1203 
1204     // float[]
1205     // ---------------------
1206     type = MethodType.methodType(int.class,
1207         new Class<?>[] {
1208           String.class, Float.class, float.class, double.class });
1209     delegate = MethodHandles.lookup().findStatic(Main.class, "spreadFloat", type);
1210 
1211     spreader = delegate.asSpreader(float[].class, 3);
1212     ret = (int) spreader.invokeExact("a",
1213         new float[] { 1.0f, 2.0f, 3.0f });
1214     assertEquals(50, ret);
1215     ret = (int) spreader.invokeExact("a",
1216         new float[] { 1.0f, 2.0f, 3.0f });
1217     assertEquals(50, ret);
1218 
1219     // double[]
1220     // ---------------------
1221     type = MethodType.methodType(int.class,
1222         new Class<?>[] { String.class, Double.class, double.class });
1223     delegate = MethodHandles.lookup().findStatic(Main.class, "spreadDouble", type);
1224 
1225     spreader = delegate.asSpreader(double[].class, 2);
1226     ret = (int) spreader.invokeExact("a", new double[] { 1.0, 2.0 });
1227     assertEquals(51, ret);
1228     ret = (int) spreader.invokeExact("a", new double[] { 1.0, 2.0 });
1229     assertEquals(51, ret);
1230   }
1231 
testInvokeWithArguments()1232   public static void testInvokeWithArguments() throws Throwable {
1233     MethodType methodType = MethodType.methodType(int.class,
1234         new Class<?>[] { String.class, String.class, String.class });
1235     MethodHandle handle = MethodHandles.lookup().findStatic(
1236         Main.class, "spreadReferences", methodType);
1237 
1238     Object ret = handle.invokeWithArguments(new Object[] { "a", "b", "c"});
1239     assertEquals(42, (int) ret);
1240     handle.invokeWithArguments(new String[] { "a", "b", "c" });
1241     assertEquals(42, (int) ret);
1242 
1243     // Pass in an array that's too small. Should throw an IAE.
1244     try {
1245       handle.invokeWithArguments(new Object[] { "a", "b" });
1246       fail();
1247     } catch (IllegalArgumentException expected) {
1248     } catch (WrongMethodTypeException expected) {
1249     }
1250 
1251     // Test implicit unboxing.
1252     MethodType methodType2 = MethodType.methodType(int.class,
1253         new Class<?>[] { String.class, int.class });
1254     MethodHandle handle2 = MethodHandles.lookup().findStatic(
1255         Main.class, "spreadReferences_Unbox", methodType2);
1256 
1257     ret = (int) handle2.invokeWithArguments(new Object[] { "a", 43 });
1258     assertEquals(43, (int) ret);
1259   }
1260 
collectBoolean(String a, boolean[] b)1261   public static int collectBoolean(String a, boolean[] b) {
1262     System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
1263     return 44;
1264   }
1265 
collectByte(String a, byte[] b)1266   public static int collectByte(String a, byte[] b) {
1267     System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
1268     return 45;
1269   }
1270 
collectChar(String a, char[] b)1271   public static int collectChar(String a, char[] b) {
1272     System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
1273     return 46;
1274   }
1275 
collectShort(String a, short[] b)1276   public static int collectShort(String a, short[] b) {
1277     System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
1278     return 47;
1279   }
1280 
collectInt(String a, int[] b)1281   public static int collectInt(String a, int[] b) {
1282     System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
1283     return 48;
1284   }
1285 
collectLong(String a, long[] b)1286   public static int collectLong(String a, long[] b) {
1287     System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
1288     return 49;
1289   }
1290 
collectFloat(String a, float[] b)1291   public static int collectFloat(String a, float[] b) {
1292     System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
1293     return 50;
1294   }
1295 
collectDouble(String a, double[] b)1296   public static int collectDouble(String a, double[] b) {
1297     System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
1298     return 51;
1299   }
1300 
collectCharSequence(String a, CharSequence[] b)1301   public static int collectCharSequence(String a, CharSequence[] b) {
1302     System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]);
1303     return 99;
1304   }
1305 
testAsCollector()1306   public static void testAsCollector() throws Throwable {
1307     // Reference arrays.
1308     // -------------------
1309     MethodHandle trailingRef = MethodHandles.lookup().findStatic(
1310         Main.class, "collectCharSequence",
1311         MethodType.methodType(int.class, String.class, CharSequence[].class));
1312 
1313     // int[] is not convertible to CharSequence[].class.
1314     try {
1315       trailingRef.asCollector(int[].class, 1);
1316       fail();
1317     } catch (IllegalArgumentException expected) {
1318     }
1319 
1320     // Object[] is not convertible to CharSequence[].class.
1321     try {
1322       trailingRef.asCollector(Object[].class, 1);
1323       fail();
1324     } catch (IllegalArgumentException expected) {
1325     }
1326 
1327     // String[].class is convertible to CharSequence.class
1328     MethodHandle collector = trailingRef.asCollector(String[].class, 2);
1329     assertEquals(99, (int) collector.invoke("a", "b", "c"));
1330 
1331     // Too few arguments should fail with a WMTE.
1332     try {
1333       collector.invoke("a", "b");
1334       fail();
1335     } catch (WrongMethodTypeException expected) {
1336     }
1337 
1338     // Too many arguments should fail with a WMTE.
1339     try {
1340       collector.invoke("a", "b", "c", "d");
1341       fail();
1342     } catch (WrongMethodTypeException expected) {
1343     }
1344 
1345     // Checks on other array types.
1346 
1347     MethodHandle target = MethodHandles.lookup().findStatic(
1348         Main.class, "collectBoolean",
1349         MethodType.methodType(int.class, String.class, boolean[].class));
1350     assertEquals(44, (int) target.asCollector(boolean[].class, 2).invoke("a", true, false));
1351 
1352     target = MethodHandles.lookup().findStatic(Main.class, "collectByte",
1353         MethodType.methodType(int.class, String.class, byte[].class));
1354     assertEquals(45, (int) target.asCollector(byte[].class, 2).invoke("a", (byte) 1, (byte) 2));
1355 
1356     target = MethodHandles.lookup().findStatic(Main.class, "collectChar",
1357         MethodType.methodType(int.class, String.class, char[].class));
1358     assertEquals(46, (int) target.asCollector(char[].class, 2).invoke("a", 'a', 'b'));
1359 
1360     target = MethodHandles.lookup().findStatic(Main.class, "collectShort",
1361         MethodType.methodType(int.class, String.class, short[].class));
1362     assertEquals(47, (int) target.asCollector(short[].class, 2).invoke("a", (short) 3, (short) 4));
1363 
1364     target = MethodHandles.lookup().findStatic(Main.class, "collectInt",
1365         MethodType.methodType(int.class, String.class, int[].class));
1366     assertEquals(48, (int) target.asCollector(int[].class, 2).invoke("a", 42, 43));
1367 
1368     target = MethodHandles.lookup().findStatic(Main.class, "collectLong",
1369         MethodType.methodType(int.class, String.class, long[].class));
1370     assertEquals(49, (int) target.asCollector(long[].class, 2).invoke("a", 100, 99));
1371 
1372     target = MethodHandles.lookup().findStatic(Main.class, "collectFloat",
1373         MethodType.methodType(int.class, String.class, float[].class));
1374     assertEquals(50, (int) target.asCollector(float[].class, 2).invoke("a", 8.9f, 9.1f));
1375 
1376     target = MethodHandles.lookup().findStatic(Main.class, "collectDouble",
1377         MethodType.methodType(int.class, String.class, double[].class));
1378     assertEquals(51, (int) target.asCollector(double[].class, 2).invoke("a", 6.7, 7.8));
1379   }
1380 
filter1(char a)1381   public static String filter1(char a) {
1382     return String.valueOf(a);
1383   }
1384 
filter2(String b)1385   public static char filter2(String b) {
1386     return b.charAt(0);
1387   }
1388 
badFilter1(char a, char b)1389   public static String badFilter1(char a, char b) {
1390     return "bad";
1391   }
1392 
filterTarget(String a, char b, String c, char d)1393   public static int filterTarget(String a, char b, String c, char d) {
1394     System.out.println("a: " + a + ", b: " + b + ", c:" + c + ", d:" + d);
1395     return 56;
1396   }
1397 
testFilterArguments()1398   public static void testFilterArguments() throws Throwable {
1399     MethodHandle filter1 = MethodHandles.lookup().findStatic(
1400         Main.class, "filter1", MethodType.methodType(String.class, char.class));
1401     MethodHandle filter2 = MethodHandles.lookup().findStatic(
1402         Main.class, "filter2", MethodType.methodType(char.class, String.class));
1403 
1404     MethodHandle target = MethodHandles.lookup().findStatic(
1405         Main.class, "filterTarget", MethodType.methodType(int.class,
1406           String.class, char.class, String.class, char.class));
1407 
1408     // In all the cases below, the values printed will be 'a', 'b', 'c', 'd'.
1409 
1410     // Filter arguments [0, 1] - all other arguments are passed through
1411     // as is.
1412     MethodHandle adapter = MethodHandles.filterArguments(
1413         target, 0, filter1, filter2);
1414     assertEquals(56, (int) adapter.invokeExact('a', "bXXXX", "c", 'd'));
1415 
1416     // Filter arguments [1, 2].
1417     adapter = MethodHandles.filterArguments(target, 1, filter2, filter1);
1418     assertEquals(56, (int) adapter.invokeExact("a", "bXXXX", 'c', 'd'));
1419 
1420     // Filter arguments [2, 3].
1421     adapter = MethodHandles.filterArguments(target, 2, filter1, filter2);
1422     assertEquals(56, (int) adapter.invokeExact("a", 'b', 'c', "dXXXXX"));
1423 
1424     // Try out a few error cases :
1425 
1426     // The return types of the filter doesn't align with the expected argument
1427     // type of the target.
1428     try {
1429       adapter = MethodHandles.filterArguments(target, 2, filter2, filter1);
1430       fail();
1431     } catch (IllegalArgumentException expected) {
1432     }
1433 
1434     // There are more filters than arguments.
1435     try {
1436       adapter = MethodHandles.filterArguments(target, 3, filter2, filter1);
1437       fail();
1438     } catch (IllegalArgumentException expected) {
1439     }
1440 
1441     // We pass in an obviously bogus position.
1442     try {
1443       adapter = MethodHandles.filterArguments(target, -1, filter2, filter1);
1444       fail();
1445     } catch (ArrayIndexOutOfBoundsException expected) {
1446     }
1447 
1448     // We pass in a function that has more than one argument.
1449     MethodHandle badFilter1 = MethodHandles.lookup().findStatic(
1450         Main.class, "badFilter1",
1451         MethodType.methodType(String.class, char.class, char.class));
1452 
1453     try {
1454       adapter = MethodHandles.filterArguments(target, 0, badFilter1, filter2);
1455       fail();
1456     } catch (IllegalArgumentException expected) {
1457     }
1458   }
1459 
voidFilter(char a, char b)1460   static void voidFilter(char a, char b) {
1461     System.out.println("voidFilter");
1462   }
1463 
filter(char a, char b)1464   static String filter(char a, char b) {
1465     return String.valueOf(a) + "+" + b;
1466   }
1467 
badFilter(char a, char b)1468   static char badFilter(char a, char b) {
1469     return 0;
1470   }
1471 
target(String a, String b, String c)1472   static int target(String a, String b, String c) {
1473     System.out.println("a: " + a + ", b: " + b + ", c: " + c);
1474     return 57;
1475   }
1476 
testCollectArguments()1477   public static void testCollectArguments() throws Throwable {
1478     // Test non-void filters.
1479     MethodHandle filter = MethodHandles.lookup().findStatic(
1480         Main.class, "filter",
1481         MethodType.methodType(String.class, char.class, char.class));
1482 
1483     MethodHandle target = MethodHandles.lookup().findStatic(
1484         Main.class, "target",
1485         MethodType.methodType(int.class, String.class, String.class, String.class));
1486 
1487     // Filter at position 0.
1488     MethodHandle adapter = MethodHandles.collectArguments(target, 0, filter);
1489     assertEquals(57, (int) adapter.invokeExact('a', 'b', "c", "d"));
1490 
1491     // Filter at position 1.
1492     adapter = MethodHandles.collectArguments(target, 1, filter);
1493     assertEquals(57, (int) adapter.invokeExact("a", 'b', 'c', "d"));
1494 
1495     // Filter at position 2.
1496     adapter = MethodHandles.collectArguments(target, 2, filter);
1497     assertEquals(57, (int) adapter.invokeExact("a", "b", 'c', 'd'));
1498 
1499     // Test void filters. Note that we're passing in one more argument
1500     // than usual because the filter returns nothing - we have to invoke with
1501     // the full set of filter args and the full set of target args.
1502     filter = MethodHandles.lookup().findStatic(Main.class, "voidFilter",
1503         MethodType.methodType(void.class, char.class, char.class));
1504     adapter = MethodHandles.collectArguments(target, 0, filter);
1505     assertEquals(57, (int) adapter.invokeExact('a', 'b', "a", "b", "c"));
1506 
1507     adapter = MethodHandles.collectArguments(target, 1, filter);
1508     assertEquals(57, (int) adapter.invokeExact("a", 'a', 'b', "b", "c"));
1509 
1510     // Test out a few failure cases.
1511     filter = MethodHandles.lookup().findStatic(
1512         Main.class, "filter",
1513         MethodType.methodType(String.class, char.class, char.class));
1514 
1515     // Bogus filter position.
1516     try {
1517       adapter = MethodHandles.collectArguments(target, 3, filter);
1518       fail();
1519     } catch (IndexOutOfBoundsException expected) {
1520     }
1521 
1522     // Mismatch in filter return type.
1523     filter = MethodHandles.lookup().findStatic(
1524         Main.class, "badFilter",
1525         MethodType.methodType(char.class, char.class, char.class));
1526     try {
1527       adapter = MethodHandles.collectArguments(target, 0, filter);
1528       fail();
1529     } catch (IllegalArgumentException expected) {
1530     }
1531   }
1532 
insertReceiver(String a, int b, Integer c, String d)1533   static int insertReceiver(String a, int b, Integer c, String d) {
1534     System.out.println("a: " + a + ", b:" + b + ", c:" + c + ", d:" + d);
1535     return 73;
1536   }
1537 
testInsertArguments()1538   public static void testInsertArguments() throws Throwable {
1539     MethodHandle target = MethodHandles.lookup().findStatic(
1540         Main.class, "insertReceiver",
1541         MethodType.methodType(int.class,
1542           String.class, int.class, Integer.class, String.class));
1543 
1544     // Basic single element array inserted at position 0.
1545     MethodHandle adapter = MethodHandles.insertArguments(
1546         target, 0, new Object[] { "foo" });
1547     assertEquals(73, (int) adapter.invokeExact(45, Integer.valueOf(56), "bar"));
1548 
1549     // Exercise unboxing.
1550     adapter = MethodHandles.insertArguments(
1551         target, 1, new Object[] { Integer.valueOf(56), 57 });
1552     assertEquals(73, (int) adapter.invokeExact("foo", "bar"));
1553 
1554     // Exercise a widening conversion.
1555     adapter = MethodHandles.insertArguments(
1556         target, 1, new Object[] { (short) 56, Integer.valueOf(57) });
1557     assertEquals(73, (int) adapter.invokeExact("foo", "bar"));
1558 
1559     // Insert an argument at the last position.
1560     adapter = MethodHandles.insertArguments(
1561         target, 3, new Object[] { "bar" });
1562     assertEquals(73, (int) adapter.invokeExact("foo", 45, Integer.valueOf(46)));
1563 
1564     // Exercise a few error cases.
1565 
1566     // A reference type that can't be cast to another reference type.
1567     try {
1568       MethodHandles.insertArguments(target, 3, new Object[] { new Object() });
1569       fail();
1570     } catch (ClassCastException expected) {
1571     }
1572 
1573     // A boxed type that can't be unboxed correctly.
1574     try {
1575       MethodHandles.insertArguments(target, 1, new Object[] { Long.valueOf(56) });
1576       fail();
1577     } catch (ClassCastException expected) {
1578     }
1579   }
1580 
foldFilter(char a, char b)1581   public static String foldFilter(char a, char b) {
1582     return String.valueOf(a) + "+" + b;
1583   }
1584 
voidFoldFilter(String e, char a, char b)1585   public static void voidFoldFilter(String e, char a, char b) {
1586     System.out.println(String.valueOf(a) + "+" + b);
1587   }
1588 
foldTarget(String a, char b, char c, String d)1589   public static int foldTarget(String a, char b, char c, String d) {
1590     System.out.println("a: " + a + " ,b:" + b + " ,c:" + c + " ,d:" + d);
1591     return 89;
1592   }
1593 
mismatchedVoidFilter(Integer a)1594   public static void mismatchedVoidFilter(Integer a) {
1595   }
1596 
mismatchedNonVoidFilter(char a, char b)1597   public static Integer mismatchedNonVoidFilter(char a, char b) {
1598     return null;
1599   }
1600 
testFoldArguments()1601   public static void testFoldArguments() throws Throwable {
1602     // Test non-void filters.
1603     MethodHandle filter = MethodHandles.lookup().findStatic(
1604         Main.class, "foldFilter",
1605         MethodType.methodType(String.class, char.class, char.class));
1606 
1607     MethodHandle target = MethodHandles.lookup().findStatic(
1608         Main.class, "foldTarget",
1609         MethodType.methodType(int.class, String.class,
1610           char.class, char.class, String.class));
1611 
1612     // Folder with a non-void type.
1613     MethodHandle adapter = MethodHandles.foldArguments(target, filter);
1614     assertEquals(89, (int) adapter.invokeExact('c', 'd', "e"));
1615 
1616     // Folder with a void type.
1617     filter = MethodHandles.lookup().findStatic(
1618         Main.class, "voidFoldFilter",
1619         MethodType.methodType(void.class, String.class, char.class, char.class));
1620     adapter = MethodHandles.foldArguments(target, filter);
1621     assertEquals(89, (int) adapter.invokeExact("a", 'c', 'd', "e"));
1622 
1623     // Test a few erroneous cases.
1624 
1625     filter = MethodHandles.lookup().findStatic(
1626         Main.class, "mismatchedVoidFilter",
1627         MethodType.methodType(void.class, Integer.class));
1628     try {
1629       adapter = MethodHandles.foldArguments(target, filter);
1630       fail();
1631     } catch (IllegalArgumentException expected) {
1632     }
1633 
1634     filter = MethodHandles.lookup().findStatic(
1635         Main.class, "mismatchedNonVoidFilter",
1636         MethodType.methodType(Integer.class, char.class, char.class));
1637     try {
1638       adapter = MethodHandles.foldArguments(target, filter);
1639       fail();
1640     } catch (IllegalArgumentException expected) {
1641     }
1642   }
1643 
fail()1644   public static void fail() {
1645     System.out.println("FAIL");
1646     Thread.dumpStack();
1647   }
1648 
fail(String message)1649   public static void fail(String message) {
1650     System.out.println("fail: " + message);
1651     Thread.dumpStack();
1652   }
1653 
assertEquals(int i1, int i2)1654   public static void assertEquals(int i1, int i2) {
1655     if (i1 != i2) throw new AssertionError("Expected: " + i1 + " was " + i2);
1656   }
1657 
assertEquals(String s1, String s2)1658   public static void assertEquals(String s1, String s2) {
1659     if (s1 == s2) {
1660       return;
1661     }
1662 
1663     if (s1 != null && s2 != null && s1.equals(s2)) {
1664       return;
1665     }
1666 
1667     throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
1668   }
1669 }
1670