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