blob: eebf55fb6110e6dec8c47378f6ae2472307c86de [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 Kamath000e1882016-10-24 17:14:25 +010037 }
38
39 public static void testThrowException() throws Throwable {
40 MethodHandle handle = MethodHandles.throwException(String.class,
41 IllegalArgumentException.class);
42
43 if (handle.type().returnType() != String.class) {
Orion Hodsonac141392017-01-13 11:53:47 +000044 fail("Unexpected return type for handle: " + handle +
Narayan Kamath000e1882016-10-24 17:14:25 +010045 " [ " + handle.type() + "]");
46 }
47
Narayan Kamath96120f42016-11-01 09:40:23 +000048 final IllegalArgumentException iae = new IllegalArgumentException("boo!");
Narayan Kamath000e1882016-10-24 17:14:25 +010049 try {
Narayan Kamath96120f42016-11-01 09:40:23 +000050 handle.invoke(iae);
Orion Hodsonac141392017-01-13 11:53:47 +000051 fail("Expected an exception of type: java.lang.IllegalArgumentException");
Narayan Kamath000e1882016-10-24 17:14:25 +010052 } catch (IllegalArgumentException expected) {
Narayan Kamath96120f42016-11-01 09:40:23 +000053 if (expected != iae) {
Orion Hodsonac141392017-01-13 11:53:47 +000054 fail("Wrong exception: expected " + iae + " but was " + expected);
Narayan Kamath96120f42016-11-01 09:40:23 +000055 }
Narayan Kamath000e1882016-10-24 17:14:25 +010056 }
57 }
Narayan Kamath96120f42016-11-01 09:40:23 +000058
59 public static void dropArguments_delegate(String message, long message2) {
60 System.out.println("Message: " + message + ", Message2: " + message2);
61 }
62
63 public static void testDropArguments() throws Throwable {
64 MethodHandle delegate = MethodHandles.lookup().findStatic(Main.class,
65 "dropArguments_delegate",
66 MethodType.methodType(void.class, new Class<?>[] { String.class, long.class }));
67
68 MethodHandle transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class);
69
70 // The transformer will accept two additional arguments at position zero.
71 try {
72 transform.invokeExact("foo", 42l);
73 fail();
74 } catch (WrongMethodTypeException expected) {
75 }
76
77 transform.invokeExact(45, new Object(), "foo", 42l);
78 transform.invoke(45, new Object(), "foo", 42l);
79
80 // Additional arguments at position 1.
81 transform = MethodHandles.dropArguments(delegate, 1, int.class, Object.class);
82 transform.invokeExact("foo", 45, new Object(), 42l);
83 transform.invoke("foo", 45, new Object(), 42l);
84
85 // Additional arguments at position 2.
86 transform = MethodHandles.dropArguments(delegate, 2, int.class, Object.class);
87 transform.invokeExact("foo", 42l, 45, new Object());
88 transform.invoke("foo", 42l, 45, new Object());
89
90 // Note that we still perform argument conversions even for the arguments that
91 // are subsequently dropped.
92 try {
93 transform.invoke("foo", 42l, 45l, new Object());
94 fail();
95 } catch (WrongMethodTypeException expected) {
96 } catch (IllegalArgumentException expected) {
97 // TODO(narayan): We currently throw the wrong type of exception here,
98 // it's IAE and should be WMTE instead.
99 }
100
Narayan Kamath0a8485e2016-11-02 18:47:11 +0000101 // Check that asType works as expected.
102 transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class);
103 transform = transform.asType(MethodType.methodType(void.class,
104 new Class<?>[] { short.class, Object.class, String.class, long.class }));
105 transform.invokeExact((short) 45, new Object(), "foo", 42l);
106
Narayan Kamath96120f42016-11-01 09:40:23 +0000107 // Invalid argument location, should not be allowed.
108 try {
109 MethodHandles.dropArguments(delegate, -1, int.class, Object.class);
110 fail();
111 } catch (IllegalArgumentException expected) {
112 }
113
114 // Invalid argument location, should not be allowed.
115 try {
116 MethodHandles.dropArguments(delegate, 3, int.class, Object.class);
117 fail();
118 } catch (IllegalArgumentException expected) {
119 }
120
121 try {
122 MethodHandles.dropArguments(delegate, 1, void.class);
123 fail();
124 } catch (IllegalArgumentException expected) {
125 }
126 }
127
128 public static String testCatchException_target(String arg1, long arg2, String exceptionMessage)
129 throws Throwable {
130 if (exceptionMessage != null) {
131 throw new IllegalArgumentException(exceptionMessage);
132 }
133
134 System.out.println("Target: Arg1: " + arg1 + ", Arg2: " + arg2);
135 return "target";
136 }
137
138 public static String testCatchException_handler(IllegalArgumentException iae, String arg1, long arg2,
139 String exMsg) {
140 System.out.println("Handler: " + iae + ", Arg1: " + arg1 + ", Arg2: " + arg2 + ", ExMsg: " + exMsg);
141 return "handler1";
142 }
143
144 public static String testCatchException_handler2(IllegalArgumentException iae, String arg1) {
145 System.out.println("Handler: " + iae + ", Arg1: " + arg1);
146 return "handler2";
147 }
148
149 public static void testCatchException() throws Throwable {
150 MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
151 "testCatchException_target",
152 MethodType.methodType(String.class, new Class<?>[] { String.class, long.class, String.class }));
153
154 MethodHandle handler = MethodHandles.lookup().findStatic(Main.class,
155 "testCatchException_handler",
156 MethodType.methodType(String.class, new Class<?>[] { IllegalArgumentException.class,
157 String.class, long.class, String.class }));
158
159 MethodHandle adapter = MethodHandles.catchException(target, IllegalArgumentException.class,
160 handler);
161
162 String returnVal = null;
163
164 // These two should end up calling the target always. We're passing a null exception
165 // message here, which means the target will not throw.
166 returnVal = (String) adapter.invoke("foo", 42, null);
167 assertEquals("target", returnVal);
168 returnVal = (String) adapter.invokeExact("foo", 42l, (String) null);
169 assertEquals("target", returnVal);
170
171 // We're passing a non-null exception message here, which means the target will throw,
172 // which in turn means that the handler must be called for the next two invokes.
173 returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
174 assertEquals("handler1", returnVal);
175 returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage");
176 assertEquals("handler1", returnVal);
177
178 handler = MethodHandles.lookup().findStatic(Main.class,
179 "testCatchException_handler2",
180 MethodType.methodType(String.class, new Class<?>[] { IllegalArgumentException.class,
181 String.class }));
182 adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler);
183
184 returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
185 assertEquals("handler2", returnVal);
186 returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage");
187 assertEquals("handler2", returnVal);
188
189 // Test that the type of the invoke doesn't matter. Here we call
190 // IllegalArgumentException.toString() on the exception that was thrown by
191 // the target.
192 handler = MethodHandles.lookup().findVirtual(IllegalArgumentException.class,
193 "toString", MethodType.methodType(String.class));
194 adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler);
195
196 returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
197 assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal);
198 returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage2");
199 assertEquals("java.lang.IllegalArgumentException: exceptionMessage2", returnVal);
Narayan Kamath0a8485e2016-11-02 18:47:11 +0000200
201 // Check that asType works as expected.
202 adapter = MethodHandles.catchException(target, IllegalArgumentException.class,
203 handler);
204 adapter = adapter.asType(MethodType.methodType(String.class,
205 new Class<?>[] { String.class, int.class, String.class }));
206 returnVal = (String) adapter.invokeExact("foo", 42, "exceptionMessage");
207 assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal);
Narayan Kamath96120f42016-11-01 09:40:23 +0000208 }
209
210 public static boolean testGuardWithTest_test(String arg1, long arg2) {
211 return "target".equals(arg1) && 42 == arg2;
212 }
213
214 public static String testGuardWithTest_target(String arg1, long arg2, int arg3) {
215 System.out.println("target: " + arg1 + ", " + arg2 + ", " + arg3);
216 return "target";
217 }
218
219 public static String testGuardWithTest_fallback(String arg1, long arg2, int arg3) {
220 System.out.println("fallback: " + arg1 + ", " + arg2 + ", " + arg3);
221 return "fallback";
222 }
223
224 public static void testGuardWithTest() throws Throwable {
225 MethodHandle test = MethodHandles.lookup().findStatic(Main.class,
226 "testGuardWithTest_test",
227 MethodType.methodType(boolean.class, new Class<?>[] { String.class, long.class }));
228
229 final MethodType type = MethodType.methodType(String.class,
230 new Class<?>[] { String.class, long.class, int.class });
231
232 final MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
233 "testGuardWithTest_target", type);
234 final MethodHandle fallback = MethodHandles.lookup().findStatic(Main.class,
235 "testGuardWithTest_fallback", type);
236
237 MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback);
238
239 String returnVal = null;
240
241 returnVal = (String) adapter.invoke("target", 42, 56);
242 assertEquals("target", returnVal);
243 returnVal = (String) adapter.invokeExact("target", 42l, 56);
244 assertEquals("target", returnVal);
245
246 returnVal = (String) adapter.invoke("fallback", 42l, 56);
247 assertEquals("fallback", returnVal);
248 returnVal = (String) adapter.invokeExact("target", 42l, 56);
249 assertEquals("target", returnVal);
Narayan Kamath0a8485e2016-11-02 18:47:11 +0000250
251 // Check that asType works as expected.
252 adapter = adapter.asType(MethodType.methodType(String.class,
253 new Class<?>[] { String.class, int.class, int.class }));
254 returnVal = (String) adapter.invokeExact("target", 42, 56);
255 assertEquals("target", returnVal);
Narayan Kamath96120f42016-11-01 09:40:23 +0000256 }
257
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000258 public static void testArrayElementGetter() throws Throwable {
259 MethodHandle getter = MethodHandles.arrayElementGetter(int[].class);
260
261 {
262 int[] array = new int[1];
263 array[0] = 42;
264 int value = (int) getter.invoke(array, 0);
265 if (value != 42) {
Orion Hodsonac141392017-01-13 11:53:47 +0000266 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000267 }
268
269 try {
270 value = (int) getter.invoke(array, -1);
271 fail();
272 } catch (ArrayIndexOutOfBoundsException expected) {
273 }
274
275 try {
276 value = (int) getter.invoke(null, -1);
277 fail();
278 } catch (NullPointerException expected) {
279 }
280 }
281
282 {
283 getter = MethodHandles.arrayElementGetter(long[].class);
284 long[] array = new long[1];
285 array[0] = 42;
286 long value = (long) getter.invoke(array, 0);
287 if (value != 42l) {
Orion Hodsonac141392017-01-13 11:53:47 +0000288 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000289 }
290 }
291
292 {
293 getter = MethodHandles.arrayElementGetter(short[].class);
294 short[] array = new short[1];
295 array[0] = 42;
296 short value = (short) getter.invoke(array, 0);
297 if (value != 42l) {
Orion Hodsonac141392017-01-13 11:53:47 +0000298 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000299 }
300 }
301
302 {
303 getter = MethodHandles.arrayElementGetter(char[].class);
304 char[] array = new char[1];
305 array[0] = 42;
306 char value = (char) getter.invoke(array, 0);
307 if (value != 42l) {
Orion Hodsonac141392017-01-13 11:53:47 +0000308 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000309 }
310 }
311
312 {
313 getter = MethodHandles.arrayElementGetter(byte[].class);
314 byte[] array = new byte[1];
315 array[0] = (byte) 0x8;
316 byte value = (byte) getter.invoke(array, 0);
317 if (value != (byte) 0x8) {
Orion Hodsonac141392017-01-13 11:53:47 +0000318 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000319 }
320 }
321
322 {
323 getter = MethodHandles.arrayElementGetter(boolean[].class);
324 boolean[] array = new boolean[1];
325 array[0] = true;
326 boolean value = (boolean) getter.invoke(array, 0);
327 if (!value) {
Orion Hodsonac141392017-01-13 11:53:47 +0000328 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000329 }
330 }
331
332 {
333 getter = MethodHandles.arrayElementGetter(float[].class);
334 float[] array = new float[1];
335 array[0] = 42.0f;
336 float value = (float) getter.invoke(array, 0);
337 if (value != 42.0f) {
Orion Hodsonac141392017-01-13 11:53:47 +0000338 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000339 }
340 }
341
342 {
343 getter = MethodHandles.arrayElementGetter(double[].class);
344 double[] array = new double[1];
345 array[0] = 42.0;
346 double value = (double) getter.invoke(array, 0);
347 if (value != 42.0) {
Orion Hodsonac141392017-01-13 11:53:47 +0000348 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000349 }
350 }
351
352 {
353 getter = MethodHandles.arrayElementGetter(String[].class);
354 String[] array = new String[3];
355 array[0] = "42";
356 array[1] = "48";
357 array[2] = "54";
358 String value = (String) getter.invoke(array, 0);
359 assertEquals("42", value);
360 value = (String) getter.invoke(array, 1);
361 assertEquals("48", value);
362 value = (String) getter.invoke(array, 2);
363 assertEquals("54", value);
364 }
365 }
366
367 public static void testArrayElementSetter() throws Throwable {
368 MethodHandle setter = MethodHandles.arrayElementSetter(int[].class);
369
370 {
371 int[] array = new int[2];
372 setter.invoke(array, 0, 42);
373 setter.invoke(array, 1, 43);
374
375 if (array[0] != 42) {
Orion Hodsonac141392017-01-13 11:53:47 +0000376 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000377 }
378 if (array[1] != 43) {
Orion Hodsonac141392017-01-13 11:53:47 +0000379 fail("Unexpected value: " + array[1]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000380 }
381
382 try {
383 setter.invoke(array, -1, 42);
384 fail();
385 } catch (ArrayIndexOutOfBoundsException expected) {
386 }
387
388 try {
389 setter.invoke(null, 0, 42);
390 fail();
391 } catch (NullPointerException expected) {
392 }
393 }
394
395 {
396 setter = MethodHandles.arrayElementSetter(long[].class);
397 long[] array = new long[1];
398 setter.invoke(array, 0, 42l);
399 if (array[0] != 42l) {
Orion Hodsonac141392017-01-13 11:53:47 +0000400 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000401 }
402 }
403
404 {
405 setter = MethodHandles.arrayElementSetter(short[].class);
406 short[] array = new short[1];
407 setter.invoke(array, 0, (short) 42);
408 if (array[0] != 42l) {
Orion Hodsonac141392017-01-13 11:53:47 +0000409 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000410 }
411 }
412
413 {
414 setter = MethodHandles.arrayElementSetter(char[].class);
415 char[] array = new char[1];
416 setter.invoke(array, 0, (char) 42);
417 if (array[0] != 42) {
Orion Hodsonac141392017-01-13 11:53:47 +0000418 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000419 }
420 }
421
422 {
423 setter = MethodHandles.arrayElementSetter(byte[].class);
424 byte[] array = new byte[1];
425 setter.invoke(array, 0, (byte) 0x8);
426 if (array[0] != (byte) 0x8) {
Orion Hodsonac141392017-01-13 11:53:47 +0000427 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000428 }
429 }
430
431 {
432 setter = MethodHandles.arrayElementSetter(boolean[].class);
433 boolean[] array = new boolean[1];
434 setter.invoke(array, 0, true);
435 if (!array[0]) {
Orion Hodsonac141392017-01-13 11:53:47 +0000436 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000437 }
438 }
439
440 {
441 setter = MethodHandles.arrayElementSetter(float[].class);
442 float[] array = new float[1];
443 setter.invoke(array, 0, 42.0f);
444 if (array[0] != 42.0f) {
Orion Hodsonac141392017-01-13 11:53:47 +0000445 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000446 }
447 }
448
449 {
450 setter = MethodHandles.arrayElementSetter(double[].class);
451 double[] array = new double[1];
452 setter.invoke(array, 0, 42.0);
453 if (array[0] != 42.0) {
Orion Hodsonac141392017-01-13 11:53:47 +0000454 fail("Unexpected value: " + array[0]);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000455 }
456 }
457
458 {
459 setter = MethodHandles.arrayElementSetter(String[].class);
460 String[] array = new String[3];
461 setter.invoke(array, 0, "42");
462 setter.invoke(array, 1, "48");
463 setter.invoke(array, 2, "54");
464 assertEquals("42", array[0]);
465 assertEquals("48", array[1]);
466 assertEquals("54", array[2]);
467 }
468 }
469
470 public static void testIdentity() throws Throwable {
471 {
472 MethodHandle identity = MethodHandles.identity(boolean.class);
473 boolean value = (boolean) identity.invoke(false);
474 if (value) {
Orion Hodsonac141392017-01-13 11:53:47 +0000475 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000476 }
477 }
478
479 {
480 MethodHandle identity = MethodHandles.identity(byte.class);
481 byte value = (byte) identity.invoke((byte) 0x8);
482 if (value != (byte) 0x8) {
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(char.class);
489 char value = (char) identity.invoke((char) -56);
490 if (value != (char) -56) {
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(short.class);
497 short value = (short) identity.invoke((short) -59);
498 if (value != (short) -59) {
Orion Hodsonac141392017-01-13 11:53:47 +0000499 fail("Unexpected value: " + Short.toString(value));
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000500 }
501 }
502
503 {
504 MethodHandle identity = MethodHandles.identity(int.class);
505 int value = (int) identity.invoke(52);
506 if (value != 52) {
Orion Hodsonac141392017-01-13 11:53:47 +0000507 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000508 }
509 }
510
511 {
512 MethodHandle identity = MethodHandles.identity(long.class);
513 long value = (long) identity.invoke(-76l);
514 if (value != (long) -76) {
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(float.class);
521 float value = (float) identity.invoke(56.0f);
522 if (value != (float) 56.0f) {
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(double.class);
529 double value = (double) identity.invoke((double) 72.0);
530 if (value != (double) 72.0) {
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(String.class);
537 String value = (String) identity.invoke("bazman");
538 assertEquals("bazman", value);
539 }
540 }
541
542 public static void testConstant() throws Throwable {
543 // int constants.
544 {
545 MethodHandle constant = MethodHandles.constant(int.class, 56);
546 int value = (int) constant.invoke();
547 if (value != 56) {
Orion Hodsonac141392017-01-13 11:53:47 +0000548 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000549 }
550
551 // short constant values are converted to int.
552 constant = MethodHandles.constant(int.class, (short) 52);
553 value = (int) constant.invoke();
554 if (value != 52) {
Orion Hodsonac141392017-01-13 11:53:47 +0000555 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000556 }
557
558 // char constant values are converted to int.
559 constant = MethodHandles.constant(int.class, (char) 'b');
560 value = (int) constant.invoke();
561 if (value != (int) 'b') {
Orion Hodsonac141392017-01-13 11:53:47 +0000562 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000563 }
564
565 // int constant values are converted to int.
566 constant = MethodHandles.constant(int.class, (byte) 0x1);
567 value = (int) constant.invoke();
568 if (value != 1) {
Orion Hodsonac141392017-01-13 11:53:47 +0000569 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000570 }
571
572 // boolean, float, double and long primitive constants are not convertible
573 // to int, so the handle creation must fail with a CCE.
574 try {
575 MethodHandles.constant(int.class, false);
576 fail();
577 } catch (ClassCastException expected) {
578 }
579
580 try {
581 MethodHandles.constant(int.class, 0.1f);
582 fail();
583 } catch (ClassCastException expected) {
584 }
585
586 try {
587 MethodHandles.constant(int.class, 0.2);
588 fail();
589 } catch (ClassCastException expected) {
590 }
591
592 try {
593 MethodHandles.constant(int.class, 73l);
594 fail();
595 } catch (ClassCastException expected) {
596 }
597 }
598
599 // long constants.
600 {
601 MethodHandle constant = MethodHandles.constant(long.class, 56l);
602 long value = (long) constant.invoke();
603 if (value != 56l) {
Orion Hodsonac141392017-01-13 11:53:47 +0000604 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000605 }
606
607 constant = MethodHandles.constant(long.class, (int) 56);
608 value = (long) constant.invoke();
609 if (value != 56l) {
Orion Hodsonac141392017-01-13 11:53:47 +0000610 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000611 }
612 }
613
614 // byte constants.
615 {
616 MethodHandle constant = MethodHandles.constant(byte.class, (byte) 0x12);
617 byte value = (byte) constant.invoke();
618 if (value != (byte) 0x12) {
Orion Hodsonac141392017-01-13 11:53:47 +0000619 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000620 }
621 }
622
623 // boolean constants.
624 {
625 MethodHandle constant = MethodHandles.constant(boolean.class, true);
626 boolean value = (boolean) constant.invoke();
627 if (!value) {
Orion Hodsonac141392017-01-13 11:53:47 +0000628 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000629 }
630 }
631
632 // char constants.
633 {
634 MethodHandle constant = MethodHandles.constant(char.class, 'f');
635 char value = (char) constant.invoke();
636 if (value != 'f') {
Orion Hodsonac141392017-01-13 11:53:47 +0000637 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000638 }
639 }
640
641 // short constants.
642 {
643 MethodHandle constant = MethodHandles.constant(short.class, (short) 123);
644 short value = (short) constant.invoke();
645 if (value != (short) 123) {
Orion Hodsonac141392017-01-13 11:53:47 +0000646 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000647 }
648 }
649
650 // float constants.
651 {
652 MethodHandle constant = MethodHandles.constant(float.class, 56.0f);
653 float value = (float) constant.invoke();
654 if (value != 56.0f) {
Orion Hodsonac141392017-01-13 11:53:47 +0000655 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000656 }
657 }
658
659 // double constants.
660 {
661 MethodHandle constant = MethodHandles.constant(double.class, 256.0);
662 double value = (double) constant.invoke();
663 if (value != 256.0) {
Orion Hodsonac141392017-01-13 11:53:47 +0000664 fail("Unexpected value: " + value);
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000665 }
666 }
667
668 // reference constants.
669 {
670 MethodHandle constant = MethodHandles.constant(String.class, "256.0");
671 String value = (String) constant.invoke();
672 assertEquals("256.0", value);
673 }
674 }
675
Narayan Kamath8677d0b2016-11-04 14:41:19 +0000676 public static void testBindTo() throws Throwable {
677 MethodHandle stringCharAt = MethodHandles.lookup().findVirtual(
678 String.class, "charAt", MethodType.methodType(char.class, int.class));
679
680 char value = (char) stringCharAt.invoke("foo", 0);
681 if (value != 'f') {
Orion Hodsonac141392017-01-13 11:53:47 +0000682 fail("Unexpected value: " + value);
Narayan Kamath8677d0b2016-11-04 14:41:19 +0000683 }
684
685 MethodHandle bound = stringCharAt.bindTo("foo");
686 value = (char) bound.invoke(0);
687 if (value != 'f') {
Orion Hodsonac141392017-01-13 11:53:47 +0000688 fail("Unexpected value: " + value);
Narayan Kamath8677d0b2016-11-04 14:41:19 +0000689 }
690
691 try {
692 stringCharAt.bindTo(new Object());
693 fail();
694 } catch (ClassCastException expected) {
695 }
696
697 bound = stringCharAt.bindTo(null);
698 try {
699 bound.invoke(0);
700 fail();
701 } catch (NullPointerException expected) {
702 }
703
704 MethodHandle integerParseInt = MethodHandles.lookup().findStatic(
705 Integer.class, "parseInt", MethodType.methodType(int.class, String.class));
706
707 bound = integerParseInt.bindTo("78452");
708 int intValue = (int) bound.invoke();
709 if (intValue != 78452) {
Orion Hodsonac141392017-01-13 11:53:47 +0000710 fail("Unexpected value: " + intValue);
Narayan Kamath8677d0b2016-11-04 14:41:19 +0000711 }
712 }
713
Narayan Kamath916a7712016-11-08 18:36:16 +0000714 public static String filterReturnValue_target(int a) {
715 return "ReturnValue" + a;
716 }
717
718 public static boolean filterReturnValue_filter(String value) {
719 return value.indexOf("42") != -1;
720 }
721
722 public static int filterReturnValue_intTarget(String a) {
723 return Integer.parseInt(a);
724 }
725
726 public static int filterReturnValue_intFilter(int b) {
727 return b + 1;
728 }
729
730 public static void filterReturnValue_voidTarget() {
731 }
732
733 public static int filterReturnValue_voidFilter() {
734 return 42;
735 }
736
737 public static void testFilterReturnValue() throws Throwable {
738 // A target that returns a reference.
739 {
740 final MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
741 "filterReturnValue_target", MethodType.methodType(String.class, int.class));
742 final MethodHandle filter = MethodHandles.lookup().findStatic(Main.class,
743 "filterReturnValue_filter", MethodType.methodType(boolean.class, String.class));
744
745 MethodHandle adapter = MethodHandles.filterReturnValue(target, filter);
746
747 boolean value = (boolean) adapter.invoke((int) 42);
748 if (!value) {
Orion Hodsonac141392017-01-13 11:53:47 +0000749 fail("Unexpected value: " + value);
Narayan Kamath916a7712016-11-08 18:36:16 +0000750 }
751 value = (boolean) adapter.invoke((int) 43);
752 if (value) {
Orion Hodsonac141392017-01-13 11:53:47 +0000753 fail("Unexpected value: " + value);
Narayan Kamath916a7712016-11-08 18:36:16 +0000754 }
755 }
756
757 // A target that returns a primitive.
758 {
759 final MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
760 "filterReturnValue_intTarget", MethodType.methodType(int.class, String.class));
761 final MethodHandle filter = MethodHandles.lookup().findStatic(Main.class,
762 "filterReturnValue_intFilter", MethodType.methodType(int.class, int.class));
763
764 MethodHandle adapter = MethodHandles.filterReturnValue(target, filter);
765
766 int value = (int) adapter.invoke("56");
767 if (value != 57) {
Orion Hodsonac141392017-01-13 11:53:47 +0000768 fail("Unexpected value: " + value);
Narayan Kamath916a7712016-11-08 18:36:16 +0000769 }
770 }
771
772 // A target that returns void.
773 {
774 final MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
775 "filterReturnValue_voidTarget", MethodType.methodType(void.class));
776 final MethodHandle filter = MethodHandles.lookup().findStatic(Main.class,
777 "filterReturnValue_voidFilter", MethodType.methodType(int.class));
778
779 MethodHandle adapter = MethodHandles.filterReturnValue(target, filter);
780
781 int value = (int) adapter.invoke();
782 if (value != 42) {
Orion Hodsonac141392017-01-13 11:53:47 +0000783 fail("Unexpected value: " + value);
Narayan Kamath916a7712016-11-08 18:36:16 +0000784 }
785 }
786 }
787
Narayan Kamath731f4c62016-11-08 19:38:48 +0000788 public static void permuteArguments_callee(boolean a, byte b, char c,
789 short d, int e, long f, float g, double h) {
790 if (a == true && b == (byte) 'b' && c == 'c' && d == (short) 56 &&
791 e == 78 && f == (long) 97 && g == 98.0f && f == 97.0) {
792 return;
793 }
794
Orion Hodsonac141392017-01-13 11:53:47 +0000795 fail("Unexpected arguments: " + a + ", " + b + ", " + c
Narayan Kamath731f4c62016-11-08 19:38:48 +0000796 + ", " + d + ", " + e + ", " + f + ", " + g + ", " + h);
797 }
798
799 public static void permuteArguments_boxingCallee(boolean a, Integer b) {
800 if (a && b.intValue() == 42) {
801 return;
802 }
803
Orion Hodsonac141392017-01-13 11:53:47 +0000804 fail("Unexpected arguments: " + a + ", " + b);
Narayan Kamath731f4c62016-11-08 19:38:48 +0000805 }
806
807 public static void testPermuteArguments() throws Throwable {
808 {
809 final MethodHandle target = MethodHandles.lookup().findStatic(
810 Main.class, "permuteArguments_callee",
811 MethodType.methodType(void.class, new Class<?>[] {
812 boolean.class, byte.class, char.class, short.class, int.class,
813 long.class, float.class, double.class }));
814
815 final MethodType newType = MethodType.methodType(void.class, new Class<?>[] {
816 double.class, float.class, long.class, int.class, short.class, char.class,
817 byte.class, boolean.class });
818
819 final MethodHandle permutation = MethodHandles.permuteArguments(
820 target, newType, new int[] { 7, 6, 5, 4, 3, 2, 1, 0 });
821
822 permutation.invoke((double) 97.0, (float) 98.0f, (long) 97, 78,
823 (short) 56, 'c', (byte) 'b', (boolean) true);
824
825 // The permutation array was not of the right length.
826 try {
827 MethodHandles.permuteArguments(target, newType,
828 new int[] { 7 });
829 fail();
830 } catch (IllegalArgumentException expected) {
831 }
832
833 // The permutation array has an element that's out of bounds
834 // (there's no argument with idx == 8).
835 try {
836 MethodHandles.permuteArguments(target, newType,
837 new int[] { 8, 6, 5, 4, 3, 2, 1, 0 });
838 fail();
839 } catch (IllegalArgumentException expected) {
840 }
841
842 // The permutation array maps to an incorrect type.
843 try {
844 MethodHandles.permuteArguments(target, newType,
845 new int[] { 7, 7, 5, 4, 3, 2, 1, 0 });
846 fail();
847 } catch (IllegalArgumentException expected) {
848 }
849 }
850
851 // Tests for reference arguments as well as permutations that
852 // repeat arguments.
853 {
854 final MethodHandle target = MethodHandles.lookup().findVirtual(
855 String.class, "concat", MethodType.methodType(String.class, String.class));
856
857 final MethodType newType = MethodType.methodType(String.class, String.class,
858 String.class);
859
860 assertEquals("foobar", (String) target.invoke("foo", "bar"));
861
862 MethodHandle permutation = MethodHandles.permuteArguments(target,
863 newType, new int[] { 1, 0 });
864 assertEquals("barfoo", (String) permutation.invoke("foo", "bar"));
865
866 permutation = MethodHandles.permuteArguments(target, newType, new int[] { 0, 0 });
867 assertEquals("foofoo", (String) permutation.invoke("foo", "bar"));
868
869 permutation = MethodHandles.permuteArguments(target, newType, new int[] { 1, 1 });
870 assertEquals("barbar", (String) permutation.invoke("foo", "bar"));
871 }
872
873 // Tests for boxing and unboxing.
874 {
875 final MethodHandle target = MethodHandles.lookup().findStatic(
876 Main.class, "permuteArguments_boxingCallee",
877 MethodType.methodType(void.class, new Class<?>[] { boolean.class, Integer.class }));
878
879 final MethodType newType = MethodType.methodType(void.class,
880 new Class<?>[] { Integer.class, boolean.class });
881
882 MethodHandle permutation = MethodHandles.permuteArguments(target,
883 newType, new int[] { 1, 0 });
884
885 permutation.invoke(42, true);
886 permutation.invoke(42, Boolean.TRUE);
887 permutation.invoke(Integer.valueOf(42), true);
888 permutation.invoke(Integer.valueOf(42), Boolean.TRUE);
889 }
890 }
891
Narayan Kamathb79bbd82017-01-16 17:48:28 +0000892 private static Object returnBar() {
893 return "bar";
894 }
895
896 public static void testInvokers() throws Throwable {
897 final MethodType targetType = MethodType.methodType(String.class, String.class);
898 final MethodHandle target = MethodHandles.lookup().findVirtual(
899 String.class, "concat", targetType);
900
901 MethodHandle invoker = MethodHandles.invoker(target.type());
902 assertEquals("barbar", (String) invoker.invoke(target, "bar", "bar"));
903 assertEquals("barbar", (String) invoker.invoke(target, (Object) returnBar(), "bar"));
904 try {
905 String foo = (String) invoker.invoke(target, "bar", "bar", 24);
906 fail();
907 } catch (WrongMethodTypeException expected) {
908 }
909
910 MethodHandle exactInvoker = MethodHandles.exactInvoker(target.type());
911 assertEquals("barbar", (String) exactInvoker.invoke(target, "bar", "bar"));
912 try {
913 String foo = (String) exactInvoker.invoke(target, (Object) returnBar(), "bar");
914 fail();
915 } catch (WrongMethodTypeException expected) {
916 }
917 try {
918 String foo = (String) exactInvoker.invoke(target, "bar", "bar", 24);
919 fail();
920 } catch (WrongMethodTypeException expected) {
921 }
922 }
923
Narayan Kamath96120f42016-11-01 09:40:23 +0000924 public static void fail() {
925 System.out.println("FAIL");
926 Thread.dumpStack();
927 }
928
Orion Hodsonac141392017-01-13 11:53:47 +0000929 public static void fail(String message) {
930 System.out.println("fail: " + message);
931 Thread.dumpStack();
932 }
933
Narayan Kamath96120f42016-11-01 09:40:23 +0000934 public static void assertEquals(String s1, String s2) {
935 if (s1 == s2) {
936 return;
937 }
938
939 if (s1 != null && s2 != null && s1.equals(s2)) {
940 return;
941 }
942
943 throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
944 }
Narayan Kamath000e1882016-10-24 17:14:25 +0100945}