1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 import java.lang.invoke.MethodHandle; 18 import java.lang.invoke.MethodHandles; 19 import java.lang.invoke.MethodHandles.Lookup; 20 import java.lang.invoke.MethodType; 21 import java.lang.invoke.WrongMethodTypeException; 22 23 public class Main { main(String[] args)24 public static void main(String[] args) throws Throwable { 25 testThrowException(); 26 testDropArguments(); 27 testCatchException(); 28 testGuardWithTest(); 29 testArrayElementGetter(); 30 testArrayElementSetter(); 31 testIdentity(); 32 testConstant(); 33 testBindTo(); 34 testFilterReturnValue(); 35 testPermuteArguments(); 36 testInvokers(); 37 testSpreaders_reference(); 38 testSpreaders_primitive(); 39 testInvokeWithArguments(); 40 testAsCollector(); 41 testFilterArguments(); 42 testCollectArguments(); 43 testInsertArguments(); 44 testFoldArguments(); 45 } 46 testThrowException()47 public static void testThrowException() throws Throwable { 48 MethodHandle handle = MethodHandles.throwException(String.class, 49 IllegalArgumentException.class); 50 51 if (handle.type().returnType() != String.class) { 52 fail("Unexpected return type for handle: " + handle + 53 " [ " + handle.type() + "]"); 54 } 55 56 final IllegalArgumentException iae = new IllegalArgumentException("boo!"); 57 try { 58 handle.invoke(iae); 59 fail("Expected an exception of type: java.lang.IllegalArgumentException"); 60 } catch (IllegalArgumentException expected) { 61 if (expected != iae) { 62 fail("Wrong exception: expected " + iae + " but was " + expected); 63 } 64 } 65 } 66 dropArguments_delegate(String message, long message2)67 public static void dropArguments_delegate(String message, long message2) { 68 System.out.println("Message: " + message + ", Message2: " + message2); 69 } 70 testDropArguments()71 public static void testDropArguments() throws Throwable { 72 MethodHandle delegate = MethodHandles.lookup().findStatic(Main.class, 73 "dropArguments_delegate", 74 MethodType.methodType(void.class, new Class<?>[] { String.class, long.class })); 75 76 MethodHandle transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class); 77 78 // The transformer will accept two additional arguments at position zero. 79 try { 80 transform.invokeExact("foo", 42l); 81 fail(); 82 } catch (WrongMethodTypeException expected) { 83 } 84 85 transform.invokeExact(45, new Object(), "foo", 42l); 86 transform.invoke(45, new Object(), "foo", 42l); 87 88 // Additional arguments at position 1. 89 transform = MethodHandles.dropArguments(delegate, 1, int.class, Object.class); 90 transform.invokeExact("foo", 45, new Object(), 42l); 91 transform.invoke("foo", 45, new Object(), 42l); 92 93 // Additional arguments at position 2. 94 transform = MethodHandles.dropArguments(delegate, 2, int.class, Object.class); 95 transform.invokeExact("foo", 42l, 45, new Object()); 96 transform.invoke("foo", 42l, 45, new Object()); 97 98 // Note that we still perform argument conversions even for the arguments that 99 // are subsequently dropped. 100 try { 101 transform.invoke("foo", 42l, 45l, new Object()); 102 fail(); 103 } catch (WrongMethodTypeException expected) { 104 } catch (IllegalArgumentException expected) { 105 // TODO(narayan): We currently throw the wrong type of exception here, 106 // it's IAE and should be WMTE instead. 107 } 108 109 // Check that asType works as expected. 110 transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class); 111 transform = transform.asType(MethodType.methodType(void.class, 112 new Class<?>[] { short.class, Object.class, String.class, long.class })); 113 transform.invokeExact((short) 45, new Object(), "foo", 42l); 114 115 // Invalid argument location, should not be allowed. 116 try { 117 MethodHandles.dropArguments(delegate, -1, int.class, Object.class); 118 fail(); 119 } catch (IllegalArgumentException expected) { 120 } 121 122 // Invalid argument location, should not be allowed. 123 try { 124 MethodHandles.dropArguments(delegate, 3, int.class, Object.class); 125 fail(); 126 } catch (IllegalArgumentException expected) { 127 } 128 129 try { 130 MethodHandles.dropArguments(delegate, 1, void.class); 131 fail(); 132 } catch (IllegalArgumentException expected) { 133 } 134 } 135 testCatchException_target(String arg1, long arg2, String exceptionMessage)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 testCatchException_handler(IllegalArgumentException iae, String arg1, long arg2, String exMsg)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 testCatchException_handler2(IllegalArgumentException iae, String arg1)152 public static String testCatchException_handler2(IllegalArgumentException iae, String arg1) { 153 System.out.println("Handler: " + iae + ", Arg1: " + arg1); 154 return "handler2"; 155 } 156 testCatchException()157 public static void testCatchException() throws Throwable { 158 MethodHandle target = MethodHandles.lookup().findStatic(Main.class, 159 "testCatchException_target", 160 MethodType.methodType(String.class, new Class<?>[] { String.class, long.class, String.class })); 161 162 MethodHandle handler = MethodHandles.lookup().findStatic(Main.class, 163 "testCatchException_handler", 164 MethodType.methodType(String.class, new Class<?>[] { IllegalArgumentException.class, 165 String.class, long.class, String.class })); 166 167 MethodHandle adapter = MethodHandles.catchException(target, IllegalArgumentException.class, 168 handler); 169 170 String returnVal = null; 171 172 // These two should end up calling the target always. We're passing a null exception 173 // message here, which means the target will not throw. 174 returnVal = (String) adapter.invoke("foo", 42, null); 175 assertEquals("target", returnVal); 176 returnVal = (String) adapter.invokeExact("foo", 42l, (String) null); 177 assertEquals("target", returnVal); 178 179 // We're passing a non-null exception message here, which means the target will throw, 180 // which in turn means that the handler must be called for the next two invokes. 181 returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage"); 182 assertEquals("handler1", returnVal); 183 returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage"); 184 assertEquals("handler1", returnVal); 185 186 handler = MethodHandles.lookup().findStatic(Main.class, 187 "testCatchException_handler2", 188 MethodType.methodType(String.class, new Class<?>[] { IllegalArgumentException.class, 189 String.class })); 190 adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler); 191 192 returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage"); 193 assertEquals("handler2", returnVal); 194 returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage"); 195 assertEquals("handler2", returnVal); 196 197 // Test that the type of the invoke doesn't matter. Here we call 198 // IllegalArgumentException.toString() on the exception that was thrown by 199 // the target. 200 handler = MethodHandles.lookup().findVirtual(IllegalArgumentException.class, 201 "toString", MethodType.methodType(String.class)); 202 adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler); 203 204 returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage"); 205 assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal); 206 returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage2"); 207 assertEquals("java.lang.IllegalArgumentException: exceptionMessage2", returnVal); 208 209 // Check that asType works as expected. 210 adapter = MethodHandles.catchException(target, IllegalArgumentException.class, 211 handler); 212 adapter = adapter.asType(MethodType.methodType(String.class, 213 new Class<?>[] { String.class, int.class, String.class })); 214 returnVal = (String) adapter.invokeExact("foo", 42, "exceptionMessage"); 215 assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal); 216 } 217 testGuardWithTest_test(String arg1, long arg2)218 public static boolean testGuardWithTest_test(String arg1, long arg2) { 219 return "target".equals(arg1) && 42 == arg2; 220 } 221 testGuardWithTest_target(String arg1, long arg2, int arg3)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 testGuardWithTest_fallback(String arg1, long arg2, int arg3)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 testGuardWithTest()232 public static void testGuardWithTest() throws Throwable { 233 MethodHandle test = MethodHandles.lookup().findStatic(Main.class, 234 "testGuardWithTest_test", 235 MethodType.methodType(boolean.class, new Class<?>[] { String.class, long.class })); 236 237 final MethodType type = MethodType.methodType(String.class, 238 new Class<?>[] { String.class, long.class, int.class }); 239 240 final MethodHandle target = MethodHandles.lookup().findStatic(Main.class, 241 "testGuardWithTest_target", type); 242 final MethodHandle fallback = MethodHandles.lookup().findStatic(Main.class, 243 "testGuardWithTest_fallback", type); 244 245 MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback); 246 247 String returnVal = null; 248 249 returnVal = (String) adapter.invoke("target", 42, 56); 250 assertEquals("target", returnVal); 251 returnVal = (String) adapter.invokeExact("target", 42l, 56); 252 assertEquals("target", returnVal); 253 254 returnVal = (String) adapter.invoke("fallback", 42l, 56); 255 assertEquals("fallback", returnVal); 256 returnVal = (String) adapter.invokeExact("target", 42l, 56); 257 assertEquals("target", returnVal); 258 259 // Check that asType works as expected. 260 adapter = adapter.asType(MethodType.methodType(String.class, 261 new Class<?>[] { String.class, int.class, int.class })); 262 returnVal = (String) adapter.invokeExact("target", 42, 56); 263 assertEquals("target", returnVal); 264 } 265 testArrayElementGetter()266 public static void testArrayElementGetter() throws Throwable { 267 MethodHandle getter = MethodHandles.arrayElementGetter(int[].class); 268 269 { 270 int[] array = new int[1]; 271 array[0] = 42; 272 int value = (int) getter.invoke(array, 0); 273 if (value != 42) { 274 fail("Unexpected value: " + value); 275 } 276 277 try { 278 value = (int) getter.invoke(array, -1); 279 fail(); 280 } catch (ArrayIndexOutOfBoundsException expected) { 281 } 282 283 try { 284 value = (int) getter.invoke(null, -1); 285 fail(); 286 } catch (NullPointerException expected) { 287 } 288 } 289 290 { 291 getter = MethodHandles.arrayElementGetter(long[].class); 292 long[] array = new long[1]; 293 array[0] = 42; 294 long value = (long) getter.invoke(array, 0); 295 if (value != 42l) { 296 fail("Unexpected value: " + value); 297 } 298 } 299 300 { 301 getter = MethodHandles.arrayElementGetter(short[].class); 302 short[] array = new short[1]; 303 array[0] = 42; 304 short value = (short) getter.invoke(array, 0); 305 if (value != 42l) { 306 fail("Unexpected value: " + value); 307 } 308 } 309 310 { 311 getter = MethodHandles.arrayElementGetter(char[].class); 312 char[] array = new char[1]; 313 array[0] = 42; 314 char value = (char) getter.invoke(array, 0); 315 if (value != 42l) { 316 fail("Unexpected value: " + value); 317 } 318 } 319 320 { 321 getter = MethodHandles.arrayElementGetter(byte[].class); 322 byte[] array = new byte[1]; 323 array[0] = (byte) 0x8; 324 byte value = (byte) getter.invoke(array, 0); 325 if (value != (byte) 0x8) { 326 fail("Unexpected value: " + value); 327 } 328 } 329 330 { 331 getter = MethodHandles.arrayElementGetter(boolean[].class); 332 boolean[] array = new boolean[1]; 333 array[0] = true; 334 boolean value = (boolean) getter.invoke(array, 0); 335 if (!value) { 336 fail("Unexpected value: " + value); 337 } 338 } 339 340 { 341 getter = MethodHandles.arrayElementGetter(float[].class); 342 float[] array = new float[1]; 343 array[0] = 42.0f; 344 float value = (float) getter.invoke(array, 0); 345 if (value != 42.0f) { 346 fail("Unexpected value: " + value); 347 } 348 } 349 350 { 351 getter = MethodHandles.arrayElementGetter(double[].class); 352 double[] array = new double[1]; 353 array[0] = 42.0; 354 double value = (double) getter.invoke(array, 0); 355 if (value != 42.0) { 356 fail("Unexpected value: " + value); 357 } 358 } 359 360 { 361 getter = MethodHandles.arrayElementGetter(String[].class); 362 String[] array = new String[3]; 363 array[0] = "42"; 364 array[1] = "48"; 365 array[2] = "54"; 366 String value = (String) getter.invoke(array, 0); 367 assertEquals("42", value); 368 value = (String) getter.invoke(array, 1); 369 assertEquals("48", value); 370 value = (String) getter.invoke(array, 2); 371 assertEquals("54", value); 372 } 373 } 374 testArrayElementSetter()375 public static void testArrayElementSetter() throws Throwable { 376 MethodHandle setter = MethodHandles.arrayElementSetter(int[].class); 377 378 { 379 int[] array = new int[2]; 380 setter.invoke(array, 0, 42); 381 setter.invoke(array, 1, 43); 382 383 if (array[0] != 42) { 384 fail("Unexpected value: " + array[0]); 385 } 386 if (array[1] != 43) { 387 fail("Unexpected value: " + array[1]); 388 } 389 390 try { 391 setter.invoke(array, -1, 42); 392 fail(); 393 } catch (ArrayIndexOutOfBoundsException expected) { 394 } 395 396 try { 397 setter.invoke(null, 0, 42); 398 fail(); 399 } catch (NullPointerException expected) { 400 } 401 } 402 403 { 404 setter = MethodHandles.arrayElementSetter(long[].class); 405 long[] array = new long[1]; 406 setter.invoke(array, 0, 42l); 407 if (array[0] != 42l) { 408 fail("Unexpected value: " + array[0]); 409 } 410 } 411 412 { 413 setter = MethodHandles.arrayElementSetter(short[].class); 414 short[] array = new short[1]; 415 setter.invoke(array, 0, (short) 42); 416 if (array[0] != 42l) { 417 fail("Unexpected value: " + array[0]); 418 } 419 } 420 421 { 422 setter = MethodHandles.arrayElementSetter(char[].class); 423 char[] array = new char[1]; 424 setter.invoke(array, 0, (char) 42); 425 if (array[0] != 42) { 426 fail("Unexpected value: " + array[0]); 427 } 428 } 429 430 { 431 setter = MethodHandles.arrayElementSetter(byte[].class); 432 byte[] array = new byte[1]; 433 setter.invoke(array, 0, (byte) 0x8); 434 if (array[0] != (byte) 0x8) { 435 fail("Unexpected value: " + array[0]); 436 } 437 } 438 439 { 440 setter = MethodHandles.arrayElementSetter(boolean[].class); 441 boolean[] array = new boolean[1]; 442 setter.invoke(array, 0, true); 443 if (!array[0]) { 444 fail("Unexpected value: " + array[0]); 445 } 446 } 447 448 { 449 setter = MethodHandles.arrayElementSetter(float[].class); 450 float[] array = new float[1]; 451 setter.invoke(array, 0, 42.0f); 452 if (array[0] != 42.0f) { 453 fail("Unexpected value: " + array[0]); 454 } 455 } 456 457 { 458 setter = MethodHandles.arrayElementSetter(double[].class); 459 double[] array = new double[1]; 460 setter.invoke(array, 0, 42.0); 461 if (array[0] != 42.0) { 462 fail("Unexpected value: " + array[0]); 463 } 464 } 465 466 { 467 setter = MethodHandles.arrayElementSetter(String[].class); 468 String[] array = new String[3]; 469 setter.invoke(array, 0, "42"); 470 setter.invoke(array, 1, "48"); 471 setter.invoke(array, 2, "54"); 472 assertEquals("42", array[0]); 473 assertEquals("48", array[1]); 474 assertEquals("54", array[2]); 475 } 476 } 477 testIdentity()478 public static void testIdentity() throws Throwable { 479 { 480 MethodHandle identity = MethodHandles.identity(boolean.class); 481 boolean value = (boolean) identity.invoke(false); 482 if (value) { 483 fail("Unexpected value: " + value); 484 } 485 } 486 487 { 488 MethodHandle identity = MethodHandles.identity(byte.class); 489 byte value = (byte) identity.invoke((byte) 0x8); 490 if (value != (byte) 0x8) { 491 fail("Unexpected value: " + value); 492 } 493 } 494 495 { 496 MethodHandle identity = MethodHandles.identity(char.class); 497 char value = (char) identity.invoke((char) -56); 498 if (value != (char) -56) { 499 fail("Unexpected value: " + value); 500 } 501 } 502 503 { 504 MethodHandle identity = MethodHandles.identity(short.class); 505 short value = (short) identity.invoke((short) -59); 506 if (value != (short) -59) { 507 fail("Unexpected value: " + Short.toString(value)); 508 } 509 } 510 511 { 512 MethodHandle identity = MethodHandles.identity(int.class); 513 int value = (int) identity.invoke(52); 514 if (value != 52) { 515 fail("Unexpected value: " + value); 516 } 517 } 518 519 { 520 MethodHandle identity = MethodHandles.identity(long.class); 521 long value = (long) identity.invoke(-76l); 522 if (value != (long) -76) { 523 fail("Unexpected value: " + value); 524 } 525 } 526 527 { 528 MethodHandle identity = MethodHandles.identity(float.class); 529 float value = (float) identity.invoke(56.0f); 530 if (value != (float) 56.0f) { 531 fail("Unexpected value: " + value); 532 } 533 } 534 535 { 536 MethodHandle identity = MethodHandles.identity(double.class); 537 double value = (double) identity.invoke((double) 72.0); 538 if (value != (double) 72.0) { 539 fail("Unexpected value: " + value); 540 } 541 } 542 543 { 544 MethodHandle identity = MethodHandles.identity(String.class); 545 String value = (String) identity.invoke("bazman"); 546 assertEquals("bazman", value); 547 } 548 } 549 testConstant()550 public static void testConstant() throws Throwable { 551 // int constants. 552 { 553 MethodHandle constant = MethodHandles.constant(int.class, 56); 554 int value = (int) constant.invoke(); 555 if (value != 56) { 556 fail("Unexpected value: " + value); 557 } 558 559 // short constant values are converted to int. 560 constant = MethodHandles.constant(int.class, (short) 52); 561 value = (int) constant.invoke(); 562 if (value != 52) { 563 fail("Unexpected value: " + value); 564 } 565 566 // char constant values are converted to int. 567 constant = MethodHandles.constant(int.class, (char) 'b'); 568 value = (int) constant.invoke(); 569 if (value != (int) 'b') { 570 fail("Unexpected value: " + value); 571 } 572 573 // int constant values are converted to int. 574 constant = MethodHandles.constant(int.class, (byte) 0x1); 575 value = (int) constant.invoke(); 576 if (value != 1) { 577 fail("Unexpected value: " + value); 578 } 579 580 // boolean, float, double and long primitive constants are not convertible 581 // to int, so the handle creation must fail with a CCE. 582 try { 583 MethodHandles.constant(int.class, false); 584 fail(); 585 } catch (ClassCastException expected) { 586 } 587 588 try { 589 MethodHandles.constant(int.class, 0.1f); 590 fail(); 591 } catch (ClassCastException expected) { 592 } 593 594 try { 595 MethodHandles.constant(int.class, 0.2); 596 fail(); 597 } catch (ClassCastException expected) { 598 } 599 600 try { 601 MethodHandles.constant(int.class, 73l); 602 fail(); 603 } catch (ClassCastException expected) { 604 } 605 } 606 607 // long constants. 608 { 609 MethodHandle constant = MethodHandles.constant(long.class, 56l); 610 long value = (long) constant.invoke(); 611 if (value != 56l) { 612 fail("Unexpected value: " + value); 613 } 614 615 constant = MethodHandles.constant(long.class, (int) 56); 616 value = (long) constant.invoke(); 617 if (value != 56l) { 618 fail("Unexpected value: " + value); 619 } 620 } 621 622 // byte constants. 623 { 624 MethodHandle constant = MethodHandles.constant(byte.class, (byte) 0x12); 625 byte value = (byte) constant.invoke(); 626 if (value != (byte) 0x12) { 627 fail("Unexpected value: " + value); 628 } 629 } 630 631 // boolean constants. 632 { 633 MethodHandle constant = MethodHandles.constant(boolean.class, true); 634 boolean value = (boolean) constant.invoke(); 635 if (!value) { 636 fail("Unexpected value: " + value); 637 } 638 } 639 640 // char constants. 641 { 642 MethodHandle constant = MethodHandles.constant(char.class, 'f'); 643 char value = (char) constant.invoke(); 644 if (value != 'f') { 645 fail("Unexpected value: " + value); 646 } 647 } 648 649 // short constants. 650 { 651 MethodHandle constant = MethodHandles.constant(short.class, (short) 123); 652 short value = (short) constant.invoke(); 653 if (value != (short) 123) { 654 fail("Unexpected value: " + value); 655 } 656 } 657 658 // float constants. 659 { 660 MethodHandle constant = MethodHandles.constant(float.class, 56.0f); 661 float value = (float) constant.invoke(); 662 if (value != 56.0f) { 663 fail("Unexpected value: " + value); 664 } 665 } 666 667 // double constants. 668 { 669 MethodHandle constant = MethodHandles.constant(double.class, 256.0); 670 double value = (double) constant.invoke(); 671 if (value != 256.0) { 672 fail("Unexpected value: " + value); 673 } 674 } 675 676 // reference constants. 677 { 678 MethodHandle constant = MethodHandles.constant(String.class, "256.0"); 679 String value = (String) constant.invoke(); 680 assertEquals("256.0", value); 681 } 682 } 683 testBindTo()684 public static void testBindTo() throws Throwable { 685 MethodHandle stringCharAt = MethodHandles.lookup().findVirtual( 686 String.class, "charAt", MethodType.methodType(char.class, int.class)); 687 688 char value = (char) stringCharAt.invoke("foo", 0); 689 if (value != 'f') { 690 fail("Unexpected value: " + value); 691 } 692 693 MethodHandle bound = stringCharAt.bindTo("foo"); 694 value = (char) bound.invoke(0); 695 if (value != 'f') { 696 fail("Unexpected value: " + value); 697 } 698 699 try { 700 stringCharAt.bindTo(new Object()); 701 fail(); 702 } catch (ClassCastException expected) { 703 } 704 705 bound = stringCharAt.bindTo(null); 706 try { 707 bound.invoke(0); 708 fail(); 709 } catch (NullPointerException expected) { 710 } 711 712 MethodHandle integerParseInt = MethodHandles.lookup().findStatic( 713 Integer.class, "parseInt", MethodType.methodType(int.class, String.class)); 714 715 bound = integerParseInt.bindTo("78452"); 716 int intValue = (int) bound.invoke(); 717 if (intValue != 78452) { 718 fail("Unexpected value: " + intValue); 719 } 720 } 721 filterReturnValue_target(int a)722 public static String filterReturnValue_target(int a) { 723 return "ReturnValue" + a; 724 } 725 filterReturnValue_filter(String value)726 public static boolean filterReturnValue_filter(String value) { 727 return value.indexOf("42") != -1; 728 } 729 filterReturnValue_intTarget(String a)730 public static int filterReturnValue_intTarget(String a) { 731 return Integer.parseInt(a); 732 } 733 filterReturnValue_intFilter(int b)734 public static int filterReturnValue_intFilter(int b) { 735 return b + 1; 736 } 737 filterReturnValue_voidTarget()738 public static void filterReturnValue_voidTarget() { 739 } 740 filterReturnValue_voidFilter()741 public static int filterReturnValue_voidFilter() { 742 return 42; 743 } 744 testFilterReturnValue()745 public static void testFilterReturnValue() throws Throwable { 746 // A target that returns a reference. 747 { 748 final MethodHandle target = MethodHandles.lookup().findStatic(Main.class, 749 "filterReturnValue_target", MethodType.methodType(String.class, int.class)); 750 final MethodHandle filter = MethodHandles.lookup().findStatic(Main.class, 751 "filterReturnValue_filter", MethodType.methodType(boolean.class, String.class)); 752 753 MethodHandle adapter = MethodHandles.filterReturnValue(target, filter); 754 755 boolean value = (boolean) adapter.invoke((int) 42); 756 if (!value) { 757 fail("Unexpected value: " + value); 758 } 759 value = (boolean) adapter.invoke((int) 43); 760 if (value) { 761 fail("Unexpected value: " + value); 762 } 763 } 764 765 // A target that returns a primitive. 766 { 767 final MethodHandle target = MethodHandles.lookup().findStatic(Main.class, 768 "filterReturnValue_intTarget", MethodType.methodType(int.class, String.class)); 769 final MethodHandle filter = MethodHandles.lookup().findStatic(Main.class, 770 "filterReturnValue_intFilter", MethodType.methodType(int.class, int.class)); 771 772 MethodHandle adapter = MethodHandles.filterReturnValue(target, filter); 773 774 int value = (int) adapter.invoke("56"); 775 if (value != 57) { 776 fail("Unexpected value: " + value); 777 } 778 } 779 780 // A target that returns void. 781 { 782 final MethodHandle target = MethodHandles.lookup().findStatic(Main.class, 783 "filterReturnValue_voidTarget", MethodType.methodType(void.class)); 784 final MethodHandle filter = MethodHandles.lookup().findStatic(Main.class, 785 "filterReturnValue_voidFilter", MethodType.methodType(int.class)); 786 787 MethodHandle adapter = MethodHandles.filterReturnValue(target, filter); 788 789 int value = (int) adapter.invoke(); 790 if (value != 42) { 791 fail("Unexpected value: " + value); 792 } 793 } 794 } 795 permuteArguments_callee(boolean a, byte b, char c, short d, int e, long f, float g, double h)796 public static void permuteArguments_callee(boolean a, byte b, char c, 797 short d, int e, long f, float g, double h) { 798 if (a == true && b == (byte) 'b' && c == 'c' && d == (short) 56 && 799 e == 78 && f == (long) 97 && g == 98.0f && f == 97.0) { 800 return; 801 } 802 803 fail("Unexpected arguments: " + a + ", " + b + ", " + c 804 + ", " + d + ", " + e + ", " + f + ", " + g + ", " + h); 805 } 806 permuteArguments_boxingCallee(boolean a, Integer b)807 public static void permuteArguments_boxingCallee(boolean a, Integer b) { 808 if (a && b.intValue() == 42) { 809 return; 810 } 811 812 fail("Unexpected arguments: " + a + ", " + b); 813 } 814 testPermuteArguments()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 returnBar()900 private static Object returnBar() { 901 return "bar"; 902 } 903 testInvokers()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 spreadReferences(String a, String b, String c)932 public static int spreadReferences(String a, String b, String c) { 933 System.out.println("a: " + a + ", b:" + b + ", c: " + c); 934 return 42; 935 } 936 spreadReferences_Unbox(String a, int b)937 public static int spreadReferences_Unbox(String a, int b) { 938 System.out.println("a: " + a + ", b:" + b); 939 return 43; 940 } 941 testSpreaders_reference()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 spreadBoolean(String a, Boolean b, boolean c)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 spreadByte(String a, Byte b, byte c, short d, int e, long f, float g, double h)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 spreadChar(String a, Character b, char c, int d, long e, float f, double g)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 spreadShort(String a, Short b, short c, int d, long e, float f, double g)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 spreadInt(String a, Integer b, int c, long d, float e, double f)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 spreadLong(String a, Long b, long c, float d, double e)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 spreadFloat(String a, Float b, float c, double d)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 spreadDouble(String a, Double b, double c)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 testSpreaders_primitive()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 testInvokeWithArguments()1232 public static void testInvokeWithArguments() throws Throwable { 1233 MethodType methodType = MethodType.methodType(int.class, 1234 new Class<?>[] { String.class, String.class, String.class }); 1235 MethodHandle handle = MethodHandles.lookup().findStatic( 1236 Main.class, "spreadReferences", methodType); 1237 1238 Object ret = handle.invokeWithArguments(new Object[] { "a", "b", "c"}); 1239 assertEquals(42, (int) ret); 1240 handle.invokeWithArguments(new String[] { "a", "b", "c" }); 1241 assertEquals(42, (int) ret); 1242 1243 // Pass in an array that's too small. Should throw an IAE. 1244 try { 1245 handle.invokeWithArguments(new Object[] { "a", "b" }); 1246 fail(); 1247 } catch (IllegalArgumentException expected) { 1248 } catch (WrongMethodTypeException expected) { 1249 } 1250 1251 // Test implicit unboxing. 1252 MethodType methodType2 = MethodType.methodType(int.class, 1253 new Class<?>[] { String.class, int.class }); 1254 MethodHandle handle2 = MethodHandles.lookup().findStatic( 1255 Main.class, "spreadReferences_Unbox", methodType2); 1256 1257 ret = (int) handle2.invokeWithArguments(new Object[] { "a", 43 }); 1258 assertEquals(43, (int) ret); 1259 } 1260 collectBoolean(String a, boolean[] b)1261 public static int collectBoolean(String a, boolean[] b) { 1262 System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); 1263 return 44; 1264 } 1265 collectByte(String a, byte[] b)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 collectChar(String a, char[] b)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 collectShort(String a, short[] b)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 collectInt(String a, int[] b)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 collectLong(String a, long[] b)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 collectFloat(String a, float[] b)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 collectDouble(String a, double[] b)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 collectCharSequence(String a, CharSequence[] b)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 testAsCollector()1306 public static void testAsCollector() throws Throwable { 1307 // Reference arrays. 1308 // ------------------- 1309 MethodHandle trailingRef = MethodHandles.lookup().findStatic( 1310 Main.class, "collectCharSequence", 1311 MethodType.methodType(int.class, String.class, CharSequence[].class)); 1312 1313 // int[] is not convertible to CharSequence[].class. 1314 try { 1315 trailingRef.asCollector(int[].class, 1); 1316 fail(); 1317 } catch (IllegalArgumentException expected) { 1318 } 1319 1320 // Object[] is not convertible to CharSequence[].class. 1321 try { 1322 trailingRef.asCollector(Object[].class, 1); 1323 fail(); 1324 } catch (IllegalArgumentException expected) { 1325 } 1326 1327 // String[].class is convertible to CharSequence.class 1328 MethodHandle collector = trailingRef.asCollector(String[].class, 2); 1329 assertEquals(99, (int) collector.invoke("a", "b", "c")); 1330 1331 // Too few arguments should fail with a WMTE. 1332 try { 1333 collector.invoke("a", "b"); 1334 fail(); 1335 } catch (WrongMethodTypeException expected) { 1336 } 1337 1338 // Too many arguments should fail with a WMTE. 1339 try { 1340 collector.invoke("a", "b", "c", "d"); 1341 fail(); 1342 } catch (WrongMethodTypeException expected) { 1343 } 1344 1345 // Checks on other array types. 1346 1347 MethodHandle target = MethodHandles.lookup().findStatic( 1348 Main.class, "collectBoolean", 1349 MethodType.methodType(int.class, String.class, boolean[].class)); 1350 assertEquals(44, (int) target.asCollector(boolean[].class, 2).invoke("a", true, false)); 1351 1352 target = MethodHandles.lookup().findStatic(Main.class, "collectByte", 1353 MethodType.methodType(int.class, String.class, byte[].class)); 1354 assertEquals(45, (int) target.asCollector(byte[].class, 2).invoke("a", (byte) 1, (byte) 2)); 1355 1356 target = MethodHandles.lookup().findStatic(Main.class, "collectChar", 1357 MethodType.methodType(int.class, String.class, char[].class)); 1358 assertEquals(46, (int) target.asCollector(char[].class, 2).invoke("a", 'a', 'b')); 1359 1360 target = MethodHandles.lookup().findStatic(Main.class, "collectShort", 1361 MethodType.methodType(int.class, String.class, short[].class)); 1362 assertEquals(47, (int) target.asCollector(short[].class, 2).invoke("a", (short) 3, (short) 4)); 1363 1364 target = MethodHandles.lookup().findStatic(Main.class, "collectInt", 1365 MethodType.methodType(int.class, String.class, int[].class)); 1366 assertEquals(48, (int) target.asCollector(int[].class, 2).invoke("a", 42, 43)); 1367 1368 target = MethodHandles.lookup().findStatic(Main.class, "collectLong", 1369 MethodType.methodType(int.class, String.class, long[].class)); 1370 assertEquals(49, (int) target.asCollector(long[].class, 2).invoke("a", 100, 99)); 1371 1372 target = MethodHandles.lookup().findStatic(Main.class, "collectFloat", 1373 MethodType.methodType(int.class, String.class, float[].class)); 1374 assertEquals(50, (int) target.asCollector(float[].class, 2).invoke("a", 8.9f, 9.1f)); 1375 1376 target = MethodHandles.lookup().findStatic(Main.class, "collectDouble", 1377 MethodType.methodType(int.class, String.class, double[].class)); 1378 assertEquals(51, (int) target.asCollector(double[].class, 2).invoke("a", 6.7, 7.8)); 1379 } 1380 filter1(char a)1381 public static String filter1(char a) { 1382 return String.valueOf(a); 1383 } 1384 filter2(String b)1385 public static char filter2(String b) { 1386 return b.charAt(0); 1387 } 1388 badFilter1(char a, char b)1389 public static String badFilter1(char a, char b) { 1390 return "bad"; 1391 } 1392 filterTarget(String a, char b, String c, char d)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 testFilterArguments()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 voidFilter(char a, char b)1460 static void voidFilter(char a, char b) { 1461 System.out.println("voidFilter"); 1462 } 1463 filter(char a, char b)1464 static String filter(char a, char b) { 1465 return String.valueOf(a) + "+" + b; 1466 } 1467 badFilter(char a, char b)1468 static char badFilter(char a, char b) { 1469 return 0; 1470 } 1471 target(String a, String b, String c)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 testCollectArguments()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 insertReceiver(String a, int b, Integer c, String d)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 testInsertArguments()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 foldFilter(char a, char b)1581 public static String foldFilter(char a, char b) { 1582 return String.valueOf(a) + "+" + b; 1583 } 1584 voidFoldFilter(String e, char a, char b)1585 public static void voidFoldFilter(String e, char a, char b) { 1586 System.out.println(String.valueOf(a) + "+" + b); 1587 } 1588 foldTarget(String a, char b, char c, String d)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 mismatchedVoidFilter(Integer a)1594 public static void mismatchedVoidFilter(Integer a) { 1595 } 1596 mismatchedNonVoidFilter(char a, char b)1597 public static Integer mismatchedNonVoidFilter(char a, char b) { 1598 return null; 1599 } 1600 testFoldArguments()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 fail()1644 public static void fail() { 1645 System.out.println("FAIL"); 1646 Thread.dumpStack(); 1647 } 1648 fail(String message)1649 public static void fail(String message) { 1650 System.out.println("fail: " + message); 1651 Thread.dumpStack(); 1652 } 1653 assertEquals(int i1, int i2)1654 public static void assertEquals(int i1, int i2) { 1655 if (i1 != i2) throw new AssertionError("Expected: " + i1 + " was " + i2); 1656 } 1657 assertEquals(String s1, String s2)1658 public static void assertEquals(String s1, String s2) { 1659 if (s1 == s2) { 1660 return; 1661 } 1662 1663 if (s1 != null && s2 != null && s1.equals(s2)) { 1664 return; 1665 } 1666 1667 throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2); 1668 } 1669 } 1670