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