blob: 0bdc80a9d8ee6044eb952a65877d081de56b405d [file] [log] [blame]
Narayan Kamath000e1882016-10-24 17:14:25 +01001/*
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
17import java.lang.invoke.MethodHandle;
18import java.lang.invoke.MethodHandles;
19import java.lang.invoke.MethodHandles.Lookup;
20import java.lang.invoke.MethodType;
21import java.lang.invoke.WrongMethodTypeException;
Narayan Kamath000e1882016-10-24 17:14:25 +010022
23public class Main {
Narayan Kamath000e1882016-10-24 17:14:25 +010024 public static void main(String[] args) throws Throwable {
25 testThrowException();
Narayan Kamath96120f42016-11-01 09:40:23 +000026 testDropArguments();
27 testCatchException();
28 testGuardWithTest();
Narayan Kamath3314dbb2016-11-03 18:01:32 +000029 testArrayElementGetter();
30 testArrayElementSetter();
31 testIdentity();
32 testConstant();
Narayan Kamath8677d0b2016-11-04 14:41:19 +000033 testBindTo();
Narayan Kamath916a7712016-11-08 18:36:16 +000034 testFilterReturnValue();
Narayan Kamath731f4c62016-11-08 19:38:48 +000035 testPermuteArguments();
Narayan Kamathb79bbd82017-01-16 17:48:28 +000036 testInvokers();
Narayan Kamathbcfd2842017-01-19 20:42:23 +000037 testSpreaders_reference();
38 testSpreaders_primitive();
Narayan Kamathc5889ce2017-01-19 20:42:23 +000039 testInvokeWithArguments();
Narayan Kamath2a3696a2017-01-24 19:16:25 +000040 testAsCollector();
Narayan Kamath0cde4eb2017-01-26 18:31:24 +000041 testFilterArguments();
42 testCollectArguments();
43 testInsertArguments();
44 testFoldArguments();
Narayan Kamath000e1882016-10-24 17:14:25 +010045 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +000052 fail("Unexpected return type for handle: " + handle +
Narayan Kamath000e1882016-10-24 17:14:25 +010053 " [ " + handle.type() + "]");
54 }
55
Narayan Kamath96120f42016-11-01 09:40:23 +000056 final IllegalArgumentException iae = new IllegalArgumentException("boo!");
Narayan Kamath000e1882016-10-24 17:14:25 +010057 try {
Narayan Kamath96120f42016-11-01 09:40:23 +000058 handle.invoke(iae);
Orion Hodsonac141392017-01-13 11:53:47 +000059 fail("Expected an exception of type: java.lang.IllegalArgumentException");
Narayan Kamath000e1882016-10-24 17:14:25 +010060 } catch (IllegalArgumentException expected) {
Narayan Kamath96120f42016-11-01 09:40:23 +000061 if (expected != iae) {
Orion Hodsonac141392017-01-13 11:53:47 +000062 fail("Wrong exception: expected " + iae + " but was " + expected);
Narayan Kamath96120f42016-11-01 09:40:23 +000063 }
Narayan Kamath000e1882016-10-24 17:14:25 +010064 }
65 }
Narayan Kamath96120f42016-11-01 09:40:23 +000066
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
Narayan Kamath0a8485e2016-11-02 18:47:11 +0000109 // 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
Narayan Kamath96120f42016-11-01 09:40:23 +0000115 // 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);
Narayan Kamath0a8485e2016-11-02 18:47:11 +0000208
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);
Narayan Kamath96120f42016-11-01 09:40:23 +0000216 }
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);
Narayan Kamath0a8485e2016-11-02 18:47:11 +0000258
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);
Narayan Kamath96120f42016-11-01 09:40:23 +0000264 }
265
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000266 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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000274 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000275 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000296 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000297 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000306 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000307 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000316 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000317 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000326 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000327 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000336 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000337 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000346 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000347 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000356 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000357 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000384 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000385 }
386 if (array[1] != 43) {
Orion Hodsonac141392017-01-13 11:53:47 +0000387 fail("Unexpected value: " + array[1]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000388 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000408 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000409 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000417 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000418 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000426 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000427 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000435 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000436 }
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]) {
Orion Hodsonac141392017-01-13 11:53:47 +0000444 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000445 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000453 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000454 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000462 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000463 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000483 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000484 }
485 }
486
487 {
488 MethodHandle identity = MethodHandles.identity(byte.class);
489 byte value = (byte) identity.invoke((byte) 0x8);
490 if (value != (byte) 0x8) {
Orion Hodsonac141392017-01-13 11:53:47 +0000491 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000492 }
493 }
494
495 {
496 MethodHandle identity = MethodHandles.identity(char.class);
497 char value = (char) identity.invoke((char) -56);
498 if (value != (char) -56) {
Orion Hodsonac141392017-01-13 11:53:47 +0000499 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000500 }
501 }
502
503 {
504 MethodHandle identity = MethodHandles.identity(short.class);
505 short value = (short) identity.invoke((short) -59);
506 if (value != (short) -59) {
Orion Hodsonac141392017-01-13 11:53:47 +0000507 fail("Unexpected value: " + Short.toString(value));
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000508 }
509 }
510
511 {
512 MethodHandle identity = MethodHandles.identity(int.class);
513 int value = (int) identity.invoke(52);
514 if (value != 52) {
Orion Hodsonac141392017-01-13 11:53:47 +0000515 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000516 }
517 }
518
519 {
520 MethodHandle identity = MethodHandles.identity(long.class);
521 long value = (long) identity.invoke(-76l);
522 if (value != (long) -76) {
Orion Hodsonac141392017-01-13 11:53:47 +0000523 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000524 }
525 }
526
527 {
528 MethodHandle identity = MethodHandles.identity(float.class);
529 float value = (float) identity.invoke(56.0f);
530 if (value != (float) 56.0f) {
Orion Hodsonac141392017-01-13 11:53:47 +0000531 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000532 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000539 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000540 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000556 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000557 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000563 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000564 }
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') {
Orion Hodsonac141392017-01-13 11:53:47 +0000570 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000571 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000577 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000578 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000612 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000613 }
614
615 constant = MethodHandles.constant(long.class, (int) 56);
616 value = (long) constant.invoke();
617 if (value != 56l) {
Orion Hodsonac141392017-01-13 11:53:47 +0000618 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000619 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000627 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000628 }
629 }
630
631 // boolean constants.
632 {
633 MethodHandle constant = MethodHandles.constant(boolean.class, true);
634 boolean value = (boolean) constant.invoke();
635 if (!value) {
Orion Hodsonac141392017-01-13 11:53:47 +0000636 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000637 }
638 }
639
640 // char constants.
641 {
642 MethodHandle constant = MethodHandles.constant(char.class, 'f');
643 char value = (char) constant.invoke();
644 if (value != 'f') {
Orion Hodsonac141392017-01-13 11:53:47 +0000645 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000646 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000654 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000655 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000663 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000664 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000672 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000673 }
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
Narayan Kamath8677d0b2016-11-04 14:41:19 +0000684 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') {
Orion Hodsonac141392017-01-13 11:53:47 +0000690 fail("Unexpected value: " + value);
Narayan Kamath8677d0b2016-11-04 14:41:19 +0000691 }
692
693 MethodHandle bound = stringCharAt.bindTo("foo");
694 value = (char) bound.invoke(0);
695 if (value != 'f') {
Orion Hodsonac141392017-01-13 11:53:47 +0000696 fail("Unexpected value: " + value);
Narayan Kamath8677d0b2016-11-04 14:41:19 +0000697 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000718 fail("Unexpected value: " + intValue);
Narayan Kamath8677d0b2016-11-04 14:41:19 +0000719 }
720 }
721
Narayan Kamath916a7712016-11-08 18:36:16 +0000722 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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000757 fail("Unexpected value: " + value);
Narayan Kamath916a7712016-11-08 18:36:16 +0000758 }
759 value = (boolean) adapter.invoke((int) 43);
760 if (value) {
Orion Hodsonac141392017-01-13 11:53:47 +0000761 fail("Unexpected value: " + value);
Narayan Kamath916a7712016-11-08 18:36:16 +0000762 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000776 fail("Unexpected value: " + value);
Narayan Kamath916a7712016-11-08 18:36:16 +0000777 }
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) {
Orion Hodsonac141392017-01-13 11:53:47 +0000791 fail("Unexpected value: " + value);
Narayan Kamath916a7712016-11-08 18:36:16 +0000792 }
793 }
794 }
795
Narayan Kamath731f4c62016-11-08 19:38:48 +0000796 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
Orion Hodsonac141392017-01-13 11:53:47 +0000803 fail("Unexpected arguments: " + a + ", " + b + ", " + c
Narayan Kamath731f4c62016-11-08 19:38:48 +0000804 + ", " + 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
Orion Hodsonac141392017-01-13 11:53:47 +0000812 fail("Unexpected arguments: " + a + ", " + b);
Narayan Kamath731f4c62016-11-08 19:38:48 +0000813 }
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
Narayan Kamathb79bbd82017-01-16 17:48:28 +0000900 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
Narayan Kamathbcfd2842017-01-19 20:42:23 +0000932 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
Narayan Kamathc5889ce2017-01-19 20:42:23 +00001232 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) {
Narayan Kamath2a3696a2017-01-24 19:16:25 +00001248 } catch (WrongMethodTypeException expected) {
Narayan Kamathc5889ce2017-01-19 20:42:23 +00001249 }
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
Narayan Kamath2a3696a2017-01-24 19:16:25 +00001261 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
Roland Levillain63959082020-07-27 15:48:20 +01001345 // Checks on other array types.
Narayan Kamath2a3696a2017-01-24 19:16:25 +00001346
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
Narayan Kamath0cde4eb2017-01-26 18:31:24 +00001381 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
Narayan Kamath96120f42016-11-01 09:40:23 +00001644 public static void fail() {
1645 System.out.println("FAIL");
1646 Thread.dumpStack();
1647 }
1648
Orion Hodsonac141392017-01-13 11:53:47 +00001649 public static void fail(String message) {
1650 System.out.println("fail: " + message);
1651 Thread.dumpStack();
1652 }
1653
Narayan Kamathbcfd2842017-01-19 20:42:23 +00001654 public static void assertEquals(int i1, int i2) {
1655 if (i1 != i2) throw new AssertionError("Expected: " + i1 + " was " + i2);
1656 }
1657
Narayan Kamath96120f42016-11-01 09:40:23 +00001658 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 }
Narayan Kamath000e1882016-10-24 17:14:25 +01001669}