blob: 6c977f4f347365f3fa692a62f44b634290a7a050 [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 Kamath000e1882016-10-24 17:14:25 +010033 }
34
35 public static void testThrowException() throws Throwable {
36 MethodHandle handle = MethodHandles.throwException(String.class,
37 IllegalArgumentException.class);
38
39 if (handle.type().returnType() != String.class) {
40 System.out.println("Unexpected return type for handle: " + handle +
41 " [ " + handle.type() + "]");
42 }
43
Narayan Kamath96120f42016-11-01 09:40:23 +000044 final IllegalArgumentException iae = new IllegalArgumentException("boo!");
Narayan Kamath000e1882016-10-24 17:14:25 +010045 try {
Narayan Kamath96120f42016-11-01 09:40:23 +000046 handle.invoke(iae);
Narayan Kamath000e1882016-10-24 17:14:25 +010047 System.out.println("Expected an exception of type: java.lang.IllegalArgumentException");
48 } catch (IllegalArgumentException expected) {
Narayan Kamath96120f42016-11-01 09:40:23 +000049 if (expected != iae) {
50 System.out.println("Wrong exception: expected " + iae + " but was " + expected);
51 }
Narayan Kamath000e1882016-10-24 17:14:25 +010052 }
53 }
Narayan Kamath96120f42016-11-01 09:40:23 +000054
55 public static void dropArguments_delegate(String message, long message2) {
56 System.out.println("Message: " + message + ", Message2: " + message2);
57 }
58
59 public static void testDropArguments() throws Throwable {
60 MethodHandle delegate = MethodHandles.lookup().findStatic(Main.class,
61 "dropArguments_delegate",
62 MethodType.methodType(void.class, new Class<?>[] { String.class, long.class }));
63
64 MethodHandle transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class);
65
66 // The transformer will accept two additional arguments at position zero.
67 try {
68 transform.invokeExact("foo", 42l);
69 fail();
70 } catch (WrongMethodTypeException expected) {
71 }
72
73 transform.invokeExact(45, new Object(), "foo", 42l);
74 transform.invoke(45, new Object(), "foo", 42l);
75
76 // Additional arguments at position 1.
77 transform = MethodHandles.dropArguments(delegate, 1, int.class, Object.class);
78 transform.invokeExact("foo", 45, new Object(), 42l);
79 transform.invoke("foo", 45, new Object(), 42l);
80
81 // Additional arguments at position 2.
82 transform = MethodHandles.dropArguments(delegate, 2, int.class, Object.class);
83 transform.invokeExact("foo", 42l, 45, new Object());
84 transform.invoke("foo", 42l, 45, new Object());
85
86 // Note that we still perform argument conversions even for the arguments that
87 // are subsequently dropped.
88 try {
89 transform.invoke("foo", 42l, 45l, new Object());
90 fail();
91 } catch (WrongMethodTypeException expected) {
92 } catch (IllegalArgumentException expected) {
93 // TODO(narayan): We currently throw the wrong type of exception here,
94 // it's IAE and should be WMTE instead.
95 }
96
Narayan Kamath0a8485e2016-11-02 18:47:11 +000097 // Check that asType works as expected.
98 transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class);
99 transform = transform.asType(MethodType.methodType(void.class,
100 new Class<?>[] { short.class, Object.class, String.class, long.class }));
101 transform.invokeExact((short) 45, new Object(), "foo", 42l);
102
Narayan Kamath96120f42016-11-01 09:40:23 +0000103 // Invalid argument location, should not be allowed.
104 try {
105 MethodHandles.dropArguments(delegate, -1, int.class, Object.class);
106 fail();
107 } catch (IllegalArgumentException expected) {
108 }
109
110 // Invalid argument location, should not be allowed.
111 try {
112 MethodHandles.dropArguments(delegate, 3, int.class, Object.class);
113 fail();
114 } catch (IllegalArgumentException expected) {
115 }
116
117 try {
118 MethodHandles.dropArguments(delegate, 1, void.class);
119 fail();
120 } catch (IllegalArgumentException expected) {
121 }
122 }
123
124 public static String testCatchException_target(String arg1, long arg2, String exceptionMessage)
125 throws Throwable {
126 if (exceptionMessage != null) {
127 throw new IllegalArgumentException(exceptionMessage);
128 }
129
130 System.out.println("Target: Arg1: " + arg1 + ", Arg2: " + arg2);
131 return "target";
132 }
133
134 public static String testCatchException_handler(IllegalArgumentException iae, String arg1, long arg2,
135 String exMsg) {
136 System.out.println("Handler: " + iae + ", Arg1: " + arg1 + ", Arg2: " + arg2 + ", ExMsg: " + exMsg);
137 return "handler1";
138 }
139
140 public static String testCatchException_handler2(IllegalArgumentException iae, String arg1) {
141 System.out.println("Handler: " + iae + ", Arg1: " + arg1);
142 return "handler2";
143 }
144
145 public static void testCatchException() throws Throwable {
146 MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
147 "testCatchException_target",
148 MethodType.methodType(String.class, new Class<?>[] { String.class, long.class, String.class }));
149
150 MethodHandle handler = MethodHandles.lookup().findStatic(Main.class,
151 "testCatchException_handler",
152 MethodType.methodType(String.class, new Class<?>[] { IllegalArgumentException.class,
153 String.class, long.class, String.class }));
154
155 MethodHandle adapter = MethodHandles.catchException(target, IllegalArgumentException.class,
156 handler);
157
158 String returnVal = null;
159
160 // These two should end up calling the target always. We're passing a null exception
161 // message here, which means the target will not throw.
162 returnVal = (String) adapter.invoke("foo", 42, null);
163 assertEquals("target", returnVal);
164 returnVal = (String) adapter.invokeExact("foo", 42l, (String) null);
165 assertEquals("target", returnVal);
166
167 // We're passing a non-null exception message here, which means the target will throw,
168 // which in turn means that the handler must be called for the next two invokes.
169 returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
170 assertEquals("handler1", returnVal);
171 returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage");
172 assertEquals("handler1", returnVal);
173
174 handler = MethodHandles.lookup().findStatic(Main.class,
175 "testCatchException_handler2",
176 MethodType.methodType(String.class, new Class<?>[] { IllegalArgumentException.class,
177 String.class }));
178 adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler);
179
180 returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
181 assertEquals("handler2", returnVal);
182 returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage");
183 assertEquals("handler2", returnVal);
184
185 // Test that the type of the invoke doesn't matter. Here we call
186 // IllegalArgumentException.toString() on the exception that was thrown by
187 // the target.
188 handler = MethodHandles.lookup().findVirtual(IllegalArgumentException.class,
189 "toString", MethodType.methodType(String.class));
190 adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler);
191
192 returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
193 assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal);
194 returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage2");
195 assertEquals("java.lang.IllegalArgumentException: exceptionMessage2", returnVal);
Narayan Kamath0a8485e2016-11-02 18:47:11 +0000196
197 // Check that asType works as expected.
198 adapter = MethodHandles.catchException(target, IllegalArgumentException.class,
199 handler);
200 adapter = adapter.asType(MethodType.methodType(String.class,
201 new Class<?>[] { String.class, int.class, String.class }));
202 returnVal = (String) adapter.invokeExact("foo", 42, "exceptionMessage");
203 assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal);
Narayan Kamath96120f42016-11-01 09:40:23 +0000204 }
205
206 public static boolean testGuardWithTest_test(String arg1, long arg2) {
207 return "target".equals(arg1) && 42 == arg2;
208 }
209
210 public static String testGuardWithTest_target(String arg1, long arg2, int arg3) {
211 System.out.println("target: " + arg1 + ", " + arg2 + ", " + arg3);
212 return "target";
213 }
214
215 public static String testGuardWithTest_fallback(String arg1, long arg2, int arg3) {
216 System.out.println("fallback: " + arg1 + ", " + arg2 + ", " + arg3);
217 return "fallback";
218 }
219
220 public static void testGuardWithTest() throws Throwable {
221 MethodHandle test = MethodHandles.lookup().findStatic(Main.class,
222 "testGuardWithTest_test",
223 MethodType.methodType(boolean.class, new Class<?>[] { String.class, long.class }));
224
225 final MethodType type = MethodType.methodType(String.class,
226 new Class<?>[] { String.class, long.class, int.class });
227
228 final MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
229 "testGuardWithTest_target", type);
230 final MethodHandle fallback = MethodHandles.lookup().findStatic(Main.class,
231 "testGuardWithTest_fallback", type);
232
233 MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback);
234
235 String returnVal = null;
236
237 returnVal = (String) adapter.invoke("target", 42, 56);
238 assertEquals("target", returnVal);
239 returnVal = (String) adapter.invokeExact("target", 42l, 56);
240 assertEquals("target", returnVal);
241
242 returnVal = (String) adapter.invoke("fallback", 42l, 56);
243 assertEquals("fallback", returnVal);
244 returnVal = (String) adapter.invokeExact("target", 42l, 56);
245 assertEquals("target", returnVal);
Narayan Kamath0a8485e2016-11-02 18:47:11 +0000246
247 // Check that asType works as expected.
248 adapter = adapter.asType(MethodType.methodType(String.class,
249 new Class<?>[] { String.class, int.class, int.class }));
250 returnVal = (String) adapter.invokeExact("target", 42, 56);
251 assertEquals("target", returnVal);
Narayan Kamath96120f42016-11-01 09:40:23 +0000252 }
253
Narayan Kamath3314dbb2016-11-03 18:01:32 +0000254 public static void testArrayElementGetter() throws Throwable {
255 MethodHandle getter = MethodHandles.arrayElementGetter(int[].class);
256
257 {
258 int[] array = new int[1];
259 array[0] = 42;
260 int value = (int) getter.invoke(array, 0);
261 if (value != 42) {
262 System.out.println("Unexpected value: " + value);
263 }
264
265 try {
266 value = (int) getter.invoke(array, -1);
267 fail();
268 } catch (ArrayIndexOutOfBoundsException expected) {
269 }
270
271 try {
272 value = (int) getter.invoke(null, -1);
273 fail();
274 } catch (NullPointerException expected) {
275 }
276 }
277
278 {
279 getter = MethodHandles.arrayElementGetter(long[].class);
280 long[] array = new long[1];
281 array[0] = 42;
282 long value = (long) getter.invoke(array, 0);
283 if (value != 42l) {
284 System.out.println("Unexpected value: " + value);
285 }
286 }
287
288 {
289 getter = MethodHandles.arrayElementGetter(short[].class);
290 short[] array = new short[1];
291 array[0] = 42;
292 short value = (short) getter.invoke(array, 0);
293 if (value != 42l) {
294 System.out.println("Unexpected value: " + value);
295 }
296 }
297
298 {
299 getter = MethodHandles.arrayElementGetter(char[].class);
300 char[] array = new char[1];
301 array[0] = 42;
302 char value = (char) getter.invoke(array, 0);
303 if (value != 42l) {
304 System.out.println("Unexpected value: " + value);
305 }
306 }
307
308 {
309 getter = MethodHandles.arrayElementGetter(byte[].class);
310 byte[] array = new byte[1];
311 array[0] = (byte) 0x8;
312 byte value = (byte) getter.invoke(array, 0);
313 if (value != (byte) 0x8) {
314 System.out.println("Unexpected value: " + value);
315 }
316 }
317
318 {
319 getter = MethodHandles.arrayElementGetter(boolean[].class);
320 boolean[] array = new boolean[1];
321 array[0] = true;
322 boolean value = (boolean) getter.invoke(array, 0);
323 if (!value) {
324 System.out.println("Unexpected value: " + value);
325 }
326 }
327
328 {
329 getter = MethodHandles.arrayElementGetter(float[].class);
330 float[] array = new float[1];
331 array[0] = 42.0f;
332 float value = (float) getter.invoke(array, 0);
333 if (value != 42.0f) {
334 System.out.println("Unexpected value: " + value);
335 }
336 }
337
338 {
339 getter = MethodHandles.arrayElementGetter(double[].class);
340 double[] array = new double[1];
341 array[0] = 42.0;
342 double value = (double) getter.invoke(array, 0);
343 if (value != 42.0) {
344 System.out.println("Unexpected value: " + value);
345 }
346 }
347
348 {
349 getter = MethodHandles.arrayElementGetter(String[].class);
350 String[] array = new String[3];
351 array[0] = "42";
352 array[1] = "48";
353 array[2] = "54";
354 String value = (String) getter.invoke(array, 0);
355 assertEquals("42", value);
356 value = (String) getter.invoke(array, 1);
357 assertEquals("48", value);
358 value = (String) getter.invoke(array, 2);
359 assertEquals("54", value);
360 }
361 }
362
363 public static void testArrayElementSetter() throws Throwable {
364 MethodHandle setter = MethodHandles.arrayElementSetter(int[].class);
365
366 {
367 int[] array = new int[2];
368 setter.invoke(array, 0, 42);
369 setter.invoke(array, 1, 43);
370
371 if (array[0] != 42) {
372 System.out.println("Unexpected value: " + array[0]);
373 }
374 if (array[1] != 43) {
375 System.out.println("Unexpected value: " + array[1]);
376 }
377
378 try {
379 setter.invoke(array, -1, 42);
380 fail();
381 } catch (ArrayIndexOutOfBoundsException expected) {
382 }
383
384 try {
385 setter.invoke(null, 0, 42);
386 fail();
387 } catch (NullPointerException expected) {
388 }
389 }
390
391 {
392 setter = MethodHandles.arrayElementSetter(long[].class);
393 long[] array = new long[1];
394 setter.invoke(array, 0, 42l);
395 if (array[0] != 42l) {
396 System.out.println("Unexpected value: " + array[0]);
397 }
398 }
399
400 {
401 setter = MethodHandles.arrayElementSetter(short[].class);
402 short[] array = new short[1];
403 setter.invoke(array, 0, (short) 42);
404 if (array[0] != 42l) {
405 System.out.println("Unexpected value: " + array[0]);
406 }
407 }
408
409 {
410 setter = MethodHandles.arrayElementSetter(char[].class);
411 char[] array = new char[1];
412 setter.invoke(array, 0, (char) 42);
413 if (array[0] != 42) {
414 System.out.println("Unexpected value: " + array[0]);
415 }
416 }
417
418 {
419 setter = MethodHandles.arrayElementSetter(byte[].class);
420 byte[] array = new byte[1];
421 setter.invoke(array, 0, (byte) 0x8);
422 if (array[0] != (byte) 0x8) {
423 System.out.println("Unexpected value: " + array[0]);
424 }
425 }
426
427 {
428 setter = MethodHandles.arrayElementSetter(boolean[].class);
429 boolean[] array = new boolean[1];
430 setter.invoke(array, 0, true);
431 if (!array[0]) {
432 System.out.println("Unexpected value: " + array[0]);
433 }
434 }
435
436 {
437 setter = MethodHandles.arrayElementSetter(float[].class);
438 float[] array = new float[1];
439 setter.invoke(array, 0, 42.0f);
440 if (array[0] != 42.0f) {
441 System.out.println("Unexpected value: " + array[0]);
442 }
443 }
444
445 {
446 setter = MethodHandles.arrayElementSetter(double[].class);
447 double[] array = new double[1];
448 setter.invoke(array, 0, 42.0);
449 if (array[0] != 42.0) {
450 System.out.println("Unexpected value: " + array[0]);
451 }
452 }
453
454 {
455 setter = MethodHandles.arrayElementSetter(String[].class);
456 String[] array = new String[3];
457 setter.invoke(array, 0, "42");
458 setter.invoke(array, 1, "48");
459 setter.invoke(array, 2, "54");
460 assertEquals("42", array[0]);
461 assertEquals("48", array[1]);
462 assertEquals("54", array[2]);
463 }
464 }
465
466 public static void testIdentity() throws Throwable {
467 {
468 MethodHandle identity = MethodHandles.identity(boolean.class);
469 boolean value = (boolean) identity.invoke(false);
470 if (value) {
471 System.out.println("Unexpected value: " + value);
472 }
473 }
474
475 {
476 MethodHandle identity = MethodHandles.identity(byte.class);
477 byte value = (byte) identity.invoke((byte) 0x8);
478 if (value != (byte) 0x8) {
479 System.out.println("Unexpected value: " + value);
480 }
481 }
482
483 {
484 MethodHandle identity = MethodHandles.identity(char.class);
485 char value = (char) identity.invoke((char) -56);
486 if (value != (char) -56) {
487 System.out.println("Unexpected value: " + value);
488 }
489 }
490
491 {
492 MethodHandle identity = MethodHandles.identity(short.class);
493 short value = (short) identity.invoke((short) -59);
494 if (value != (short) -59) {
495 System.out.println("Unexpected value: " + value);
496 }
497 }
498
499 {
500 MethodHandle identity = MethodHandles.identity(int.class);
501 int value = (int) identity.invoke(52);
502 if (value != 52) {
503 System.out.println("Unexpected value: " + value);
504 }
505 }
506
507 {
508 MethodHandle identity = MethodHandles.identity(long.class);
509 long value = (long) identity.invoke(-76l);
510 if (value != (long) -76) {
511 System.out.println("Unexpected value: " + value);
512 }
513 }
514
515 {
516 MethodHandle identity = MethodHandles.identity(float.class);
517 float value = (float) identity.invoke(56.0f);
518 if (value != (float) 56.0f) {
519 System.out.println("Unexpected value: " + value);
520 }
521 }
522
523 {
524 MethodHandle identity = MethodHandles.identity(double.class);
525 double value = (double) identity.invoke((double) 72.0);
526 if (value != (double) 72.0) {
527 System.out.println("Unexpected value: " + value);
528 }
529 }
530
531 {
532 MethodHandle identity = MethodHandles.identity(String.class);
533 String value = (String) identity.invoke("bazman");
534 assertEquals("bazman", value);
535 }
536 }
537
538 public static void testConstant() throws Throwable {
539 // int constants.
540 {
541 MethodHandle constant = MethodHandles.constant(int.class, 56);
542 int value = (int) constant.invoke();
543 if (value != 56) {
544 System.out.println("Unexpected value: " + value);
545 }
546
547 // short constant values are converted to int.
548 constant = MethodHandles.constant(int.class, (short) 52);
549 value = (int) constant.invoke();
550 if (value != 52) {
551 System.out.println("Unexpected value: " + value);
552 }
553
554 // char constant values are converted to int.
555 constant = MethodHandles.constant(int.class, (char) 'b');
556 value = (int) constant.invoke();
557 if (value != (int) 'b') {
558 System.out.println("Unexpected value: " + value);
559 }
560
561 // int constant values are converted to int.
562 constant = MethodHandles.constant(int.class, (byte) 0x1);
563 value = (int) constant.invoke();
564 if (value != 1) {
565 System.out.println("Unexpected value: " + value);
566 }
567
568 // boolean, float, double and long primitive constants are not convertible
569 // to int, so the handle creation must fail with a CCE.
570 try {
571 MethodHandles.constant(int.class, false);
572 fail();
573 } catch (ClassCastException expected) {
574 }
575
576 try {
577 MethodHandles.constant(int.class, 0.1f);
578 fail();
579 } catch (ClassCastException expected) {
580 }
581
582 try {
583 MethodHandles.constant(int.class, 0.2);
584 fail();
585 } catch (ClassCastException expected) {
586 }
587
588 try {
589 MethodHandles.constant(int.class, 73l);
590 fail();
591 } catch (ClassCastException expected) {
592 }
593 }
594
595 // long constants.
596 {
597 MethodHandle constant = MethodHandles.constant(long.class, 56l);
598 long value = (long) constant.invoke();
599 if (value != 56l) {
600 System.out.println("Unexpected value: " + value);
601 }
602
603 constant = MethodHandles.constant(long.class, (int) 56);
604 value = (long) constant.invoke();
605 if (value != 56l) {
606 System.out.println("Unexpected value: " + value);
607 }
608 }
609
610 // byte constants.
611 {
612 MethodHandle constant = MethodHandles.constant(byte.class, (byte) 0x12);
613 byte value = (byte) constant.invoke();
614 if (value != (byte) 0x12) {
615 System.out.println("Unexpected value: " + value);
616 }
617 }
618
619 // boolean constants.
620 {
621 MethodHandle constant = MethodHandles.constant(boolean.class, true);
622 boolean value = (boolean) constant.invoke();
623 if (!value) {
624 System.out.println("Unexpected value: " + value);
625 }
626 }
627
628 // char constants.
629 {
630 MethodHandle constant = MethodHandles.constant(char.class, 'f');
631 char value = (char) constant.invoke();
632 if (value != 'f') {
633 System.out.println("Unexpected value: " + value);
634 }
635 }
636
637 // short constants.
638 {
639 MethodHandle constant = MethodHandles.constant(short.class, (short) 123);
640 short value = (short) constant.invoke();
641 if (value != (short) 123) {
642 System.out.println("Unexpected value: " + value);
643 }
644 }
645
646 // float constants.
647 {
648 MethodHandle constant = MethodHandles.constant(float.class, 56.0f);
649 float value = (float) constant.invoke();
650 if (value != 56.0f) {
651 System.out.println("Unexpected value: " + value);
652 }
653 }
654
655 // double constants.
656 {
657 MethodHandle constant = MethodHandles.constant(double.class, 256.0);
658 double value = (double) constant.invoke();
659 if (value != 256.0) {
660 System.out.println("Unexpected value: " + value);
661 }
662 }
663
664 // reference constants.
665 {
666 MethodHandle constant = MethodHandles.constant(String.class, "256.0");
667 String value = (String) constant.invoke();
668 assertEquals("256.0", value);
669 }
670 }
671
Narayan Kamath96120f42016-11-01 09:40:23 +0000672 public static void fail() {
673 System.out.println("FAIL");
674 Thread.dumpStack();
675 }
676
677 public static void assertEquals(String s1, String s2) {
678 if (s1 == s2) {
679 return;
680 }
681
682 if (s1 != null && s2 != null && s1.equals(s2)) {
683 return;
684 }
685
686 throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
687 }
Narayan Kamath000e1882016-10-24 17:14:25 +0100688}
689
690