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 // Android-added: package name for grouping tests.
25 package test.java.lang.invoke;
26 
27 import sun.invoke.util.Wrapper;
28 import test.java.lang.invoke.lib.Helper;
29 
30 import java.io.File;
31 import java.io.Serializable;
32 import java.lang.invoke.MethodHandle;
33 import java.lang.invoke.MethodHandles;
34 import java.lang.invoke.MethodType;
35 import java.lang.invoke.WrongMethodTypeException;
36 import java.util.HashMap;
37 import java.util.Map;
38 import java.util.Random;
39 
40 // Android-added: for @Test annotating test methods.
41 import org.testng.*;
42 import org.testng.annotations.*;
43 
44 /*
45  * @test
46  * @bug 8060483 8066746
47  * @key randomness
48  * @library /lib/testlibrary /java/lang/invoke/common
49  * @modules java.base/sun.invoke.util
50  * @summary unit tests for MethodHandles.explicitCastArguments()
51  * @run main ExplicitCastArgumentsTest
52  */
53 
54 /**
55  * Tests for MethodHandles.explicitCastArguments().
56  */
57 public class ExplicitCastArgumentsTest {
58 
59     private static final boolean VERBOSE = Helper.IS_VERBOSE;
60     private static final Class<?> THIS_CLASS = ExplicitCastArgumentsTest.class;
61     private static final Random RNG = Helper.RNG;
62     private static final Map<Wrapper, Object> RANDOM_VALUES = new HashMap<>(9);
63 
64     static {
RANDOM_VALUES.put(Wrapper.BOOLEAN, RNG.nextBoolean())65         RANDOM_VALUES.put(Wrapper.BOOLEAN, RNG.nextBoolean());
RANDOM_VALUES.put(Wrapper.BYTE, (byte) RNG.nextInt())66         RANDOM_VALUES.put(Wrapper.BYTE, (byte) RNG.nextInt());
RANDOM_VALUES.put(Wrapper.SHORT, (short) RNG.nextInt())67         RANDOM_VALUES.put(Wrapper.SHORT, (short) RNG.nextInt());
RANDOM_VALUES.put(Wrapper.CHAR, (char) RNG.nextInt())68         RANDOM_VALUES.put(Wrapper.CHAR, (char) RNG.nextInt());
RANDOM_VALUES.put(Wrapper.INT, RNG.nextInt())69         RANDOM_VALUES.put(Wrapper.INT, RNG.nextInt());
RANDOM_VALUES.put(Wrapper.LONG, RNG.nextLong())70         RANDOM_VALUES.put(Wrapper.LONG, RNG.nextLong());
RANDOM_VALUES.put(Wrapper.FLOAT, RNG.nextFloat())71         RANDOM_VALUES.put(Wrapper.FLOAT, RNG.nextFloat());
RANDOM_VALUES.put(Wrapper.DOUBLE, RNG.nextDouble())72         RANDOM_VALUES.put(Wrapper.DOUBLE, RNG.nextDouble());
RANDOM_VALUES.put(Wrapper.OBJECT, new Object())73         RANDOM_VALUES.put(Wrapper.OBJECT, new Object());
74     }
75 
main(String[] args)76     public static void main(String[] args) throws Throwable {
77         testVarargsCollector();
78         testNullRef2Prim();
79         testRef2Prim();
80         testPrim2Ref();
81         testPrim2Prim();
82         testNonBCPRef2NonBCPRef();
83         testBCPRef2BCPRef();
84         testNonBCPRef2BCPRef();
85         testReturnAny2Void();
86         testReturnVoid2Any();
87         testMultipleArgs();
88         System.out.println("TEST PASSED");
89     }
90 
91     // Android-changed: use 'sample' in comment.
92     /**
93      * Sample method used in {@link #testVarargsCollector} test to form a method
94      * handle.
95      *
96      * @param args - any args
97      * @return - returns args
98      */
f(String... args)99     public static String[] f(String... args) {
100         return args;
101     }
102 
103     // Android-added: @Test annotation
104     @Test
105     /**
106      * Tests that MHs.explicitCastArguments does incorrect type checks for
107      * VarargsCollector. Bug 8066746.
108      *
109      * @throws java.lang.Throwable
110      */
testVarargsCollector()111     public static void testVarargsCollector() throws Throwable {
112         MethodType mt = MethodType.methodType(String[].class, String[].class);
113         MethodHandle mh = MethodHandles.publicLookup()
114                 .findStatic(THIS_CLASS, "f", mt);
115         mh = MethodHandles.explicitCastArguments(mh,
116                 MethodType.methodType(Object.class, Object.class));
117         mh.invokeWithArguments((Object) (new String[]{"str1", "str2"}));
118     }
119 
120     // Android-added: @Test annotation
121     @Test
122     /**
123      * Tests that null wrapper reference is successfully converted to primitive
124      * types. Converted result should be zero for a primitive. Bug 8060483.
125      */
testNullRef2Prim()126     public static void testNullRef2Prim() {
127         for (Wrapper from : Wrapper.values()) {
128             for (Wrapper to : Wrapper.values()) {
129                 if (from == Wrapper.VOID || to == Wrapper.VOID) {
130                     continue;
131                 }
132                 // MHs.eCA javadoc:
133                 //    If T0 is a reference and T1 a primitive, and if the reference
134                 //    is null at runtime, a zero value is introduced.
135                 for (TestConversionMode mode : TestConversionMode.values()) {
136                     testConversion(mode, from.wrapperType(),
137                             to.primitiveType(), null, to.zero(), false, null);
138                 }
139             }
140         }
141     }
142 
143     // Android-added: @Test annotation
144     @Test
145     /**
146      * Tests that non-null wrapper reference is successfully converted to
147      * primitive types.
148      */
testRef2Prim()149     public static void testRef2Prim() {
150         for (Wrapper from : Wrapper.values()) {
151             for (Wrapper to : Wrapper.values()) {
152                 if (from == Wrapper.VOID || to == Wrapper.VOID
153                         || to == Wrapper.OBJECT) {
154                     continue;
155                 }
156                 Object value = RANDOM_VALUES.get(from);
157                 for (TestConversionMode mode : TestConversionMode.values()) {
158                     if (from != Wrapper.OBJECT) {
159                         Object convValue = to.wrap(value);
160                         testConversion(mode, from.wrapperType(),
161                                 to.primitiveType(), value, convValue, false, null);
162                     } else {
163                         testConversion(mode, from.wrapperType(),
164                                 to.primitiveType(), value, null,
165                                 true, ClassCastException.class);
166                     }
167                 }
168             }
169         }
170     }
171 
172     // Android-added: @Test annotation
173     @Test
174     /**
175      * Tests that primitive is successfully converted to wrapper reference
176      * types, to the Number type (if possible) and to the Object type.
177      */
testPrim2Ref()178     public static void testPrim2Ref() {
179         for (Wrapper from : Wrapper.values()) {
180             for (Wrapper to : Wrapper.values()) {
181                 if (from == Wrapper.VOID || from == Wrapper.OBJECT
182                         || to == Wrapper.VOID || to == Wrapper.OBJECT) {
183                     continue;
184                 }
185                 Object value = RANDOM_VALUES.get(from);
186                 for (TestConversionMode mode : TestConversionMode.values()) {
187                     if (from == to) {
188                         testConversion(mode, from.primitiveType(),
189                                 to.wrapperType(), value, value, false, null);
190                     } else {
191                         testConversion(mode, from.primitiveType(),
192                                 to.wrapperType(), value, null, true, ClassCastException.class);
193                     }
194                     if (from != Wrapper.BOOLEAN && from != Wrapper.CHAR) {
195                         testConversion(mode, from.primitiveType(),
196                                 Number.class, value, value, false, null);
197                     } else {
198                         testConversion(mode, from.primitiveType(),
199                                 Number.class, value, null,
200                                 true, ClassCastException.class);
201                     }
202                     testConversion(mode, from.primitiveType(),
203                             Object.class, value, value, false, null);
204                 }
205             }
206         }
207     }
208 
209     // Android-added: @Test annotation
210     @Test
211     /**
212      * Tests that primitive is successfully converted to other primitive type.
213      */
testPrim2Prim()214     public static void testPrim2Prim() {
215         for (Wrapper from : Wrapper.values()) {
216             for (Wrapper to : Wrapper.values()) {
217                 if (from == Wrapper.VOID || to == Wrapper.VOID
218                         || from == Wrapper.OBJECT || to == Wrapper.OBJECT) {
219                     continue;
220                 }
221                 Object value = RANDOM_VALUES.get(from);
222                 Object convValue = to.wrap(value);
223                 for (TestConversionMode mode : TestConversionMode.values()) {
224                     testConversion(mode, from.primitiveType(),
225                             to.primitiveType(), value, convValue, false, null);
226                 }
227             }
228         }
229     }
230 
231     // Android-changed: use 'sample' in comment.
232     /**
233      * Sample interface for {@link #testNonBCPRef2Ref} test.
234      */
235     public static interface TestInterface {}
236 
237     // Android-changed: use 'sample' in comment.
238     /**
239      * Sample class for {@link #testNonBCPRef2Ref} test.
240      */
241     public static class TestSuperClass implements TestInterface {}
242 
243     // Android-changed: use 'sample' in comment.
244     /**
245      * Sample class for {@link #testNonBCPRef2Ref} test.
246      */
247     public static class TestSubClass1 extends TestSuperClass {}
248 
249     // Android-changed: use 'sample' in comment.
250     /**
251      * Sample class for {@link #testNonBCPRef2Ref} test.
252      */
253     public static class TestSubClass2 extends TestSuperClass {}
254 
255     // Android-added: @Test annotation
256     @Test
257     /**
258      * Tests non-bootclasspath reference to reference conversions.
259      *
260      * @throws java.lang.Throwable
261      */
testNonBCPRef2NonBCPRef()262     public static void testNonBCPRef2NonBCPRef() throws Throwable {
263         Class testInterface = TestInterface.class;
264         Class testSuperClass = TestSuperClass.class;
265         Class testSubClass1 = TestSubClass1.class;
266         Class testSubClass2 = TestSubClass2.class;
267         Object testSuperObj = new TestSuperClass();
268         Object testObj01 = new TestSubClass1();
269         Object testObj02 = new TestSubClass2();
270         Class[] parents = {testInterface, testSuperClass};
271         Class[] children = {testSubClass1, testSubClass2};
272         Object[] childInst = {testObj01, testObj02};
273         for (TestConversionMode mode : TestConversionMode.values()) {
274             for (Class parent : parents) {
275                 for (int j = 0; j < children.length; j++) {
276                     // Android-changed: spelling fix in next comment.
277                     // Child type to parent type non-null conversion, should succeed
278                     testConversion(mode, children[j], parent, childInst[j],
279                             childInst[j], false, null);
280                     // Android-changed: spelling fix in next comment.
281                     // Child type to parent type null conversion, should succeed
282                     testConversion(mode, children[j], parent, null,
283                             null, false, null);
284                     // Parent type to child type non-null conversion with parent
285                     // type instance, should fail
286                     testConversion(mode, parent, children[j], testSuperObj,
287                             null, true, ClassCastException.class);
288                     // Parent type to child type non-null conversion with child
289                     // type instance, should succeed
290                     testConversion(mode, parent, children[j], childInst[j],
291                             childInst[j], false, null);
292                     // Parent type to child type null conversion, should succeed
293                     testConversion(mode, parent, children[j], null,
294                             null, false, null);
295                 }
296                 // Parent type to child type non-null conversion with sibling
297                 // type instance, should fail
298                 testConversion(mode, parent, testSubClass1, testObj02,
299                         null, true, ClassCastException.class);
300             }
301             // Sibling type non-null conversion, should fail
302             testConversion(mode, testSubClass1,
303                     testSubClass2, testObj01, null, true,
304                     ClassCastException.class);
305             // Sibling type null conversion, should succeed
306             testConversion(mode, testSubClass1,
307                     testSubClass2, null, null, false, null);
308         }
309     }
310 
311     // Android-changed: use 'sample' in comment.
312     /**
313      * Sample interface for {@link #testNonBCPRef2BCPRef} test.
314      */
315     public static interface TestSerializableInterface extends Serializable {}
316 
317     // Android-changed: use 'sample' in comment.
318     /**
319      * Sample class for {@link #testNonBCPRef2BCPRef} test.
320      */
321     public static class TestSerializableClass
322             implements TestSerializableInterface {}
323 
324     // Android-changed: use 'sample' in comment.
325     /**
326      * Sample class for {@link #testNonBCPRef2BCPRef} test.
327      */
328     public static class TestFileChildClass extends File
329             implements TestSerializableInterface {
TestFileChildClass(String pathname)330         public TestFileChildClass(String pathname) {
331             super(pathname);
332         }
333     }
334 
335     // Android-added: @Test annotation
336     @Test
337     /**
338      * Tests non-bootclasspath reference to bootclasspath reference conversions
339      * and vice-versa.
340      *
341      * @throws java.lang.Throwable
342      */
testNonBCPRef2BCPRef()343     public static void testNonBCPRef2BCPRef() throws Throwable {
344         Class bcpInterface = Serializable.class;
345         Class bcpSuperClass = File.class;
346         Class nonBcpInterface = TestSerializableInterface.class;
347         Class nonBcpSuperSiblingClass = TestSerializableClass.class;
348         Class nonBcpSubClass = TestFileChildClass.class;
349         Object bcpSuperObj = new File(".");
350         Object testSuperSiblingObj = new TestSerializableClass();
351         Object testSubObj = new TestFileChildClass(".");
352         Class[] parents = {bcpInterface, bcpSuperClass};
353         for (TestConversionMode mode : TestConversionMode.values()) {
354             for (Class parent : parents) {
355                 // Android-changed: spelling fix in next comment.
356                 // Child type to parent type non-null conversion, should succeed
357                 testConversion(mode, nonBcpSubClass, parent, testSubObj,
358                         testSubObj, false, null);
359                 // Android-changed: spelling fix in next comment.
360                 // Child type to parent type null conversion, should succeed
361                 testConversion(mode, nonBcpSubClass, parent, null, null,
362                         false, null);
363                 // Parent type to child type non-null conversion with parent
364                 // type instance, should fail
365                 testConversion(mode, parent, nonBcpSubClass, bcpSuperObj, null,
366                         true, ClassCastException.class);
367                 // Parent type to child type non-null conversion with child
368                 // type instance, should succeed
369                 testConversion(mode, parent, nonBcpSubClass, testSubObj,
370                         testSubObj, false, null);
371                 // Parent type to child type null conversion, should succeed
372                 testConversion(mode, parent, nonBcpSubClass, null, null,
373                         false, null);
374             }
375             // Parent type to child type non-null conversion with
376             // super sibling type instance, should fail
377             testConversion(mode, bcpInterface, nonBcpSubClass,
378                     testSuperSiblingObj, null, true, ClassCastException.class);
379             Class[] siblings = {nonBcpSubClass, bcpSuperClass};
380             for (Class sibling : siblings) {
381                 // Non-bcp class to bcp/non-bcp sibling class non-null
382                 // conversion with nonBcpSuperSiblingClass instance, should fail
383                 testConversion(mode, nonBcpSuperSiblingClass, sibling,
384                         testSuperSiblingObj, null, true, ClassCastException.class);
385                 // Non-bcp class to bcp/non-bcp sibling class null conversion,
386                 // should succeed
387                 testConversion(mode, nonBcpSuperSiblingClass, sibling,
388                         null, null, false, null);
389                 // Non-bcp interface to bcp/non-bcp sibling class non-null
390                 // conversion with nonBcpSubClass instance, should succeed
391                 testConversion(mode, nonBcpInterface, sibling, testSubObj,
392                         testSubObj, false, null);
393                 // Non-bcp interface to bcp/non-bcp sibling class
394                 // null conversion, should succeed
395                 testConversion(mode, nonBcpInterface, sibling, null, null,
396                         false, null);
397                 // Non-bcp interface to bcp/non-bcp sibling class non-null
398                 // conversion with nonBcpSuperSiblingClass instance, should fail
399                 testConversion(mode, nonBcpInterface, sibling,
400                         testSuperSiblingObj, testSubObj,
401                         true, ClassCastException.class);
402             }
403         }
404     }
405 
406     // Android-added: @Test annotation
407     @Test
408     /**
409      * Tests bootclasspath reference to reference conversions.
410      */
testBCPRef2BCPRef()411     public static void testBCPRef2BCPRef() {
412         Class bcpInterface = CharSequence.class;
413         Class bcpSubClass1 = String.class;
414         Class bcpSubClass2 = StringBuffer.class;
415         Object testObj01 = new String("test");
416         Object testObj02 = new StringBuffer("test");
417         Class[] children = {bcpSubClass1, bcpSubClass2};
418         Object[] childInst = {testObj01, testObj02};
419         for (TestConversionMode mode : TestConversionMode.values()) {
420             for (int i = 0; i < children.length; i++) {
421                 // Android-changed: spelling fix in next comment.
422                 // Child type to parent type non-null conversion, should succeed
423                 testConversion(mode, children[i], bcpInterface, childInst[i],
424                         childInst[i], false, null);
425                 // Android-changed: spelling fix in next comment.
426                 // Child type to parent type null conversion, should succeed
427                 testConversion(mode, children[i], bcpInterface, null,
428                         null, false, null);
429                 // Parent type to child type non-null conversion with child
430                 // type instance, should succeed
431                 testConversion(mode, bcpInterface,
432                         children[i], childInst[i], childInst[i], false, null);
433                 // Parent type to child type null conversion, should succeed
434                 testConversion(mode, bcpInterface,
435                         children[i], null, null, false, null);
436             }
437             // Sibling type non-null conversion, should fail
438             testConversion(mode, bcpSubClass1,
439                     bcpSubClass2, testObj01, null, true,
440                     ClassCastException.class);
441             // Sibling type null conversion, should succeed
442             testConversion(mode, bcpSubClass1,
443                     bcpSubClass2, null, null, false, null);
444             // Parent type to child type non-null conversion with sibling
445             // type instance, should fail
446             testConversion(mode, bcpInterface, bcpSubClass1, testObj02,
447                     null, true, ClassCastException.class);
448         }
449     }
450 
451     // Android-changed: use 'sample' in comment.
452     /**
453      * Sample method used in {@link #testReturnAny2Void} and
454      * {@link #testReturnVoid2Any} tests to form a method handle.
455      */
retVoid()456     public static void retVoid() {}
457 
458     // Android-added: @Test annotation
459     @Test
460     /**
461      * Tests that non-null any return is successfully converted to non-type
462      * void.
463      */
testReturnAny2Void()464     public static void testReturnAny2Void() {
465         for (Wrapper from : Wrapper.values()) {
466             testConversion(TestConversionMode.RETURN_VALUE, from.wrapperType(),
467                     void.class, RANDOM_VALUES.get(from),
468                     null, false, null);
469             testConversion(TestConversionMode.RETURN_VALUE, from.primitiveType(),
470                     void.class, RANDOM_VALUES.get(from),
471                     null, false, null);
472         }
473     }
474 
475     // Android-added: @Test annotation
476     @Test
477     /**
478      * Tests that void return is successfully converted to primitive and
479      * reference. Result should be zero for primitives and null for references.
480      */
testReturnVoid2Any()481     public static void testReturnVoid2Any() {
482         for (Wrapper to : Wrapper.values()) {
483             testConversion(TestConversionMode.RETURN_VALUE, void.class,
484                     to.primitiveType(), null,
485                     to.zero(), false, null);
486             testConversion(TestConversionMode.RETURN_VALUE, void.class,
487                     to.wrapperType(), null,
488                     null, false, null);
489         }
490     }
491 
checkForWrongMethodTypeException(MethodHandle mh, MethodType mt)492     private static void checkForWrongMethodTypeException(MethodHandle mh, MethodType mt) {
493         try {
494             MethodHandles.explicitCastArguments(mh, mt);
495             throw new AssertionError("Expected WrongMethodTypeException is not thrown");
496         } catch (WrongMethodTypeException wmte) {
497             if (VERBOSE) {
498                 System.out.printf("Expected exception %s: %s\n",
499                         wmte.getClass(), wmte.getMessage());
500             }
501         }
502     }
503 
504     // Android-added: @Test annotation with invocationCount as the test is non-deterministic.
505     @Test(invocationCount=10)
506     /**
507      * Tests that MHs.eCA method works correctly with MHs with multiple arguments.
508      * @throws Throwable
509      */
testMultipleArgs()510     public static void testMultipleArgs() throws Throwable {
511         int arity = 1 + RNG.nextInt(Helper.MAX_ARITY / 2 - 2);
512         int arityMinus = RNG.nextInt(arity);
513         int arityPlus = arity + RNG.nextInt(Helper.MAX_ARITY / 2 - arity) + 1;
514         MethodType mType = Helper.randomMethodTypeGenerator(arity);
515         MethodType mTypeNew = Helper.randomMethodTypeGenerator(arity);
516         MethodType mTypeNewMinus = Helper.randomMethodTypeGenerator(arityMinus);
517         MethodType mTypeNewPlus = Helper.randomMethodTypeGenerator(arityPlus);
518         Class<?> rType = mType.returnType();
519         MethodHandle original;
520         if (rType.equals(void.class)) {
521             MethodType mt = MethodType.methodType(void.class);
522             original = MethodHandles.publicLookup()
523                     .findStatic(THIS_CLASS, "retVoid", mt);
524         } else {
525             Object rValue = Helper.castToWrapper(1, rType);
526             original = MethodHandles.constant(rType, rValue);
527         }
528         original = Helper.addTrailingArgs(original, arity, mType.parameterList());
529         MethodHandle target = MethodHandles
530                     .explicitCastArguments(original, mTypeNew);
531         Object[] parList = Helper.randomArgs(mTypeNew.parameterList());
532         for (int i = 0; i < parList.length; i++) {
533             if (parList[i] instanceof String) {
534                 parList[i] = null; //getting rid of Stings produced by randomArgs
535             }
536         }
537         target.invokeWithArguments(parList);
538         checkForWrongMethodTypeException(original, mTypeNewMinus);
539         checkForWrongMethodTypeException(original, mTypeNewPlus);
540     }
541 
542     /**
543      * Enumeration of test conversion modes.
544      */
545     public enum TestConversionMode {
546         RETURN_VALUE,
547         ARGUMENT;
548     }
549 
550     // Android-added: @Test annotation
551     @Test
552     /**
553      * Tests type and value conversion. Comparing with the given expected result.
554      *
555      * @param mode - test conversion mode. See {@link #TestConversionMode}.
556      * @param from - source type.
557      * @param to - destination type.
558      * @param param - value to be converted.
559      * @param expectedResult - expected value after conversion.
560      * @param failureExpected - true if conversion failure expected.
561      * @param expectedException - expected exception class if
562      * {@code failureExpected} is true.
563      */
testConversion(TestConversionMode mode, Class<?> from, Class<?> to, Object param, Object expectedResult, boolean failureExpected, Class<? extends Throwable> expectedException)564     public static void testConversion(TestConversionMode mode,
565             Class<?> from, Class<?> to, Object param,
566             Object expectedResult, boolean failureExpected,
567             Class<? extends Throwable> expectedException) {
568         if (VERBOSE) {
569             System.out.printf("Testing return value conversion: "
570                     + "%-10s => %-10s: %5s: ", from.getSimpleName(),
571                     to.getSimpleName(), param);
572         }
573         MethodHandle original = null;
574         MethodType newType = null;
575         switch (mode) {
576             case RETURN_VALUE:
577                 if (from.equals(void.class)) {
578                     MethodType mt = MethodType.methodType(void.class);
579                     try {
580                         original = MethodHandles.publicLookup()
581                                 .findStatic(THIS_CLASS, "retVoid", mt);
582                     } catch (NoSuchMethodException | IllegalAccessException ex) {
583                         throw new Error("Unexpected issue", ex);
584                     }
585                 } else {
586                     original = MethodHandles.constant(from, param);
587                 }
588                 newType = original.type().changeReturnType(to);
589                 break;
590             case ARGUMENT:
591                 if (from.equals(void.class) || to.equals(void.class)) {
592                     throw new Error("Test issue: argument conversion does not"
593                             + " work with non-type void");
594                 }
595                 original = MethodHandles.identity(to);
596                 newType = original.type().changeParameterType(0, from);
597                 break;
598             default:
599                 // Android-changed: spelling fix in msg.
600                 String msg = String.format("Test issue: unknown test"
601                         + " conversion mode %s.", mode.name());
602                 throw new Error(msg);
603         }
604         try {
605             MethodHandle target = MethodHandles
606                     .explicitCastArguments(original, newType);
607             Object result;
608             switch (mode) {
609                 case RETURN_VALUE:
610                     result = target.invokeWithArguments();
611                     break;
612                 case ARGUMENT:
613                     result = target.invokeWithArguments(param);
614                     break;
615                 default:
616                     // Android-changed: spelling fix in msg.
617                     String msg = String.format("Test issue: unknown test"
618                             + " conversion mode %s.", mode.name());
619                     throw new Error(msg);
620             }
621             if (!failureExpected
622                     && (expectedResult != null && !expectedResult.equals(result)
623                     || expectedResult == null && result != null)) {
624                 String msg = String.format("Conversion result %s is not equal"
625                         + " to the expected result %10s",
626                         result, expectedResult);
627                 throw new AssertionError(msg);
628             }
629             if (VERBOSE) {
630                 String resultStr;
631                 if (result != null) {
632                     resultStr = String.format("Converted value and type are"
633                             + " %10s (%10s)", "'" + result + "'",
634                             result.getClass().getSimpleName());
635                 } else {
636                     resultStr = String.format("Converted value is %10s", result);
637                 }
638                 System.out.println(resultStr);
639             }
640             if (failureExpected) {
641                 String msg = String.format("No exception thrown while testing"
642                         + " return value conversion: %10s => %10s;"
643                         + " parameter: %10s",
644                         from, to, param);
645                 throw new AssertionError(msg);
646             }
647         } catch (AssertionError e) {
648             throw e; // report test failure
649         } catch (Throwable e) {
650             if (VERBOSE) {
651                 System.out.printf("%s: %s\n", e.getClass(), e.getMessage());
652             }
653             if (!failureExpected || !e.getClass().equals(expectedException)) {
654                 String msg = String.format("Unexpected exception was thrown"
655                         + " while testing return value conversion:"
656                         + " %s => %s; parameter: %s", from, to, param);
657                 throw new AssertionError(msg, e);
658             }
659         }
660     }
661 }
662