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 }