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