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 }