001/*
002 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package jdk.internal.jvmci.runtime.test;
024
025import static org.junit.Assert.*;
026
027import java.lang.annotation.*;
028import java.lang.invoke.*;
029import java.lang.reflect.*;
030import java.util.*;
031
032import jdk.internal.jvmci.meta.*;
033
034import org.junit.*;
035
036/**
037 * Tests for {@link ResolvedJavaMethod}.
038 */
039public class TestResolvedJavaMethod extends MethodUniverse {
040
041    public TestResolvedJavaMethod() {
042    }
043
044    /**
045     * @see ResolvedJavaMethod#getCode()
046     */
047    @Test
048    public void getCodeTest() {
049        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
050            ResolvedJavaMethod m = e.getValue();
051            byte[] code = m.getCode();
052            if (code == null) {
053                assertTrue(m.getCodeSize() == 0);
054            } else {
055                if (m.isAbstract()) {
056                    assertTrue(code.length == 0);
057                } else if (!m.isNative()) {
058                    assertTrue(code.length > 0);
059                }
060            }
061        }
062    }
063
064    /**
065     * @see ResolvedJavaMethod#getCodeSize()
066     */
067    @Test
068    public void getCodeSizeTest() {
069        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
070            ResolvedJavaMethod m = e.getValue();
071            int codeSize = m.getCodeSize();
072            if (m.isAbstract()) {
073                assertTrue(codeSize == 0);
074            } else if (!m.isNative()) {
075                assertTrue(codeSize > 0);
076            }
077        }
078    }
079
080    @Test
081    public void getModifiersTest() {
082        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
083            ResolvedJavaMethod m = e.getValue();
084            int expected = e.getKey().getModifiers();
085            int actual = m.getModifiers();
086            assertEquals(String.format("%s: 0x%x != 0x%x", m, expected, actual), expected, actual);
087        }
088        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
089            ResolvedJavaMethod m = e.getValue();
090            int expected = e.getKey().getModifiers();
091            int actual = m.getModifiers();
092            assertEquals(String.format("%s: 0x%x != 0x%x", m, expected, actual), expected, actual);
093        }
094    }
095
096    /**
097     * @see ResolvedJavaMethod#isClassInitializer()
098     */
099    @Test
100    public void isClassInitializerTest() {
101        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
102            // Class initializers are hidden from reflection
103            ResolvedJavaMethod m = e.getValue();
104            assertFalse(m.isClassInitializer());
105        }
106        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
107            ResolvedJavaMethod m = e.getValue();
108            assertFalse(m.isClassInitializer());
109        }
110    }
111
112    @Test
113    public void isConstructorTest() {
114        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
115            ResolvedJavaMethod m = e.getValue();
116            assertFalse(m.isConstructor());
117        }
118        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
119            ResolvedJavaMethod m = e.getValue();
120            assertTrue(m.isConstructor());
121        }
122    }
123
124    @Test
125    public void isSyntheticTest() {
126        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
127            ResolvedJavaMethod m = e.getValue();
128            assertEquals(e.getKey().isSynthetic(), m.isSynthetic());
129        }
130        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
131            ResolvedJavaMethod m = e.getValue();
132            assertEquals(e.getKey().isSynthetic(), m.isSynthetic());
133        }
134    }
135
136    @Test
137    public void isBridgeTest() {
138        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
139            ResolvedJavaMethod m = e.getValue();
140            assertEquals(e.getKey().isBridge(), m.isBridge());
141        }
142        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
143            ResolvedJavaMethod m = e.getValue();
144            assertEquals(false, m.isBridge());
145        }
146    }
147
148    @Test
149    public void isVarArgsTest() {
150        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
151            ResolvedJavaMethod m = e.getValue();
152            assertEquals(e.getKey().isVarArgs(), m.isVarArgs());
153        }
154        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
155            ResolvedJavaMethod m = e.getValue();
156            assertEquals(e.getKey().isVarArgs(), m.isVarArgs());
157        }
158    }
159
160    @Test
161    public void isSynchronizedTest() {
162        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
163            ResolvedJavaMethod m = e.getValue();
164            assertEquals(Modifier.isSynchronized(e.getKey().getModifiers()), m.isSynchronized());
165        }
166        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
167            ResolvedJavaMethod m = e.getValue();
168            assertEquals(Modifier.isSynchronized(e.getKey().getModifiers()), m.isSynchronized());
169        }
170    }
171
172    @Test
173    public void canBeStaticallyBoundTest() {
174        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
175            ResolvedJavaMethod m = e.getValue();
176            assertEquals(m.canBeStaticallyBound(), canBeStaticallyBound(e.getKey()));
177        }
178        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
179            ResolvedJavaMethod m = e.getValue();
180            assertEquals(m.canBeStaticallyBound(), canBeStaticallyBound(e.getKey()));
181        }
182    }
183
184    private static boolean canBeStaticallyBound(Member method) {
185        int modifiers = method.getModifiers();
186        return (Modifier.isFinal(modifiers) || Modifier.isPrivate(modifiers) || Modifier.isStatic(modifiers) || Modifier.isFinal(method.getDeclaringClass().getModifiers())) &&
187                        !Modifier.isAbstract(modifiers);
188    }
189
190    private static String methodWithExceptionHandlers(String p1, Object o2) {
191        try {
192            return p1.substring(100) + o2.toString();
193        } catch (IndexOutOfBoundsException e) {
194            e.printStackTrace();
195        } catch (NullPointerException e) {
196            e.printStackTrace();
197        } catch (RuntimeException e) {
198            e.printStackTrace();
199        }
200        return null;
201    }
202
203    @Test
204    public void getExceptionHandlersTest() throws NoSuchMethodException {
205        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithExceptionHandlers", String.class, Object.class));
206        ExceptionHandler[] handlers = method.getExceptionHandlers();
207        assertNotNull(handlers);
208        assertEquals(handlers.length, 3);
209        handlers[0].getCatchType().equals(metaAccess.lookupJavaType(IndexOutOfBoundsException.class));
210        handlers[1].getCatchType().equals(metaAccess.lookupJavaType(NullPointerException.class));
211        handlers[2].getCatchType().equals(metaAccess.lookupJavaType(RuntimeException.class));
212    }
213
214    private static String nullPointerExceptionOnFirstLine(Object o, String ignored) {
215        return o.toString() + ignored;
216    }
217
218    @Test
219    public void asStackTraceElementTest() throws NoSuchMethodException {
220        try {
221            nullPointerExceptionOnFirstLine(null, "ignored");
222            Assert.fail("should not reach here");
223        } catch (NullPointerException e) {
224            StackTraceElement expected = e.getStackTrace()[0];
225            ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("nullPointerExceptionOnFirstLine", Object.class, String.class));
226            StackTraceElement actual = method.asStackTraceElement(0);
227            assertEquals(expected, actual);
228        }
229    }
230
231    @Test
232    public void getConstantPoolTest() {
233        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
234            ResolvedJavaMethod m = e.getValue();
235            ConstantPool cp = m.getConstantPool();
236            assertTrue(cp.length() > 0);
237        }
238    }
239
240    @Test(timeout = 1000L)
241    public void getAnnotationTest() throws NoSuchMethodException {
242        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("getAnnotationTest"));
243        Test annotation = method.getAnnotation(Test.class);
244        assertNotNull(annotation);
245        assertEquals(1000L, annotation.timeout());
246    }
247
248    @Test(timeout = 1000L)
249    public void getAnnotationsTest() throws NoSuchMethodException {
250        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("getAnnotationsTest"));
251        Annotation[] annotations = method.getAnnotations();
252        assertNotNull(annotations);
253        assertEquals(1, annotations.length);
254        assertEquals(1000L, ((Test) annotations[0]).timeout());
255    }
256
257    @Retention(RetentionPolicy.RUNTIME)
258    @Target(ElementType.PARAMETER)
259    @interface NonNull {
260    }
261
262    @Retention(RetentionPolicy.RUNTIME)
263    @Target(ElementType.PARAMETER)
264    @interface Special {
265    }
266
267    private static native void methodWithAnnotatedParameters(@NonNull HashMap<String, String> p1, @Special @NonNull Class<? extends Annotation> p2);
268
269    @Test
270    public void getParameterAnnotationsTest() throws NoSuchMethodException {
271        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class));
272        Annotation[][] annotations = method.getParameterAnnotations();
273        assertEquals(2, annotations.length);
274        assertEquals(1, annotations[0].length);
275        assertEquals(NonNull.class, annotations[0][0].annotationType());
276        assertEquals(2, annotations[1].length);
277        assertEquals(Special.class, annotations[1][0].annotationType());
278        assertEquals(NonNull.class, annotations[1][1].annotationType());
279    }
280
281    @Test
282    public void getGenericParameterTypesTest() throws NoSuchMethodException {
283        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class));
284        Type[] genericParameterTypes = method.getGenericParameterTypes();
285        assertEquals(2, genericParameterTypes.length);
286        assertEquals("java.util.HashMap<java.lang.String, java.lang.String>", genericParameterTypes[0].toString());
287        assertEquals("java.lang.Class<? extends java.lang.annotation.Annotation>", genericParameterTypes[1].toString());
288    }
289
290    @Test
291    public void getMaxLocalsTest() throws NoSuchMethodException {
292        ResolvedJavaMethod method1 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class));
293        ResolvedJavaMethod method2 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("nullPointerExceptionOnFirstLine", Object.class, String.class));
294        assertEquals(0, method1.getMaxLocals());
295        assertEquals(2, method2.getMaxLocals());
296
297    }
298
299    @Test
300    public void getMaxStackSizeTest() throws NoSuchMethodException {
301        ResolvedJavaMethod method1 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class));
302        ResolvedJavaMethod method2 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("nullPointerExceptionOnFirstLine", Object.class, String.class));
303        assertEquals(0, method1.getMaxStackSize());
304        // some versions of javac produce bytecode with a stacksize of 2 for this method
305        // JSR 292 also sometimes need one more stack slot
306        int method2StackSize = method2.getMaxStackSize();
307        assertTrue(2 <= method2StackSize && method2StackSize <= 4);
308    }
309
310    @Test
311    public void isDefaultTest() {
312        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
313            ResolvedJavaMethod m = e.getValue();
314            assertEquals(e.getKey().isDefault(), m.isDefault());
315        }
316        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
317            ResolvedJavaMethod m = e.getValue();
318            assertFalse(m.isDefault());
319        }
320    }
321
322    @Test
323    public void hasReceiverTest() {
324        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
325            ResolvedJavaMethod m = e.getValue();
326            assertTrue(m.hasReceiver() != Modifier.isStatic(e.getKey().getModifiers()));
327        }
328        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
329            ResolvedJavaMethod m = e.getValue();
330            assertTrue(m.hasReceiver());
331        }
332    }
333
334    @Test
335    public void hasBytecodesTest() {
336        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
337            ResolvedJavaMethod m = e.getValue();
338            assertTrue(m.hasBytecodes() == (m.isConcrete() && !m.isNative()));
339        }
340        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
341            ResolvedJavaMethod m = e.getValue();
342            assertTrue(m.hasBytecodes());
343        }
344    }
345
346    @Test
347    public void isJavaLangObjectInitTest() throws NoSuchMethodException {
348        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(Object.class.getConstructor());
349        assertTrue(method.isJavaLangObjectInit());
350        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
351            ResolvedJavaMethod m = e.getValue();
352            assertFalse(m.isJavaLangObjectInit());
353        }
354        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
355            ResolvedJavaMethod m = e.getValue();
356            Constructor<?> key = e.getKey();
357            if (key.getDeclaringClass() == Object.class && key.getParameters().length == 0) {
358                assertTrue(m.isJavaLangObjectInit());
359            } else {
360                assertFalse(m.isJavaLangObjectInit());
361            }
362        }
363    }
364
365    @Test
366    public void isSignaturePolymorphicTest() {
367        ResolvedJavaType methodHandleType = metaAccess.lookupJavaType(MethodHandle.class);
368        assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "invokeExact", metaAccess));
369        assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "invoke", metaAccess));
370        assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "invokeBasic", metaAccess));
371        assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "linkToVirtual", metaAccess));
372        assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "linkToStatic", metaAccess));
373        assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "linkToSpecial", metaAccess));
374        assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "linkToInterface", metaAccess));
375        assertFalse(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "type", metaAccess));
376        assertFalse(ResolvedJavaMethod.isSignaturePolymorphic(metaAccess.lookupJavaType(Object.class), "toString", metaAccess));
377    }
378
379    private Method findTestMethod(Method apiMethod) {
380        String testName = apiMethod.getName() + "Test";
381        for (Method m : getClass().getDeclaredMethods()) {
382            if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) {
383                return m;
384            }
385        }
386        return null;
387    }
388
389    // @formatter:off
390    private static final String[] untestedApiMethods = {
391        "invoke",
392        "newInstance",
393        "getDeclaringClass",
394        "getEncoding",
395        "getProfilingInfo",
396        "reprofile",
397        "getCompilerStorage",
398        "canBeInlined",
399        "shouldBeInlined",
400        "getLineNumberTable",
401        "getLocalVariableTable",
402        "isInVirtualMethodTable",
403        "toParameterTypes",
404        "getParameterAnnotation",
405        "getSpeculationLog",
406        "isFinal",
407        "$jacocoInit"
408    };
409    // @formatter:on
410
411    /**
412     * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written
413     * for them or are added to {@link #untestedApiMethods}.
414     */
415    @Test
416    public void testCoverage() {
417        Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods));
418        for (Method m : ResolvedJavaMethod.class.getDeclaredMethods()) {
419            if (Modifier.isStatic(m.getModifiers())) {
420                continue;
421            }
422            if (findTestMethod(m) == null) {
423                assertTrue("test missing for " + m, known.contains(m.getName()));
424            } else {
425                assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName()));
426            }
427        }
428    }
429}