1 /*
2  * Copyright (c) 2014, 2018, 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 {
RANDOM_VALUES.put(Wrapper.BOOLEAN, RNG.nextBoolean())58         RANDOM_VALUES.put(Wrapper.BOOLEAN, RNG.nextBoolean());
RANDOM_VALUES.put(Wrapper.BYTE, (byte) RNG.nextInt())59         RANDOM_VALUES.put(Wrapper.BYTE, (byte) RNG.nextInt());
RANDOM_VALUES.put(Wrapper.SHORT, (short) RNG.nextInt())60         RANDOM_VALUES.put(Wrapper.SHORT, (short) RNG.nextInt());
RANDOM_VALUES.put(Wrapper.CHAR, (char) RNG.nextInt())61         RANDOM_VALUES.put(Wrapper.CHAR, (char) RNG.nextInt());
RANDOM_VALUES.put(Wrapper.INT, RNG.nextInt())62         RANDOM_VALUES.put(Wrapper.INT, RNG.nextInt());
RANDOM_VALUES.put(Wrapper.LONG, RNG.nextLong())63         RANDOM_VALUES.put(Wrapper.LONG, RNG.nextLong());
RANDOM_VALUES.put(Wrapper.FLOAT, RNG.nextFloat())64         RANDOM_VALUES.put(Wrapper.FLOAT, RNG.nextFloat());
RANDOM_VALUES.put(Wrapper.DOUBLE, RNG.nextDouble())65         RANDOM_VALUES.put(Wrapper.DOUBLE, RNG.nextDouble());
RANDOM_VALUES.put(Wrapper.OBJECT, new Object())66         RANDOM_VALUES.put(Wrapper.OBJECT, new Object());
67     }
68 
main(String[] args)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      */
f(String... args)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      */
testVarargsCollector()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      */
testNullRef2Prim()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      */
testRef2Prim()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      */
testPrim2Ref()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      */
testPrim2Prim()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      */
testNonBCPRef2NonBCPRef()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 {
TestFileChildClass(String pathname)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      */
testNonBCPRef2BCPRef()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      */
testBCPRef2BCPRef()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      */
retVoid()418     public static void retVoid() {}
419 
420     /**
421      * Tests that non-null any return is successfully converted to non-type
422      * void.
423      */
testReturnAny2Void()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      */
testReturnVoid2Any()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 
checkForWrongMethodTypeException(MethodHandle mh, MethodType mt)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      */
testMultipleArgs()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      */
testConversion(TestConversionMode mode, Class<?> from, Class<?> to, Object param, Object expectedResult, boolean failureExpected, Class<? extends Throwable> expectedException)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 }
614