ValueConversionsTest.java revision 4183:4732a76af216
1/*
2 * Copyright (c) 2009, 2011, 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
24package test.sun.invoke.util;
25
26import sun.invoke.util.ValueConversions;
27import sun.invoke.util.Wrapper;
28import java.lang.invoke.MethodType;
29import java.lang.invoke.MethodHandle;
30import java.lang.invoke.MethodHandles;
31import java.io.Serializable;
32import java.util.Arrays;
33import java.util.Collections;
34import org.junit.Ignore;
35import org.junit.Test;
36import static org.junit.Assert.*;
37
38/* @test
39 * @summary unit tests for value-type conversion utilities
40 * @ignore This test requires a special compilation environment to access sun.inovke.util.  Run by hand.
41 * @run junit/othervm test.sun.invoke.util.ValueConversionsTest
42 * @run junit/othervm
43 *          -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.START_ARITY=250
44 *              test.sun.invoke.util.ValueConversionsTest
45 */
46
47// This might take a while and burn lots of metadata:
48// @run junit/othervm -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.EXHAUSTIVE=true test.sun.invoke.util.ValueConversionsTest
49
50/**
51 *
52 * @author jrose
53 */
54public class ValueConversionsTest {
55    private static final Class CLASS = ValueConversionsTest.class;
56    private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40);
57    private static final int START_ARITY = Integer.getInteger(CLASS.getSimpleName()+".START_ARITY", 0);
58    private static final boolean EXHAUSTIVE = Boolean.getBoolean(CLASS.getSimpleName()+".EXHAUSTIVE");
59
60    @Test
61    public void testUnbox() throws Throwable {
62        testUnbox(false);
63    }
64
65    @Test
66    public void testUnboxCast() throws Throwable {
67        testUnbox(true);
68    }
69
70    private void testUnbox(boolean doCast) throws Throwable {
71        //System.out.println("unbox");
72        for (Wrapper dst : Wrapper.values()) {
73            //System.out.println(dst);
74            for (Wrapper src : Wrapper.values()) {
75                testUnbox(doCast, dst, src);
76            }
77        }
78    }
79
80    private void testUnbox(boolean doCast, Wrapper dst, Wrapper src) throws Throwable {
81        boolean expectThrow = !doCast && !dst.isConvertibleFrom(src);
82        if (dst == Wrapper.OBJECT || src == Wrapper.OBJECT)  return;  // must have prims
83        if (dst == Wrapper.OBJECT)
84            expectThrow = false;  // everything (even VOID==null here) converts to OBJECT
85        try {
86            for (int n = -5; n < 10; n++) {
87                Object box = src.wrap(n);
88                switch (src) {
89                    case VOID:   assertEquals(box, null); break;
90                    case OBJECT: box = box.toString(); break;
91                    case SHORT:  assertEquals(box.getClass(), Short.class); break;
92                    default:     assertEquals(box.getClass(), src.wrapperType()); break;
93                }
94                MethodHandle unboxer;
95                if (doCast)
96                    unboxer = ValueConversions.unboxCast(dst.primitiveType());
97                else
98                    unboxer = ValueConversions.unbox(dst.primitiveType());
99                Object expResult = (box == null) ? dst.zero() : dst.wrap(box);
100                Object result = null;
101                switch (dst) {
102                    case INT:     result = (int)     unboxer.invokeExact(box); break;
103                    case LONG:    result = (long)    unboxer.invokeExact(box); break;
104                    case FLOAT:   result = (float)   unboxer.invokeExact(box); break;
105                    case DOUBLE:  result = (double)  unboxer.invokeExact(box); break;
106                    case CHAR:    result = (char)    unboxer.invokeExact(box); break;
107                    case BYTE:    result = (byte)    unboxer.invokeExact(box); break;
108                    case SHORT:   result = (short)   unboxer.invokeExact(box); break;
109                    case OBJECT:  result = (Object)  unboxer.invokeExact(box); break;
110                    case BOOLEAN: result = (boolean) unboxer.invokeExact(box); break;
111                    case VOID:    result = null;     unboxer.invokeExact(box); break;
112                }
113                if (expectThrow) {
114                    expResult = "(need an exception)";
115                }
116                assertEquals("(doCast,expectThrow,dst,src,n,box)="+Arrays.asList(doCast,expectThrow,dst,src,n,box),
117                             expResult, result);
118            }
119        } catch (RuntimeException ex) {
120            if (expectThrow)  return;
121            System.out.println("Unexpected throw for (doCast,expectThrow,dst,src)="+Arrays.asList(doCast,expectThrow,dst,src));
122            throw ex;
123        }
124    }
125
126    @Test
127    public void testUnboxRaw() throws Throwable {
128        //System.out.println("unboxRaw");
129        for (Wrapper w : Wrapper.values()) {
130            if (w == Wrapper.OBJECT)  continue;  // skip this; no raw form
131            //System.out.println(w);
132            for (int n = -5; n < 10; n++) {
133                Object box = w.wrap(n);
134                long expResult = w.unwrapRaw(box);
135                Object box2 = w.wrapRaw(expResult);
136                assertEquals(box, box2);
137                MethodHandle unboxer = ValueConversions.unboxRaw(w.primitiveType());
138                long result = -1;
139                switch (w) {
140                    case INT:     result = (int)  unboxer.invokeExact(box); break;
141                    case LONG:    result = (long) unboxer.invokeExact(box); break;
142                    case FLOAT:   result = (int)  unboxer.invokeExact(box); break;
143                    case DOUBLE:  result = (long) unboxer.invokeExact(box); break;
144                    case CHAR:    result = (int)  unboxer.invokeExact(box); break;
145                    case BYTE:    result = (int)  unboxer.invokeExact(box); break;
146                    case SHORT:   result = (int)  unboxer.invokeExact(box); break;
147                    case BOOLEAN: result = (int)  unboxer.invokeExact(box); break;
148                    case VOID:    result = (int)  unboxer.invokeExact(box); break;
149                }
150                assertEquals("(w,n,box)="+Arrays.asList(w,n,box),
151                             expResult, result);
152            }
153        }
154    }
155
156    @Test
157    public void testBox() throws Throwable {
158        //System.out.println("box");
159        for (Wrapper w : Wrapper.values()) {
160            if (w == Wrapper.VOID)  continue;  // skip this; no unboxed form
161            //System.out.println(w);
162            for (int n = -5; n < 10; n++) {
163                Object box = w.wrap(n);
164                MethodHandle boxer = ValueConversions.box(w.primitiveType());
165                Object expResult = box;
166                Object result = null;
167                switch (w) {
168                    case INT:     result = boxer.invokeExact((int)n); break;
169                    case LONG:    result = boxer.invokeExact((long)n); break;
170                    case FLOAT:   result = boxer.invokeExact((float)n); break;
171                    case DOUBLE:  result = boxer.invokeExact((double)n); break;
172                    case CHAR:    result = boxer.invokeExact((char)n); break;
173                    case BYTE:    result = boxer.invokeExact((byte)n); break;
174                    case SHORT:   result = boxer.invokeExact((short)n); break;
175                    case OBJECT:  result = boxer.invokeExact((Object)n); break;
176                    case BOOLEAN: result = boxer.invokeExact((n & 1) != 0); break;
177                }
178                assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
179                             expResult, result);
180            }
181        }
182    }
183
184    @Test
185    public void testBoxRaw() throws Throwable {
186        //System.out.println("boxRaw");
187        for (Wrapper w : Wrapper.values()) {
188            if (w == Wrapper.VOID)  continue;  // skip this; no unboxed form
189            if (w == Wrapper.OBJECT)  continue;  // skip this; no raw form
190            //System.out.println(w);
191            for (int n = -5; n < 10; n++) {
192                Object box = w.wrap(n);
193                long   raw = w.unwrapRaw(box);
194                Object expResult = box;
195                MethodHandle boxer = ValueConversions.boxRaw(w.primitiveType());
196                Object result = null;
197                switch (w) {
198                case INT:     result = boxer.invokeExact((int)raw); break;
199                case LONG:    result = boxer.invokeExact(raw); break;
200                case FLOAT:   result = boxer.invokeExact((int)raw); break;
201                case DOUBLE:  result = boxer.invokeExact(raw); break;
202                case CHAR:    result = boxer.invokeExact((int)raw); break;
203                case BYTE:    result = boxer.invokeExact((int)raw); break;
204                case SHORT:   result = boxer.invokeExact((int)raw); break;
205                case BOOLEAN: result = boxer.invokeExact((int)raw); break;
206                }
207                assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
208                             expResult, result);
209            }
210        }
211    }
212
213    @Test
214    public void testReboxRaw() throws Throwable {
215        //System.out.println("reboxRaw");
216        for (Wrapper w : Wrapper.values()) {
217            Wrapper pw = Wrapper.forPrimitiveType(w.rawPrimitiveType());
218            if (w == Wrapper.VOID)  continue;  // skip this; no unboxed form
219            if (w == Wrapper.OBJECT)  continue;  // skip this; no raw form
220            //System.out.println(w);
221            for (int n = -5; n < 10; n++) {
222                Object box = w.wrap(n);
223                Object raw = pw.wrap(w.unwrapRaw(box));
224                Object expResult = box;
225                MethodHandle boxer = ValueConversions.rebox(w.primitiveType());
226                Object result = null;
227                switch (w) {
228                case INT:     result = boxer.invokeExact(raw); break;
229                case LONG:    result = boxer.invokeExact(raw); break;
230                case FLOAT:   result = boxer.invokeExact(raw); break;
231                case DOUBLE:  result = boxer.invokeExact(raw); break;
232                case CHAR:    result = boxer.invokeExact(raw); break;
233                case BYTE:    result = boxer.invokeExact(raw); break;
234                case SHORT:   result = boxer.invokeExact(raw); break;
235                case BOOLEAN: result = boxer.invokeExact(raw); break;
236                }
237                assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
238                             expResult, result);
239            }
240        }
241    }
242
243    @Test
244    public void testCast() throws Throwable {
245        //System.out.println("cast");
246        Class<?>[] types = { Object.class, Serializable.class, String.class, Number.class, Integer.class };
247        Object[] objects = { new Object(), Boolean.FALSE,      "hello",      (Long)12L,    (Integer)6    };
248        for (Class<?> dst : types) {
249            MethodHandle caster = ValueConversions.cast(dst);
250            assertEquals(caster.type(), ValueConversions.identity().type());
251            for (Object obj : objects) {
252                Class<?> src = obj.getClass();
253                boolean canCast;
254                if (dst.isInterface()) {
255                    canCast = true;
256                } else {
257                    canCast = dst.isAssignableFrom(src);
258                    assertEquals(canCast, dst.isInstance(obj));
259                }
260                //System.out.println("obj="+obj+" <: dst="+dst);
261                try {
262                    Object result = caster.invokeExact(obj);
263                    if (canCast)
264                        assertEquals(obj, result);
265                    else
266                        assertEquals("cast should not have succeeded", dst, obj);
267                } catch (ClassCastException ex) {
268                    if (canCast)
269                        throw ex;
270                }
271            }
272        }
273    }
274
275    @Test
276    public void testIdentity() throws Throwable {
277        //System.out.println("identity");
278        MethodHandle id = ValueConversions.identity();
279        Object expResult = "foo";
280        Object result = id.invokeExact(expResult);
281        // compiler bug:  ValueConversions.identity().invokeExact("bar");
282        assertEquals(expResult, result);
283    }
284
285    @Test
286    public void testVarargsArray() throws Throwable {
287        //System.out.println("varargsArray");
288        final int MIN = START_ARITY;
289        final int MAX = MAX_ARITY-2;  // 253+1 would cause parameter overflow with 'this' added
290        for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 17, MAX)) {
291            MethodHandle target = ValueConversions.varargsArray(nargs);
292            Object[] args = new Object[nargs];
293            for (int i = 0; i < nargs; i++)
294                args[i] = "#"+i;
295            Object res = target.invokeWithArguments(args);
296            assertArrayEquals(args, (Object[])res);
297        }
298    }
299
300    @Test
301    public void testVarargsReferenceArray() throws Throwable {
302        //System.out.println("varargsReferenceArray");
303        testTypedVarargsArray(Object[].class);
304        testTypedVarargsArray(String[].class);
305        testTypedVarargsArray(Number[].class);
306    }
307
308    @Test
309    public void testVarargsPrimitiveArray() throws Throwable {
310        //System.out.println("varargsPrimitiveArray");
311        testTypedVarargsArray(int[].class);
312        testTypedVarargsArray(long[].class);
313        testTypedVarargsArray(byte[].class);
314        testTypedVarargsArray(boolean[].class);
315        testTypedVarargsArray(short[].class);
316        testTypedVarargsArray(char[].class);
317        testTypedVarargsArray(float[].class);
318        testTypedVarargsArray(double[].class);
319    }
320
321    private static int nextArgCount(int nargs, int density, int MAX) {
322        if (EXHAUSTIVE)  return nargs + 1;
323        if (nargs >= MAX)  return Integer.MAX_VALUE;
324        int BOT = 20, TOP = MAX-5;
325        if (density < 10) { BOT = 10; MAX = TOP-2; }
326        if (nargs <= BOT || nargs >= TOP) {
327            ++nargs;
328        } else {
329            int bump = Math.max(1, 100 / density);
330            nargs += bump;
331            if (nargs > TOP)  nargs = TOP;
332        }
333        return nargs;
334    }
335
336    private void testTypedVarargsArray(Class<?> arrayType) throws Throwable {
337        System.out.println(arrayType.getSimpleName());
338        Class<?> elemType = arrayType.getComponentType();
339        int MIN = START_ARITY;
340        int MAX = MAX_ARITY-2;  // 253+1 would cause parameter overflow with 'this' added
341        int density = 3;
342        if (elemType == int.class || elemType == long.class)  density = 7;
343        if (elemType == long.class || elemType == double.class) { MAX /= 2; MIN /= 2; }
344        for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, density, MAX)) {
345            Object[] args = makeTestArray(elemType, nargs);
346            MethodHandle varargsArray = ValueConversions.varargsArray(arrayType, nargs);
347            MethodType vaType = varargsArray.type();
348            assertEquals(arrayType, vaType.returnType());
349            if (nargs != 0) {
350                assertEquals(elemType, vaType.parameterType(0));
351                assertEquals(elemType, vaType.parameterType(vaType.parameterCount()-1));
352            }
353            assertEquals(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)),
354                         vaType);
355            Object res = varargsArray.invokeWithArguments(args);
356            String resString = toArrayString(res);
357            assertEquals(Arrays.toString(args), resString);
358
359            MethodHandle spreader = varargsArray.asSpreader(arrayType, nargs);
360            MethodType stype = spreader.type();
361            assert(stype == MethodType.methodType(arrayType, arrayType));
362            if (nargs <= 5) {
363                // invoke target as a spreader also:
364                Object res2 = spreader.invokeWithArguments((Object)res);
365                String res2String = toArrayString(res2);
366                assertEquals(Arrays.toString(args), res2String);
367                // invoke the spreader on a generic Object[] array; check for error
368                try {
369                    Object res3 = spreader.invokeWithArguments((Object)args);
370                    String res3String = toArrayString(res3);
371                    assertTrue(arrayType.getName(), arrayType.isAssignableFrom(Object[].class));
372                    assertEquals(Arrays.toString(args), res3String);
373                } catch (ClassCastException ex) {
374                    assertFalse(arrayType.getName(), arrayType.isAssignableFrom(Object[].class));
375                }
376            }
377            if (nargs == 0) {
378                // invoke spreader on null arglist
379                Object res3 = spreader.invokeWithArguments((Object)null);
380                String res3String = toArrayString(res3);
381                assertEquals(Arrays.toString(args), res3String);
382            }
383        }
384    }
385
386    private static Object[] makeTestArray(Class<?> elemType, int len) {
387        Wrapper elem = null;
388        if (elemType.isPrimitive())
389            elem = Wrapper.forPrimitiveType(elemType);
390        else if (Wrapper.isWrapperType(elemType))
391            elem = Wrapper.forWrapperType(elemType);
392        Object[] args = new Object[len];
393        for (int i = 0; i < len; i++) {
394            Object arg = i * 100;
395            if (elem == null) {
396                if (elemType == String.class)
397                    arg = "#"+arg;
398                arg = elemType.cast(arg);  // just to make sure
399            } else {
400                switch (elem) {
401                    case BOOLEAN: arg = (i % 3 == 0);           break;
402                    case CHAR:    arg = 'a' + i;                break;
403                    case LONG:    arg = (long)i * 1000_000_000; break;
404                    case FLOAT:   arg = (float)i / 100;         break;
405                    case DOUBLE:  arg = (double)i / 1000_000;   break;
406                }
407                arg = elem.cast(arg, elemType);
408            }
409            args[i] = arg;
410        }
411        //System.out.println(elemType.getName()+Arrays.toString(args));
412        return args;
413    }
414
415    private static String toArrayString(Object a) {
416        if (a == null)  return "null";
417        Class<?> elemType = a.getClass().getComponentType();
418        if (elemType == null)  return a.toString();
419        if (elemType.isPrimitive()) {
420            switch (Wrapper.forPrimitiveType(elemType)) {
421                case INT:      return Arrays.toString((int[])a);
422                case BYTE:     return Arrays.toString((byte[])a);
423                case BOOLEAN:  return Arrays.toString((boolean[])a);
424                case SHORT:    return Arrays.toString((short[])a);
425                case CHAR:     return Arrays.toString((char[])a);
426                case FLOAT:    return Arrays.toString((float[])a);
427                case LONG:     return Arrays.toString((long[])a);
428                case DOUBLE:   return Arrays.toString((double[])a);
429            }
430        }
431        return Arrays.toString((Object[])a);
432    }
433
434    @Test
435    public void testVarargsList() throws Throwable {
436        //System.out.println("varargsList");
437        final int MIN = START_ARITY;
438        final int MAX = MAX_ARITY-2;  // 253+1 would cause parameter overflow with 'this' added
439        for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 7, MAX)) {
440            MethodHandle target = ValueConversions.varargsList(nargs);
441            Object[] args = new Object[nargs];
442            for (int i = 0; i < nargs; i++)
443                args[i] = "#"+i;
444            Object res = target.invokeWithArguments(args);
445            assertEquals(Arrays.asList(args), res);
446        }
447    }
448}
449