1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package libcore.java.lang.invoke;
18
19import junit.framework.TestCase;
20
21import java.lang.invoke.MethodType;
22import java.util.Arrays;
23import java.util.List;
24
25public class MethodTypeTest extends TestCase {
26    private static final Class<?>[] LARGE_PARAMETER_ARRAY;
27
28    static {
29        LARGE_PARAMETER_ARRAY = new Class<?>[254];
30        for (int i = 0; i < 254; ++i) {
31            LARGE_PARAMETER_ARRAY[i] = Object.class;
32        }
33    }
34
35    public void test_methodType_basicTestsReturnTypeAndParameterClassArray() {
36        MethodType mt = MethodType.methodType(int.class,
37                new Class<?>[] { String.class, long.class});
38
39        assertEquals(int.class, mt.returnType());
40        assertParameterTypes(mt, String.class, long.class);
41
42        try {
43            MethodType.methodType(null, new Class<?>[] { String.class });
44            fail();
45        } catch (NullPointerException expected) {
46        }
47
48        try {
49            MethodType.methodType(int.class, (Class<?>[]) null);
50            fail();
51        } catch (NullPointerException expected) {
52        }
53
54        try {
55            MethodType.methodType(int.class, new Class<?>[] {void.class});
56            fail();
57        } catch (IllegalArgumentException expected) {
58        }
59    }
60
61    public void test_methodType_basicTestsReturnTypeAndParameterClassList() {
62        MethodType mt = MethodType.methodType(int.class, Arrays.asList(String.class, long.class));
63
64        assertEquals(int.class, mt.returnType());
65        assertParameterTypes(mt, String.class, long.class);
66
67        try {
68            MethodType.methodType(null, Arrays.asList(String.class));
69            fail();
70        } catch (NullPointerException expected) {
71        }
72
73        try {
74            MethodType.methodType(int.class, (List<Class<?>>) null);
75            fail();
76        } catch (NullPointerException expected) {
77        }
78
79        try {
80            MethodType.methodType(int.class, Arrays.asList(void.class));
81            fail();
82        } catch (IllegalArgumentException expected) {
83        }
84    }
85
86    public void test_methodType_basicTestsReturnTypeAndVarargsParameters() {
87        MethodType mt = MethodType.methodType(int.class, String.class, long.class);
88
89        assertEquals(int.class, mt.returnType());
90        assertParameterTypes(mt, String.class, long.class);
91
92        try {
93            MethodType.methodType(null, String.class);
94            fail();
95        } catch (NullPointerException expected) {
96        }
97
98        try {
99            MethodType.methodType(int.class, String.class, null);
100            fail();
101        } catch (NullPointerException expected) {
102        }
103
104        try {
105            MethodType.methodType(int.class, void.class, String.class);
106            fail();
107        } catch (IllegalArgumentException expected) {
108        }
109    }
110
111    public void test_methodType_basicTestsReturnTypeOnly() {
112        MethodType mt = MethodType.methodType(int.class);
113
114        assertEquals(int.class, mt.returnType());
115        assertEquals(0, mt.parameterCount());
116
117        try {
118            MethodType.methodType(null);
119            fail();
120        } catch (NullPointerException expected) {
121        }
122    }
123
124    public void test_methodType_basicTestsReturnTypeAndSingleParameter() {
125        MethodType mt = MethodType.methodType(int.class, long.class);
126
127        assertEquals(int.class, mt.returnType());
128        assertParameterTypes(mt, long.class);
129
130        try {
131            MethodType.methodType(null);
132            fail();
133        } catch (NullPointerException expected) {
134        }
135
136        try {
137            MethodType.methodType(null, String.class);
138            fail();
139        } catch (NullPointerException expected) {
140        }
141
142        try {
143            MethodType.methodType(int.class, (Class<?>) null);
144            fail();
145        } catch (NullPointerException expected) {
146        }
147
148        try {
149            MethodType.methodType(int.class, void.class);
150            fail();
151        } catch (IllegalArgumentException expected) {
152        }
153    }
154
155    public void test_methodType_basicTestsReturnTypeAndMethodTypeParameters() {
156        MethodType mt = MethodType.methodType(int.class, long.class, String.class);
157        assertEquals(int.class, mt.returnType());
158
159        MethodType mt2 = MethodType.methodType(long.class, mt);
160
161        assertEquals(long.class, mt2.returnType());
162        assertEquals(long.class, mt2.parameterType(0));
163        assertEquals(String.class, mt2.parameterType(1));
164
165        try {
166            MethodType.methodType(int.class, (MethodType) null);
167            fail();
168        } catch (NullPointerException expected) {
169        }
170    }
171
172    public void testGenericMethodType() {
173        MethodType mt = MethodType.genericMethodType(0);
174        assertEquals(0, mt.parameterCount());
175        assertEquals(Object.class, mt.returnType());
176
177        mt = MethodType.genericMethodType(3);
178        assertEquals(Object.class, mt.returnType());
179
180        assertEquals(3, mt.parameterCount());
181        assertParameterTypes(mt, Object.class, Object.class, Object.class);
182
183        try {
184            MethodType.genericMethodType(-1);
185            fail();
186        } catch (IllegalArgumentException expected) {
187        }
188
189        try {
190            MethodType.genericMethodType(256);
191            fail();
192        } catch (IllegalArgumentException expected) {
193        }
194    }
195
196    public void testGenericMethodTypeWithTrailingArray() {
197        MethodType mt = MethodType.genericMethodType(3, false /* finalArray */);
198        assertEquals(Object.class, mt.returnType());
199        assertParameterTypes(mt, Object.class, Object.class, Object.class);
200
201        mt = MethodType.genericMethodType(0, true /* finalArray */);
202        assertEquals(Object.class, mt.returnType());
203        assertParameterTypes(mt, Object[].class);
204
205        mt = MethodType.genericMethodType(2, true /* finalArray */);
206        assertEquals(Object.class, mt.returnType());
207        assertParameterTypes(mt, Object.class, Object.class, Object[].class);
208
209        try {
210            MethodType.genericMethodType(-1, true);
211            fail();
212        } catch (IllegalArgumentException expected) {
213        }
214
215        try {
216            MethodType.genericMethodType(255, true);
217            fail();
218        } catch (IllegalArgumentException expected) {
219        }
220    }
221
222    public void testChangeParameterType() {
223        // int method(String, Object, List);
224        MethodType mt = MethodType.methodType(int.class, String.class, Object.class, List.class);
225        assertEquals(Object.class, mt.parameterType(1));
226
227        MethodType changed = mt.changeParameterType(1, String.class);
228        assertEquals(String.class, changed.parameterType(1));
229
230        // Assert that the return types and the other parameter types haven't changed.
231        assertEquals(mt.parameterCount(), changed.parameterCount());
232        assertEquals(mt.returnType(), changed.returnType());
233        assertEquals(mt.parameterType(0), changed.parameterType(0));
234        assertEquals(mt.parameterType(2), changed.parameterType(2));
235
236        try {
237            mt.changeParameterType(-1, String.class);
238            fail();
239        } catch (ArrayIndexOutOfBoundsException expected) {
240        }
241
242        try {
243            mt.changeParameterType(3, String.class);
244            fail();
245        } catch (ArrayIndexOutOfBoundsException expected) {
246        }
247
248        try {
249            mt.changeParameterType(1, void.class);
250            fail();
251        } catch (IllegalArgumentException expected) {
252        }
253
254        try {
255            mt.changeParameterType(1, null);
256            fail();
257        } catch (NullPointerException expected) {
258        }
259    }
260
261    public void testInsertParameterTypes_varargs() {
262        MethodType mt = MethodType.methodType(int.class, String.class, Object.class);
263
264        MethodType insert0 = mt.insertParameterTypes(0, Integer.class, Long.class);
265        assertEquals(int.class, insert0.returnType());
266        assertParameterTypes(insert0, Integer.class, Long.class, String.class, Object.class);
267
268        MethodType insert1 = mt.insertParameterTypes(1, Integer.class, Long.class);
269        assertParameterTypes(insert1, String.class, Integer.class, Long.class, Object.class);
270
271        MethodType insert2 = mt.insertParameterTypes(2, Integer.class, Long.class);
272        assertParameterTypes(insert2, String.class, Object.class, Integer.class, Long.class);
273
274        try {
275            mt.insertParameterTypes(1, LARGE_PARAMETER_ARRAY);
276            fail();
277        } catch (IllegalArgumentException expected) {
278        }
279
280        try {
281            mt.insertParameterTypes(1, void.class);
282            fail();
283        } catch (IllegalArgumentException expected) {
284        }
285
286        try {
287            mt.insertParameterTypes(1, (Class<?>) null);
288            fail();
289        } catch (NullPointerException expected) {
290        }
291
292        try {
293            mt.insertParameterTypes(-1, String.class);
294            fail();
295        } catch (IndexOutOfBoundsException expected) {
296        }
297
298        try {
299            mt.insertParameterTypes(3, String.class);
300            fail();
301        } catch (IndexOutOfBoundsException expected) {
302        }
303    }
304
305    public void testInsertParameterTypes_list() {
306        MethodType mt = MethodType.methodType(int.class, String.class, Object.class);
307
308        MethodType insert0 = mt.insertParameterTypes(0, Arrays.asList(Integer.class, Long.class));
309        assertEquals(int.class, insert0.returnType());
310        assertParameterTypes(insert0, Integer.class, Long.class, String.class, Object.class);
311
312        MethodType insert1 = mt.insertParameterTypes(1, Arrays.asList(Integer.class, Long.class));
313        assertParameterTypes(insert1, String.class, Integer.class, Long.class, Object.class);
314
315        MethodType insert2 = mt.insertParameterTypes(2, Arrays.asList(Integer.class, Long.class));
316        assertParameterTypes(insert2, String.class, Object.class, Integer.class, Long.class);
317
318        try {
319            mt.insertParameterTypes(1, Arrays.asList(LARGE_PARAMETER_ARRAY));
320            fail();
321        } catch (IllegalArgumentException expected) {
322        }
323
324        try {
325            mt.insertParameterTypes(1, Arrays.asList(void.class));
326            fail();
327        } catch (IllegalArgumentException expected) {
328        }
329
330        try {
331            mt.insertParameterTypes(1, (List<Class<?>>) null);
332            fail();
333        } catch (NullPointerException expected) {
334        }
335
336        try {
337            mt.insertParameterTypes(1, Arrays.asList(null));
338            fail();
339        } catch (NullPointerException expected) {
340        }
341
342        try {
343            mt.insertParameterTypes(-1, Arrays.asList(String.class));
344            fail();
345        } catch (IndexOutOfBoundsException expected) {
346        }
347
348        try {
349            mt.insertParameterTypes(3, Arrays.asList(String.class));
350            fail();
351        } catch (IndexOutOfBoundsException expected) {
352        }
353    }
354
355    public void testAppendParameterTypes_varargs() {
356        MethodType mt = MethodType.methodType(int.class, String.class, String.class);
357
358        MethodType appended = mt.appendParameterTypes(List.class, Integer.class);
359        assertEquals(int.class, appended.returnType());
360        assertParameterTypes(appended, String.class, String.class, List.class, Integer.class);
361
362        try {
363            mt.appendParameterTypes(LARGE_PARAMETER_ARRAY);
364            fail();
365        } catch (IllegalArgumentException expected) {
366        }
367
368        try {
369            mt.appendParameterTypes(void.class);
370            fail();
371        } catch (IllegalArgumentException expected) {
372        }
373
374        try {
375            mt.appendParameterTypes((Class<?>) null);
376            fail();
377        } catch (NullPointerException expected) {
378        }
379    }
380
381    public void testAppendParameterTypes_list() {
382        MethodType mt = MethodType.methodType(int.class, String.class, String.class);
383
384        MethodType appended = mt.appendParameterTypes(Arrays.asList(List.class, Integer.class));
385        assertEquals(int.class, appended.returnType());
386        assertParameterTypes(appended, String.class, String.class, List.class, Integer.class);
387
388        try {
389            mt.appendParameterTypes(Arrays.asList(LARGE_PARAMETER_ARRAY));
390            fail();
391        } catch (IllegalArgumentException expected) {
392        }
393
394        try {
395            mt.appendParameterTypes(Arrays.asList(void.class));
396            fail();
397        } catch (IllegalArgumentException expected) {
398        }
399
400        try {
401            mt.appendParameterTypes((List<Class<?>>) null);
402            fail();
403        } catch (NullPointerException expected) {
404        }
405
406        try {
407            mt.appendParameterTypes(Arrays.asList(null));
408            fail();
409        } catch (NullPointerException expected) {
410        }
411    }
412
413    public void testDropParameterTypes() {
414        MethodType mt = MethodType.methodType(int.class, String.class, List.class, Object.class);
415
416        MethodType dropNone = mt.dropParameterTypes(0, 0);
417        assertEquals(int.class, dropNone.returnType());
418        assertParameterTypes(dropNone, String.class, List.class, Object.class);
419
420        MethodType dropFirst = mt.dropParameterTypes(0, 1);
421        assertEquals(int.class, dropFirst.returnType());
422        assertParameterTypes(dropFirst, List.class, Object.class);
423
424        MethodType dropAll = mt.dropParameterTypes(0, 3);
425        assertEquals(0, dropAll.parameterCount());
426        assertEquals(int.class, dropAll.returnType());
427
428        try {
429            mt.dropParameterTypes(-1, 1);
430            fail();
431        } catch (IndexOutOfBoundsException expected) {
432        }
433
434        try {
435            mt.dropParameterTypes(1, 4);
436            fail();
437        } catch (IndexOutOfBoundsException expected) {
438        }
439
440        try {
441            mt.dropParameterTypes(2, 1);
442            fail();
443        } catch (IndexOutOfBoundsException expected) {
444        }
445    }
446
447    public void testChangeReturnType() {
448        MethodType mt = MethodType.methodType(int.class, String.class);
449
450        MethodType changed = mt.changeReturnType(long.class);
451        assertEquals(long.class, changed.returnType());
452        assertParameterTypes(changed, String.class);
453
454        try {
455            mt.changeReturnType(null);
456            fail();
457        } catch (NullPointerException expected) {
458        }
459    }
460
461    public void testHasPrimitives() {
462        MethodType mt = MethodType.methodType(Integer.class, Object.class, String.class);
463        assertFalse(mt.hasPrimitives());
464
465        mt = MethodType.methodType(int.class, Object.class);
466        assertTrue(mt.hasPrimitives());
467
468        mt = MethodType.methodType(Integer.class, long.class);
469        assertTrue(mt.hasPrimitives());
470
471        mt = MethodType.methodType(Integer.class, int[].class);
472        assertFalse(mt.hasPrimitives());
473
474        mt = MethodType.methodType(void.class);
475        assertTrue(mt.hasPrimitives());
476    }
477
478    public void testHasWrappers() {
479        MethodType mt = MethodType.methodType(Integer.class);
480        assertTrue(mt.hasWrappers());
481
482        mt = MethodType.methodType(String.class, Integer.class);
483        assertTrue(mt.hasWrappers());
484
485        mt = MethodType.methodType(int.class, long.class);
486        assertFalse(mt.hasWrappers());
487    }
488
489    public void testErase() {
490        // String mt(int, String, Object) should be erased to Object mt(int, Object, Object);
491        MethodType mt = MethodType.methodType(String.class, int.class, String.class, Object.class);
492
493        MethodType erased = mt.erase();
494        assertEquals(Object.class, erased.returnType());
495        assertParameterTypes(erased, int.class, Object.class, Object.class);
496
497        // Void returns must be left alone.
498        mt = MethodType.methodType(void.class, int.class);
499        erased = mt.erase();
500        assertEquals(mt, erased);
501    }
502
503    public void testGeneric() {
504        // String mt(int, String, Object) should be generified to Object mt(Object, Object, Object).
505        // In other words, it must be equal to genericMethodType(3 /* parameterCount */);
506        MethodType mt = MethodType.methodType(String.class, int.class, String.class, Object.class);
507
508        MethodType generic = mt.generic();
509
510        assertEquals(generic, MethodType.genericMethodType(mt.parameterCount()));
511        assertEquals(generic, mt.wrap().erase());
512
513        assertEquals(Object.class, generic.returnType());
514        assertParameterTypes(generic, Object.class, Object.class, Object.class);
515
516        // Primitive return types must also become Object.
517        generic = MethodType.methodType(int.class).generic();
518        assertEquals(Object.class, generic.returnType());
519
520        // void returns get converted to object returns (the same as wrap).
521        generic = MethodType.methodType(void.class).generic();
522        assertEquals(Object.class, generic.returnType());
523    }
524
525    public void testWrap() {
526        // int mt(String, int, long, float, double, short, char, byte) should be wrapped to
527        // Integer mt(String, Integer, Long, Float, Double, Short, Character, Byte);
528        MethodType mt = MethodType.methodType(int.class, String.class, int.class, long.class,
529                float.class, double.class, short.class, char.class, byte.class);
530
531        MethodType wrapped = mt.wrap();
532        assertFalse(wrapped.hasPrimitives());
533        assertTrue(wrapped.hasWrappers());
534
535        assertEquals(Integer.class, wrapped.returnType());
536        assertParameterTypes(wrapped, String.class, Integer.class, Long.class, Float.class,
537                Double.class, Short.class, Character.class, Byte.class);
538
539        // (semi) special case - void return types get wrapped to Void.
540        wrapped = MethodType.methodType(void.class, int.class).wrap();
541        assertEquals(Void.class, wrapped.returnType());
542    }
543
544    public void testUnwrap() {
545        // Integer mt(String, Integer, Long, Float, Double, Short, Character, Byte);
546        // should be unwrapped to :
547        // int mt(String, int, long, float, double, short, char, byte).
548        MethodType mt = MethodType.methodType(Integer.class, String.class, Integer.class,
549                Long.class, Float.class, Double.class, Short.class, Character.class, Byte.class);
550
551        MethodType unwrapped = mt.unwrap();
552        assertTrue(unwrapped.hasPrimitives());
553        assertFalse(unwrapped.hasWrappers());
554
555        assertEquals(int.class, unwrapped.returnType());
556        assertParameterTypes(unwrapped, String.class, int.class, long.class, float.class,
557                double.class, short.class, char.class, byte.class);
558
559        // (semi) special case - void return types get wrapped to Void.
560        unwrapped = MethodType.methodType(Void.class, int.class).unwrap();
561        assertEquals(void.class, unwrapped.returnType());
562    }
563
564    public void testParameterListAndArray() {
565        MethodType mt = MethodType.methodType(String.class, int.class, String.class, Object.class);
566
567        List<Class<?>> paramsList = mt.parameterList();
568        Class<?>[] paramsArray = mt.parameterArray();
569
570        assertEquals(3, mt.parameterCount());
571
572        for (int i = 0; i < 3; ++i) {
573            Class<?> param = mt.parameterType(i);
574            assertEquals(param, paramsList.get(i));
575            assertEquals(param, paramsArray[i]);
576        }
577
578        mt = MethodType.methodType(int.class);
579        assertEquals(0, mt.parameterCount());
580
581        paramsList = mt.parameterList();
582        paramsArray = mt.parameterArray();
583
584        assertEquals(0, paramsList.size());
585        assertEquals(0, paramsArray.length);
586    }
587
588    public void testEquals() {
589        MethodType mt = MethodType.methodType(int.class, String.class);
590        MethodType mt2 = MethodType.methodType(int.class, String.class);
591
592        assertEquals(mt, mt2);
593        assertEquals(mt, mt);
594
595        assertFalse(mt.equals(null));
596        assertFalse(mt.equals(MethodType.methodType(Integer.class, String.class)));
597    }
598
599    public void testHashCode() {
600        MethodType mt = MethodType.methodType(int.class, String.class, Object.class);
601        int hashCode = mt.hashCode();
602
603        // The hash code should change if we change the return type or any of the parameters,
604        // or if we add or remove parameters from the list.
605        assertFalse(hashCode == mt.changeReturnType(long.class).hashCode());
606        assertFalse(hashCode == mt.changeParameterType(0, Object.class).hashCode());
607        assertFalse(hashCode == mt.appendParameterTypes(List.class).hashCode());
608        assertFalse(hashCode == mt.dropParameterTypes(0, 1).hashCode());
609    }
610
611    public void testToString() {
612        assertEquals("(String,Object)int",
613                MethodType.methodType(int.class, String.class, Object.class).toString());
614        assertEquals("()int", MethodType.methodType(int.class).toString());
615        assertEquals("()void", MethodType.methodType(void.class).toString());
616        assertEquals("()int[]", MethodType.methodType(int[].class).toString());
617    }
618
619    public void testFromMethodDescriptorString() {
620        assertEquals(
621                MethodType.methodType(int.class, String.class, Object.class),
622                MethodType.fromMethodDescriptorString("(Ljava/lang/String;Ljava/lang/Object;)I", null));
623
624        assertEquals(MethodType.fromMethodDescriptorString("()I", null),
625                MethodType.methodType(int.class));
626        assertEquals(MethodType.fromMethodDescriptorString("()[I", null),
627                MethodType.methodType(int[].class));
628        assertEquals(MethodType.fromMethodDescriptorString("([I)V", null),
629                MethodType.methodType(void.class, int[].class));
630
631        try {
632            MethodType.fromMethodDescriptorString(null, null);
633            fail();
634        } catch (NullPointerException expected) {
635        }
636
637        try {
638            MethodType.fromMethodDescriptorString("(a/b/c)I", null);
639            fail();
640        } catch (IllegalArgumentException expected) {
641        }
642
643        try {
644            MethodType.fromMethodDescriptorString("(A)I", null);
645            fail();
646        } catch (IllegalArgumentException expected) {
647        }
648
649        try {
650            MethodType.fromMethodDescriptorString("(Ljava/lang/String)I", null);
651            fail();
652        } catch (IllegalArgumentException expected) {
653        }
654
655        try {
656            MethodType.fromMethodDescriptorString("(Ljava/lang/String;)", null);
657            fail();
658        } catch (IllegalArgumentException expected) {
659        }
660
661        try {
662            MethodType.fromMethodDescriptorString("(Ljava/lang/NonExistentString;)I", null);
663            fail();
664        } catch (TypeNotPresentException expected) {
665        }
666    }
667
668    public void testToMethodDescriptorString() {
669        assertEquals("(Ljava/lang/String;Ljava/lang/Object;)I", MethodType.methodType(
670                int.class, String.class, Object.class).toMethodDescriptorString());
671
672        assertEquals("()I", MethodType.methodType(int.class).toMethodDescriptorString());
673        assertEquals("()[I", MethodType.methodType(int[].class).toMethodDescriptorString());
674
675        assertEquals("([I)V", MethodType.methodType(void.class, int[].class)
676                .toMethodDescriptorString());
677    }
678
679    private static void assertParameterTypes(MethodType type, Class<?>... params) {
680        assertEquals(params.length, type.parameterCount());
681
682        List<Class<?>> paramsList = type.parameterList();
683        for (int i = 0; i < params.length; ++i) {
684            assertEquals(params[i], type.parameterType(i));
685            assertEquals(params[i], paramsList.get(i));
686        }
687
688        assertTrue(Arrays.equals(params, type.parameterArray()));
689    }
690}
691