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