TestResolvedJavaMethod.java revision 12651:6ef01bd40ce2
1303887Sjhb/*
2303887Sjhb * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
3303887Sjhb * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4303887Sjhb *
5303887Sjhb * This code is free software; you can redistribute it and/or modify it
6303887Sjhb * under the terms of the GNU General Public License version 2 only, as
7303887Sjhb * published by the Free Software Foundation.
8303887Sjhb *
9303887Sjhb * This code is distributed in the hope that it will be useful, but WITHOUT
10303887Sjhb * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11303887Sjhb * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12303887Sjhb * version 2 for more details (a copy is included in the LICENSE file that
13303887Sjhb * accompanied this code).
14303887Sjhb *
15303887Sjhb * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/**
25 * @test
26 * @requires vm.jvmci
27 * @library ../../../../../
28 * @modules jdk.internal.vm.ci/jdk.vm.ci.meta
29 *          jdk.internal.vm.ci/jdk.vm.ci.runtime
30 *          java.base/jdk.internal.misc
31 * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI jdk.vm.ci.runtime.test.TestResolvedJavaMethod
32 */
33
34package jdk.vm.ci.runtime.test;
35
36import static org.junit.Assert.assertArrayEquals;
37import static org.junit.Assert.assertEquals;
38import static org.junit.Assert.assertFalse;
39import static org.junit.Assert.assertNotNull;
40import static org.junit.Assert.assertTrue;
41
42import java.lang.annotation.Annotation;
43import java.lang.annotation.ElementType;
44import java.lang.annotation.Retention;
45import java.lang.annotation.RetentionPolicy;
46import java.lang.annotation.Target;
47import java.lang.reflect.Constructor;
48import java.lang.reflect.Member;
49import java.lang.reflect.Method;
50import java.lang.reflect.Modifier;
51import java.lang.reflect.Type;
52import java.util.Arrays;
53import java.util.HashMap;
54import java.util.HashSet;
55import java.util.Map;
56import java.util.Set;
57
58import org.junit.Assert;
59import org.junit.Test;
60
61import jdk.vm.ci.meta.ConstantPool;
62import jdk.vm.ci.meta.ExceptionHandler;
63import jdk.vm.ci.meta.ResolvedJavaMethod;
64import jdk.vm.ci.meta.ResolvedJavaMethod.Parameter;
65import jdk.vm.ci.meta.ResolvedJavaType;
66
67/**
68 * Tests for {@link ResolvedJavaMethod}.
69 */
70public class TestResolvedJavaMethod extends MethodUniverse {
71
72    public TestResolvedJavaMethod() {
73    }
74
75    /**
76     * @see ResolvedJavaMethod#getCode()
77     */
78    @Test
79    public void getCodeTest() {
80        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
81            ResolvedJavaMethod m = e.getValue();
82            byte[] code = m.getCode();
83            if (code == null) {
84                assertTrue(m.getCodeSize() == 0);
85            } else {
86                if (m.isAbstract()) {
87                    assertTrue(code.length == 0);
88                } else if (!m.isNative()) {
89                    assertTrue(code.length > 0);
90                }
91            }
92        }
93    }
94
95    /**
96     * @see ResolvedJavaMethod#getCodeSize()
97     */
98    @Test
99    public void getCodeSizeTest() {
100        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
101            ResolvedJavaMethod m = e.getValue();
102            int codeSize = m.getCodeSize();
103            if (m.isAbstract()) {
104                assertTrue(codeSize == 0);
105            } else if (!m.isNative()) {
106                assertTrue(codeSize > 0);
107            }
108        }
109    }
110
111    @Test
112    public void getModifiersTest() {
113        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
114            ResolvedJavaMethod m = e.getValue();
115            int expected = e.getKey().getModifiers();
116            int actual = m.getModifiers();
117            assertEquals(String.format("%s: 0x%x != 0x%x", m, expected, actual), expected, actual);
118        }
119        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
120            ResolvedJavaMethod m = e.getValue();
121            int expected = e.getKey().getModifiers();
122            int actual = m.getModifiers();
123            assertEquals(String.format("%s: 0x%x != 0x%x", m, expected, actual), expected, actual);
124        }
125    }
126
127    /**
128     * @see ResolvedJavaMethod#isClassInitializer()
129     */
130    @Test
131    public void isClassInitializerTest() {
132        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
133            // Class initializers are hidden from reflection
134            ResolvedJavaMethod m = e.getValue();
135            assertFalse(m.isClassInitializer());
136        }
137        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
138            ResolvedJavaMethod m = e.getValue();
139            assertFalse(m.isClassInitializer());
140        }
141    }
142
143    @Test
144    public void isConstructorTest() {
145        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
146            ResolvedJavaMethod m = e.getValue();
147            assertFalse(m.isConstructor());
148        }
149        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
150            ResolvedJavaMethod m = e.getValue();
151            assertTrue(m.isConstructor());
152        }
153    }
154
155    @Test
156    public void isSyntheticTest() {
157        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
158            ResolvedJavaMethod m = e.getValue();
159            assertEquals(e.getKey().isSynthetic(), m.isSynthetic());
160        }
161        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
162            ResolvedJavaMethod m = e.getValue();
163            assertEquals(e.getKey().isSynthetic(), m.isSynthetic());
164        }
165    }
166
167    @Test
168    public void isBridgeTest() {
169        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
170            ResolvedJavaMethod m = e.getValue();
171            assertEquals(e.getKey().isBridge(), m.isBridge());
172        }
173        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
174            ResolvedJavaMethod m = e.getValue();
175            assertEquals(false, m.isBridge());
176        }
177    }
178
179    @Test
180    public void isVarArgsTest() {
181        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
182            ResolvedJavaMethod m = e.getValue();
183            assertEquals(e.getKey().isVarArgs(), m.isVarArgs());
184        }
185        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
186            ResolvedJavaMethod m = e.getValue();
187            assertEquals(e.getKey().isVarArgs(), m.isVarArgs());
188        }
189    }
190
191    @Test
192    public void isSynchronizedTest() {
193        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
194            ResolvedJavaMethod m = e.getValue();
195            assertEquals(Modifier.isSynchronized(e.getKey().getModifiers()), m.isSynchronized());
196        }
197        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
198            ResolvedJavaMethod m = e.getValue();
199            assertEquals(Modifier.isSynchronized(e.getKey().getModifiers()), m.isSynchronized());
200        }
201    }
202
203    @Test
204    public void canBeStaticallyBoundTest() {
205        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
206            ResolvedJavaMethod m = e.getValue();
207            assertEquals(m.canBeStaticallyBound(), canBeStaticallyBound(e.getKey()));
208        }
209        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
210            ResolvedJavaMethod m = e.getValue();
211            assertEquals(m.canBeStaticallyBound(), canBeStaticallyBound(e.getKey()));
212        }
213    }
214
215    private static boolean canBeStaticallyBound(Member method) {
216        int modifiers = method.getModifiers();
217        return (Modifier.isFinal(modifiers) || Modifier.isPrivate(modifiers) || Modifier.isStatic(modifiers) || Modifier.isFinal(method.getDeclaringClass().getModifiers())) &&
218                        !Modifier.isAbstract(modifiers);
219    }
220
221    private static String methodWithExceptionHandlers(String p1, Object o2) {
222        try {
223            return p1.substring(100) + o2.toString();
224        } catch (IndexOutOfBoundsException e) {
225            e.printStackTrace();
226        } catch (NullPointerException e) {
227            e.printStackTrace();
228        } catch (RuntimeException e) {
229            e.printStackTrace();
230        }
231        return null;
232    }
233
234    @Test
235    public void getExceptionHandlersTest() throws NoSuchMethodException {
236        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithExceptionHandlers", String.class, Object.class));
237        ExceptionHandler[] handlers = method.getExceptionHandlers();
238        assertNotNull(handlers);
239        assertEquals(handlers.length, 3);
240        handlers[0].getCatchType().equals(metaAccess.lookupJavaType(IndexOutOfBoundsException.class));
241        handlers[1].getCatchType().equals(metaAccess.lookupJavaType(NullPointerException.class));
242        handlers[2].getCatchType().equals(metaAccess.lookupJavaType(RuntimeException.class));
243    }
244
245    private static String nullPointerExceptionOnFirstLine(Object o, String ignored) {
246        return o.toString() + ignored;
247    }
248
249    @Test
250    public void asStackTraceElementTest() throws NoSuchMethodException {
251        try {
252            nullPointerExceptionOnFirstLine(null, "ignored");
253            Assert.fail("should not reach here");
254        } catch (NullPointerException e) {
255            StackTraceElement expected = e.getStackTrace()[0];
256            ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("nullPointerExceptionOnFirstLine", Object.class, String.class));
257            StackTraceElement actual = method.asStackTraceElement(0);
258            assertEquals(expected, actual);
259        }
260    }
261
262    @Test
263    public void getConstantPoolTest() {
264        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
265            ResolvedJavaMethod m = e.getValue();
266            ConstantPool cp = m.getConstantPool();
267            assertTrue(cp.length() > 0);
268        }
269    }
270
271    @Test
272    public void getParametersTest() {
273        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
274            java.lang.reflect.Parameter[] expected = e.getKey().getParameters();
275            Parameter[] actual = e.getValue().getParameters();
276            assertEquals(actual.length, expected.length);
277            for (int i = 0; i < actual.length; i++) {
278                java.lang.reflect.Parameter exp = expected[i];
279                Parameter act = actual[i];
280                assertEquals(exp.getName(), act.getName());
281                assertEquals(exp.isNamePresent(), act.isNamePresent());
282                assertEquals(exp.getModifiers(), act.getModifiers());
283                assertArrayEquals(exp.getAnnotations(), act.getAnnotations());
284                assertEquals(exp.getType().getName(), act.getType().toClassName());
285                assertEquals(exp.getParameterizedType(), act.getParameterizedType());
286                assertEquals(metaAccess.lookupJavaMethod(exp.getDeclaringExecutable()), act.getDeclaringMethod());
287            }
288        }
289    }
290
291    @Retention(RetentionPolicy.RUNTIME)
292    @Target(ElementType.METHOD)
293    @interface TestAnnotation {
294        long value();
295    }
296
297    @Test
298    @TestAnnotation(value = 1000L)
299    public void getAnnotationTest() throws NoSuchMethodException {
300        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("getAnnotationTest"));
301        TestAnnotation annotation = method.getAnnotation(TestAnnotation.class);
302        assertNotNull(annotation);
303        assertEquals(1000L, annotation.value());
304    }
305
306    @Test
307    @TestAnnotation(value = 1000L)
308    public void getAnnotationsTest() throws NoSuchMethodException {
309        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("getAnnotationsTest"));
310        Annotation[] annotations = method.getAnnotations();
311        assertNotNull(annotations);
312        assertEquals(2, annotations.length);
313        TestAnnotation annotation = null;
314        for (Annotation a : annotations) {
315            if (a instanceof TestAnnotation) {
316                annotation = (TestAnnotation) a;
317                break;
318            }
319        }
320        assertNotNull(annotation);
321        assertEquals(1000L, annotation.value());
322    }
323
324    @Retention(RetentionPolicy.RUNTIME)
325    @Target(ElementType.PARAMETER)
326    @interface NonNull {
327    }
328
329    @Retention(RetentionPolicy.RUNTIME)
330    @Target(ElementType.PARAMETER)
331    @interface Special {
332    }
333
334    private static native void methodWithAnnotatedParameters(@NonNull HashMap<String, String> p1, @Special @NonNull Class<? extends Annotation> p2);
335
336    @Test
337    public void getParameterAnnotationsTest() throws NoSuchMethodException {
338        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class));
339        Annotation[][] annotations = method.getParameterAnnotations();
340        assertEquals(2, annotations.length);
341        assertEquals(1, annotations[0].length);
342        assertEquals(NonNull.class, annotations[0][0].annotationType());
343        assertEquals(2, annotations[1].length);
344        assertEquals(Special.class, annotations[1][0].annotationType());
345        assertEquals(NonNull.class, annotations[1][1].annotationType());
346    }
347
348    @Test
349    public void getGenericParameterTypesTest() throws NoSuchMethodException {
350        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class));
351        Type[] genericParameterTypes = method.getGenericParameterTypes();
352        assertEquals(2, genericParameterTypes.length);
353        assertEquals("java.util.HashMap<java.lang.String, java.lang.String>", genericParameterTypes[0].toString());
354        assertEquals("java.lang.Class<? extends java.lang.annotation.Annotation>", genericParameterTypes[1].toString());
355    }
356
357    @Test
358    public void getMaxLocalsTest() throws NoSuchMethodException {
359        ResolvedJavaMethod method1 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class));
360        ResolvedJavaMethod method2 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("nullPointerExceptionOnFirstLine", Object.class, String.class));
361        assertEquals(0, method1.getMaxLocals());
362        assertEquals(2, method2.getMaxLocals());
363
364    }
365
366    @Test
367    public void getMaxStackSizeTest() throws NoSuchMethodException {
368        ResolvedJavaMethod method1 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class));
369        ResolvedJavaMethod method2 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("nullPointerExceptionOnFirstLine", Object.class, String.class));
370        assertEquals(0, method1.getMaxStackSize());
371        // some versions of javac produce bytecode with a stacksize of 2 for this method
372        // JSR 292 also sometimes need one more stack slot
373        int method2StackSize = method2.getMaxStackSize();
374        assertTrue(2 <= method2StackSize && method2StackSize <= 4);
375    }
376
377    @Test
378    public void isDefaultTest() {
379        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
380            ResolvedJavaMethod m = e.getValue();
381            assertEquals(e.getKey().isDefault(), m.isDefault());
382        }
383        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
384            ResolvedJavaMethod m = e.getValue();
385            assertFalse(m.isDefault());
386        }
387    }
388
389    @Test
390    public void hasReceiverTest() {
391        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
392            ResolvedJavaMethod m = e.getValue();
393            assertTrue(m.hasReceiver() != Modifier.isStatic(e.getKey().getModifiers()));
394        }
395        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
396            ResolvedJavaMethod m = e.getValue();
397            assertTrue(m.hasReceiver());
398        }
399    }
400
401    @Test
402    public void hasBytecodesTest() {
403        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
404            ResolvedJavaMethod m = e.getValue();
405            assertTrue(m.hasBytecodes() == (m.isConcrete() && !m.isNative()));
406        }
407        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
408            ResolvedJavaMethod m = e.getValue();
409            assertTrue(m.hasBytecodes());
410        }
411    }
412
413    @Test
414    public void isJavaLangObjectInitTest() throws NoSuchMethodException {
415        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(Object.class.getConstructor());
416        assertTrue(method.isJavaLangObjectInit());
417        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
418            ResolvedJavaMethod m = e.getValue();
419            assertFalse(m.isJavaLangObjectInit());
420        }
421        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
422            ResolvedJavaMethod m = e.getValue();
423            Constructor<?> key = e.getKey();
424            if (key.getDeclaringClass() == Object.class && key.getParameters().length == 0) {
425                assertTrue(m.isJavaLangObjectInit());
426            } else {
427                assertFalse(m.isJavaLangObjectInit());
428            }
429        }
430    }
431
432    /**
433     * All public non-final methods should be available in the vtable.
434     */
435    @Test
436    public void testVirtualMethodTableAccess() {
437        for (Class<?> c : classes) {
438            if (c.isPrimitive() || c.isInterface()) {
439                continue;
440            }
441            ResolvedJavaType receiverType = metaAccess.lookupJavaType(c);
442            for (Method m : c.getMethods()) {
443                ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m);
444                if (!method.isStatic() && !method.isFinal() && !method.getDeclaringClass().isLeaf() && !method.getDeclaringClass().isInterface()) {
445                    assertTrue(method + " not available in " + receiverType, method.isInVirtualMethodTable(receiverType));
446                }
447            }
448        }
449    }
450
451    private Method findTestMethod(Method apiMethod) {
452        String testName = apiMethod.getName() + "Test";
453        for (Method m : getClass().getDeclaredMethods()) {
454            if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) {
455                return m;
456            }
457        }
458        return null;
459    }
460
461    // @formatter:off
462    private static final String[] untestedApiMethods = {
463        "newInstance",
464        "getDeclaringClass",
465        "getEncoding",
466        "getProfilingInfo",
467        "reprofile",
468        "getCompilerStorage",
469        "hasNeverInlineDirective",
470        "canBeInlined",
471        "shouldBeInlined",
472        "getLineNumberTable",
473        "getLocalVariableTable",
474        "isInVirtualMethodTable",
475        "toParameterTypes",
476        "getParameterAnnotation",
477        "getSpeculationLog",
478        "isFinal",
479        "$jacocoInit"
480    };
481    // @formatter:on
482
483    /**
484     * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written
485     * for them or are added to {@link #untestedApiMethods}.
486     */
487    @Test
488    public void testCoverage() {
489        Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods));
490        for (Method m : ResolvedJavaMethod.class.getDeclaredMethods()) {
491            if (Modifier.isStatic(m.getModifiers())) {
492                continue;
493            }
494            if (findTestMethod(m) == null) {
495                assertTrue("test missing for " + m, known.contains(m.getName()));
496            } else {
497                assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName()));
498            }
499        }
500    }
501}
502