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 /test/lib /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],
 254                             childInst[j], false, null);
 255                     // Child type to parent type null conversion, shoud succeed
 256                     testConversion(mode, children[j], parent, null,
 257                             null, false, null);
 258                     // Parent type to child type non-null conversion with parent
 259                     // type instance, should fail
 260                     testConversion(mode, parent, children[j], testSuperObj,
 261                             null, true, ClassCastException.class);
 262                     // Parent type to child type non-null conversion with child
 263                     // type instance, should succeed
 264                     testConversion(mode, parent, children[j], childInst[j],
 265                             childInst[j], false, null);
 266                     // Parent type to child type null conversion, should succeed
 267                     testConversion(mode, parent, children[j], null,
 268                             null, false, null);
 269                 }
 270                 // Parent type to child type non-null conversion with sibling
 271                 // type instance, should fail
 272                 testConversion(mode, parent, testSubClass1, testObj02,
 273                         null, true, ClassCastException.class);
 274             }
 275             // Sibling type non-null conversion, should fail
 276             testConversion(mode, testSubClass1,
 277                     testSubClass2, testObj01, null, true,
 278                     ClassCastException.class);
 279             // Sibling type null conversion, should succeed
 280             testConversion(mode, testSubClass1,
 281                     testSubClass2, null, null, false, null);
 282         }
 283     }
 284 
 285     /**
 286      * Dummy interface for {@link #testNonBCPRef2BCPRef} test.
 287      */
 288     public static interface TestSerializableInterface extends Serializable {}
 289 
 290     /**
 291      * Dummy class for {@link #testNonBCPRef2BCPRef} test.
 292      */
 293     public static class TestSerializableClass
 294             implements TestSerializableInterface {}
 295 
 296     /**
 297      * Dummy class for {@link #testNonBCPRef2BCPRef} test.
 298      */
 299     public static class TestFileChildClass extends File
 300             implements TestSerializableInterface {
 301         public TestFileChildClass(String pathname) {
 302             super(pathname);
 303         }
 304     }
 305 
 306     /**
 307      * Tests non-bootclasspath reference to bootclasspath reference conversions
 308      * and vice-versa.
 309      *
 310      * @throws java.lang.Throwable
 311      */
 312     public static void testNonBCPRef2BCPRef() throws Throwable {
 313         Class bcpInterface = Serializable.class;
 314         Class bcpSuperClass = File.class;
 315         Class nonBcpInterface = TestSerializableInterface.class;
 316         Class nonBcpSuperSiblingClass = TestSerializableClass.class;
 317         Class nonBcpSubClass = TestFileChildClass.class;
 318         Object bcpSuperObj = new File(".");
 319         Object testSuperSiblingObj = new TestSerializableClass();
 320         Object testSubObj = new TestFileChildClass(".");
 321         Class[] parents = {bcpInterface, bcpSuperClass};
 322         for (TestConversionMode mode : TestConversionMode.values()) {
 323             for (Class parent : parents) {
 324                 // Child type to parent type non-null conversion, shoud succeed
 325                 testConversion(mode, nonBcpSubClass, parent, testSubObj,
 326                         testSubObj, false, null);
 327                 // Child type to parent type null conversion, shoud succeed
 328                 testConversion(mode, nonBcpSubClass, parent, null, null,
 329                         false, null);
 330                 // Parent type to child type non-null conversion with parent
 331                 // type instance, should fail
 332                 testConversion(mode, parent, nonBcpSubClass, bcpSuperObj, null,
 333                         true, ClassCastException.class);
 334                 // Parent type to child type non-null conversion with child
 335                 // type instance, should succeed
 336                 testConversion(mode, parent, nonBcpSubClass, testSubObj,
 337                         testSubObj, false, null);
 338                 // Parent type to child type null conversion, should succeed
 339                 testConversion(mode, parent, nonBcpSubClass, null, null,
 340                         false, null);
 341             }
 342             // Parent type to child type non-null conversion with
 343             // super sibling type instance, should fail
 344             testConversion(mode, bcpInterface, nonBcpSubClass,
 345                     testSuperSiblingObj, null, true, ClassCastException.class);
 346             Class[] siblings = {nonBcpSubClass, bcpSuperClass};
 347             for (Class sibling : siblings) {
 348                 // Non-bcp class to bcp/non-bcp sibling class non-null
 349                 // conversion with nonBcpSuperSiblingClass instance, should fail
 350                 testConversion(mode, nonBcpSuperSiblingClass, sibling,
 351                         testSuperSiblingObj, null, true, ClassCastException.class);
 352                 // Non-bcp class to bcp/non-bcp sibling class null conversion,
 353                 // should succeed
 354                 testConversion(mode, nonBcpSuperSiblingClass, sibling,
 355                         null, null, false, null);
 356                 // Non-bcp interface to bcp/non-bcp sibling class non-null
 357                 // conversion with nonBcpSubClass instance, should succeed
 358                 testConversion(mode, nonBcpInterface, sibling, testSubObj,
 359                         testSubObj, false, null);
 360                 // Non-bcp interface to bcp/non-bcp sibling class
 361                 // null conversion, should succeed
 362                 testConversion(mode, nonBcpInterface, sibling, null, null,
 363                         false, null);
 364                 // Non-bcp interface to bcp/non-bcp sibling class non-null
 365                 // conversion with nonBcpSuperSiblingClass instance, should fail
 366                 testConversion(mode, nonBcpInterface, sibling,
 367                         testSuperSiblingObj, testSubObj,
 368                         true, ClassCastException.class);
 369             }
 370         }
 371     }
 372 
 373     /**
 374      * Tests bootclasspath reference to reference conversions.
 375      */
 376     public static void testBCPRef2BCPRef() {
 377         Class bcpInterface = CharSequence.class;
 378         Class bcpSubClass1 = String.class;
 379         Class bcpSubClass2 = StringBuffer.class;
 380         Object testObj01 = new String("test");
 381         Object testObj02 = new StringBuffer("test");
 382         Class[] children = {bcpSubClass1, bcpSubClass2};
 383         Object[] childInst = {testObj01, testObj02};
 384         for (TestConversionMode mode : TestConversionMode.values()) {
 385             for (int i = 0; i < children.length; i++) {
 386                 // Child type to parent type non-null conversion, shoud succeed
 387                 testConversion(mode, children[i], bcpInterface, childInst[i],
 388                         childInst[i], false, null);
 389                 // Child type to parent type null conversion, shoud succeed
 390                 testConversion(mode, children[i], bcpInterface, null,
 391                         null, false, null);
 392                 // Parent type to child type non-null conversion with child
 393                 // type instance, should succeed
 394                 testConversion(mode, bcpInterface,
 395                         children[i], childInst[i], childInst[i], false, null);
 396                 // Parent type to child type null conversion, should succeed
 397                 testConversion(mode, bcpInterface,
 398                         children[i], null, null, false, null);
 399             }
 400             // Sibling type non-null conversion, should fail
 401             testConversion(mode, bcpSubClass1,
 402                     bcpSubClass2, testObj01, null, true,
 403                     ClassCastException.class);
 404             // Sibling type null conversion, should succeed
 405             testConversion(mode, bcpSubClass1,
 406                     bcpSubClass2, null, null, false, null);
 407             // Parent type to child type non-null conversion with sibling
 408             // type instance, should fail
 409             testConversion(mode, bcpInterface, bcpSubClass1, testObj02,
 410                     null, true, ClassCastException.class);
 411         }
 412     }
 413 
 414     /**
 415      * Dummy method used in {@link #testReturnAny2Void} and
 416      * {@link #testReturnVoid2Any} tests to form a method handle.
 417      */
 418     public static void retVoid() {}
 419 
 420     /**
 421      * Tests that non-null any return is successfully converted to non-type
 422      * void.
 423      */
 424     public static void testReturnAny2Void() {
 425         for (Wrapper from : Wrapper.values()) {
 426             testConversion(TestConversionMode.RETURN_VALUE, from.wrapperType(),
 427                     void.class, RANDOM_VALUES.get(from),
 428                     null, false, null);
 429             testConversion(TestConversionMode.RETURN_VALUE, from.primitiveType(),
 430                     void.class, RANDOM_VALUES.get(from),
 431                     null, false, null);
 432         }
 433     }
 434 
 435     /**
 436      * Tests that void return is successfully converted to primitive and
 437      * reference. Result should be zero for primitives and null for references.
 438      */
 439     public static void testReturnVoid2Any() {
 440         for (Wrapper to : Wrapper.values()) {
 441             testConversion(TestConversionMode.RETURN_VALUE, void.class,
 442                     to.primitiveType(), null,
 443                     to.zero(), false, null);
 444             testConversion(TestConversionMode.RETURN_VALUE, void.class,
 445                     to.wrapperType(), null,
 446                     null, false, null);
 447         }
 448     }
 449 
 450     private static void checkForWrongMethodTypeException(MethodHandle mh, MethodType mt) {
 451         try {
 452             MethodHandles.explicitCastArguments(mh, mt);
 453             throw new AssertionError("Expected WrongMethodTypeException is not thrown");
 454         } catch (WrongMethodTypeException wmte) {
 455             if (VERBOSE) {
 456                 System.out.printf("Expected exception %s: %s\n",
 457                         wmte.getClass(), wmte.getMessage());
 458             }
 459         }
 460     }
 461 
 462     /**
 463      * Tests that MHs.eCA method works correctly with MHs with multiple arguments.
 464      * @throws Throwable
 465      */
 466     public static void testMultipleArgs() throws Throwable {
 467         int arity = 1 + RNG.nextInt(Helper.MAX_ARITY / 2 - 2);
 468         int arityMinus = RNG.nextInt(arity);
 469         int arityPlus = arity + RNG.nextInt(Helper.MAX_ARITY / 2 - arity) + 1;
 470         MethodType mType = Helper.randomMethodTypeGenerator(arity);
 471         MethodType mTypeNew = Helper.randomMethodTypeGenerator(arity);
 472         MethodType mTypeNewMinus = Helper.randomMethodTypeGenerator(arityMinus);
 473         MethodType mTypeNewPlus = Helper.randomMethodTypeGenerator(arityPlus);
 474         Class<?> rType = mType.returnType();
 475         MethodHandle original;
 476         if (rType.equals(void.class)) {
 477             MethodType mt = MethodType.methodType(void.class);
 478             original = MethodHandles.publicLookup()
 479                     .findStatic(THIS_CLASS, "retVoid", mt);
 480         } else {
 481             Object rValue = Helper.castToWrapper(1, rType);
 482             original = MethodHandles.constant(rType, rValue);
 483         }
 484         original = Helper.addTrailingArgs(original, arity, mType.parameterList());
 485         MethodHandle target = MethodHandles
 486                     .explicitCastArguments(original, mTypeNew);
 487         Object[] parList = Helper.randomArgs(mTypeNew.parameterList());
 488         for (int i = 0; i < parList.length; i++) {
 489             if (parList[i] instanceof String) {
 490                 parList[i] = null; //getting rid of Stings produced by randomArgs
 491             }
 492         }
 493         target.invokeWithArguments(parList);
 494         checkForWrongMethodTypeException(original, mTypeNewMinus);
 495         checkForWrongMethodTypeException(original, mTypeNewPlus);
 496     }
 497 
 498     /**
 499      * Enumeration of test conversion modes.
 500      */
 501     public enum TestConversionMode {
 502         RETURN_VALUE,
 503         ARGUMENT;
 504     }
 505 
 506     /**
 507      * Tests type and value conversion. Comparing with the given expected result.
 508      *
 509      * @param mode - test conversion mode. See {@link #TestConversionMode}.
 510      * @param from - source type.
 511      * @param to - destination type.
 512      * @param param - value to be converted.
 513      * @param expectedResult - expected value after conversion.
 514      * @param failureExpected - true if conversion failure expected.
 515      * @param expectedException - expected exception class if
 516      * {@code failureExpected} is true.
 517      */
 518     public static void testConversion(TestConversionMode mode,
 519             Class<?> from, Class<?> to, Object param,
 520             Object expectedResult, boolean failureExpected,
 521             Class<? extends Throwable> expectedException) {
 522         if (VERBOSE) {
 523             System.out.printf("Testing return value conversion: "
 524                     + "%-10s => %-10s: %5s: ", from.getSimpleName(),
 525                     to.getSimpleName(), param);
 526         }
 527         MethodHandle original = null;
 528         MethodType newType = null;
 529         switch (mode) {
 530             case RETURN_VALUE:
 531                 if (from.equals(void.class)) {
 532                     MethodType mt = MethodType.methodType(void.class);
 533                     try {
 534                         original = MethodHandles.publicLookup()
 535                                 .findStatic(THIS_CLASS, "retVoid", mt);
 536                     } catch (NoSuchMethodException | IllegalAccessException ex) {
 537                         throw new Error("Unexpected issue", ex);
 538                     }
 539                 } else {
 540                     original = MethodHandles.constant(from, param);
 541                 }
 542                 newType = original.type().changeReturnType(to);
 543                 break;
 544             case ARGUMENT:
 545                 if (from.equals(void.class) || to.equals(void.class)) {
 546                     throw new Error("Test issue: argument conversion does not"
 547                             + " work with non-type void");
 548                 }
 549                 original = MethodHandles.identity(to);
 550                 newType = original.type().changeParameterType(0, from);
 551                 break;
 552             default:
 553                 String msg = String.format("Test issue: unknown test"
 554                         + " convertion mode %s.", mode.name());
 555                 throw new Error(msg);
 556         }
 557         try {
 558             MethodHandle target = MethodHandles
 559                     .explicitCastArguments(original, newType);
 560             Object result;
 561             switch (mode) {
 562                 case RETURN_VALUE:
 563                     result = target.invokeWithArguments();
 564                     break;
 565                 case ARGUMENT:
 566                     result = target.invokeWithArguments(param);
 567                     break;
 568                 default:
 569                     String msg = String.format("Test issue: unknown test"
 570                             + " convertion mode %s.", mode.name());
 571                     throw new Error(msg);
 572             }
 573             if (!failureExpected
 574                     && (expectedResult != null && !expectedResult.equals(result)
 575                     || expectedResult == null && result != null)) {
 576                 String msg = String.format("Conversion result %s is not equal"
 577                         + " to the expected result %10s",
 578                         result, expectedResult);
 579                 throw new AssertionError(msg);
 580             }
 581             if (VERBOSE) {
 582                 String resultStr;
 583                 if (result != null) {
 584                     resultStr = String.format("Converted value and type are"
 585                             + " %10s (%10s)", "'" + result + "'",
 586                             result.getClass().getSimpleName());
 587                 } else {
 588                     resultStr = String.format("Converted value is %10s", result);
 589                 }
 590                 System.out.println(resultStr);
 591             }
 592             if (failureExpected) {
 593                 String msg = String.format("No exception thrown while testing"
 594                         + " return value conversion: %10s => %10s;"
 595                         + " parameter: %10s",
 596                         from, to, param);
 597                 throw new AssertionError(msg);
 598             }
 599         } catch (AssertionError e) {
 600             throw e; // report test failure
 601         } catch (Throwable e) {
 602             if (VERBOSE) {
 603                 System.out.printf("%s: %s\n", e.getClass(), e.getMessage());
 604             }
 605             if (!failureExpected || !e.getClass().equals(expectedException)) {
 606                 String msg = String.format("Unexpected exception was thrown"
 607                         + " while testing return value conversion:"
 608                         + " %s => %s; parameter: %s", from, to, param);
 609                 throw new AssertionError(msg, e);
 610             }
 611         }
 612     }
 613 }