1/*
2 * Copyright (C) 2017 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
17package libcore.java.lang.invoke;
18
19import java.lang.Thread;
20import java.lang.invoke.MethodHandle;
21import java.lang.invoke.MethodHandles;
22import java.lang.invoke.MethodType;
23import java.lang.invoke.WrongMethodTypeException;
24import java.util.ArrayList;
25import java.util.Arrays;
26
27import junit.framework.TestCase;
28
29public class MethodHandleCombinersTest extends TestCase {
30
31    static final int TEST_THREAD_ITERATIONS = 1000;
32
33    public static void testThrowException() throws Throwable {
34        MethodHandle handle = MethodHandles.throwException(String.class,
35                IllegalArgumentException.class);
36
37        if (handle.type().returnType() != String.class) {
38            fail("Unexpected return type for handle: " + handle +
39                    " [ " + handle.type() + "]");
40        }
41
42        final IllegalArgumentException iae = new IllegalArgumentException("boo!");
43        try {
44            handle.invoke(iae);
45            fail("Expected an exception of type: java.lang.IllegalArgumentException");
46        } catch (IllegalArgumentException expected) {
47            if (expected != iae) {
48                fail("Wrong exception: expected " + iae + " but was " + expected);
49            }
50        }
51    }
52
53    public static void dropArguments_delegate(String message, long message2) {
54        assertEquals("foo", message);
55        assertEquals(42l, message2);
56    }
57
58    public static void testDropArguments() throws Throwable {
59        MethodHandle delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
60                "dropArguments_delegate",
61                MethodType.methodType(void.class, new Class<?>[]{String.class, long.class}));
62
63        MethodHandle transform = MethodHandles.dropArguments(
64                delegate, 0, int.class, Object.class);
65
66        // The transformer will accept two additional arguments at position zero.
67        try {
68            transform.invokeExact("foo", 42l);
69            fail();
70        } catch (WrongMethodTypeException expected) {
71        }
72
73        transform.invokeExact(45, new Object(), "foo", 42l);
74        transform.invoke(45, new Object(), "foo", 42l);
75
76        // Additional arguments at position 1.
77        transform = MethodHandles.dropArguments(delegate, 1, int.class, Object.class);
78        transform.invokeExact("foo", 45, new Object(), 42l);
79        transform.invoke("foo", 45, new Object(), 42l);
80
81        // Additional arguments at position 2.
82        transform = MethodHandles.dropArguments(delegate, 2, int.class, Object.class);
83        transform.invokeExact("foo", 42l, 45, new Object());
84        transform.invoke("foo", 42l, 45, new Object());
85
86        // Note that we still perform argument conversions even for the arguments that
87        // are subsequently dropped.
88        try {
89            transform.invoke("foo", 42l, 45l, new Object());
90            fail();
91        } catch (WrongMethodTypeException expected) {
92        } catch (IllegalArgumentException expected) {
93            // TODO(narayan): We currently throw the wrong type of exception here,
94            // it's IAE and should be WMTE instead.
95        }
96
97        // Check that asType works as expected.
98        transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class);
99        transform = transform.asType(MethodType.methodType(void.class,
100                new Class<?>[]{short.class, Object.class, String.class, long.class}));
101        transform.invokeExact((short) 45, new Object(), "foo", 42l);
102
103        // Invalid argument location, should not be allowed.
104        try {
105            MethodHandles.dropArguments(delegate, -1, int.class, Object.class);
106            fail();
107        } catch (IllegalArgumentException expected) {
108        }
109
110        // Invalid argument location, should not be allowed.
111        try {
112            MethodHandles.dropArguments(delegate, 3, int.class, Object.class);
113            fail();
114        } catch (IllegalArgumentException expected) {
115        }
116
117        try {
118            MethodHandles.dropArguments(delegate, 1, void.class);
119            fail();
120        } catch (IllegalArgumentException expected) {
121        }
122    }
123
124    public static void testDropArguments_List() throws Throwable {
125        MethodHandle delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
126                "dropArguments_delegate",
127                MethodType.methodType(void.class, new Class<?>[]{String.class, long.class}));
128
129        MethodHandle transform = MethodHandles.dropArguments(
130                delegate, 0, Arrays.asList(int.class, Object.class));
131
132        transform.invokeExact(45, new Object(), "foo", 42l);
133        transform.invoke(45, new Object(), "foo", 42l);
134
135        // Check that asType works as expected.
136        transform = transform.asType(MethodType.methodType(void.class,
137                new Class<?>[]{short.class, Object.class, String.class, long.class}));
138        transform.invokeExact((short) 45, new Object(), "foo", 42l);
139    }
140
141    public static String testCatchException_target(String arg1, long arg2, String exceptionMessage)
142            throws Throwable {
143        if (exceptionMessage != null) {
144            throw new IllegalArgumentException(exceptionMessage);
145        }
146
147        assertEquals(null, exceptionMessage);
148        assertEquals(42l, arg2);
149        return "target";
150    }
151
152    public static String testCatchException_handler(IllegalArgumentException iae, String arg1,
153                                                    long arg2,
154                                                    String exMsg) {
155        // Check that the thrown exception has the right message.
156        assertEquals("exceptionMessage", iae.getMessage());
157        // Check the other arguments.
158        assertEquals("foo", arg1);
159        assertEquals(42, arg2);
160        assertEquals("exceptionMessage", exMsg);
161
162        return "handler1";
163    }
164
165    public static String testCatchException_handler2(IllegalArgumentException iae, String arg1) {
166        assertEquals("exceptionMessage", iae.getMessage());
167        assertEquals("foo", arg1);
168
169        return "handler2";
170    }
171
172    public static void testCatchException() throws Throwable {
173        MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
174                "testCatchException_target",
175                MethodType
176                        .methodType(String.class, new Class<?>[]{String.class, long.class, String.class}));
177
178        MethodHandle handler = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
179                "testCatchException_handler",
180                MethodType.methodType(String.class, new Class<?>[]{IllegalArgumentException.class,
181                        String.class, long.class, String.class}));
182
183        MethodHandle adapter = MethodHandles.catchException(target, IllegalArgumentException.class,
184                handler);
185
186        String returnVal = null;
187
188        // These two should end up calling the target always. We're passing a null exception
189        // message here, which means the target will not throw.
190        returnVal = (String) adapter.invoke("foo", 42, null);
191        assertEquals("target", returnVal);
192        returnVal = (String) adapter.invokeExact("foo", 42l, (String) null);
193        assertEquals("target", returnVal);
194
195        // We're passing a non-null exception message here, which means the target will throw,
196        // which in turn means that the handler must be called for the next two invokes.
197        returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
198        assertEquals("handler1", returnVal);
199        returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage");
200        assertEquals("handler1", returnVal);
201
202        handler = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
203                "testCatchException_handler2",
204                MethodType.methodType(String.class, new Class<?>[]{IllegalArgumentException.class,
205                        String.class}));
206        adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler);
207
208        returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
209        assertEquals("handler2", returnVal);
210        returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage");
211        assertEquals("handler2", returnVal);
212
213        // Test that the type of the invoke doesn't matter. Here we call
214        // IllegalArgumentException.toString() on the exception that was thrown by
215        // the target.
216        handler = MethodHandles.lookup().findVirtual(IllegalArgumentException.class,
217                "toString", MethodType.methodType(String.class));
218        adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler);
219
220        returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
221        assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal);
222        returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage");
223        assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal);
224
225        // Check that asType works as expected.
226        adapter = MethodHandles.catchException(target, IllegalArgumentException.class,
227                handler);
228        adapter = adapter.asType(MethodType.methodType(String.class,
229                new Class<?>[]{String.class, int.class, String.class}));
230        returnVal = (String) adapter.invokeExact("foo", 42, "exceptionMessage");
231        assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal);
232    }
233
234    public static boolean testGuardWithTest_test(String arg1, long arg2) {
235        return "target".equals(arg1) && 42 == arg2;
236    }
237
238    public static String testGuardWithTest_target(String arg1, long arg2, int arg3) {
239        // Make sure that the test passed.
240        assertTrue(testGuardWithTest_test(arg1, arg2));
241        // Make sure remaining arguments were passed through unmodified.
242        assertEquals(56, arg3);
243
244        return "target";
245    }
246
247    public static String testGuardWithTest_fallback(String arg1, long arg2, int arg3) {
248        // Make sure that the test failed.
249        assertTrue(!testGuardWithTest_test(arg1, arg2));
250        // Make sure remaining arguments were passed through unmodified.
251        assertEquals(56, arg3);
252
253        return "fallback";
254    }
255
256    public static void testGuardWithTest() throws Throwable {
257        MethodHandle test = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
258                "testGuardWithTest_test",
259                MethodType.methodType(boolean.class, new Class<?>[]{String.class, long.class}));
260
261        final MethodType type = MethodType.methodType(String.class,
262                new Class<?>[]{String.class, long.class, int.class});
263
264        final MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
265                "testGuardWithTest_target", type);
266        final MethodHandle fallback = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
267                "testGuardWithTest_fallback", type);
268
269        MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback);
270
271        String returnVal = null;
272
273        returnVal = (String) adapter.invoke("target", 42, 56);
274        assertEquals("target", returnVal);
275        returnVal = (String) adapter.invokeExact("target", 42l, 56);
276        assertEquals("target", returnVal);
277
278        returnVal = (String) adapter.invoke("fallback", 42l, 56);
279        assertEquals("fallback", returnVal);
280        returnVal = (String) adapter.invoke("target", 46l, 56);
281        assertEquals("fallback", returnVal);
282        returnVal = (String) adapter.invokeExact("target", 42l, 56);
283        assertEquals("target", returnVal);
284
285        // Check that asType works as expected.
286        adapter = adapter.asType(MethodType.methodType(String.class,
287                new Class<?>[]{String.class, int.class, int.class}));
288        returnVal = (String) adapter.invokeExact("target", 42, 56);
289        assertEquals("target", returnVal);
290    }
291
292    public static void testArrayElementGetter() throws Throwable {
293        MethodHandle getter = MethodHandles.arrayElementGetter(int[].class);
294
295        {
296            int[] array = new int[1];
297            array[0] = 42;
298            int value = (int) getter.invoke(array, 0);
299            assertEquals(42, value);
300
301            try {
302                value = (int) getter.invoke(array, -1);
303                fail();
304            } catch (ArrayIndexOutOfBoundsException expected) {
305            }
306
307            try {
308                value = (int) getter.invoke(null, -1);
309                fail();
310            } catch (NullPointerException expected) {
311            }
312        }
313
314        {
315            getter = MethodHandles.arrayElementGetter(long[].class);
316            long[] array = new long[1];
317            array[0] = 42;
318            long value = (long) getter.invoke(array, 0);
319            assertEquals(42l, value);
320        }
321
322        {
323            getter = MethodHandles.arrayElementGetter(short[].class);
324            short[] array = new short[1];
325            array[0] = 42;
326            short value = (short) getter.invoke(array, 0);
327            assertEquals((short) 42, value);
328        }
329
330        {
331            getter = MethodHandles.arrayElementGetter(char[].class);
332            char[] array = new char[1];
333            array[0] = 42;
334            char value = (char) getter.invoke(array, 0);
335            assertEquals((char) 42, value);
336        }
337
338        {
339            getter = MethodHandles.arrayElementGetter(byte[].class);
340            byte[] array = new byte[1];
341            array[0] = (byte) 0x8;
342            byte value = (byte) getter.invoke(array, 0);
343            assertEquals((byte) 0x8, value);
344        }
345
346        {
347            getter = MethodHandles.arrayElementGetter(boolean[].class);
348            boolean[] array = new boolean[1];
349            array[0] = true;
350            boolean value = (boolean) getter.invoke(array, 0);
351            assertTrue(value);
352        }
353
354        {
355            getter = MethodHandles.arrayElementGetter(float[].class);
356            float[] array = new float[1];
357            array[0] = 42.0f;
358            float value = (float) getter.invoke(array, 0);
359            assertEquals(42.0f, value);
360        }
361
362        {
363            getter = MethodHandles.arrayElementGetter(double[].class);
364            double[] array = new double[1];
365            array[0] = 42.0;
366            double value = (double) getter.invoke(array, 0);
367            assertEquals(42.0, value);
368        }
369
370        {
371            getter = MethodHandles.arrayElementGetter(String[].class);
372            String[] array = new String[3];
373            array[0] = "42";
374            array[1] = "48";
375            array[2] = "54";
376            String value = (String) getter.invoke(array, 0);
377            assertEquals("42", value);
378            value = (String) getter.invoke(array, 1);
379            assertEquals("48", value);
380            value = (String) getter.invoke(array, 2);
381            assertEquals("54", value);
382        }
383    }
384
385    public static void testArrayElementSetter() throws Throwable {
386        MethodHandle setter = MethodHandles.arrayElementSetter(int[].class);
387
388        {
389            int[] array = new int[2];
390            setter.invoke(array, 0, 42);
391            setter.invoke(array, 1, 43);
392
393            assertEquals(42, array[0]);
394            assertEquals(43, array[1]);
395
396            try {
397                setter.invoke(array, -1, 42);
398                fail();
399            } catch (ArrayIndexOutOfBoundsException expected) {
400            }
401
402            try {
403                setter.invoke(null, 0, 42);
404                fail();
405            } catch (NullPointerException expected) {
406            }
407        }
408
409        {
410            setter = MethodHandles.arrayElementSetter(long[].class);
411            long[] array = new long[1];
412            setter.invoke(array, 0, 42l);
413            assertEquals(42l, array[0]);
414        }
415
416        {
417            setter = MethodHandles.arrayElementSetter(short[].class);
418            short[] array = new short[1];
419            setter.invoke(array, 0, (short) 42);
420            assertEquals((short) 42, array[0]);
421        }
422
423        {
424            setter = MethodHandles.arrayElementSetter(char[].class);
425            char[] array = new char[1];
426            setter.invoke(array, 0, (char) 42);
427            assertEquals((char) 42, array[0]);
428        }
429
430        {
431            setter = MethodHandles.arrayElementSetter(byte[].class);
432            byte[] array = new byte[1];
433            setter.invoke(array, 0, (byte) 0x8);
434            assertEquals((byte) 0x8, array[0]);
435        }
436
437        {
438            setter = MethodHandles.arrayElementSetter(boolean[].class);
439            boolean[] array = new boolean[1];
440            setter.invoke(array, 0, true);
441            assertTrue(array[0]);
442        }
443
444        {
445            setter = MethodHandles.arrayElementSetter(float[].class);
446            float[] array = new float[1];
447            setter.invoke(array, 0, 42.0f);
448            assertEquals(42.0f, array[0]);
449        }
450
451        {
452            setter = MethodHandles.arrayElementSetter(double[].class);
453            double[] array = new double[1];
454            setter.invoke(array, 0, 42.0);
455            assertEquals(42.0, array[0]);
456        }
457
458        {
459            setter = MethodHandles.arrayElementSetter(String[].class);
460            String[] array = new String[3];
461            setter.invoke(array, 0, "42");
462            setter.invoke(array, 1, "48");
463            setter.invoke(array, 2, "54");
464            assertEquals("42", array[0]);
465            assertEquals("48", array[1]);
466            assertEquals("54", array[2]);
467        }
468    }
469
470    public static void testIdentity() throws Throwable {
471        {
472            MethodHandle identity = MethodHandles.identity(boolean.class);
473            boolean value = (boolean) identity.invoke(false);
474            assertFalse(value);
475        }
476
477        {
478            MethodHandle identity = MethodHandles.identity(byte.class);
479            byte value = (byte) identity.invoke((byte) 0x8);
480            assertEquals((byte) 0x8, value);
481        }
482
483        {
484            MethodHandle identity = MethodHandles.identity(char.class);
485            char value = (char) identity.invoke((char) -56);
486            assertEquals((char) -56, value);
487        }
488
489        {
490            MethodHandle identity = MethodHandles.identity(short.class);
491            short value = (short) identity.invoke((short) -59);
492            assertEquals((short) -59, value);
493        }
494
495        {
496            MethodHandle identity = MethodHandles.identity(int.class);
497            int value = (int) identity.invoke(52);
498            assertEquals((int) 52, value);
499        }
500
501        {
502            MethodHandle identity = MethodHandles.identity(long.class);
503            long value = (long) identity.invoke(-76l);
504            assertEquals(-76l, value);
505        }
506
507        {
508            MethodHandle identity = MethodHandles.identity(float.class);
509            float value = (float) identity.invoke(56.0f);
510            assertEquals(56.0f, value);
511        }
512
513        {
514            MethodHandle identity = MethodHandles.identity(double.class);
515            double value = (double) identity.invoke((double) 72.0);
516            assertEquals(72.0, value);
517        }
518
519        {
520            MethodHandle identity = MethodHandles.identity(String.class);
521            String value = (String) identity.invoke("bazman");
522            assertEquals("bazman", value);
523        }
524    }
525
526    public static void testConstant() throws Throwable {
527        // int constants.
528        {
529            MethodHandle constant = MethodHandles.constant(int.class, 56);
530            assertEquals(56, (int) constant.invoke());
531
532            // short constant values are converted to int.
533            constant = MethodHandles.constant(int.class, (short) 52);
534            assertEquals(52, (int) constant.invoke());
535
536            // char constant values are converted to int.
537            constant = MethodHandles.constant(int.class, (char) 'b');
538            assertEquals('b', (int) constant.invoke());
539
540            // int constant values are converted to int.
541            constant = MethodHandles.constant(int.class, (byte) 0x1);
542            assertEquals(0x1, (int) constant.invoke());
543
544            // boolean, float, double and long primitive constants are not convertible
545            // to int, so the handle creation must fail with a CCE.
546            try {
547                MethodHandles.constant(int.class, false);
548                fail();
549            } catch (ClassCastException expected) {
550            }
551
552            try {
553                MethodHandles.constant(int.class, 0.1f);
554                fail();
555            } catch (ClassCastException expected) {
556            }
557
558            try {
559                MethodHandles.constant(int.class, 0.2);
560                fail();
561            } catch (ClassCastException expected) {
562            }
563
564            try {
565                MethodHandles.constant(int.class, 73l);
566                fail();
567            } catch (ClassCastException expected) {
568            }
569        }
570
571        // long constants.
572        {
573            MethodHandle constant = MethodHandles.constant(long.class, 56l);
574            assertEquals(56l, (long) constant.invoke());
575
576            constant = MethodHandles.constant(long.class, (int) 56);
577            assertEquals(56l, (long) constant.invoke());
578        }
579
580        // byte constants.
581        {
582            MethodHandle constant = MethodHandles.constant(byte.class, (byte) 0x12);
583            assertEquals((byte) 0x12, (byte) constant.invoke());
584        }
585
586        // boolean constants.
587        {
588            MethodHandle constant = MethodHandles.constant(boolean.class, true);
589            assertTrue((boolean) constant.invoke());
590        }
591
592        // char constants.
593        {
594            MethodHandle constant = MethodHandles.constant(char.class, 'f');
595            assertEquals('f', (char) constant.invoke());
596        }
597
598        // short constants.
599        {
600            MethodHandle constant = MethodHandles.constant(short.class, (short) 123);
601            assertEquals((short) 123, (short) constant.invoke());
602        }
603
604        // float constants.
605        {
606            MethodHandle constant = MethodHandles.constant(float.class, 56.0f);
607            assertEquals(56.0f, (float) constant.invoke());
608        }
609
610        // double constants.
611        {
612            MethodHandle constant = MethodHandles.constant(double.class, 256.0);
613            assertEquals(256.0, (double) constant.invoke());
614        }
615
616        // reference constants.
617        {
618            MethodHandle constant = MethodHandles.constant(String.class, "256.0");
619            assertEquals("256.0", (String) constant.invoke());
620        }
621    }
622
623    public static void testBindTo() throws Throwable {
624        MethodHandle stringCharAt = MethodHandles.lookup().findVirtual(
625                String.class, "charAt", MethodType.methodType(char.class, int.class));
626
627        char value = (char) stringCharAt.invoke("foo", 0);
628        if (value != 'f') {
629            fail("Unexpected value: " + value);
630        }
631
632        MethodHandle bound = stringCharAt.bindTo("foo");
633        value = (char) bound.invoke(0);
634        if (value != 'f') {
635            fail("Unexpected value: " + value);
636        }
637
638        try {
639            stringCharAt.bindTo(new Object());
640            fail();
641        } catch (ClassCastException expected) {
642        }
643
644        bound = stringCharAt.bindTo(null);
645        try {
646            bound.invoke(0);
647            fail();
648        } catch (NullPointerException expected) {
649        }
650
651        MethodHandle integerParseInt = MethodHandles.lookup().findStatic(
652                Integer.class, "parseInt", MethodType.methodType(int.class, String.class));
653
654        bound = integerParseInt.bindTo("78452");
655        int intValue = (int) bound.invoke();
656        if (intValue != 78452) {
657            fail("Unexpected value: " + intValue);
658        }
659    }
660
661    public static String filterReturnValue_target(int a) {
662        return "ReturnValue" + a;
663    }
664
665    public static boolean filterReturnValue_filter(String value) {
666        return value.indexOf("42") != -1;
667    }
668
669    public static int filterReturnValue_intTarget(String a) {
670        return Integer.parseInt(a);
671    }
672
673    public static int filterReturnValue_intFilter(int b) {
674        return b + 1;
675    }
676
677    public static void filterReturnValue_voidTarget() {
678    }
679
680    public static int filterReturnValue_voidFilter() {
681        return 42;
682    }
683
684    public static void testFilterReturnValue() throws Throwable {
685        // A target that returns a reference.
686        {
687            final MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
688                    "filterReturnValue_target", MethodType.methodType(String.class, int.class));
689            final MethodHandle filter = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
690                    "filterReturnValue_filter", MethodType.methodType(boolean.class, String.class));
691
692            MethodHandle adapter = MethodHandles.filterReturnValue(target, filter);
693
694            boolean value = (boolean) adapter.invoke((int) 42);
695            if (!value) {
696                fail("Unexpected value: " + value);
697            }
698            value = (boolean) adapter.invoke((int) 43);
699            if (value) {
700                fail("Unexpected value: " + value);
701            }
702        }
703
704        // A target that returns a primitive.
705        {
706            final MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
707                    "filterReturnValue_intTarget", MethodType.methodType(int.class, String.class));
708            final MethodHandle filter = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
709                    "filterReturnValue_intFilter", MethodType.methodType(int.class, int.class));
710
711            MethodHandle adapter = MethodHandles.filterReturnValue(target, filter);
712
713            int value = (int) adapter.invoke("56");
714            if (value != 57) {
715                fail("Unexpected value: " + value);
716            }
717        }
718
719        // A target that returns void.
720        {
721            final MethodHandle target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
722                    "filterReturnValue_voidTarget", MethodType.methodType(void.class));
723            final MethodHandle filter = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
724                    "filterReturnValue_voidFilter", MethodType.methodType(int.class));
725
726            MethodHandle adapter = MethodHandles.filterReturnValue(target, filter);
727
728            int value = (int) adapter.invoke();
729            if (value != 42) {
730                fail("Unexpected value: " + value);
731            }
732        }
733    }
734
735    public static void permuteArguments_callee(boolean a, byte b, char c,
736                                               short d, int e, long f, float g, double h) {
737        assertTrue(a);
738        assertEquals((byte) 'b', b);
739        assertEquals('c', c);
740        assertEquals((short) 56, d);
741        assertEquals(78, e);
742        assertEquals(97l, f);
743        assertEquals(98.0f, g);
744        assertEquals(97.0, h);
745    }
746
747    public static void permuteArguments_boxingCallee(boolean a, Integer b) {
748        assertTrue(a);
749        assertEquals(Integer.valueOf(42), b);
750    }
751
752    public static void testPermuteArguments() throws Throwable {
753        {
754            final MethodHandle target = MethodHandles.lookup().findStatic(
755                    MethodHandleCombinersTest.class, "permuteArguments_callee",
756                    MethodType.methodType(void.class, new Class<?>[]{
757                            boolean.class, byte.class, char.class, short.class, int.class,
758                            long.class, float.class, double.class}));
759
760            final MethodType newType = MethodType.methodType(void.class, new Class<?>[]{
761                    double.class, float.class, long.class, int.class, short.class, char.class,
762                    byte.class, boolean.class});
763
764            final MethodHandle permutation = MethodHandles.permuteArguments(
765                    target, newType, new int[]{7, 6, 5, 4, 3, 2, 1, 0});
766
767            permutation.invoke((double) 97.0, (float) 98.0f, (long) 97, 78,
768                    (short) 56, 'c', (byte) 'b', (boolean) true);
769
770            // The permutation array was not of the right length.
771            try {
772                MethodHandles.permuteArguments(target, newType,
773                        new int[]{7});
774                fail();
775            } catch (IllegalArgumentException expected) {
776            }
777
778            // The permutation array has an element that's out of bounds
779            // (there's no argument with idx == 8).
780            try {
781                MethodHandles.permuteArguments(target, newType,
782                        new int[]{8, 6, 5, 4, 3, 2, 1, 0});
783                fail();
784            } catch (IllegalArgumentException expected) {
785            }
786
787            // The permutation array maps to an incorrect type.
788            try {
789                MethodHandles.permuteArguments(target, newType,
790                        new int[]{7, 7, 5, 4, 3, 2, 1, 0});
791                fail();
792            } catch (IllegalArgumentException expected) {
793            }
794        }
795
796        // Tests for reference arguments as well as permutations that
797        // repeat arguments.
798        {
799            final MethodHandle target = MethodHandles.lookup().findVirtual(
800                    String.class, "concat", MethodType.methodType(String.class, String.class));
801
802            final MethodType newType = MethodType.methodType(String.class, String.class,
803                    String.class);
804
805            assertEquals("foobar", (String) target.invoke("foo", "bar"));
806
807            MethodHandle permutation = MethodHandles.permuteArguments(target,
808                    newType, new int[]{1, 0});
809            assertEquals("barfoo", (String) permutation.invoke("foo", "bar"));
810
811            permutation = MethodHandles.permuteArguments(target, newType, new int[]{0, 0});
812            assertEquals("foofoo", (String) permutation.invoke("foo", "bar"));
813
814            permutation = MethodHandles.permuteArguments(target, newType, new int[]{1, 1});
815            assertEquals("barbar", (String) permutation.invoke("foo", "bar"));
816        }
817
818        // Tests for boxing and unboxing.
819        {
820            final MethodHandle target = MethodHandles.lookup().findStatic(
821                    MethodHandleCombinersTest.class, "permuteArguments_boxingCallee",
822                    MethodType.methodType(void.class, new Class<?>[]{boolean.class, Integer.class}));
823
824            final MethodType newType = MethodType.methodType(void.class,
825                    new Class<?>[]{Integer.class, boolean.class});
826
827            MethodHandle permutation = MethodHandles.permuteArguments(target,
828                    newType, new int[]{1, 0});
829
830            permutation.invoke(42, true);
831            permutation.invoke(42, Boolean.TRUE);
832            permutation.invoke(Integer.valueOf(42), true);
833            permutation.invoke(Integer.valueOf(42), Boolean.TRUE);
834        }
835    }
836
837    private static Object returnBar() {
838        return "bar";
839    }
840
841    public static void testInvokers() throws Throwable {
842        final MethodType targetType = MethodType.methodType(String.class, String.class);
843        final MethodHandle target = MethodHandles.lookup().findVirtual(
844                String.class, "concat", targetType);
845
846        MethodHandle invoker = MethodHandles.invoker(target.type());
847        assertEquals("barbar", (String) invoker.invoke(target, "bar", "bar"));
848        assertEquals("barbar", (String) invoker.invoke(target, (Object) returnBar(), "bar"));
849        try {
850            String foo = (String) invoker.invoke(target, "bar", "bar", 24);
851            fail();
852        } catch (WrongMethodTypeException expected) {
853        }
854
855        MethodHandle exactInvoker = MethodHandles.exactInvoker(target.type());
856        assertEquals("barbar", (String) exactInvoker.invoke(target, "bar", "bar"));
857        try {
858            String foo = (String) exactInvoker.invoke(target, (Object) returnBar(), "bar");
859            fail();
860        } catch (WrongMethodTypeException expected) {
861        }
862        try {
863            String foo = (String) exactInvoker.invoke(target, "bar", "bar", 24);
864            fail();
865        } catch (WrongMethodTypeException expected) {
866        }
867    }
868
869    public static int spreadReferences(String a, String b, String c) {
870        assertEquals("a", a);
871        assertEquals("b", b);
872        assertEquals("c", c);
873        return 42;
874    }
875
876    public static int spreadReferences_Unbox(String a, int b) {
877        assertEquals("a", a);
878        assertEquals(43, b);
879        return 43;
880    }
881
882    public static void testSpreaders_reference() throws Throwable {
883        MethodType methodType = MethodType.methodType(int.class,
884                new Class<?>[]{String.class, String.class, String.class});
885        MethodHandle delegate = MethodHandles.lookup().findStatic(
886                MethodHandleCombinersTest.class, "spreadReferences", methodType);
887
888        // Basic checks on array lengths.
889        //
890        // Array size = 0
891        MethodHandle mhAsSpreader = delegate.asSpreader(String[].class, 0);
892        int ret = (int) mhAsSpreader.invoke("a", "b", "c", new String[]{});
893        assertEquals(42, ret);
894        // Array size = 1
895        mhAsSpreader = delegate.asSpreader(String[].class, 1);
896        ret = (int) mhAsSpreader.invoke("a", "b", new String[]{"c"});
897        assertEquals(42, ret);
898        // Array size = 2
899        mhAsSpreader = delegate.asSpreader(String[].class, 2);
900        ret = (int) mhAsSpreader.invoke("a", new String[]{"b", "c"});
901        assertEquals(42, ret);
902        // Array size = 3
903        mhAsSpreader = delegate.asSpreader(String[].class, 3);
904        ret = (int) mhAsSpreader.invoke(new String[]{"a", "b", "c"});
905        assertEquals(42, ret);
906
907        // Exception case, array size = 4 is illegal.
908        try {
909            delegate.asSpreader(String[].class, 4);
910            fail();
911        } catch (IllegalArgumentException expected) {
912        }
913
914        // Exception case, calling with an arg of the wrong size.
915        // Array size = 3
916        mhAsSpreader = delegate.asSpreader(String[].class, 3);
917        try {
918            ret = (int) mhAsSpreader.invoke(new String[]{"a", "b"});
919        } catch (IllegalArgumentException expected) {
920        }
921
922        // Various other hijinks, pass as Object[] arrays, Object etc.
923        mhAsSpreader = delegate.asSpreader(Object[].class, 2);
924        ret = (int) mhAsSpreader.invoke("a", new String[]{"b", "c"});
925        assertEquals(42, ret);
926
927        mhAsSpreader = delegate.asSpreader(Object[].class, 2);
928        ret = (int) mhAsSpreader.invoke("a", new Object[]{"b", "c"});
929        assertEquals(42, ret);
930
931        mhAsSpreader = delegate.asSpreader(Object[].class, 2);
932        ret = (int) mhAsSpreader.invoke("a", (Object) new Object[]{"b", "c"});
933        assertEquals(42, ret);
934
935        // Test implicit unboxing.
936        MethodType methodType2 = MethodType.methodType(int.class,
937                new Class<?>[]{String.class, int.class});
938        MethodHandle delegate2 = MethodHandles.lookup().findStatic(
939                MethodHandleCombinersTest.class, "spreadReferences_Unbox", methodType2);
940
941        // .. with an Integer[] array.
942        mhAsSpreader = delegate2.asSpreader(Integer[].class, 1);
943        ret = (int) mhAsSpreader.invoke("a", new Integer[]{43});
944        assertEquals(43, ret);
945
946        // .. with an Integer[] array declared as an Object[] argument type.
947        mhAsSpreader = delegate2.asSpreader(Object[].class, 1);
948        ret = (int) mhAsSpreader.invoke("a", new Integer[]{43});
949        assertEquals(43, ret);
950
951        // .. with an Object[] array.
952        mhAsSpreader = delegate2.asSpreader(Object[].class, 1);
953        ret = (int) mhAsSpreader.invoke("a", new Object[]{Integer.valueOf(43)});
954        assertEquals(43, ret);
955
956        // -- Part 2--
957        // Run a subset of these tests on MethodHandles.spreadInvoker, which only accepts
958        // a trailing argument type of Object[].
959        MethodHandle spreadInvoker = MethodHandles.spreadInvoker(methodType2, 1);
960        ret = (int) spreadInvoker.invoke(delegate2, "a", new Object[]{Integer.valueOf(43)});
961        assertEquals(43, ret);
962
963        ret = (int) spreadInvoker.invoke(delegate2, "a", new Integer[]{43});
964        assertEquals(43, ret);
965
966        // NOTE: Annoyingly, the second argument here is leadingArgCount and not
967        // arrayLength.
968        spreadInvoker = MethodHandles.spreadInvoker(methodType, 3);
969        ret = (int) spreadInvoker.invoke(delegate, "a", "b", "c", new String[]{});
970        assertEquals(42, ret);
971
972        spreadInvoker = MethodHandles.spreadInvoker(methodType, 0);
973        ret = (int) spreadInvoker.invoke(delegate, new String[]{"a", "b", "c"});
974        assertEquals(42, ret);
975
976        // Exact invokes: Double check that the expected parameter type is
977        // Object[] and not T[].
978        try {
979            spreadInvoker.invokeExact(delegate, new String[]{"a", "b", "c"});
980            fail();
981        } catch (WrongMethodTypeException expected) {
982        }
983
984        ret = (int) spreadInvoker.invoke(delegate, new Object[]{"a", "b", "c"});
985        assertEquals(42, ret);
986    }
987
988    public static int spreadBoolean(String a, Boolean b, boolean c) {
989        assertEquals("a", a);
990        assertSame(Boolean.TRUE, b);
991        assertFalse(c);
992
993        return 44;
994    }
995
996    public static int spreadByte(String a, Byte b, byte c,
997                                 short d, int e, long f, float g, double h) {
998        assertEquals("a", a);
999        assertEquals(Byte.valueOf((byte) 1), b);
1000        assertEquals((byte) 2, c);
1001        assertEquals((short) 3, d);
1002        assertEquals(4, e);
1003        assertEquals(5l, f);
1004        assertEquals(6.0f, g);
1005        assertEquals(7.0, h);
1006
1007        return 45;
1008    }
1009
1010    public static int spreadChar(String a, Character b, char c,
1011                                 int d, long e, float f, double g) {
1012        assertEquals("a", a);
1013        assertEquals(Character.valueOf('1'), b);
1014        assertEquals('2', c);
1015        assertEquals((short) '3', d);
1016        assertEquals('4', e);
1017        assertEquals((float) '5', f);
1018        assertEquals((double) '6', g);
1019
1020        return 46;
1021    }
1022
1023    public static int spreadShort(String a, Short b, short c,
1024                                  int d, long e, float f, double g) {
1025        assertEquals("a", a);
1026        assertEquals(Short.valueOf((short) 1), b);
1027        assertEquals(2, c);
1028        assertEquals(3, d);
1029        assertEquals(4l, e);
1030        assertEquals(5.0f, f);
1031        assertEquals(6.0, g);
1032
1033        return 47;
1034    }
1035
1036    public static int spreadInt(String a, Integer b, int c,
1037                                long d, float e, double f) {
1038        assertEquals("a", a);
1039        assertEquals(Integer.valueOf(1), b);
1040        assertEquals(2, c);
1041        assertEquals(3l, d);
1042        assertEquals(4.0f, e);
1043        assertEquals(5.0, f);
1044
1045        return 48;
1046    }
1047
1048    public static int spreadLong(String a, Long b, long c, float d, double e) {
1049        assertEquals("a", a);
1050        assertEquals(Long.valueOf(1), b);
1051        assertEquals(2l, c);
1052        assertEquals(3.0f, d);
1053        assertEquals(4.0, e);
1054
1055        return 49;
1056    }
1057
1058    public static int spreadFloat(String a, Float b, float c, double d) {
1059        assertEquals("a", a);
1060        assertEquals(Float.valueOf(1.0f), b);
1061        assertEquals(2.0f, c);
1062        assertEquals(3.0, d);
1063
1064        return 50;
1065    }
1066
1067    public static int spreadDouble(String a, Double b, double c) {
1068        assertEquals("a", a);
1069        assertEquals(Double.valueOf(1.0), b);
1070        assertEquals(2.0, c);
1071
1072        return 51;
1073    }
1074
1075    public static void testSpreaders_primitive() throws Throwable {
1076        // boolean[]
1077        // ---------------------
1078        MethodType type = MethodType.methodType(int.class,
1079                new Class<?>[]{String.class, Boolean.class, boolean.class});
1080        MethodHandle delegate = MethodHandles.lookup().findStatic(
1081                MethodHandleCombinersTest.class, "spreadBoolean", type);
1082
1083        MethodHandle spreader = delegate.asSpreader(boolean[].class, 2);
1084        int ret = (int) spreader.invokeExact("a", new boolean[]{true, false});
1085        assertEquals(44, ret);
1086        ret = (int) spreader.invoke("a", new boolean[]{true, false});
1087        assertEquals(44, ret);
1088
1089        // boolean can't be cast to String (the first argument to the method).
1090        try {
1091            delegate.asSpreader(boolean[].class, 3);
1092            fail();
1093        } catch (WrongMethodTypeException expected) {
1094        }
1095
1096        // int can't be cast to boolean to supply the last argument to the method.
1097        try {
1098            delegate.asSpreader(int[].class, 1);
1099            fail();
1100        } catch (WrongMethodTypeException expected) {
1101        }
1102
1103        // byte[]
1104        // ---------------------
1105        type = MethodType.methodType(int.class,
1106                new Class<?>[]{
1107                        String.class, Byte.class, byte.class,
1108                        short.class, int.class, long.class,
1109                        float.class, double.class});
1110        delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadByte", type);
1111
1112        spreader = delegate.asSpreader(byte[].class, 7);
1113        ret = (int) spreader.invokeExact("a",
1114                new byte[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7});
1115        assertEquals(45, ret);
1116        ret = (int) spreader.invoke("a",
1117                new byte[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7});
1118        assertEquals(45, ret);
1119
1120        // char[]
1121        // ---------------------
1122        type = MethodType.methodType(int.class,
1123                new Class<?>[]{
1124                        String.class, Character.class, char.class,
1125                        int.class, long.class, float.class, double.class});
1126        delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadChar", type);
1127
1128        spreader = delegate.asSpreader(char[].class, 6);
1129        ret = (int) spreader.invokeExact("a",
1130                new char[]{'1', '2', '3', '4', '5', '6'});
1131        assertEquals(46, ret);
1132        ret = (int) spreader.invokeExact("a",
1133                new char[]{'1', '2', '3', '4', '5', '6'});
1134        assertEquals(46, ret);
1135
1136        // short[]
1137        // ---------------------
1138        type = MethodType.methodType(int.class,
1139                new Class<?>[]{
1140                        String.class, Short.class, short.class,
1141                        int.class, long.class, float.class, double.class});
1142        delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadShort", type);
1143
1144        spreader = delegate.asSpreader(short[].class, 6);
1145        ret = (int) spreader.invokeExact("a",
1146                new short[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6});
1147        assertEquals(47, ret);
1148        ret = (int) spreader.invoke("a",
1149                new short[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6});
1150        assertEquals(47, ret);
1151
1152        // int[]
1153        // ---------------------
1154        type = MethodType.methodType(int.class,
1155                new Class<?>[]{
1156                        String.class, Integer.class, int.class,
1157                        long.class, float.class, double.class});
1158        delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadInt", type);
1159
1160        spreader = delegate.asSpreader(int[].class, 5);
1161        ret = (int) spreader.invokeExact("a", new int[]{1, 2, 3, 4, 5});
1162        assertEquals(48, ret);
1163        ret = (int) spreader.invokeExact("a", new int[]{1, 2, 3, 4, 5});
1164        assertEquals(48, ret);
1165
1166        // long[]
1167        // ---------------------
1168        type = MethodType.methodType(int.class,
1169                new Class<?>[]{
1170                        String.class, Long.class, long.class, float.class, double.class});
1171        delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadLong", type);
1172
1173        spreader = delegate.asSpreader(long[].class, 4);
1174        ret = (int) spreader.invokeExact("a",
1175                new long[]{0x1, 0x2, 0x3, 0x4});
1176        assertEquals(49, ret);
1177        ret = (int) spreader.invoke("a",
1178                new long[]{0x1, 0x2, 0x3, 0x4});
1179        assertEquals(49, ret);
1180
1181        // float[]
1182        // ---------------------
1183        type = MethodType.methodType(int.class,
1184                new Class<?>[]{
1185                        String.class, Float.class, float.class, double.class});
1186        delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadFloat", type);
1187
1188        spreader = delegate.asSpreader(float[].class, 3);
1189        ret = (int) spreader.invokeExact("a",
1190                new float[]{1.0f, 2.0f, 3.0f});
1191        assertEquals(50, ret);
1192        ret = (int) spreader.invokeExact("a",
1193                new float[]{1.0f, 2.0f, 3.0f});
1194        assertEquals(50, ret);
1195
1196        // double[]
1197        // ---------------------
1198        type = MethodType.methodType(int.class,
1199                new Class<?>[]{String.class, Double.class, double.class});
1200        delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "spreadDouble", type);
1201
1202        spreader = delegate.asSpreader(double[].class, 2);
1203        ret = (int) spreader.invokeExact("a", new double[]{1.0, 2.0});
1204        assertEquals(51, ret);
1205        ret = (int) spreader.invokeExact("a", new double[]{1.0, 2.0});
1206        assertEquals(51, ret);
1207    }
1208
1209    public static void testInvokeWithArguments() throws Throwable {
1210        MethodType methodType = MethodType.methodType(int.class,
1211                new Class<?>[]{String.class, String.class, String.class});
1212        MethodHandle handle = MethodHandles.lookup().findStatic(
1213                MethodHandleCombinersTest.class, "spreadReferences", methodType);
1214
1215        Object ret = handle.invokeWithArguments(new Object[]{"a", "b", "c"});
1216        assertEquals(42, (int) ret);
1217        ret = handle.invokeWithArguments(new String[]{"a", "b", "c"});
1218        assertEquals(42, (int) ret);
1219
1220        // Also test the versions that take a List<?> instead of an array.
1221        ret = handle.invokeWithArguments(Arrays.asList(new Object[] {"a", "b", "c"}));
1222        assertEquals(42, (int) ret);
1223        ret = handle.invokeWithArguments(Arrays.asList(new String[]{"a", "b", "c"}));
1224        assertEquals(42, (int) ret);
1225
1226        // Pass in an array that's too small. Should throw an IAE.
1227        try {
1228            handle.invokeWithArguments(new Object[]{"a", "b"});
1229            fail();
1230        } catch (IllegalArgumentException expected) {
1231        } catch (WrongMethodTypeException expected) {
1232        }
1233
1234        try {
1235            handle.invokeWithArguments(Arrays.asList(new Object[]{"a", "b"}));
1236            fail();
1237        } catch (IllegalArgumentException expected) {
1238        } catch (WrongMethodTypeException expected) {
1239        }
1240
1241
1242        // Test implicit unboxing.
1243        MethodType methodType2 = MethodType.methodType(int.class,
1244                new Class<?>[]{String.class, int.class});
1245        MethodHandle handle2 = MethodHandles.lookup().findStatic(
1246                MethodHandleCombinersTest.class, "spreadReferences_Unbox", methodType2);
1247
1248        ret = (int) handle2.invokeWithArguments(new Object[]{"a", 43});
1249        assertEquals(43, (int) ret);
1250    }
1251
1252    public static int collectBoolean(String a, boolean[] b) {
1253        assertEquals("a", a);
1254        assertTrue(b[0]);
1255        assertFalse(b[1]);
1256
1257        return 44;
1258    }
1259
1260    public static int collectByte(String a, byte[] b) {
1261        assertEquals("a", a);
1262        assertEquals((byte) 1, b[0]);
1263        assertEquals((byte) 2, b[1]);
1264        return 45;
1265    }
1266
1267    public static int collectChar(String a, char[] b) {
1268        assertEquals("a", a);
1269        assertEquals('a', b[0]);
1270        assertEquals('b', b[1]);
1271        return 46;
1272    }
1273
1274    public static int collectShort(String a, short[] b) {
1275        assertEquals("a", a);
1276        assertEquals((short) 3, b[0]);
1277        assertEquals((short) 4, b[1]);
1278
1279        return 47;
1280    }
1281
1282    public static int collectInt(String a, int[] b) {
1283        assertEquals("a", a);
1284        assertEquals(42, b[0]);
1285        assertEquals(43, b[1]);
1286
1287        return 48;
1288    }
1289
1290    public static int collectLong(String a, long[] b) {
1291        assertEquals("a", a);
1292        assertEquals(100l, b[0]);
1293        assertEquals(99l, b[1]);
1294
1295        return 49;
1296    }
1297
1298    public static int collectFloat(String a, float[] b) {
1299        assertEquals("a", a);
1300        assertEquals(8.9f, b[0]);
1301        assertEquals(9.1f, b[1]);
1302
1303        return 50;
1304    }
1305
1306    public static int collectDouble(String a, double[] b) {
1307        assertEquals("a", a);
1308        assertEquals(6.7, b[0]);
1309        assertEquals(7.8, b[1]);
1310
1311        return 51;
1312    }
1313
1314    public static int collectCharSequence(String a, CharSequence[] b) {
1315        assertEquals("a", a);
1316        assertEquals("b", b[0]);
1317        assertEquals("c", b[1]);
1318        return 99;
1319    }
1320
1321    public static void testAsCollector() throws Throwable {
1322        // Reference arrays.
1323        // -------------------
1324        MethodHandle trailingRef = MethodHandles.lookup().findStatic(
1325                MethodHandleCombinersTest.class, "collectCharSequence",
1326                MethodType.methodType(int.class, String.class, CharSequence[].class));
1327
1328        // int[] is not convertible to CharSequence[].class.
1329        try {
1330            trailingRef.asCollector(int[].class, 1);
1331            fail();
1332        } catch (IllegalArgumentException expected) {
1333        }
1334
1335        // Object[] is not convertible to CharSequence[].class.
1336        try {
1337            trailingRef.asCollector(Object[].class, 1);
1338            fail();
1339        } catch (IllegalArgumentException expected) {
1340        }
1341
1342        // String[].class is convertible to CharSequence.class
1343        MethodHandle collector = trailingRef.asCollector(String[].class, 2);
1344        assertEquals(99, (int) collector.invoke("a", "b", "c"));
1345
1346        // Too few arguments should fail with a WMTE.
1347        try {
1348            collector.invoke("a", "b");
1349            fail();
1350        } catch (WrongMethodTypeException expected) {
1351        }
1352
1353        // Too many arguments should fail with a WMTE.
1354        try {
1355            collector.invoke("a", "b", "c", "d");
1356            fail();
1357        } catch (WrongMethodTypeException expected) {
1358        }
1359
1360        // Sanity checks on other array types.
1361
1362        MethodHandle target = MethodHandles.lookup().findStatic(
1363                MethodHandleCombinersTest.class, "collectBoolean",
1364                MethodType.methodType(int.class, String.class, boolean[].class));
1365        assertEquals(44, (int) target.asCollector(boolean[].class, 2).invoke("a", true, false));
1366
1367        target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectByte",
1368                MethodType.methodType(int.class, String.class, byte[].class));
1369        assertEquals(45, (int) target.asCollector(byte[].class, 2).invoke("a", (byte) 1, (byte) 2));
1370
1371        target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectChar",
1372                MethodType.methodType(int.class, String.class, char[].class));
1373        assertEquals(46, (int) target.asCollector(char[].class, 2).invoke("a", 'a', 'b'));
1374
1375        target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectShort",
1376                MethodType.methodType(int.class, String.class, short[].class));
1377        assertEquals(47, (int) target.asCollector(short[].class, 2).invoke("a", (short) 3, (short) 4));
1378
1379        target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectInt",
1380                MethodType.methodType(int.class, String.class, int[].class));
1381        assertEquals(48, (int) target.asCollector(int[].class, 2).invoke("a", 42, 43));
1382
1383        target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectLong",
1384                MethodType.methodType(int.class, String.class, long[].class));
1385        assertEquals(49, (int) target.asCollector(long[].class, 2).invoke("a", 100, 99));
1386
1387        target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectFloat",
1388                MethodType.methodType(int.class, String.class, float[].class));
1389        assertEquals(50, (int) target.asCollector(float[].class, 2).invoke("a", 8.9f, 9.1f));
1390
1391        target = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "collectDouble",
1392                MethodType.methodType(int.class, String.class, double[].class));
1393        assertEquals(51, (int) target.asCollector(double[].class, 2).invoke("a", 6.7, 7.8));
1394    }
1395
1396    public static String filter1(char a) {
1397        return String.valueOf(a);
1398    }
1399
1400    public static char filter2(String b) {
1401        return b.charAt(0);
1402    }
1403
1404    public static String badFilter1(char a, char b) {
1405        return "bad";
1406    }
1407
1408    public static int filterTarget(String a, char b, String c, char d) {
1409        assertEquals("a", a);
1410        assertEquals('b', b);
1411        assertEquals("c", c);
1412        assertEquals('d', d);
1413        return 56;
1414    }
1415
1416    public static void testFilterArguments() throws Throwable {
1417        MethodHandle filter1 = MethodHandles.lookup().findStatic(
1418                MethodHandleCombinersTest.class, "filter1", MethodType.methodType(String.class, char.class));
1419        MethodHandle filter2 = MethodHandles.lookup().findStatic(
1420                MethodHandleCombinersTest.class, "filter2", MethodType.methodType(char.class, String.class));
1421
1422        MethodHandle target = MethodHandles.lookup().findStatic(
1423                MethodHandleCombinersTest.class, "filterTarget", MethodType.methodType(int.class,
1424                        String.class, char.class, String.class, char.class));
1425
1426        // In all the cases below, the values printed will be 'a', 'b', 'c', 'd'.
1427
1428        // Filter arguments [0, 1] - all other arguments are passed through
1429        // as is.
1430        MethodHandle adapter = MethodHandles.filterArguments(
1431                target, 0, filter1, filter2);
1432        assertEquals(56, (int) adapter.invokeExact('a', "bXXXX", "c", 'd'));
1433
1434        // Filter arguments [1, 2].
1435        adapter = MethodHandles.filterArguments(target, 1, filter2, filter1);
1436        assertEquals(56, (int) adapter.invokeExact("a", "bXXXX", 'c', 'd'));
1437
1438        // Filter arguments [2, 3].
1439        adapter = MethodHandles.filterArguments(target, 2, filter1, filter2);
1440        assertEquals(56, (int) adapter.invokeExact("a", 'b', 'c', "dXXXXX"));
1441
1442        // Try out a few error cases :
1443
1444        // The return types of the filter doesn't align with the expected argument
1445        // type of the target.
1446        try {
1447            adapter = MethodHandles.filterArguments(target, 2, filter2, filter1);
1448            fail();
1449        } catch (IllegalArgumentException expected) {
1450        }
1451
1452        // There are more filters than arguments.
1453        try {
1454            adapter = MethodHandles.filterArguments(target, 3, filter2, filter1);
1455            fail();
1456        } catch (IllegalArgumentException expected) {
1457        }
1458
1459        // We pass in an obviously bogus position.
1460        try {
1461            adapter = MethodHandles.filterArguments(target, -1, filter2, filter1);
1462            fail();
1463        } catch (ArrayIndexOutOfBoundsException expected) {
1464        }
1465
1466        // We pass in a function that has more than one argument.
1467        MethodHandle badFilter1 = MethodHandles.lookup().findStatic(
1468                MethodHandleCombinersTest.class, "badFilter1",
1469                MethodType.methodType(String.class, char.class, char.class));
1470
1471        try {
1472            adapter = MethodHandles.filterArguments(target, 0, badFilter1, filter2);
1473            fail();
1474        } catch (IllegalArgumentException expected) {
1475        }
1476    }
1477
1478    static void voidFilter(char a, char b) {
1479    }
1480
1481    static String filter(char a, char b) {
1482        return String.valueOf(a) + "+" + b;
1483    }
1484
1485    static char badFilter(char a, char b) {
1486        return 0;
1487    }
1488
1489    static String target(String a, String b, String c) {
1490        return ("a: " + a + ", b: " + b + ", c: " + c);
1491    }
1492
1493    public static void testCollectArguments() throws Throwable {
1494        // Test non-void filters.
1495        MethodHandle filter = MethodHandles.lookup().findStatic(
1496                MethodHandleCombinersTest.class, "filter",
1497                MethodType.methodType(String.class, char.class, char.class));
1498
1499        MethodHandle target = MethodHandles.lookup().findStatic(
1500                MethodHandleCombinersTest.class, "target",
1501                MethodType.methodType(String.class, String.class, String.class, String.class));
1502
1503        // Filter at position 0.
1504        MethodHandle adapter = MethodHandles.collectArguments(target, 0, filter);
1505        assertEquals("a: a+b, b: c, c: d",
1506                (String) adapter.invokeExact('a', 'b', "c", "d"));
1507
1508        // Filter at position 1.
1509        adapter = MethodHandles.collectArguments(target, 1, filter);
1510        assertEquals("a: a, b: b+c, c: d",
1511                (String) adapter.invokeExact("a", 'b', 'c', "d"));
1512
1513        // Filter at position 2.
1514        adapter = MethodHandles.collectArguments(target, 2, filter);
1515        assertEquals("a: a, b: b, c: c+d",
1516                (String) adapter.invokeExact("a", "b", 'c', 'd'));
1517
1518        // Test void filters. Note that we're passing in one more argument
1519        // than usual because the filter returns nothing - we have to invoke with
1520        // the full set of filter args and the full set of target args.
1521        filter = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class, "voidFilter",
1522                MethodType.methodType(void.class, char.class, char.class));
1523        adapter = MethodHandles.collectArguments(target, 0, filter);
1524        assertEquals("a: a, b: b, c: c",
1525                (String) adapter.invokeExact('a', 'b', "a", "b", "c"));
1526
1527        adapter = MethodHandles.collectArguments(target, 1, filter);
1528        assertEquals("a: a, b: b, c: c",
1529                (String) adapter.invokeExact("a", 'a', 'b', "b", "c"));
1530
1531        // Test out a few failure cases.
1532        filter = MethodHandles.lookup().findStatic(
1533                MethodHandleCombinersTest.class, "filter",
1534                MethodType.methodType(String.class, char.class, char.class));
1535
1536        // Bogus filter position.
1537        try {
1538            adapter = MethodHandles.collectArguments(target, 3, filter);
1539            fail();
1540        } catch (IndexOutOfBoundsException expected) {
1541        }
1542
1543        // Mismatch in filter return type.
1544        filter = MethodHandles.lookup().findStatic(
1545                MethodHandleCombinersTest.class, "badFilter",
1546                MethodType.methodType(char.class, char.class, char.class));
1547        try {
1548            adapter = MethodHandles.collectArguments(target, 0, filter);
1549            fail();
1550        } catch (IllegalArgumentException expected) {
1551        }
1552    }
1553
1554    static int insertReceiver(String a, int b, Integer c, String d) {
1555        assertEquals("foo", a);
1556        assertEquals(56, b);
1557        assertEquals(Integer.valueOf(57), c);
1558        assertEquals("bar", d);
1559
1560        return 73;
1561    }
1562
1563    public static void testInsertArguments() throws Throwable {
1564        MethodHandle target = MethodHandles.lookup().findStatic(
1565                MethodHandleCombinersTest.class, "insertReceiver",
1566                MethodType.methodType(int.class,
1567                        String.class, int.class, Integer.class, String.class));
1568
1569        // Basic single element array inserted at position 0.
1570        MethodHandle adapter = MethodHandles.insertArguments(
1571                target, 0, new Object[]{"foo"});
1572        assertEquals(73, (int) adapter.invokeExact(56, Integer.valueOf(57), "bar"));
1573
1574        // Exercise unboxing.
1575        adapter = MethodHandles.insertArguments(
1576                target, 1, new Object[]{Integer.valueOf(56), 57});
1577        assertEquals(73, (int) adapter.invokeExact("foo", "bar"));
1578
1579        // Exercise a widening conversion.
1580        adapter = MethodHandles.insertArguments(
1581                target, 1, new Object[]{(short) 56, Integer.valueOf(57)});
1582        assertEquals(73, (int) adapter.invokeExact("foo", "bar"));
1583
1584        // Insert an argument at the last position.
1585        adapter = MethodHandles.insertArguments(
1586                target, 3, new Object[]{"bar"});
1587        assertEquals(73, (int) adapter.invokeExact("foo", 56, Integer.valueOf(57)));
1588
1589        // Exercise a few error cases.
1590
1591        // A reference type that can't be cast to another reference type.
1592        try {
1593            MethodHandles.insertArguments(target, 3, new Object[]{new Object()});
1594            fail();
1595        } catch (ClassCastException expected) {
1596        }
1597
1598        // A boxed type that can't be unboxed correctly.
1599        try {
1600            MethodHandles.insertArguments(target, 1, new Object[]{Long.valueOf(56)});
1601            fail();
1602        } catch (ClassCastException expected) {
1603        }
1604    }
1605
1606    public static String foldFilter(char a, char b) {
1607        return String.valueOf(a) + "+" + b;
1608    }
1609
1610    public static void voidFoldFilter(String e, char a, char b) {
1611        assertEquals("a", e);
1612        assertEquals('c', a);
1613        assertEquals('d', b);
1614    }
1615
1616    public static String foldTarget(String a, char b, char c, String d) {
1617        return ("a: " + a + " ,b:" + b + " ,c:" + c + " ,d:" + d);
1618    }
1619
1620    public static void mismatchedVoidFilter(Integer a) {
1621    }
1622
1623    public static Integer mismatchedNonVoidFilter(char a, char b) {
1624        return null;
1625    }
1626
1627    public static void testFoldArguments() throws Throwable {
1628        // Test non-void filters.
1629        MethodHandle filter = MethodHandles.lookup().findStatic(
1630                MethodHandleCombinersTest.class, "foldFilter",
1631                MethodType.methodType(String.class, char.class, char.class));
1632
1633        MethodHandle target = MethodHandles.lookup().findStatic(
1634                MethodHandleCombinersTest.class, "foldTarget",
1635                MethodType.methodType(String.class, String.class,
1636                        char.class, char.class, String.class));
1637
1638        // Folder with a non-void type.
1639        MethodHandle adapter = MethodHandles.foldArguments(target, filter);
1640        assertEquals("a: c+d ,b:c ,c:d ,d:e",
1641                (String) adapter.invokeExact('c', 'd', "e"));
1642
1643        // Folder with a void type.
1644        filter = MethodHandles.lookup().findStatic(
1645                MethodHandleCombinersTest.class, "voidFoldFilter",
1646                MethodType.methodType(void.class, String.class, char.class, char.class));
1647        adapter = MethodHandles.foldArguments(target, filter);
1648        assertEquals("a: a ,b:c ,c:d ,d:e",
1649                (String) adapter.invokeExact("a", 'c', 'd', "e"));
1650
1651        // Test a few erroneous cases.
1652
1653        filter = MethodHandles.lookup().findStatic(
1654                MethodHandleCombinersTest.class, "mismatchedVoidFilter",
1655                MethodType.methodType(void.class, Integer.class));
1656        try {
1657            adapter = MethodHandles.foldArguments(target, filter);
1658            fail();
1659        } catch (IllegalArgumentException expected) {
1660        }
1661
1662        filter = MethodHandles.lookup().findStatic(
1663                MethodHandleCombinersTest.class, "mismatchedNonVoidFilter",
1664                MethodType.methodType(Integer.class, char.class, char.class));
1665        try {
1666            adapter = MethodHandles.foldArguments(target, filter);
1667            fail();
1668        } catch (IllegalArgumentException expected) {
1669        }
1670    }
1671
1672    // An exception thrown on worker threads and re-thrown on the main thread.
1673    static Throwable workerException = null;
1674
1675    private static void invokeMultiThreaded(final MethodHandle mh) throws Throwable {
1676        // Create enough worker threads to be oversubscribed in bid to force some parallelism.
1677        final int threadCount = Runtime.getRuntime().availableProcessors() + 1;
1678        final Thread threads [] = new Thread [threadCount];
1679
1680        // Launch worker threads and iterate invoking method handle.
1681        for (int i = 0; i < threadCount; ++i) {
1682            threads[i] = new Thread(new Runnable() {
1683                    @Override
1684                    public void run() {
1685                        try {
1686                            for (int j = 0; j < TEST_THREAD_ITERATIONS; ++j) {
1687                                mh.invoke();
1688                            }
1689                        } catch (Throwable t) {
1690                            workerException = t;
1691                            fail("Unexpected exception " + workerException);
1692                        }
1693                    }});
1694            threads[i].start();
1695        }
1696
1697        // Wait for completion
1698        for (int i = 0; i < threadCount; ++i) {
1699            threads[i].join();
1700        }
1701
1702        // Fail on main thread to avoid test appearing to complete successfully.
1703        Throwable t = workerException;
1704        workerException = null;
1705        if (t != null) {
1706            throw t;
1707        }
1708    }
1709
1710    public static void testDropInsertArgumentsMultithreaded() throws Throwable {
1711        MethodHandle delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
1712                "dropArguments_delegate",
1713                MethodType.methodType(void.class, new Class<?>[]{String.class, long.class}));
1714        MethodHandle mh = MethodHandles.dropArguments(delegate, 0, int.class, Object.class);
1715        mh = MethodHandles.insertArguments(mh, 0, 3333, "bogon", "foo", 42);
1716        invokeMultiThreaded(mh);
1717    }
1718
1719    private static void exceptionHandler_delegate(NumberFormatException e, int x, int y, long z)
1720            throws Throwable {
1721        assertEquals(e.getClass(), NumberFormatException.class);
1722        assertEquals(e.getMessage(), "fake");
1723        assertEquals(x, 66);
1724        assertEquals(y, 51);
1725        assertEquals(z, 20000000000l);
1726    }
1727
1728    public static void testThrowCatchExceptionMultiThreaded() throws Throwable {
1729        MethodHandle thrower = MethodHandles.throwException(void.class,
1730                                                            NumberFormatException.class);
1731        thrower = MethodHandles.dropArguments(thrower, 0, int.class, int.class, long.class);
1732        MethodHandle handler = MethodHandles.lookup().findStatic(
1733            MethodHandleCombinersTest.class, "exceptionHandler_delegate",
1734            MethodType.methodType(void.class, NumberFormatException.class,
1735                                  int.class, int.class, long.class));
1736        MethodHandle catcher =
1737            MethodHandles.catchException(thrower, NumberFormatException.class, handler);
1738        MethodHandle caller = MethodHandles.insertArguments(catcher, 0, 66, 51, 20000000000l,
1739                                                            new NumberFormatException("fake"));
1740        invokeMultiThreaded(caller);
1741    }
1742
1743    private static void testTargetAndFallback_delegate(MethodHandle mh) throws Throwable {
1744        String actual = (String) mh.invoke("target", 42, 56);
1745        assertEquals("target", actual);
1746        actual = (String) mh.invoke("blah", 41, 56);
1747        assertEquals("fallback", actual);
1748    }
1749
1750    public static void testGuardWithTestMultiThreaded() throws Throwable {
1751        MethodHandle test =
1752                MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
1753                                                  "testGuardWithTest_test",
1754                                                  MethodType.methodType(boolean.class,
1755                                                                        new Class<?>[]{String.class,
1756                                                                                    long.class}));
1757        final MethodType type = MethodType.methodType(String.class,
1758                new Class<?>[]{String.class, long.class, int.class});
1759        final MethodHandle target =
1760                MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
1761                                                  "testGuardWithTest_target", type);
1762        final MethodHandle fallback =
1763                MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
1764                                                  "testGuardWithTest_fallback", type);
1765        MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback);
1766        MethodHandle tester = MethodHandles.lookup().findStatic(
1767            MethodHandleCombinersTest.class,
1768            "testTargetAndFallback_delegate",
1769            MethodType.methodType(void.class, MethodHandle.class));
1770        invokeMultiThreaded(MethodHandles.insertArguments(tester, 0, adapter));
1771    }
1772
1773    private static void arrayElementSetterGetter_delegate(MethodHandle getter,
1774                                                          MethodHandle setter,
1775                                                          int [] values)
1776            throws Throwable{
1777        for (int i = 0; i < values.length; ++i) {
1778            int value = i * 13;
1779            setter.invoke(values, i, value);
1780            assertEquals(values[i], value);
1781            assertEquals(getter.invoke(values, i), values[i]);
1782        }
1783    }
1784
1785    public static void testReferenceArrayGetterMultiThreaded() throws Throwable {
1786        MethodHandle getter = MethodHandles.arrayElementGetter(int[].class);
1787        MethodHandle setter = MethodHandles.arrayElementSetter(int[].class);
1788        MethodHandle mh = MethodHandles.lookup().findStatic(
1789            MethodHandleCombinersTest.class,
1790            "arrayElementSetterGetter_delegate",
1791            MethodType.methodType(void.class, MethodHandle.class, MethodHandle.class, int[].class));
1792        mh = MethodHandles.insertArguments(mh, 0, getter, setter,
1793                                           new int[] { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23 });
1794        invokeMultiThreaded(mh);
1795    }
1796
1797    private static void checkConstant_delegate(MethodHandle mh, double value) throws Throwable {
1798        assertEquals(mh.invoke(), value);
1799    }
1800
1801    public static void testConstantMultithreaded() throws Throwable {
1802        final double value = 7.77e77;
1803        MethodHandle constant = MethodHandles.constant(double.class, value);
1804        MethodHandle mh = MethodHandles.lookup().findStatic(
1805            MethodHandleCombinersTest.class, "checkConstant_delegate",
1806            MethodType.methodType(void.class, MethodHandle.class, double.class));
1807        mh = MethodHandles.insertArguments(mh, 0, constant, value);
1808        invokeMultiThreaded(mh);
1809    }
1810
1811    private static void checkIdentity_delegate(MethodHandle mh, char value) throws Throwable {
1812        assertEquals(mh.invoke(value), value);
1813    }
1814
1815    public static void testIdentityMultiThreaded() throws Throwable {
1816        final char value = 'z';
1817        MethodHandle identity = MethodHandles.identity(char.class);
1818        MethodHandle mh = MethodHandles.lookup().findStatic(
1819            MethodHandleCombinersTest.class, "checkIdentity_delegate",
1820            MethodType.methodType(void.class, MethodHandle.class, char.class));
1821        mh = MethodHandles.insertArguments(mh, 0, identity, value);
1822        invokeMultiThreaded(mh);
1823    }
1824
1825    private static int multiplyByTwo(int x) { return x * 2; }
1826    private static int divideByTwo(int x) { return x / 2; }
1827    private static void assertMethodHandleInvokeEquals(MethodHandle mh, int value) throws Throwable{
1828        assertEquals(mh.invoke(value), value);
1829    }
1830
1831    public static void testFilterReturnValueMultiThreaded() throws Throwable {
1832        MethodHandle target = MethodHandles.lookup().findStatic(
1833            MethodHandleCombinersTest.class, "multiplyByTwo",
1834            MethodType.methodType(int.class, int.class));
1835        MethodHandle filter = MethodHandles.lookup().findStatic(
1836            MethodHandleCombinersTest.class, "divideByTwo",
1837            MethodType.methodType(int.class, int.class));
1838        MethodHandle filtered = MethodHandles.filterReturnValue(target, filter);
1839        assertEquals(filtered.invoke(33), 33);
1840        MethodHandle mh = MethodHandles.lookup().findStatic(
1841            MethodHandleCombinersTest.class, "assertMethodHandleInvokeEquals",
1842            MethodType.methodType(void.class, MethodHandle.class, int.class));
1843        invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, filtered, 77));
1844    }
1845
1846    public static void compareStringAndFloat(String s, float f) {
1847        assertEquals(s, Float.toString(f));
1848    }
1849
1850    public static void testPermuteArgumentsMultiThreaded() throws Throwable {
1851        MethodHandle mh = MethodHandles.lookup().findStatic(
1852            MethodHandleCombinersTest.class, "compareStringAndFloat",
1853            MethodType.methodType(void.class, String.class, float.class));
1854        mh = MethodHandles.permuteArguments(
1855            mh, MethodType.methodType(void.class, float.class, String.class), 1, 0);
1856        invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, 2.22f, "2.22"));
1857    }
1858
1859    public static void testSpreadInvokerMultiThreaded() throws Throwable {
1860        MethodType methodType = MethodType.methodType(
1861            int.class, new Class<?>[]{String.class, String.class, String.class});
1862        MethodHandle delegate = MethodHandles.lookup().findStatic(
1863            MethodHandleCombinersTest.class, "spreadReferences", methodType);
1864        MethodHandle mh = delegate.asSpreader(String[].class, 3);
1865        mh = MethodHandles.insertArguments(mh, 0, new Object[] { new String [] { "a", "b", "c" }});
1866        invokeMultiThreaded(mh);
1867    }
1868
1869    public static void testCollectorMultiThreaded() throws Throwable {
1870        MethodHandle trailingRef = MethodHandles.lookup().findStatic(
1871                MethodHandleCombinersTest.class, "collectCharSequence",
1872                MethodType.methodType(int.class, String.class, CharSequence[].class));
1873        MethodHandle mh = trailingRef.asCollector(String[].class, 2);
1874        mh = MethodHandles.insertArguments(mh, 0, "a", "b", "c");
1875        invokeMultiThreaded(mh);
1876    }
1877
1878    public static void testFilterArgumentsMultiThreaded() throws Throwable {
1879        MethodHandle filter1 = MethodHandles.lookup().findStatic(
1880            MethodHandleCombinersTest.class, "filter1",
1881            MethodType.methodType(String.class, char.class));
1882        MethodHandle filter2 = MethodHandles.lookup().findStatic(
1883            MethodHandleCombinersTest.class, "filter2",
1884            MethodType.methodType(char.class, String.class));
1885        MethodHandle target = MethodHandles.lookup().findStatic(
1886            MethodHandleCombinersTest.class, "filterTarget",
1887            MethodType.methodType(int.class, String.class, char.class, String.class, char.class));
1888        MethodHandle adapter = MethodHandles.filterArguments(target, 2, filter1, filter2);
1889        invokeMultiThreaded(MethodHandles.insertArguments(adapter, 0, "a", 'b', 'c', "dXXXXX"));
1890    }
1891
1892    private static void checkStringResult_delegate(MethodHandle mh,
1893                                                   String expected) throws Throwable {
1894        assertEquals(mh.invoke(), expected);
1895    }
1896
1897    public static void testCollectArgumentsMultiThreaded() throws Throwable {
1898        MethodHandle filter = MethodHandles.lookup().findStatic(
1899            MethodHandleCombinersTest.class, "filter",
1900            MethodType.methodType(String.class, char.class, char.class));
1901        MethodHandle target = MethodHandles.lookup().findStatic(
1902                MethodHandleCombinersTest.class, "target",
1903                MethodType.methodType(String.class, String.class, String.class, String.class));
1904        MethodHandle collect = MethodHandles.collectArguments(target, 2, filter);
1905        collect = MethodHandles.insertArguments(collect, 0, "a", "b", 'c', 'd');
1906        MethodHandle mh = MethodHandles.lookup().findStatic(
1907            MethodHandleCombinersTest.class, "checkStringResult_delegate",
1908            MethodType.methodType(void.class, MethodHandle.class, String.class));
1909        invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, collect, "a: a, b: b, c: c+d"));
1910    }
1911
1912    public static void testFoldArgumentsMultiThreaded() throws Throwable {
1913        MethodHandle target = MethodHandles.lookup().findStatic(
1914            MethodHandleCombinersTest.class, "foldTarget",
1915            MethodType.methodType(String.class, String.class,
1916                                  char.class, char.class, String.class));
1917        MethodHandle filter = MethodHandles.lookup().findStatic(
1918            MethodHandleCombinersTest.class, "foldFilter",
1919            MethodType.methodType(String.class, char.class, char.class));
1920        MethodHandle adapter = MethodHandles.foldArguments(target, filter);
1921        adapter = MethodHandles.insertArguments(adapter, 0, 'c', 'd', "e");
1922        MethodHandle mh = MethodHandles.lookup().findStatic(
1923            MethodHandleCombinersTest.class, "checkStringResult_delegate",
1924            MethodType.methodType(void.class, MethodHandle.class, String.class));
1925        invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, adapter, "a: c+d ,b:c ,c:d ,d:e"));
1926    }
1927
1928    private static void checkBooleanCast_delegate(boolean expected, boolean z,  boolean b,
1929                                                  boolean c, boolean s, boolean i, boolean j,
1930                                                  boolean f, boolean d, boolean l) {
1931        assertEquals(expected, z);
1932        assertEquals(expected, b);
1933        assertEquals(expected, c);
1934        assertEquals(expected, s);
1935        assertEquals(expected, i);
1936        assertEquals(expected, j);
1937        assertEquals(expected, f);
1938        assertEquals(expected, d);
1939        assertEquals(expected, l);
1940    }
1941
1942    private static void checkByteCast_delegate(byte expected, byte z, byte b, byte c, byte s,
1943                                               byte i, byte j, byte f, byte d, byte l) {
1944        int mask = 0xff;
1945        assertEquals(expected & 1, z);
1946        assertEquals(expected, b);
1947        assertEquals(expected, c & mask);
1948        assertEquals(expected, s & mask);
1949        assertEquals(expected, i & mask);
1950        assertEquals(expected, j & mask);
1951        assertEquals(expected, f & mask);
1952        assertEquals(expected, d & mask);
1953        assertEquals(expected, l);
1954    }
1955
1956    private static void checkCharCast_delegate(char expected, char z, char b, char c, char s,
1957                                               char i, char j, char f, char d, char l) {
1958        int mask = 0xffff;
1959        assertEquals(expected & 1, z);
1960        assertEquals(expected & 0xff, b);
1961        assertEquals(expected, c);
1962        assertEquals(expected, s & mask);
1963        assertEquals(expected, i & mask);
1964        assertEquals(expected, j & mask);
1965        assertEquals(expected, f & mask);
1966        assertEquals(expected, d & mask);
1967        assertEquals(expected, l);
1968    }
1969
1970    private static void checkShortCast_delegate(short expected, short z, short b, short c, short s,
1971                                                short i, short j, short f, short d, short l) {
1972        int mask = 0xffff;
1973        assertEquals(expected & 1, z);
1974        assertEquals(expected & 0xff, b);
1975        assertEquals(expected, c & mask);
1976        assertEquals(expected, s);
1977        assertEquals(expected, i & mask);
1978        assertEquals(expected, j & mask);
1979        assertEquals(expected, f & mask);
1980        assertEquals(expected, d & mask);
1981        assertEquals(expected, l);
1982    }
1983
1984    private static void checkIntCast_delegate(int expected, int z, int b, int c, int s, int i,
1985                                              int j, int f, int d, int l) {
1986        int mask = 0xffffffff;
1987        assertEquals(expected & 1, z);
1988        assertEquals(expected & 0xff, b);
1989        assertEquals(expected & 0xffff, c);
1990        assertEquals(expected & 0xffff, s);
1991        assertEquals(expected, i & mask);
1992        assertEquals(expected, j & mask);
1993        assertEquals(expected, f & mask);
1994        assertEquals(expected, d & mask);
1995        assertEquals(expected, l);
1996    }
1997
1998    private static void checkLongCast_delegate(long expected, long z, long b, long c, long s,
1999                                               long i, long j, long f, long d, long l) {
2000        long mask = 0xffffffffl;
2001        assertEquals(expected & 1, z);
2002        assertEquals(expected & 0xff, b);
2003        assertEquals(expected & 0xffff, c);
2004        assertEquals(expected & 0xffff, s);
2005        assertEquals(expected & mask, i & mask);
2006        assertEquals(expected, j);
2007        assertEquals(expected & mask, f & mask);
2008        assertEquals(expected, d);
2009        assertEquals(expected, l);
2010    }
2011
2012    private static void checkFloatCast_delegate(float expected, float z, float b, float c, float s,
2013                                                float i, float j, float f, float d, float l) {
2014        assertEquals((byte) expected & 1, (int) z);
2015        assertEquals((byte) expected, (int) b & 0xff);
2016        assertEquals((char) expected & 0xffff, (int) c& 0xffff);
2017        assertEquals((short) expected & 0xffff, (int) s & 0xffff);
2018        assertEquals((int) expected, (int) i);
2019        assertEquals((long) expected, (long) j);
2020        assertEquals(expected, f);
2021        assertEquals(expected, d);
2022        assertEquals(expected, l);
2023    }
2024
2025    private static void checkDoubleCast_delegate(double expected, double z, double b, double c,
2026                                                 double s, double i, double j, double f, double d,
2027                                                 double l) {
2028        assertEquals((byte) expected & 1, (int) z);
2029        assertEquals((byte) expected & 0xff, (int) b & 0xff);
2030        assertEquals((int) expected & 0xffff, (int) c & 0xffff);
2031        assertEquals((int) expected & 0xffff, (int) s & 0xffff);
2032        assertEquals((int) expected, (int) i);
2033        assertEquals((long) expected, (long) j);
2034        assertEquals((float) expected, (float) f);
2035        assertEquals(expected, d);
2036        assertEquals(expected, l);
2037    }
2038
2039    private static void checkBoxingCasts_delegate(boolean expected, Boolean z, Byte b, Character c,
2040                                                  Short s, Integer i, Long j, Float f, Double d) {
2041        int v = expected ? 1 : 0;
2042        assertEquals(Boolean.valueOf(expected ? true : false), z);
2043        assertEquals(Byte.valueOf((byte) v), b);
2044        assertEquals(Character.valueOf((char) v), c);
2045        assertEquals(Short.valueOf((short) v), s);
2046        assertEquals(Integer.valueOf(v), i);
2047        assertEquals(Long.valueOf(v), j);
2048        assertEquals(Float.valueOf(v), f);
2049        assertEquals(Double.valueOf(v), d);
2050    }
2051
2052    public static void testExplicitCastArguments() throws Throwable {
2053        MethodHandle target = MethodHandles.lookup().findStatic(
2054            MethodHandleCombinersTest.class, "checkBooleanCast_delegate",
2055            MethodType.methodType(void.class, boolean.class, boolean.class, boolean.class,
2056                                  boolean.class, boolean.class, boolean.class, boolean.class,
2057                                  boolean.class, boolean.class, boolean.class));
2058        MethodHandle mh = MethodHandles.explicitCastArguments(
2059            target, MethodType.methodType(void.class, boolean.class, boolean.class, byte.class,
2060                                          char.class, short.class, int.class, long.class,
2061                                          float.class, double.class, Boolean.class));
2062        mh.invokeExact(false, false, (byte) 0, (char) 0, (short) 0, 0, 0l, 0.0f, 0.0,
2063                       Boolean.valueOf(false));
2064        mh.invokeExact(false, false, (byte) 2, (char) 2, (short) 2, 2, 2l, 2.2f, 2.2,
2065                       Boolean.valueOf(false));
2066        mh.invokeExact(true, true, (byte) 1, (char) 1, (short) 1, 1, 1l, 1.0f, 1.0,
2067                       Boolean.valueOf(true));
2068        mh.invokeExact(true, true, (byte) 51, (char) 51, (short) 51, 51, 51l, 51.0f, 51.0,
2069                       Boolean.valueOf(true));
2070        MethodHandles.explicitCastArguments(
2071            target, MethodType.methodType(void.class, boolean.class, boolean.class, byte.class,
2072                                          char.class, short.class, int.class, long.class,
2073                                          float.class, double.class, String.class));
2074        try {
2075            mh.invoke(true, true, (byte) 51, (char) 51, (short) 51, 51, 51l, 51.0f, 51.0,
2076                      "ClassCastException here!");
2077            fail();
2078        } catch (ClassCastException e) {
2079        }
2080
2081        target = MethodHandles.lookup().findStatic(
2082            MethodHandleCombinersTest.class, "checkByteCast_delegate",
2083            MethodType.methodType(void.class, byte.class, byte.class, byte.class,
2084                                  byte.class, byte.class, byte.class, byte.class,
2085                                  byte.class, byte.class, byte.class));
2086        mh = MethodHandles.explicitCastArguments(
2087            target, MethodType.methodType(void.class, byte.class, boolean.class, byte.class,
2088                                          char.class, short.class, int.class, long.class,
2089                                          float.class, double.class, Byte.class));
2090        mh.invokeExact((byte) 0x5a, false, (byte) 0x5a, (char) 0x5a5a, (short) 0x5a5a, (int) 0x5a5a,
2091                       (long) 0x5a5a, (float) 0x5a5a, (double) 0x5a5a, Byte.valueOf((byte) 0x5a));
2092        try {
2093            mh.invoke((byte) 0x5a, false, (byte) 0x5a, (char) 0x5a5a, (short) 0x5a5a, (int) 0x5a5a,
2094                      (long) 0x5a5a, (float) 0x5a5a, (double) 0x5a5a,
2095                      Short.valueOf((short) 0x5a5a));
2096            fail();
2097        } catch (ClassCastException e) {
2098        }
2099
2100        target = MethodHandles.lookup().findStatic(
2101            MethodHandleCombinersTest.class, "checkCharCast_delegate",
2102            MethodType.methodType(void.class, char.class, char.class, char.class,
2103                                  char.class, char.class, char.class, char.class,
2104                                  char.class, char.class, char.class));
2105        mh = MethodHandles.explicitCastArguments(
2106            target, MethodType.methodType(void.class, char.class, boolean.class, byte.class,
2107                                          char.class, short.class, int.class, long.class,
2108                                          float.class, double.class, Character.class));
2109        mh.invokeExact((char) 0x5555, true, (byte) 0x5555, (char) 0x5555, (short) 0x5555,
2110                       (int) 0x5555, (long) 0x5555, (float) 0x5555, (double) 0x5555,
2111                       Character.valueOf((char) 0x5555));
2112        try {
2113            mh.invoke((char) 0x5555, false, (byte) 0x5555, (char) 0x5555, (short) 0x5555,
2114                      (int) 0x5555, (long) 0x5555, (float) 0x5555, (double) 0x5555,
2115                      Integer.valueOf((int) 0x5555));
2116            fail();
2117        } catch (ClassCastException e) {
2118        }
2119
2120        target = MethodHandles.lookup().findStatic(
2121            MethodHandleCombinersTest.class, "checkShortCast_delegate",
2122            MethodType.methodType(void.class, short.class, short.class, short.class,
2123                                  short.class, short.class, short.class, short.class,
2124                                  short.class, short.class, short.class));
2125        mh = MethodHandles.explicitCastArguments(
2126            target, MethodType.methodType(void.class, short.class, boolean.class, byte.class,
2127                                          char.class, short.class, int.class, long.class,
2128                                          float.class, double.class, Short.class));
2129        mh.invokeExact((short) 0x3773, true, (byte) 0x3773, (char) 0x3773, (short) 0x3773,
2130                       (int) 0x3773, (long) 0x3773, (float) 0x3773, (double) 0x3773,
2131                       Short.valueOf((short) 0x3773));
2132        try {
2133            mh.invoke((short) 0x3773, true, (byte) 0x3773, (char) 0x3773, (short) 0x3773,
2134                      (int) 0x3773, (long) 0x3773, (float) 0x3773, (double) 0x3773,
2135                      Long.valueOf((long) 0x3773));
2136            fail();
2137        } catch (ClassCastException e) {
2138        }
2139
2140        target = MethodHandles.lookup().findStatic(
2141            MethodHandleCombinersTest.class, "checkIntCast_delegate",
2142            MethodType.methodType(void.class, int.class, int.class, int.class,
2143                                  int.class, int.class, int.class, int.class,
2144                                  int.class, int.class, int.class));
2145        mh = MethodHandles.explicitCastArguments(
2146            target, MethodType.methodType(void.class, int.class, boolean.class, byte.class,
2147                                          char.class, short.class, int.class, long.class,
2148                                          float.class, double.class, Integer.class));
2149        mh.invokeExact((int) 0x3773470, false, (byte) 0x3773470, (char) 0x3773470,
2150                       (short) 0x3773470, (int) 0x3773470, (long) 0x3773470, (float) 0x3773470,
2151                       (double) 0x3773470, Integer.valueOf(0x3773470));
2152        try {
2153            mh.invoke((int) 0x3773470, false, (byte) 0x3773470, (char) 0x3773470,
2154                      (short) 0x3773470, (int) 0x3773470, (long) 0x3773470, (float) 0x3773470,
2155                      (double) 0x3773470, Long.valueOf((long) 0x3773470));
2156            fail();
2157        } catch (ClassCastException e) {
2158        }
2159
2160        target = MethodHandles.lookup().findStatic(
2161            MethodHandleCombinersTest.class, "checkLongCast_delegate",
2162            MethodType.methodType(void.class, long.class, long.class, long.class,
2163                                  long.class, long.class, long.class, long.class,
2164                                  long.class, long.class, long.class));
2165        mh = MethodHandles.explicitCastArguments(
2166            target, MethodType.methodType(void.class, long.class, boolean.class, byte.class,
2167                                          char.class, short.class, int.class, long.class,
2168                                          float.class, double.class, Long.class));
2169        long longValue = 0x770000000l;
2170        mh.invokeExact((long) longValue, false, (byte) longValue, (char) longValue,
2171                       (short) longValue, (int) longValue, (long) longValue, (float) longValue,
2172                       (double) longValue, Long.valueOf(longValue));
2173        try {
2174            mh.invoke((long) longValue, false, (byte) longValue, (char) longValue,
2175                      (short) longValue, (int) longValue, (long) longValue, (float) longValue,
2176                      (double) longValue, Integer.valueOf(3));
2177            fail();
2178        } catch (ClassCastException e) {
2179        }
2180
2181        target = MethodHandles.lookup().findStatic(
2182            MethodHandleCombinersTest.class, "checkFloatCast_delegate",
2183            MethodType.methodType(void.class, float.class, float.class, float.class,
2184                                  float.class, float.class, float.class, float.class,
2185                                  float.class, float.class, float.class));
2186        mh = MethodHandles.explicitCastArguments(
2187            target, MethodType.methodType(void.class, float.class, boolean.class, byte.class,
2188                                          char.class, short.class, int.class, long.class,
2189                                          float.class, double.class, Float.class));
2190        float floatValue = 33333.141f;
2191        mh.invokeExact(floatValue, true, (byte) floatValue, (char) floatValue, (short) floatValue,
2192                       (int) floatValue, (long) floatValue, floatValue, (double) floatValue,
2193                       Float.valueOf(floatValue));
2194        try {
2195            mh.invoke(floatValue, true, (byte) floatValue, (char) floatValue,
2196                      (short) floatValue, (int) floatValue, (long) floatValue, floatValue,
2197                      (double) floatValue, Integer.valueOf((int) floatValue));
2198            fail();
2199        } catch (ClassCastException e) {
2200        }
2201
2202        target = MethodHandles.lookup().findStatic(
2203            MethodHandleCombinersTest.class, "checkDoubleCast_delegate",
2204            MethodType.methodType(void.class, double.class, double.class, double.class,
2205                                  double.class, double.class, double.class, double.class,
2206                                  double.class, double.class, double.class));
2207        mh = MethodHandles.explicitCastArguments(
2208            target, MethodType.methodType(void.class, double.class, boolean.class, byte.class,
2209                                          char.class, short.class, int.class, long.class,
2210                                          float.class, double.class, Double.class));
2211        double doubleValue = 33333333333.141;
2212        mh.invokeExact(doubleValue, true, (byte) doubleValue, (char) doubleValue,
2213                       (short) doubleValue, (int) doubleValue, (long) doubleValue,
2214                       (float) doubleValue, doubleValue, Double.valueOf(doubleValue));
2215        try {
2216            mh.invoke(doubleValue, true, (byte) doubleValue, (char) doubleValue,
2217                      (short) doubleValue, (int) doubleValue, (long) doubleValue,
2218                      (float) doubleValue, (double) doubleValue,
2219                      Integer.valueOf((int) doubleValue));
2220            fail();
2221        } catch (ClassCastException e) {
2222        }
2223
2224        target = MethodHandles.lookup().findStatic(
2225            MethodHandleCombinersTest.class, "checkBoxingCasts_delegate",
2226            MethodType.methodType(void.class, boolean.class, Boolean.class, Byte.class,
2227                                  Character.class, Short.class, Integer.class, Long.class,
2228                                  Float.class, Double.class));
2229        mh = MethodHandles.explicitCastArguments(
2230            target, MethodType.methodType(void.class, boolean.class, boolean.class, byte.class,
2231                                          char.class, short.class, int.class, long.class,
2232                                          float.class, double.class));
2233        mh.invokeExact(false, false, (byte) 0, (char) 0, (short) 0, 0, 0l, 0.0f, 0.0);
2234        mh.invokeExact(true, true, (byte) 1, (char) 1, (short) 1, 1, 1l, 1.0f, 1.0);
2235        mh.invoke(Boolean.valueOf(false), Boolean.valueOf(false), Byte.valueOf((byte) 0),
2236                  Character.valueOf((char) 0), Short.valueOf((short) 0), Integer.valueOf(0),
2237                  Long.valueOf(0l), Float.valueOf(0.0f), Double.valueOf(0.0));
2238        mh.invoke(Boolean.valueOf(true), Boolean.valueOf(true), Byte.valueOf((byte) 1),
2239                  Character.valueOf((char) 1), Short.valueOf((short) 1), Integer.valueOf(1),
2240                  Long.valueOf(1l), Float.valueOf(1.0f), Double.valueOf(1.0));
2241        mh = MethodHandles.explicitCastArguments(
2242            target, MethodType.methodType(void.class, double.class, boolean.class, byte.class,
2243                                          char.class, short.class, int.class, long.class,
2244                                          float.class, double.class));
2245        mh.invokeExact(0.0, false, (byte) 0, (char) 0, (short) 0, 0, 0l, 0.0f, 0.0);
2246    }
2247
2248    static void returnVoid() {}
2249
2250    static boolean returnBoolean(boolean b) { return b; }
2251
2252    static Boolean returnBooleanObject(boolean b) { return b; }
2253
2254    public static void testExplicitCastReturnValues() throws Throwable {
2255        MethodHandle target = MethodHandles.lookup().findStatic(
2256            MethodHandleCombinersTest.class, "returnVoid", MethodType.methodType(void.class));
2257        assertEquals(false,
2258                     MethodHandles
2259                     .explicitCastArguments(target, MethodType.methodType(boolean.class))
2260                     .invoke());
2261        assertEquals(null,
2262                     MethodHandles
2263                     .explicitCastArguments(target, MethodType.methodType(Boolean.class))
2264                     .invoke());
2265        assertEquals(0l,
2266                     MethodHandles
2267                     .explicitCastArguments(target, MethodType.methodType(long.class))
2268                     .invoke());
2269        assertEquals(null,
2270                     MethodHandles
2271                     .explicitCastArguments(target, MethodType.methodType(Long.class))
2272                     .invoke());
2273
2274        target = MethodHandles.lookup().findStatic(
2275            MethodHandleCombinersTest.class, "returnBoolean",
2276            MethodType.methodType(boolean.class, boolean.class));
2277        assertEquals(false,
2278                     MethodHandles
2279                     .explicitCastArguments(target,
2280                                            MethodType.methodType(boolean.class, boolean.class))
2281                     .invoke(false));
2282        assertEquals(true,
2283                     MethodHandles
2284                     .explicitCastArguments(target,
2285                                            MethodType.methodType(boolean.class, boolean.class))
2286                     .invoke(true));
2287        assertEquals(Boolean.valueOf(false),
2288                     MethodHandles
2289                     .explicitCastArguments(target,
2290                                            MethodType.methodType(Boolean.class, boolean.class))
2291                     .invoke(false));
2292        assertEquals(Boolean.valueOf(true),
2293                     MethodHandles
2294                     .explicitCastArguments(target,
2295                                            MethodType.methodType(Boolean.class, boolean.class))
2296                     .invoke(true));
2297        assertEquals((byte) 0,
2298                     MethodHandles
2299                     .explicitCastArguments(target,
2300                                            MethodType.methodType(byte.class, boolean.class))
2301                     .invoke(false));
2302        assertEquals((byte) 1,
2303                     MethodHandles
2304                     .explicitCastArguments(target,
2305                                            MethodType.methodType(byte.class, boolean.class))
2306                     .invoke(true));
2307        try {
2308            assertEquals(Byte.valueOf((byte) 0),
2309                         MethodHandles
2310                         .explicitCastArguments(target,
2311                                                MethodType.methodType(Byte.class, boolean.class))
2312                         .invoke(false));
2313            fail();
2314        } catch (ClassCastException e) {
2315        }
2316
2317        try {
2318            assertEquals(Byte.valueOf((byte) 1),
2319                         MethodHandles
2320                         .explicitCastArguments(target,
2321                                                MethodType.methodType(Byte.class, boolean.class))
2322                         .invoke(true));
2323        } catch (ClassCastException e) {
2324        }
2325
2326        target = MethodHandles.lookup().findStatic(
2327            MethodHandleCombinersTest.class, "returnBooleanObject",
2328            MethodType.methodType(Boolean.class, boolean.class));
2329        assertEquals(false,
2330                     (boolean) MethodHandles
2331                     .explicitCastArguments(target,
2332                                            MethodType.methodType(boolean.class, boolean.class))
2333                     .invokeExact(false));
2334        assertEquals(true,
2335                     (boolean) MethodHandles
2336                     .explicitCastArguments(target,
2337                                            MethodType.methodType(boolean.class, boolean.class))
2338                     .invokeExact(true));
2339    }
2340}
2341