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