1 /* 2 * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import sun.invoke.util.Wrapper; 25 import test.java.lang.invoke.lib.Helper; 26 27 import java.io.File; 28 import java.io.Serializable; 29 import java.lang.invoke.MethodHandle; 30 import java.lang.invoke.MethodHandles; 31 import java.lang.invoke.MethodType; 32 import java.lang.invoke.WrongMethodTypeException; 33 import java.util.HashMap; 34 import java.util.Map; 35 import java.util.Random; 36 37 /* 38 * @test 39 * @bug 8060483 8066746 40 * @key randomness 41 * @library /lib/testlibrary /java/lang/invoke/common 42 * @modules java.base/sun.invoke.util 43 * @summary unit tests for MethodHandles.explicitCastArguments() 44 * @run main ExplicitCastArgumentsTest 45 */ 46 47 /** 48 * Tests for MethodHandles.explicitCastArguments(). 49 */ 50 public class ExplicitCastArgumentsTest { 51 52 private static final boolean VERBOSE = Helper.IS_VERBOSE; 53 private static final Class<?> THIS_CLASS = ExplicitCastArgumentsTest.class; 54 private static final Random RNG = Helper.RNG; 55 private static final Map<Wrapper, Object> RANDOM_VALUES = new HashMap<>(9); 56 57 static { 58 RANDOM_VALUES.put(Wrapper.BOOLEAN, RNG.nextBoolean()); 59 RANDOM_VALUES.put(Wrapper.BYTE, (byte) RNG.nextInt()); 60 RANDOM_VALUES.put(Wrapper.SHORT, (short) RNG.nextInt()); 61 RANDOM_VALUES.put(Wrapper.CHAR, (char) RNG.nextInt()); 62 RANDOM_VALUES.put(Wrapper.INT, RNG.nextInt()); 63 RANDOM_VALUES.put(Wrapper.LONG, RNG.nextLong()); 64 RANDOM_VALUES.put(Wrapper.FLOAT, RNG.nextFloat()); 65 RANDOM_VALUES.put(Wrapper.DOUBLE, RNG.nextDouble()); 66 RANDOM_VALUES.put(Wrapper.OBJECT, new Object()); 67 } 68 69 public static void main(String[] args) throws Throwable { 70 testVarargsCollector(); 71 testNullRef2Prim(); 72 testRef2Prim(); 73 testPrim2Ref(); 74 testPrim2Prim(); 75 testNonBCPRef2NonBCPRef(); 76 testBCPRef2BCPRef(); 77 testNonBCPRef2BCPRef(); 78 testReturnAny2Void(); 79 testReturnVoid2Any(); 80 testMultipleArgs(); 81 System.out.println("TEST PASSED"); 82 } 83 84 /** 85 * Dummy method used in {@link #testVarargsCollector} test to form a method 86 * handle. 87 * 88 * @param args - any args 89 * @return - returns args 90 */ 91 public static String[] f(String... args) { 92 return args; 93 } 94 95 /** 96 * Tests that MHs.explicitCastArguments does incorrect type checks for 97 * VarargsCollector. Bug 8066746. 98 * 99 * @throws java.lang.Throwable 100 */ 101 public static void testVarargsCollector() throws Throwable { 102 MethodType mt = MethodType.methodType(String[].class, String[].class); 103 MethodHandle mh = MethodHandles.publicLookup() 104 .findStatic(THIS_CLASS, "f", mt); 105 mh = MethodHandles.explicitCastArguments(mh, 106 MethodType.methodType(Object.class, Object.class)); 107 mh.invokeWithArguments((Object) (new String[]{"str1", "str2"})); 108 } 109 110 /** 111 * Tests that null wrapper reference is successfully converted to primitive 112 * types. Converted result should be zero for a primitive. Bug 8060483. 113 */ 114 public static void testNullRef2Prim() { 115 for (Wrapper from : Wrapper.values()) { 116 for (Wrapper to : Wrapper.values()) { 117 if (from == Wrapper.VOID || to == Wrapper.VOID) { 118 continue; 119 } 120 // MHs.eCA javadoc: 121 // If T0 is a reference and T1 a primitive, and if the reference 122 // is null at runtime, a zero value is introduced. 123 for (TestConversionMode mode : TestConversionMode.values()) { 124 testConversion(mode, from.wrapperType(), 125 to.primitiveType(), null, to.zero(), false, null); 126 } 127 } 128 } 129 } 130 131 /** 132 * Tests that non-null wrapper reference is successfully converted to 133 * primitive types. 134 */ 135 public static void testRef2Prim() { 136 for (Wrapper from : Wrapper.values()) { 137 for (Wrapper to : Wrapper.values()) { 138 if (from == Wrapper.VOID || to == Wrapper.VOID 139 || to == Wrapper.OBJECT) { 140 continue; 141 } 142 Object value = RANDOM_VALUES.get(from); 143 for (TestConversionMode mode : TestConversionMode.values()) { 144 if (from != Wrapper.OBJECT) { 145 Object convValue = to.wrap(value); 146 testConversion(mode, from.wrapperType(), 147 to.primitiveType(), value, convValue, false, null); 148 } else { 149 testConversion(mode, from.wrapperType(), 150 to.primitiveType(), value, null, 151 true, ClassCastException.class); 152 } 153 } 154 } 155 } 156 } 157 158 /** 159 * Tests that primitive is successfully converted to wrapper reference 160 * types, to the Number type (if possible) and to the Object type. 161 */ 162 public static void testPrim2Ref() { 163 for (Wrapper from : Wrapper.values()) { 164 for (Wrapper to : Wrapper.values()) { 165 if (from == Wrapper.VOID || from == Wrapper.OBJECT 166 || to == Wrapper.VOID || to == Wrapper.OBJECT) { 167 continue; 168 } 169 Object value = RANDOM_VALUES.get(from); 170 for (TestConversionMode mode : TestConversionMode.values()) { 171 if (from == to) { 172 testConversion(mode, from.primitiveType(), 173 to.wrapperType(), value, value, false, null); 174 } else { 175 testConversion(mode, from.primitiveType(), 176 to.wrapperType(), value, null, true, ClassCastException.class); 177 } 178 if (from != Wrapper.BOOLEAN && from != Wrapper.CHAR) { 179 testConversion(mode, from.primitiveType(), 180 Number.class, value, value, false, null); 181 } else { 182 testConversion(mode, from.primitiveType(), 183 Number.class, value, null, 184 true, ClassCastException.class); 185 } 186 testConversion(mode, from.primitiveType(), 187 Object.class, value, value, false, null); 188 } 189 } 190 } 191 } 192 193 /** 194 * Tests that primitive is successfully converted to other primitive type. 195 */ 196 public static void testPrim2Prim() { 197 for (Wrapper from : Wrapper.values()) { 198 for (Wrapper to : Wrapper.values()) { 199 if (from == Wrapper.VOID || to == Wrapper.VOID 200 || from == Wrapper.OBJECT || to == Wrapper.OBJECT) { 201 continue; 202 } 203 Object value = RANDOM_VALUES.get(from); 204 Object convValue = to.wrap(value); 205 for (TestConversionMode mode : TestConversionMode.values()) { 206 testConversion(mode, from.primitiveType(), 207 to.primitiveType(), value, convValue, false, null); 208 } 209 } 210 } 211 } 212 213 /** 214 * Dummy interface for {@link #testNonBCPRef2Ref} test. 215 */ 216 public static interface TestInterface {} 217 218 /** 219 * Dummy class for {@link #testNonBCPRef2Ref} test. 220 */ 221 public static class TestSuperClass implements TestInterface {} 222 223 /** 224 * Dummy class for {@link #testNonBCPRef2Ref} test. 225 */ 226 public static class TestSubClass1 extends TestSuperClass {} 227 228 /** 229 * Dummy class for {@link #testNonBCPRef2Ref} test. 230 */ 231 public static class TestSubClass2 extends TestSuperClass {} 232 233 /** 234 * Tests non-bootclasspath reference to reference conversions. 235 * 236 * @throws java.lang.Throwable 237 */ 238 public static void testNonBCPRef2NonBCPRef() throws Throwable { 239 Class testInterface = TestInterface.class; 240 Class testSuperClass = TestSuperClass.class; 241 Class testSubClass1 = TestSubClass1.class; 242 Class testSubClass2 = TestSubClass2.class; 243 Object testSuperObj = new TestSuperClass(); 244 Object testObj01 = new TestSubClass1(); 245 Object testObj02 = new TestSubClass2(); 246 Class[] parents = {testInterface, testSuperClass}; 247 Class[] children = {testSubClass1, testSubClass2}; 248 Object[] childInst = {testObj01, testObj02}; 249 for (TestConversionMode mode : TestConversionMode.values()) { 250 for (Class parent : parents) { 251 for (int j = 0; j < children.length; j++) { 252 // Child type to parent type non-null conversion, shoud succeed 253 testConversion(mode, children[j], parent, childInst[j], childInst[j], false, null); 254 // Child type to parent type null conversion, shoud succeed 255 testConversion(mode, children[j], parent, null, null, false, null); 256 // Parent type to child type non-null conversion with parent 257 // type instance, should fail 258 testConversion(mode, parent, children[j], testSuperObj, null, true, ClassCastException.class); 259 // Parent type to child type non-null conversion with child 260 // type instance, should succeed 261 testConversion(mode, parent, children[j], childInst[j], childInst[j], false, null); 262 // Parent type to child type null conversion, should succeed 263 testConversion(mode, parent, children[j], null, null, false, null); 264 } 265 // Parent type to child type non-null conversion with sibling 266 // type instance, should fail 267 testConversion(mode, parent, testSubClass1, testObj02, null, true, ClassCastException.class); 268 } 269 // Sibling type non-null conversion, should fail 270 testConversion(mode, testSubClass1, 271 testSubClass2, testObj01, null, true, 272 ClassCastException.class); 273 // Sibling type null conversion, should succeed 274 testConversion(mode, testSubClass1, 275 testSubClass2, null, null, false, null); 276 } 277 } 278 279 /** 280 * Dummy interface for {@link #testNonBCPRef2BCPRef} test. 281 */ 282 public static interface TestSerializableInterface extends Serializable {} 283 284 /** 285 * Dummy class for {@link #testNonBCPRef2BCPRef} test. 286 */ 287 public static class TestSerializableClass 288 implements TestSerializableInterface {} 289 290 /** 291 * Dummy class for {@link #testNonBCPRef2BCPRef} test. 292 */ 293 public static class TestFileChildClass extends File 294 implements TestSerializableInterface { 295 public TestFileChildClass(String pathname) { 296 super(pathname); 297 } 298 } 299 300 /** 301 * Tests non-bootclasspath reference to bootclasspath reference conversions 302 * and vice-versa. 303 * 304 * @throws java.lang.Throwable 305 */ 306 public static void testNonBCPRef2BCPRef() throws Throwable { 307 Class bcpInterface = Serializable.class; 308 Class bcpSuperClass = File.class; 309 Class nonBcpInterface = TestSerializableInterface.class; 310 Class nonBcpSuperSiblingClass = TestSerializableClass.class; 311 Class nonBcpSubClass = TestFileChildClass.class; 312 Object bcpSuperObj = new File("."); 313 Object testSuperSiblingObj = new TestSerializableClass(); 314 Object testSubObj = new TestFileChildClass("."); 315 Class[] parents = {bcpInterface, bcpSuperClass}; 316 for (TestConversionMode mode : TestConversionMode.values()) { 317 for (Class parent : parents) { 318 // Child type to parent type non-null conversion, shoud succeed 319 testConversion(mode, nonBcpSubClass, parent, testSubObj, 320 testSubObj, false, null); 321 // Child type to parent type null conversion, shoud succeed 322 testConversion(mode, nonBcpSubClass, parent, null, null, 323 false, null); 324 // Parent type to child type non-null conversion with parent 325 // type instance, should fail 326 testConversion(mode, parent, nonBcpSubClass, bcpSuperObj, null, 327 true, ClassCastException.class); 328 // Parent type to child type non-null conversion with child 329 // type instance, should succeed 330 testConversion(mode, parent, nonBcpSubClass, testSubObj, 331 testSubObj, false, null); 332 // Parent type to child type null conversion, should succeed 333 testConversion(mode, parent, nonBcpSubClass, null, null, 334 false, null); 335 } 336 // Parent type to child type non-null conversion with 337 // super sibling type instance, should fail 338 testConversion(mode, bcpInterface, nonBcpSubClass, 339 testSuperSiblingObj, null, true, ClassCastException.class); 340 Class[] siblings = {nonBcpSubClass, bcpSuperClass}; 341 for (Class sibling : siblings) { 342 // Non-bcp class to bcp/non-bcp sibling class non-null 343 // conversion with nonBcpSuperSiblingClass instance, should fail 344 testConversion(mode, nonBcpSuperSiblingClass, sibling, 345 testSuperSiblingObj, null, true, ClassCastException.class); 346 // Non-bcp class to bcp/non-bcp sibling class null conversion, 347 // should succeed 348 testConversion(mode, nonBcpSuperSiblingClass, sibling, 349 null, null, false, null); 350 // Non-bcp interface to bcp/non-bcp sibling class non-null 351 // conversion with nonBcpSubClass instance, should succeed 352 testConversion(mode, nonBcpInterface, sibling, testSubObj, 353 testSubObj, false, null); 354 // Non-bcp interface to bcp/non-bcp sibling class 355 // null conversion, should succeed 356 testConversion(mode, nonBcpInterface, sibling, null, null, 357 false, null); 358 // Non-bcp interface to bcp/non-bcp sibling class non-null 359 // conversion with nonBcpSuperSiblingClass instance, should fail 360 testConversion(mode, nonBcpInterface, sibling, 361 testSuperSiblingObj, testSubObj, 362 true, ClassCastException.class); 363 } 364 } 365 } 366 367 /** 368 * Tests bootclasspath reference to reference conversions. 369 */ 370 public static void testBCPRef2BCPRef() { 371 Class bcpInterface = CharSequence.class; 372 Class bcpSubClass1 = String.class; 373 Class bcpSubClass2 = StringBuffer.class; 374 Object testObj01 = new String("test"); 375 Object testObj02 = new StringBuffer("test"); 376 Class[] children = {bcpSubClass1, bcpSubClass2}; 377 Object[] childInst = {testObj01, testObj02}; 378 for (TestConversionMode mode : TestConversionMode.values()) { 379 for (int i = 0; i < children.length; i++) { 380 // Child type to parent type non-null conversion, shoud succeed 381 testConversion(mode, children[i], bcpInterface, childInst[i], 382 childInst[i], false, null); 383 // Child type to parent type null conversion, shoud succeed 384 testConversion(mode, children[i], bcpInterface, null, 385 null, false, null); 386 // Parent type to child type non-null conversion with child 387 // type instance, should succeed 388 testConversion(mode, bcpInterface, 389 children[i], childInst[i], childInst[i], false, null); 390 // Parent type to child type null conversion, should succeed 391 testConversion(mode, bcpInterface, 392 children[i], null, null, false, null); 393 } 394 // Sibling type non-null conversion, should fail 395 testConversion(mode, bcpSubClass1, 396 bcpSubClass2, testObj01, null, true, 397 ClassCastException.class); 398 // Sibling type null conversion, should succeed 399 testConversion(mode, bcpSubClass1, 400 bcpSubClass2, null, null, false, null); 401 // Parent type to child type non-null conversion with sibling 402 // type instance, should fail 403 testConversion(mode, bcpInterface, bcpSubClass1, testObj02, 404 null, true, ClassCastException.class); 405 } 406 } 407 408 /** 409 * Dummy method used in {@link #testReturnAny2Void} and 410 * {@link #testReturnVoid2Any} tests to form a method handle. 411 */ 412 public static void retVoid() {} 413 414 /** 415 * Tests that non-null any return is successfully converted to non-type 416 * void. 417 */ 418 public static void testReturnAny2Void() { 419 for (Wrapper from : Wrapper.values()) { 420 testConversion(TestConversionMode.RETURN_VALUE, from.wrapperType(), 421 void.class, RANDOM_VALUES.get(from), 422 null, false, null); 423 testConversion(TestConversionMode.RETURN_VALUE, from.primitiveType(), 424 void.class, RANDOM_VALUES.get(from), 425 null, false, null); 426 } 427 } 428 429 /** 430 * Tests that void return is successfully converted to primitive and 431 * reference. Result should be zero for primitives and null for references. 432 */ 433 public static void testReturnVoid2Any() { 434 for (Wrapper to : Wrapper.values()) { 435 testConversion(TestConversionMode.RETURN_VALUE, void.class, 436 to.primitiveType(), null, 437 to.zero(), false, null); 438 testConversion(TestConversionMode.RETURN_VALUE, void.class, 439 to.wrapperType(), null, 440 null, false, null); 441 } 442 } 443 444 private static void checkForWrongMethodTypeException(MethodHandle mh, MethodType mt) { 445 try { 446 MethodHandles.explicitCastArguments(mh, mt); 447 throw new AssertionError("Expected WrongMethodTypeException is not thrown"); 448 } catch (WrongMethodTypeException wmte) { 449 if (VERBOSE) { 450 System.out.printf("Expected exception %s: %s\n", 451 wmte.getClass(), wmte.getMessage()); 452 } 453 } 454 } 455 456 /** 457 * Tests that MHs.eCA method works correctly with MHs with multiple arguments. 458 * @throws Throwable 459 */ 460 public static void testMultipleArgs() throws Throwable { 461 int arity = 1 + RNG.nextInt(Helper.MAX_ARITY / 2 - 2); 462 int arityMinus = RNG.nextInt(arity); 463 int arityPlus = arity + RNG.nextInt(Helper.MAX_ARITY / 2 - arity) + 1; 464 MethodType mType = Helper.randomMethodTypeGenerator(arity); 465 MethodType mTypeNew = Helper.randomMethodTypeGenerator(arity); 466 MethodType mTypeNewMinus = Helper.randomMethodTypeGenerator(arityMinus); 467 MethodType mTypeNewPlus = Helper.randomMethodTypeGenerator(arityPlus); 468 Class<?> rType = mType.returnType(); 469 MethodHandle original; 470 if (rType.equals(void.class)) { 471 MethodType mt = MethodType.methodType(void.class); 472 original = MethodHandles.publicLookup() 473 .findStatic(THIS_CLASS, "retVoid", mt); 474 } else { 475 Object rValue = Helper.castToWrapper(1, rType); 476 original = MethodHandles.constant(rType, rValue); 477 } 478 original = Helper.addTrailingArgs(original, arity, mType.parameterList()); 479 MethodHandle target = MethodHandles 480 .explicitCastArguments(original, mTypeNew); 481 Object[] parList = Helper.randomArgs(mTypeNew.parameterList()); 482 for (int i = 0; i < parList.length; i++) { 483 if (parList[i] instanceof String) { 484 parList[i] = null; //getting rid of Stings produced by randomArgs 485 } 486 } 487 target.invokeWithArguments(parList); 488 checkForWrongMethodTypeException(original, mTypeNewMinus); 489 checkForWrongMethodTypeException(original, mTypeNewPlus); 490 } 491 492 /** 493 * Enumeration of test conversion modes. 494 */ 495 public enum TestConversionMode { 496 RETURN_VALUE, 497 ARGUMENT; 498 } 499 500 /** 501 * Tests type and value conversion. Comparing with the given expected result. 502 * 503 * @param mode - test conversion mode. See {@link #TestConversionMode}. 504 * @param from - source type. 505 * @param to - destination type. 506 * @param param - value to be converted. 507 * @param expectedResult - expected value after conversion. 508 * @param failureExpected - true if conversion failure expected. 509 * @param expectedException - expected exception class if 510 * {@code failureExpected} is true. 511 */ 512 public static void testConversion(TestConversionMode mode, 513 Class<?> from, Class<?> to, Object param, 514 Object expectedResult, boolean failureExpected, 515 Class<? extends Throwable> expectedException) { 516 if (VERBOSE) { 517 System.out.printf("Testing return value conversion: " 518 + "%-10s => %-10s: %5s: ", from.getSimpleName(), 519 to.getSimpleName(), param); 520 } 521 MethodHandle original = null; 522 MethodType newType = null; 523 switch (mode) { 524 case RETURN_VALUE: 525 if (from.equals(void.class)) { 526 MethodType mt = MethodType.methodType(void.class); 527 try { 528 original = MethodHandles.publicLookup() 529 .findStatic(THIS_CLASS, "retVoid", mt); 530 } catch (NoSuchMethodException | IllegalAccessException ex) { 531 throw new Error("Unexpected issue", ex); 532 } 533 } else { 534 original = MethodHandles.constant(from, param); 535 } 536 newType = original.type().changeReturnType(to); 537 break; 538 case ARGUMENT: 539 if (from.equals(void.class) || to.equals(void.class)) { 540 throw new Error("Test issue: argument conversion does not" 541 + " work with non-type void"); 542 } 543 original = MethodHandles.identity(to); 544 newType = original.type().changeParameterType(0, from); 545 break; 546 default: 547 String msg = String.format("Test issue: unknown test" 548 + " convertion mode %s.", mode.name()); 549 throw new Error(msg); 550 } 551 try { 552 MethodHandle target = MethodHandles 553 .explicitCastArguments(original, newType); 554 Object result; 555 switch (mode) { 556 case RETURN_VALUE: 557 result = target.invokeWithArguments(); 558 break; 559 case ARGUMENT: 560 result = target.invokeWithArguments(param); 561 break; 562 default: 563 String msg = String.format("Test issue: unknown test" 564 + " convertion mode %s.", mode.name()); 565 throw new Error(msg); 566 } 567 if (!failureExpected 568 && (expectedResult != null && !expectedResult.equals(result) 569 || expectedResult == null && result != null)) { 570 String msg = String.format("Conversion result %s is not equal" 571 + " to the expected result %10s", 572 result, expectedResult); 573 throw new AssertionError(msg); 574 } 575 if (VERBOSE) { 576 String resultStr; 577 if (result != null) { 578 resultStr = String.format("Converted value and type are" 579 + " %10s (%10s)", "'" + result + "'", 580 result.getClass().getSimpleName()); 581 } else { 582 resultStr = String.format("Converted value is %10s", result); 583 } 584 System.out.println(resultStr); 585 } 586 if (failureExpected) { 587 String msg = String.format("No exception thrown while testing" 588 + " return value conversion: %10s => %10s;" 589 + " parameter: %10s", 590 from, to, param); 591 throw new AssertionError(msg); 592 } 593 } catch (AssertionError e) { 594 throw e; // report test failure 595 } catch (Throwable e) { 596 if (VERBOSE) { 597 System.out.printf("%s: %s\n", e.getClass(), e.getMessage()); 598 } 599 if (!failureExpected || !e.getClass().equals(expectedException)) { 600 String msg = String.format("Unexpected exception was thrown" 601 + " while testing return value conversion:" 602 + " %s => %s; parameter: %s", from, to, param); 603 throw new AssertionError(msg, e); 604 } 605 } 606 } 607 }