|  | /* | 
|  | * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. | 
|  | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
|  | * | 
|  | * This code is free software; you can redistribute it and/or modify it | 
|  | * under the terms of the GNU General Public License version 2 only, as | 
|  | * published by the Free Software Foundation. | 
|  | * | 
|  | * This code is distributed in the hope that it will be useful, but WITHOUT | 
|  | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|  | * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
|  | * version 2 for more details (a copy is included in the LICENSE file that | 
|  | * accompanied this code). | 
|  | * | 
|  | * You should have received a copy of the GNU General Public License version | 
|  | * 2 along with this work; if not, write to the Free Software Foundation, | 
|  | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
|  | * | 
|  | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
|  | * or visit www.oracle.com if you need additional information or have any | 
|  | * questions. | 
|  | */ | 
|  |  | 
|  | import com.oracle.testlibrary.jsr292.Helper; | 
|  | import java.io.File; | 
|  | import java.io.Serializable; | 
|  | import java.lang.invoke.MethodHandle; | 
|  | import java.lang.invoke.MethodHandles; | 
|  | import java.lang.invoke.MethodType; | 
|  | import java.lang.invoke.WrongMethodTypeException; | 
|  | import java.util.HashMap; | 
|  | import java.util.Map; | 
|  | import java.util.Random; | 
|  | import sun.invoke.util.Wrapper; | 
|  |  | 
|  | /* | 
|  | * @test | 
|  | * @bug 8060483 8066746 | 
|  | * @library /lib/testlibrary /lib/testlibrary/jsr292 | 
|  | * @summary unit tests for MethodHandles.explicitCastArguments() | 
|  | * @run main ExplicitCastArgumentsTest | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * Tests for MethodHandles.explicitCastArguments(). | 
|  | */ | 
|  | public class ExplicitCastArgumentsTest { | 
|  |  | 
|  | private static final boolean VERBOSE = Helper.IS_VERBOSE; | 
|  | private static final Class<?> THIS_CLASS = ExplicitCastArgumentsTest.class; | 
|  | private static final Random RNG = Helper.RNG; | 
|  | private static final Map<Wrapper, Object> RANDOM_VALUES = new HashMap<>(9); | 
|  |  | 
|  | static { | 
|  | RANDOM_VALUES.put(Wrapper.BOOLEAN, RNG.nextBoolean()); | 
|  | RANDOM_VALUES.put(Wrapper.BYTE, (byte) RNG.nextInt()); | 
|  | RANDOM_VALUES.put(Wrapper.SHORT, (short) RNG.nextInt()); | 
|  | RANDOM_VALUES.put(Wrapper.CHAR, (char) RNG.nextInt()); | 
|  | RANDOM_VALUES.put(Wrapper.INT, RNG.nextInt()); | 
|  | RANDOM_VALUES.put(Wrapper.LONG, RNG.nextLong()); | 
|  | RANDOM_VALUES.put(Wrapper.FLOAT, RNG.nextFloat()); | 
|  | RANDOM_VALUES.put(Wrapper.DOUBLE, RNG.nextDouble()); | 
|  | RANDOM_VALUES.put(Wrapper.OBJECT, new Object()); | 
|  | } | 
|  |  | 
|  | public static void main(String[] args) throws Throwable { | 
|  | testVarargsCollector(); | 
|  | testNullRef2Prim(); | 
|  | testRef2Prim(); | 
|  | testPrim2Ref(); | 
|  | testPrim2Prim(); | 
|  | testNonBCPRef2NonBCPRef(); | 
|  | testBCPRef2BCPRef(); | 
|  | testNonBCPRef2BCPRef(); | 
|  | testReturnAny2Void(); | 
|  | testReturnVoid2Any(); | 
|  | testMultipleArgs(); | 
|  | System.out.println("TEST PASSED"); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Dummy method used in {@link #testVarargsCollector} test to form a method | 
|  | * handle. | 
|  | * | 
|  | * @param args - any args | 
|  | * @return - returns args | 
|  | */ | 
|  | public static String[] f(String... args) { | 
|  | return args; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Tests that MHs.explicitCastArguments does incorrect type checks for | 
|  | * VarargsCollector. Bug 8066746. | 
|  | * | 
|  | * @throws java.lang.Throwable | 
|  | */ | 
|  | public static void testVarargsCollector() throws Throwable { | 
|  | MethodType mt = MethodType.methodType(String[].class, String[].class); | 
|  | MethodHandle mh = MethodHandles.publicLookup() | 
|  | .findStatic(THIS_CLASS, "f", mt); | 
|  | mh = MethodHandles.explicitCastArguments(mh, | 
|  | MethodType.methodType(Object.class, Object.class)); | 
|  | mh.invokeWithArguments((Object) (new String[]{"str1", "str2"})); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Tests that null wrapper reference is successfully converted to primitive | 
|  | * types. Converted result should be zero for a primitive. Bug 8060483. | 
|  | */ | 
|  | public static void testNullRef2Prim() { | 
|  | for (Wrapper from : Wrapper.values()) { | 
|  | for (Wrapper to : Wrapper.values()) { | 
|  | if (from == Wrapper.VOID || to == Wrapper.VOID) { | 
|  | continue; | 
|  | } | 
|  | // MHs.eCA javadoc: | 
|  | //    If T0 is a reference and T1 a primitive, and if the reference | 
|  | //    is null at runtime, a zero value is introduced. | 
|  | for (TestConversionMode mode : TestConversionMode.values()) { | 
|  | testConversion(mode, from.wrapperType(), | 
|  | to.primitiveType(), null, to.zero(), false, null); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Tests that non-null wrapper reference is successfully converted to | 
|  | * primitive types. | 
|  | */ | 
|  | public static void testRef2Prim() { | 
|  | for (Wrapper from : Wrapper.values()) { | 
|  | for (Wrapper to : Wrapper.values()) { | 
|  | if (from == Wrapper.VOID || to == Wrapper.VOID | 
|  | || to == Wrapper.OBJECT) { | 
|  | continue; | 
|  | } | 
|  | Object value = RANDOM_VALUES.get(from); | 
|  | for (TestConversionMode mode : TestConversionMode.values()) { | 
|  | if (from != Wrapper.OBJECT) { | 
|  | Object convValue = to.wrap(value); | 
|  | testConversion(mode, from.wrapperType(), | 
|  | to.primitiveType(), value, convValue, false, null); | 
|  | } else { | 
|  | testConversion(mode, from.wrapperType(), | 
|  | to.primitiveType(), value, null, | 
|  | true, ClassCastException.class); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Tests that primitive is successfully converted to wrapper reference | 
|  | * types, to the Number type (if possible) and to the Object type. | 
|  | */ | 
|  | public static void testPrim2Ref() { | 
|  | for (Wrapper from : Wrapper.values()) { | 
|  | for (Wrapper to : Wrapper.values()) { | 
|  | if (from == Wrapper.VOID || from == Wrapper.OBJECT | 
|  | || to == Wrapper.VOID || to == Wrapper.OBJECT) { | 
|  | continue; | 
|  | } | 
|  | Object value = RANDOM_VALUES.get(from); | 
|  | for (TestConversionMode mode : TestConversionMode.values()) { | 
|  | if (from == to) { | 
|  | testConversion(mode, from.primitiveType(), | 
|  | to.wrapperType(), value, value, false, null); | 
|  | } else { | 
|  | testConversion(mode, from.primitiveType(), | 
|  | to.wrapperType(), value, null, true, ClassCastException.class); | 
|  | } | 
|  | if (from != Wrapper.BOOLEAN && from != Wrapper.CHAR) { | 
|  | testConversion(mode, from.primitiveType(), | 
|  | Number.class, value, value, false, null); | 
|  | } else { | 
|  | testConversion(mode, from.primitiveType(), | 
|  | Number.class, value, null, | 
|  | true, ClassCastException.class); | 
|  | } | 
|  | testConversion(mode, from.primitiveType(), | 
|  | Object.class, value, value, false, null); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Tests that primitive is successfully converted to other primitive type. | 
|  | */ | 
|  | public static void testPrim2Prim() { | 
|  | for (Wrapper from : Wrapper.values()) { | 
|  | for (Wrapper to : Wrapper.values()) { | 
|  | if (from == Wrapper.VOID || to == Wrapper.VOID | 
|  | || from == Wrapper.OBJECT || to == Wrapper.OBJECT) { | 
|  | continue; | 
|  | } | 
|  | Object value = RANDOM_VALUES.get(from); | 
|  | Object convValue = to.wrap(value); | 
|  | for (TestConversionMode mode : TestConversionMode.values()) { | 
|  | testConversion(mode, from.primitiveType(), | 
|  | to.primitiveType(), value, convValue, false, null); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Dummy interface for {@link #testNonBCPRef2Ref} test. | 
|  | */ | 
|  | public static interface TestInterface {} | 
|  |  | 
|  | /** | 
|  | * Dummy class for {@link #testNonBCPRef2Ref} test. | 
|  | */ | 
|  | public static class TestSuperClass implements TestInterface {} | 
|  |  | 
|  | /** | 
|  | * Dummy class for {@link #testNonBCPRef2Ref} test. | 
|  | */ | 
|  | public static class TestSubClass1 extends TestSuperClass {} | 
|  |  | 
|  | /** | 
|  | * Dummy class for {@link #testNonBCPRef2Ref} test. | 
|  | */ | 
|  | public static class TestSubClass2 extends TestSuperClass {} | 
|  |  | 
|  | /** | 
|  | * Tests non-bootclasspath reference to reference conversions. | 
|  | * | 
|  | * @throws java.lang.Throwable | 
|  | */ | 
|  | public static void testNonBCPRef2NonBCPRef() throws Throwable { | 
|  | Class testInterface = TestInterface.class; | 
|  | Class testSuperClass = TestSuperClass.class; | 
|  | Class testSubClass1 = TestSubClass1.class; | 
|  | Class testSubClass2 = TestSubClass2.class; | 
|  | Object testSuperObj = new TestSuperClass(); | 
|  | Object testObj01 = new TestSubClass1(); | 
|  | Object testObj02 = new TestSubClass2(); | 
|  | Class[] parents = {testInterface, testSuperClass}; | 
|  | Class[] children = {testSubClass1, testSubClass2}; | 
|  | Object[] childInst = {testObj01, testObj02}; | 
|  | for (TestConversionMode mode : TestConversionMode.values()) { | 
|  | for (Class parent : parents) { | 
|  | for (int j = 0; j < children.length; j++) { | 
|  | // Child type to parent type non-null conversion, shoud succeed | 
|  | testConversion(mode, children[j], parent, childInst[j], childInst[j], false, null); | 
|  | // Child type to parent type null conversion, shoud succeed | 
|  | testConversion(mode, children[j], parent, null, null, false, null); | 
|  | // Parent type to child type non-null conversion with parent | 
|  | // type instance, should fail | 
|  | testConversion(mode, parent, children[j], testSuperObj, null, true, ClassCastException.class); | 
|  | // Parent type to child type non-null conversion with child | 
|  | // type instance, should succeed | 
|  | testConversion(mode, parent, children[j], childInst[j], childInst[j], false, null); | 
|  | // Parent type to child type null conversion, should succeed | 
|  | testConversion(mode, parent, children[j], null, null, false, null); | 
|  | } | 
|  | // Parent type to child type non-null conversion with sibling | 
|  | // type instance, should fail | 
|  | testConversion(mode, parent, testSubClass1, testObj02, null, true, ClassCastException.class); | 
|  | } | 
|  | // Sibling type non-null conversion, should fail | 
|  | testConversion(mode, testSubClass1, | 
|  | testSubClass2, testObj01, null, true, | 
|  | ClassCastException.class); | 
|  | // Sibling type null conversion, should succeed | 
|  | testConversion(mode, testSubClass1, | 
|  | testSubClass2, null, null, false, null); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Dummy interface for {@link #testNonBCPRef2BCPRef} test. | 
|  | */ | 
|  | public static interface TestSerializableInterface extends Serializable {} | 
|  |  | 
|  | /** | 
|  | * Dummy class for {@link #testNonBCPRef2BCPRef} test. | 
|  | */ | 
|  | public static class TestSerializableClass | 
|  | implements TestSerializableInterface {} | 
|  |  | 
|  | /** | 
|  | * Dummy class for {@link #testNonBCPRef2BCPRef} test. | 
|  | */ | 
|  | public static class TestFileChildClass extends File | 
|  | implements TestSerializableInterface { | 
|  | public TestFileChildClass(String pathname) { | 
|  | super(pathname); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Tests non-bootclasspath reference to bootclasspath reference conversions | 
|  | * and vice-versa. | 
|  | * | 
|  | * @throws java.lang.Throwable | 
|  | */ | 
|  | public static void testNonBCPRef2BCPRef() throws Throwable { | 
|  | Class bcpInterface = Serializable.class; | 
|  | Class bcpSuperClass = File.class; | 
|  | Class nonBcpInterface = TestSerializableInterface.class; | 
|  | Class nonBcpSuperSiblingClass = TestSerializableClass.class; | 
|  | Class nonBcpSubClass = TestFileChildClass.class; | 
|  | Object bcpSuperObj = new File("."); | 
|  | Object testSuperSiblingObj = new TestSerializableClass(); | 
|  | Object testSubObj = new TestFileChildClass("."); | 
|  | Class[] parents = {bcpInterface, bcpSuperClass}; | 
|  | for (TestConversionMode mode : TestConversionMode.values()) { | 
|  | for (Class parent : parents) { | 
|  | // Child type to parent type non-null conversion, shoud succeed | 
|  | testConversion(mode, nonBcpSubClass, parent, testSubObj, | 
|  | testSubObj, false, null); | 
|  | // Child type to parent type null conversion, shoud succeed | 
|  | testConversion(mode, nonBcpSubClass, parent, null, null, | 
|  | false, null); | 
|  | // Parent type to child type non-null conversion with parent | 
|  | // type instance, should fail | 
|  | testConversion(mode, parent, nonBcpSubClass, bcpSuperObj, null, | 
|  | true, ClassCastException.class); | 
|  | // Parent type to child type non-null conversion with child | 
|  | // type instance, should succeed | 
|  | testConversion(mode, parent, nonBcpSubClass, testSubObj, | 
|  | testSubObj, false, null); | 
|  | // Parent type to child type null conversion, should succeed | 
|  | testConversion(mode, parent, nonBcpSubClass, null, null, | 
|  | false, null); | 
|  | } | 
|  | // Parent type to child type non-null conversion with | 
|  | // super sibling type instance, should fail | 
|  | testConversion(mode, bcpInterface, nonBcpSubClass, | 
|  | testSuperSiblingObj, null, true, ClassCastException.class); | 
|  | Class[] siblings = {nonBcpSubClass, bcpSuperClass}; | 
|  | for (Class sibling : siblings) { | 
|  | // Non-bcp class to bcp/non-bcp sibling class non-null | 
|  | // conversion with nonBcpSuperSiblingClass instance, should fail | 
|  | testConversion(mode, nonBcpSuperSiblingClass, sibling, | 
|  | testSuperSiblingObj, null, true, ClassCastException.class); | 
|  | // Non-bcp class to bcp/non-bcp sibling class null conversion, | 
|  | // should succeed | 
|  | testConversion(mode, nonBcpSuperSiblingClass, sibling, | 
|  | null, null, false, null); | 
|  | // Non-bcp interface to bcp/non-bcp sibling class non-null | 
|  | // conversion with nonBcpSubClass instance, should succeed | 
|  | testConversion(mode, nonBcpInterface, sibling, testSubObj, | 
|  | testSubObj, false, null); | 
|  | // Non-bcp interface to bcp/non-bcp sibling class | 
|  | // null conversion, should succeed | 
|  | testConversion(mode, nonBcpInterface, sibling, null, null, | 
|  | false, null); | 
|  | // Non-bcp interface to bcp/non-bcp sibling class non-null | 
|  | // conversion with nonBcpSuperSiblingClass instance, should fail | 
|  | testConversion(mode, nonBcpInterface, sibling, | 
|  | testSuperSiblingObj, testSubObj, | 
|  | true, ClassCastException.class); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Tests bootclasspath reference to reference conversions. | 
|  | */ | 
|  | public static void testBCPRef2BCPRef() { | 
|  | Class bcpInterface = CharSequence.class; | 
|  | Class bcpSubClass1 = String.class; | 
|  | Class bcpSubClass2 = StringBuffer.class; | 
|  | Object testObj01 = new String("test"); | 
|  | Object testObj02 = new StringBuffer("test"); | 
|  | Class[] children = {bcpSubClass1, bcpSubClass2}; | 
|  | Object[] childInst = {testObj01, testObj02}; | 
|  | for (TestConversionMode mode : TestConversionMode.values()) { | 
|  | for (int i = 0; i < children.length; i++) { | 
|  | // Child type to parent type non-null conversion, shoud succeed | 
|  | testConversion(mode, children[i], bcpInterface, childInst[i], | 
|  | childInst[i], false, null); | 
|  | // Child type to parent type null conversion, shoud succeed | 
|  | testConversion(mode, children[i], bcpInterface, null, | 
|  | null, false, null); | 
|  | // Parent type to child type non-null conversion with child | 
|  | // type instance, should succeed | 
|  | testConversion(mode, bcpInterface, | 
|  | children[i], childInst[i], childInst[i], false, null); | 
|  | // Parent type to child type null conversion, should succeed | 
|  | testConversion(mode, bcpInterface, | 
|  | children[i], null, null, false, null); | 
|  | } | 
|  | // Sibling type non-null conversion, should fail | 
|  | testConversion(mode, bcpSubClass1, | 
|  | bcpSubClass2, testObj01, null, true, | 
|  | ClassCastException.class); | 
|  | // Sibling type null conversion, should succeed | 
|  | testConversion(mode, bcpSubClass1, | 
|  | bcpSubClass2, null, null, false, null); | 
|  | // Parent type to child type non-null conversion with sibling | 
|  | // type instance, should fail | 
|  | testConversion(mode, bcpInterface, bcpSubClass1, testObj02, | 
|  | null, true, ClassCastException.class); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Dummy method used in {@link #testReturnAny2Void} and | 
|  | * {@link #testReturnVoid2Any} tests to form a method handle. | 
|  | */ | 
|  | public static void retVoid() {} | 
|  |  | 
|  | /** | 
|  | * Tests that non-null any return is successfully converted to non-type | 
|  | * void. | 
|  | */ | 
|  | public static void testReturnAny2Void() { | 
|  | for (Wrapper from : Wrapper.values()) { | 
|  | testConversion(TestConversionMode.RETURN_VALUE, from.wrapperType(), | 
|  | void.class, RANDOM_VALUES.get(from), | 
|  | null, false, null); | 
|  | testConversion(TestConversionMode.RETURN_VALUE, from.primitiveType(), | 
|  | void.class, RANDOM_VALUES.get(from), | 
|  | null, false, null); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Tests that void return is successfully converted to primitive and | 
|  | * reference. Result should be zero for primitives and null for references. | 
|  | */ | 
|  | public static void testReturnVoid2Any() { | 
|  | for (Wrapper to : Wrapper.values()) { | 
|  | testConversion(TestConversionMode.RETURN_VALUE, void.class, | 
|  | to.primitiveType(), null, | 
|  | to.zero(), false, null); | 
|  | testConversion(TestConversionMode.RETURN_VALUE, void.class, | 
|  | to.wrapperType(), null, | 
|  | null, false, null); | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void checkForWrongMethodTypeException(MethodHandle mh, MethodType mt) { | 
|  | try { | 
|  | MethodHandles.explicitCastArguments(mh, mt); | 
|  | throw new AssertionError("Expected WrongMethodTypeException is not thrown"); | 
|  | } catch (WrongMethodTypeException wmte) { | 
|  | if (VERBOSE) { | 
|  | System.out.printf("Expected exception %s: %s\n", | 
|  | wmte.getClass(), wmte.getMessage()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Tests that MHs.eCA method works correctly with MHs with multiple arguments. | 
|  | * @throws Throwable | 
|  | */ | 
|  | public static void testMultipleArgs() throws Throwable { | 
|  | int arity = 1 + RNG.nextInt(Helper.MAX_ARITY / 2 - 2); | 
|  | int arityMinus = RNG.nextInt(arity); | 
|  | int arityPlus = arity + RNG.nextInt(Helper.MAX_ARITY / 2 - arity) + 1; | 
|  | MethodType mType = Helper.randomMethodTypeGenerator(arity); | 
|  | MethodType mTypeNew = Helper.randomMethodTypeGenerator(arity); | 
|  | MethodType mTypeNewMinus = Helper.randomMethodTypeGenerator(arityMinus); | 
|  | MethodType mTypeNewPlus = Helper.randomMethodTypeGenerator(arityPlus); | 
|  | Class<?> rType = mType.returnType(); | 
|  | MethodHandle original; | 
|  | if (rType.equals(void.class)) { | 
|  | MethodType mt = MethodType.methodType(void.class); | 
|  | original = MethodHandles.publicLookup() | 
|  | .findStatic(THIS_CLASS, "retVoid", mt); | 
|  | } else { | 
|  | Object rValue = Helper.castToWrapper(1, rType); | 
|  | original = MethodHandles.constant(rType, rValue); | 
|  | } | 
|  | original = Helper.addTrailingArgs(original, arity, mType.parameterList()); | 
|  | MethodHandle target = MethodHandles | 
|  | .explicitCastArguments(original, mTypeNew); | 
|  | Object[] parList = Helper.randomArgs(mTypeNew.parameterList()); | 
|  | for (int i = 0; i < parList.length; i++) { | 
|  | if (parList[i] instanceof String) { | 
|  | parList[i] = null; //getting rid of Stings produced by randomArgs | 
|  | } | 
|  | } | 
|  | target.invokeWithArguments(parList); | 
|  | checkForWrongMethodTypeException(original, mTypeNewMinus); | 
|  | checkForWrongMethodTypeException(original, mTypeNewPlus); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Enumeration of test conversion modes. | 
|  | */ | 
|  | public enum TestConversionMode { | 
|  | RETURN_VALUE, | 
|  | ARGUMENT; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Tests type and value conversion. Comparing with the given expected result. | 
|  | * | 
|  | * @param mode - test conversion mode. See {@link #TestConversionMode}. | 
|  | * @param from - source type. | 
|  | * @param to - destination type. | 
|  | * @param param - value to be converted. | 
|  | * @param expectedResult - expected value after conversion. | 
|  | * @param failureExpected - true if conversion failure expected. | 
|  | * @param expectedException - expected exception class if | 
|  | * {@code failureExpected} is true. | 
|  | */ | 
|  | public static void testConversion(TestConversionMode mode, | 
|  | Class<?> from, Class<?> to, Object param, | 
|  | Object expectedResult, boolean failureExpected, | 
|  | Class<? extends Throwable> expectedException) { | 
|  | if (VERBOSE) { | 
|  | System.out.printf("Testing return value conversion: " | 
|  | + "%-10s => %-10s: %5s: ", from.getSimpleName(), | 
|  | to.getSimpleName(), param); | 
|  | } | 
|  | MethodHandle original = null; | 
|  | MethodType newType = null; | 
|  | switch (mode) { | 
|  | case RETURN_VALUE: | 
|  | if (from.equals(void.class)) { | 
|  | MethodType mt = MethodType.methodType(void.class); | 
|  | try { | 
|  | original = MethodHandles.publicLookup() | 
|  | .findStatic(THIS_CLASS, "retVoid", mt); | 
|  | } catch (NoSuchMethodException | IllegalAccessException ex) { | 
|  | throw new Error("Unexpected issue", ex); | 
|  | } | 
|  | } else { | 
|  | original = MethodHandles.constant(from, param); | 
|  | } | 
|  | newType = original.type().changeReturnType(to); | 
|  | break; | 
|  | case ARGUMENT: | 
|  | if (from.equals(void.class) || to.equals(void.class)) { | 
|  | throw new Error("Test issue: argument conversion does not" | 
|  | + " work with non-type void"); | 
|  | } | 
|  | original = MethodHandles.identity(to); | 
|  | newType = original.type().changeParameterType(0, from); | 
|  | break; | 
|  | default: | 
|  | String msg = String.format("Test issue: unknown test" | 
|  | + " convertion mode %s.", mode.name()); | 
|  | throw new Error(msg); | 
|  | } | 
|  | try { | 
|  | MethodHandle target = MethodHandles | 
|  | .explicitCastArguments(original, newType); | 
|  | Object result; | 
|  | switch (mode) { | 
|  | case RETURN_VALUE: | 
|  | result = target.invokeWithArguments(); | 
|  | break; | 
|  | case ARGUMENT: | 
|  | result = target.invokeWithArguments(param); | 
|  | break; | 
|  | default: | 
|  | String msg = String.format("Test issue: unknown test" | 
|  | + " convertion mode %s.", mode.name()); | 
|  | throw new Error(msg); | 
|  | } | 
|  | if (!failureExpected | 
|  | && (expectedResult != null && !expectedResult.equals(result) | 
|  | || expectedResult == null && result != null)) { | 
|  | String msg = String.format("Conversion result %s is not equal" | 
|  | + " to the expected result %10s", | 
|  | result, expectedResult); | 
|  | throw new AssertionError(msg); | 
|  | } | 
|  | if (VERBOSE) { | 
|  | String resultStr; | 
|  | if (result != null) { | 
|  | resultStr = String.format("Converted value and type are" | 
|  | + " %10s (%10s)", "'" + result + "'", | 
|  | result.getClass().getSimpleName()); | 
|  | } else { | 
|  | resultStr = String.format("Converted value is %10s", result); | 
|  | } | 
|  | System.out.println(resultStr); | 
|  | } | 
|  | if (failureExpected) { | 
|  | String msg = String.format("No exception thrown while testing" | 
|  | + " return value conversion: %10s => %10s;" | 
|  | + " parameter: %10s", | 
|  | from, to, param); | 
|  | throw new AssertionError(msg); | 
|  | } | 
|  | } catch (AssertionError e) { | 
|  | throw e; // report test failure | 
|  | } catch (Throwable e) { | 
|  | if (VERBOSE) { | 
|  | System.out.printf("%s: %s\n", e.getClass(), e.getMessage()); | 
|  | } | 
|  | if (!failureExpected || !e.getClass().equals(expectedException)) { | 
|  | String msg = String.format("Unexpected exception was thrown" | 
|  | + " while testing return value conversion:" | 
|  | + " %s => %s; parameter: %s", from, to, param); | 
|  | throw new AssertionError(msg, e); | 
|  | } | 
|  | } | 
|  | } | 
|  | } |