TestResolvedJavaMethod.java revision 10420:c558850fac57
1/*
2 * Copyright (c) 2012, 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
24/**
25 * @test
26 * @requires (os.simpleArch == "x64" | os.simpleArch == "sparcv9" | os.simpleArch == "aarch64")
27 * @library ../../../../../
28 * @modules jdk.vm.ci/jdk.vm.ci.meta
29 *          jdk.vm.ci/jdk.vm.ci.runtime
30 * @build jdk.vm.ci.runtime.test.TestResolvedJavaMethod
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.assertEquals;
37import static org.junit.Assert.assertFalse;
38import static org.junit.Assert.assertNotNull;
39import static org.junit.Assert.assertTrue;
40
41import java.lang.annotation.Annotation;
42import java.lang.annotation.ElementType;
43import java.lang.annotation.Retention;
44import java.lang.annotation.RetentionPolicy;
45import java.lang.annotation.Target;
46import java.lang.invoke.MethodHandle;
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 jdk.vm.ci.meta.ConstantPool;
59import jdk.vm.ci.meta.ExceptionHandler;
60import jdk.vm.ci.meta.ResolvedJavaMethod;
61import jdk.vm.ci.meta.ResolvedJavaType;
62
63import org.junit.Assert;
64import org.junit.Test;
65
66/**
67 * Tests for {@link ResolvedJavaMethod}.
68 */
69public class TestResolvedJavaMethod extends MethodUniverse {
70
71    public TestResolvedJavaMethod() {
72    }
73
74    /**
75     * @see ResolvedJavaMethod#getCode()
76     */
77    @Test
78    public void getCodeTest() {
79        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
80            ResolvedJavaMethod m = e.getValue();
81            byte[] code = m.getCode();
82            if (code == null) {
83                assertTrue(m.getCodeSize() == 0);
84            } else {
85                if (m.isAbstract()) {
86                    assertTrue(code.length == 0);
87                } else if (!m.isNative()) {
88                    assertTrue(code.length > 0);
89                }
90            }
91        }
92    }
93
94    /**
95     * @see ResolvedJavaMethod#getCodeSize()
96     */
97    @Test
98    public void getCodeSizeTest() {
99        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
100            ResolvedJavaMethod m = e.getValue();
101            int codeSize = m.getCodeSize();
102            if (m.isAbstract()) {
103                assertTrue(codeSize == 0);
104            } else if (!m.isNative()) {
105                assertTrue(codeSize > 0);
106            }
107        }
108    }
109
110    @Test
111    public void getModifiersTest() {
112        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
113            ResolvedJavaMethod m = e.getValue();
114            int expected = e.getKey().getModifiers();
115            int actual = m.getModifiers();
116            assertEquals(String.format("%s: 0x%x != 0x%x", m, expected, actual), expected, actual);
117        }
118        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
119            ResolvedJavaMethod m = e.getValue();
120            int expected = e.getKey().getModifiers();
121            int actual = m.getModifiers();
122            assertEquals(String.format("%s: 0x%x != 0x%x", m, expected, actual), expected, actual);
123        }
124    }
125
126    /**
127     * @see ResolvedJavaMethod#isClassInitializer()
128     */
129    @Test
130    public void isClassInitializerTest() {
131        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
132            // Class initializers are hidden from reflection
133            ResolvedJavaMethod m = e.getValue();
134            assertFalse(m.isClassInitializer());
135        }
136        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
137            ResolvedJavaMethod m = e.getValue();
138            assertFalse(m.isClassInitializer());
139        }
140    }
141
142    @Test
143    public void isConstructorTest() {
144        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
145            ResolvedJavaMethod m = e.getValue();
146            assertFalse(m.isConstructor());
147        }
148        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
149            ResolvedJavaMethod m = e.getValue();
150            assertTrue(m.isConstructor());
151        }
152    }
153
154    @Test
155    public void isSyntheticTest() {
156        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
157            ResolvedJavaMethod m = e.getValue();
158            assertEquals(e.getKey().isSynthetic(), m.isSynthetic());
159        }
160        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
161            ResolvedJavaMethod m = e.getValue();
162            assertEquals(e.getKey().isSynthetic(), m.isSynthetic());
163        }
164    }
165
166    @Test
167    public void isBridgeTest() {
168        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
169            ResolvedJavaMethod m = e.getValue();
170            assertEquals(e.getKey().isBridge(), m.isBridge());
171        }
172        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
173            ResolvedJavaMethod m = e.getValue();
174            assertEquals(false, m.isBridge());
175        }
176    }
177
178    @Test
179    public void isVarArgsTest() {
180        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
181            ResolvedJavaMethod m = e.getValue();
182            assertEquals(e.getKey().isVarArgs(), m.isVarArgs());
183        }
184        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
185            ResolvedJavaMethod m = e.getValue();
186            assertEquals(e.getKey().isVarArgs(), m.isVarArgs());
187        }
188    }
189
190    @Test
191    public void isSynchronizedTest() {
192        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
193            ResolvedJavaMethod m = e.getValue();
194            assertEquals(Modifier.isSynchronized(e.getKey().getModifiers()), m.isSynchronized());
195        }
196        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
197            ResolvedJavaMethod m = e.getValue();
198            assertEquals(Modifier.isSynchronized(e.getKey().getModifiers()), m.isSynchronized());
199        }
200    }
201
202    @Test
203    public void canBeStaticallyBoundTest() {
204        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
205            ResolvedJavaMethod m = e.getValue();
206            assertEquals(m.canBeStaticallyBound(), canBeStaticallyBound(e.getKey()));
207        }
208        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
209            ResolvedJavaMethod m = e.getValue();
210            assertEquals(m.canBeStaticallyBound(), canBeStaticallyBound(e.getKey()));
211        }
212    }
213
214    private static boolean canBeStaticallyBound(Member method) {
215        int modifiers = method.getModifiers();
216        return (Modifier.isFinal(modifiers) || Modifier.isPrivate(modifiers) || Modifier.isStatic(modifiers) || Modifier.isFinal(method.getDeclaringClass().getModifiers())) &&
217                        !Modifier.isAbstract(modifiers);
218    }
219
220    private static String methodWithExceptionHandlers(String p1, Object o2) {
221        try {
222            return p1.substring(100) + o2.toString();
223        } catch (IndexOutOfBoundsException e) {
224            e.printStackTrace();
225        } catch (NullPointerException e) {
226            e.printStackTrace();
227        } catch (RuntimeException e) {
228            e.printStackTrace();
229        }
230        return null;
231    }
232
233    @Test
234    public void getExceptionHandlersTest() throws NoSuchMethodException {
235        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithExceptionHandlers", String.class, Object.class));
236        ExceptionHandler[] handlers = method.getExceptionHandlers();
237        assertNotNull(handlers);
238        assertEquals(handlers.length, 3);
239        handlers[0].getCatchType().equals(metaAccess.lookupJavaType(IndexOutOfBoundsException.class));
240        handlers[1].getCatchType().equals(metaAccess.lookupJavaType(NullPointerException.class));
241        handlers[2].getCatchType().equals(metaAccess.lookupJavaType(RuntimeException.class));
242    }
243
244    private static String nullPointerExceptionOnFirstLine(Object o, String ignored) {
245        return o.toString() + ignored;
246    }
247
248    @Test
249    public void asStackTraceElementTest() throws NoSuchMethodException {
250        try {
251            nullPointerExceptionOnFirstLine(null, "ignored");
252            Assert.fail("should not reach here");
253        } catch (NullPointerException e) {
254            StackTraceElement expected = e.getStackTrace()[0];
255            ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("nullPointerExceptionOnFirstLine", Object.class, String.class));
256            StackTraceElement actual = method.asStackTraceElement(0);
257            assertEquals(expected, actual);
258        }
259    }
260
261    @Test
262    public void getConstantPoolTest() {
263        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
264            ResolvedJavaMethod m = e.getValue();
265            ConstantPool cp = m.getConstantPool();
266            assertTrue(cp.length() > 0);
267        }
268    }
269
270    @Retention(RetentionPolicy.RUNTIME)
271    @Target(ElementType.METHOD)
272    @interface TestAnnotation {
273        long value();
274    }
275
276    @Test
277    @TestAnnotation(value = 1000L)
278    public void getAnnotationTest() throws NoSuchMethodException {
279        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("getAnnotationTest"));
280        TestAnnotation annotation = method.getAnnotation(TestAnnotation.class);
281        assertNotNull(annotation);
282        assertEquals(1000L, annotation.value());
283    }
284
285    @Test
286    @TestAnnotation(value = 1000L)
287    public void getAnnotationsTest() throws NoSuchMethodException {
288        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("getAnnotationsTest"));
289        Annotation[] annotations = method.getAnnotations();
290        assertNotNull(annotations);
291        assertEquals(2, annotations.length);
292        TestAnnotation annotation = null;
293        for (Annotation a : annotations) {
294            if (a instanceof TestAnnotation) {
295                annotation = (TestAnnotation) a;
296                break;
297            }
298        }
299        assertNotNull(annotation);
300        assertEquals(1000L, annotation.value());
301    }
302
303    @Retention(RetentionPolicy.RUNTIME)
304    @Target(ElementType.PARAMETER)
305    @interface NonNull {
306    }
307
308    @Retention(RetentionPolicy.RUNTIME)
309    @Target(ElementType.PARAMETER)
310    @interface Special {
311    }
312
313    private static native void methodWithAnnotatedParameters(@NonNull HashMap<String, String> p1, @Special @NonNull Class<? extends Annotation> p2);
314
315    @Test
316    public void getParameterAnnotationsTest() throws NoSuchMethodException {
317        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class));
318        Annotation[][] annotations = method.getParameterAnnotations();
319        assertEquals(2, annotations.length);
320        assertEquals(1, annotations[0].length);
321        assertEquals(NonNull.class, annotations[0][0].annotationType());
322        assertEquals(2, annotations[1].length);
323        assertEquals(Special.class, annotations[1][0].annotationType());
324        assertEquals(NonNull.class, annotations[1][1].annotationType());
325    }
326
327    @Test
328    public void getGenericParameterTypesTest() throws NoSuchMethodException {
329        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class));
330        Type[] genericParameterTypes = method.getGenericParameterTypes();
331        assertEquals(2, genericParameterTypes.length);
332        assertEquals("java.util.HashMap<java.lang.String, java.lang.String>", genericParameterTypes[0].toString());
333        assertEquals("java.lang.Class<? extends java.lang.annotation.Annotation>", genericParameterTypes[1].toString());
334    }
335
336    @Test
337    public void getMaxLocalsTest() throws NoSuchMethodException {
338        ResolvedJavaMethod method1 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class));
339        ResolvedJavaMethod method2 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("nullPointerExceptionOnFirstLine", Object.class, String.class));
340        assertEquals(0, method1.getMaxLocals());
341        assertEquals(2, method2.getMaxLocals());
342
343    }
344
345    @Test
346    public void getMaxStackSizeTest() throws NoSuchMethodException {
347        ResolvedJavaMethod method1 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class));
348        ResolvedJavaMethod method2 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("nullPointerExceptionOnFirstLine", Object.class, String.class));
349        assertEquals(0, method1.getMaxStackSize());
350        // some versions of javac produce bytecode with a stacksize of 2 for this method
351        // JSR 292 also sometimes need one more stack slot
352        int method2StackSize = method2.getMaxStackSize();
353        assertTrue(2 <= method2StackSize && method2StackSize <= 4);
354    }
355
356    @Test
357    public void isDefaultTest() {
358        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
359            ResolvedJavaMethod m = e.getValue();
360            assertEquals(e.getKey().isDefault(), m.isDefault());
361        }
362        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
363            ResolvedJavaMethod m = e.getValue();
364            assertFalse(m.isDefault());
365        }
366    }
367
368    @Test
369    public void hasReceiverTest() {
370        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
371            ResolvedJavaMethod m = e.getValue();
372            assertTrue(m.hasReceiver() != Modifier.isStatic(e.getKey().getModifiers()));
373        }
374        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
375            ResolvedJavaMethod m = e.getValue();
376            assertTrue(m.hasReceiver());
377        }
378    }
379
380    @Test
381    public void hasBytecodesTest() {
382        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
383            ResolvedJavaMethod m = e.getValue();
384            assertTrue(m.hasBytecodes() == (m.isConcrete() && !m.isNative()));
385        }
386        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
387            ResolvedJavaMethod m = e.getValue();
388            assertTrue(m.hasBytecodes());
389        }
390    }
391
392    @Test
393    public void isJavaLangObjectInitTest() throws NoSuchMethodException {
394        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(Object.class.getConstructor());
395        assertTrue(method.isJavaLangObjectInit());
396        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
397            ResolvedJavaMethod m = e.getValue();
398            assertFalse(m.isJavaLangObjectInit());
399        }
400        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
401            ResolvedJavaMethod m = e.getValue();
402            Constructor<?> key = e.getKey();
403            if (key.getDeclaringClass() == Object.class && key.getParameters().length == 0) {
404                assertTrue(m.isJavaLangObjectInit());
405            } else {
406                assertFalse(m.isJavaLangObjectInit());
407            }
408        }
409    }
410
411    @Test
412    public void isSignaturePolymorphicTest() {
413        ResolvedJavaType methodHandleType = metaAccess.lookupJavaType(MethodHandle.class);
414        assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "invokeExact", metaAccess));
415        assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "invoke", metaAccess));
416        assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "invokeBasic", metaAccess));
417        assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "linkToVirtual", metaAccess));
418        assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "linkToStatic", metaAccess));
419        assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "linkToSpecial", metaAccess));
420        assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "linkToInterface", metaAccess));
421        assertFalse(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "type", metaAccess));
422        assertFalse(ResolvedJavaMethod.isSignaturePolymorphic(metaAccess.lookupJavaType(Object.class), "toString", metaAccess));
423    }
424
425    private Method findTestMethod(Method apiMethod) {
426        String testName = apiMethod.getName() + "Test";
427        for (Method m : getClass().getDeclaredMethods()) {
428            if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) {
429                return m;
430            }
431        }
432        return null;
433    }
434
435    // @formatter:off
436    private static final String[] untestedApiMethods = {
437        "invoke",
438        "newInstance",
439        "getDeclaringClass",
440        "getEncoding",
441        "getProfilingInfo",
442        "reprofile",
443        "getCompilerStorage",
444        "canBeInlined",
445        "shouldBeInlined",
446        "getLineNumberTable",
447        "getLocalVariableTable",
448        "isInVirtualMethodTable",
449        "toParameterTypes",
450        "getParameterAnnotation",
451        "getSpeculationLog",
452        "isFinal",
453        "$jacocoInit"
454    };
455    // @formatter:on
456
457    /**
458     * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written
459     * for them or are added to {@link #untestedApiMethods}.
460     */
461    @Test
462    public void testCoverage() {
463        Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods));
464        for (Method m : ResolvedJavaMethod.class.getDeclaredMethods()) {
465            if (Modifier.isStatic(m.getModifiers())) {
466                continue;
467            }
468            if (findTestMethod(m) == null) {
469                assertTrue("test missing for " + m, known.contains(m.getName()));
470            } else {
471                assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName()));
472            }
473        }
474    }
475}
476