TestResolvedJavaType.java revision 9814:22fd02fad88b
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 * @compile TestResolvedJavaType.java TypeUniverse.java TestMetaAccessProvider.java NameAndSignature.java
28 * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI jdk.vm.ci.runtime.test.TestResolvedJavaType
29 */
30
31package jdk.vm.ci.runtime.test;
32
33import static java.lang.reflect.Modifier.isAbstract;
34import static java.lang.reflect.Modifier.isFinal;
35import static java.lang.reflect.Modifier.isPrivate;
36import static java.lang.reflect.Modifier.isProtected;
37import static java.lang.reflect.Modifier.isPublic;
38import static java.lang.reflect.Modifier.isStatic;
39import static org.junit.Assert.assertArrayEquals;
40import static org.junit.Assert.assertEquals;
41import static org.junit.Assert.assertFalse;
42import static org.junit.Assert.assertNotNull;
43import static org.junit.Assert.assertNull;
44import static org.junit.Assert.assertTrue;
45
46import java.lang.annotation.Annotation;
47import java.lang.reflect.Field;
48import java.lang.reflect.Method;
49import java.lang.reflect.Modifier;
50import java.net.URL;
51import java.util.Arrays;
52import java.util.Collections;
53import java.util.HashMap;
54import java.util.HashSet;
55import java.util.Map;
56import java.util.Set;
57
58import jdk.vm.ci.common.JVMCIError;
59import jdk.vm.ci.meta.Assumptions.AssumptionResult;
60import jdk.vm.ci.meta.JavaConstant;
61import jdk.vm.ci.meta.JavaKind;
62import jdk.vm.ci.meta.ModifiersProvider;
63import jdk.vm.ci.meta.ResolvedJavaField;
64import jdk.vm.ci.meta.ResolvedJavaMethod;
65import jdk.vm.ci.meta.ResolvedJavaType;
66import jdk.vm.ci.meta.TrustedInterface;
67
68import org.junit.Test;
69
70import sun.reflect.ConstantPool;
71
72/**
73 * Tests for {@link ResolvedJavaType}.
74 */
75public class TestResolvedJavaType extends TypeUniverse {
76
77    public TestResolvedJavaType() {
78    }
79
80    @Test
81    public void findInstanceFieldWithOffsetTest() {
82        for (Class<?> c : classes) {
83            ResolvedJavaType type = metaAccess.lookupJavaType(c);
84            Set<Field> reflectionFields = getInstanceFields(c, true);
85            for (Field f : reflectionFields) {
86                ResolvedJavaField rf = lookupField(type.getInstanceFields(true), f);
87                assertNotNull(rf);
88                long offset = isStatic(f.getModifiers()) ? unsafe.staticFieldOffset(f) : unsafe.objectFieldOffset(f);
89                ResolvedJavaField result = type.findInstanceFieldWithOffset(offset, rf.getJavaKind());
90                assertNotNull(result);
91                assertTrue(fieldsEqual(f, result));
92            }
93        }
94    }
95
96    @Test
97    public void isInterfaceTest() {
98        for (Class<?> c : classes) {
99            ResolvedJavaType type = metaAccess.lookupJavaType(c);
100            boolean expected = c.isInterface();
101            boolean actual = type.isInterface();
102            assertEquals(expected, actual);
103        }
104    }
105
106    @Test
107    public void isInstanceClassTest() {
108        for (Class<?> c : classes) {
109            ResolvedJavaType type = metaAccess.lookupJavaType(c);
110            boolean expected = !c.isArray() && !c.isPrimitive() && !c.isInterface();
111            boolean actual = type.isInstanceClass();
112            assertEquals(expected, actual);
113        }
114    }
115
116    @Test
117    public void isArrayTest() {
118        for (Class<?> c : classes) {
119            ResolvedJavaType type = metaAccess.lookupJavaType(c);
120            boolean expected = c.isArray();
121            boolean actual = type.isArray();
122            assertEquals(expected, actual);
123        }
124    }
125
126    @Test
127    public void getModifiersTest() {
128        for (Class<?> c : classes) {
129            ResolvedJavaType type = metaAccess.lookupJavaType(c);
130            int expected = c.getModifiers() & ModifiersProvider.jvmClassModifiers();
131            int actual = type.getModifiers() & ModifiersProvider.jvmClassModifiers();
132            Class<?> elementalType = c;
133            while (elementalType.isArray()) {
134                elementalType = elementalType.getComponentType();
135            }
136            if (elementalType.isMemberClass()) {
137                // member class get their modifiers from the inner-class attribute in the JVM and
138                // from the classfile header in jvmci
139                expected &= ~(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED);
140                actual &= ~(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED);
141            }
142            assertEquals(String.format("%s: 0x%x != 0x%x", type, expected, actual), expected, actual);
143        }
144    }
145
146    @Test
147    public void isAssignableFromTest() {
148        Class<?>[] all = classes.toArray(new Class<?>[classes.size()]);
149        for (int i = 0; i < all.length; i++) {
150            Class<?> c1 = all[i];
151            for (int j = i; j < all.length; j++) {
152                Class<?> c2 = all[j];
153                ResolvedJavaType t1 = metaAccess.lookupJavaType(c1);
154                ResolvedJavaType t2 = metaAccess.lookupJavaType(c2);
155                boolean expected = c1.isAssignableFrom(c2);
156                boolean actual = t1.isAssignableFrom(t2);
157                assertEquals(expected, actual);
158                if (expected && t1 != t2) {
159                    assertFalse(t2.isAssignableFrom(t1));
160                }
161            }
162        }
163    }
164
165    @Test
166    public void isInstanceTest() {
167        for (ConstantValue cv : constants()) {
168            JavaConstant c = cv.value;
169            if (c.getJavaKind() == JavaKind.Object && !c.isNull()) {
170                ResolvedJavaType cType = metaAccess.lookupJavaType(c);
171                for (ResolvedJavaType t : javaTypes) {
172                    if (t.isAssignableFrom(cType)) {
173                        assertTrue(t.isInstance(c));
174                    } else {
175                        assertFalse(t.isInstance(c));
176                    }
177                }
178            }
179        }
180    }
181
182    private static Class<?> asExactClass(Class<?> c) {
183        if (c.isArray()) {
184            if (asExactClass(c.getComponentType()) != null) {
185                return c;
186            }
187        } else {
188            if (c.isPrimitive() || Modifier.isFinal(c.getModifiers())) {
189                return c;
190            }
191        }
192        return null;
193    }
194
195    @Test
196    public void asExactTypeTest() {
197        for (Class<?> c : classes) {
198            ResolvedJavaType type = metaAccess.lookupJavaType(c);
199            ResolvedJavaType exactType = type.asExactType();
200            Class<?> expected = asExactClass(c);
201            if (expected == null) {
202                assertTrue("exact(" + c.getName() + ") != null", exactType == null);
203            } else {
204                assertNotNull(exactType);
205                assertTrue(exactType.equals(metaAccess.lookupJavaType(expected)));
206            }
207        }
208    }
209
210    @Test
211    public void getSuperclassTest() {
212        for (Class<?> c : classes) {
213            ResolvedJavaType type = metaAccess.lookupJavaType(c);
214            Class<?> expected = c.getSuperclass();
215            ResolvedJavaType actual = type.getSuperclass();
216            if (expected == null) {
217                assertTrue(actual == null);
218            } else {
219                assertNotNull(actual);
220                assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
221            }
222        }
223    }
224
225    @Test
226    public void getInterfacesTest() {
227        for (Class<?> c : classes) {
228            ResolvedJavaType type = metaAccess.lookupJavaType(c);
229            Class<?>[] expected = c.getInterfaces();
230            ResolvedJavaType[] actual = type.getInterfaces();
231            assertEquals(expected.length, actual.length);
232            for (int i = 0; i < expected.length; i++) {
233                assertTrue(actual[i].equals(metaAccess.lookupJavaType(expected[i])));
234            }
235        }
236    }
237
238    public Class<?> getSupertype(Class<?> c) {
239        assert !c.isPrimitive();
240        if (c.isArray()) {
241            Class<?> componentType = c.getComponentType();
242            if (componentType.isPrimitive() || componentType == Object.class) {
243                return Object.class;
244            }
245            return getArrayClass(getSupertype(componentType));
246        }
247        if (c.isInterface()) {
248            return Object.class;
249        }
250        return c.getSuperclass();
251    }
252
253    public Class<?> findLeastCommonAncestor(Class<?> c1Initial, Class<?> c2Initial) {
254        if (c1Initial.isPrimitive() || c2Initial.isPrimitive()) {
255            return null;
256        } else {
257            Class<?> c1 = c1Initial;
258            Class<?> c2 = c2Initial;
259            while (true) {
260                if (c1.isAssignableFrom(c2)) {
261                    return c1;
262                }
263                if (c2.isAssignableFrom(c1)) {
264                    return c2;
265                }
266                c1 = getSupertype(c1);
267                c2 = getSupertype(c2);
268            }
269        }
270    }
271
272    @Test
273    public void findLeastCommonAncestorTest() {
274        Class<?>[] all = classes.toArray(new Class<?>[classes.size()]);
275        for (int i = 0; i < all.length; i++) {
276            Class<?> c1 = all[i];
277            for (int j = i; j < all.length; j++) {
278                Class<?> c2 = all[j];
279                ResolvedJavaType t1 = metaAccess.lookupJavaType(c1);
280                ResolvedJavaType t2 = metaAccess.lookupJavaType(c2);
281                Class<?> expected = findLeastCommonAncestor(c1, c2);
282                ResolvedJavaType actual = t1.findLeastCommonAncestor(t2);
283                if (expected == null) {
284                    assertTrue(actual == null);
285                } else {
286                    assertNotNull(actual);
287                    assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
288                }
289            }
290        }
291    }
292
293    private static class Base {
294    }
295
296    abstract static class Abstract1 extends Base {
297    }
298
299    interface Interface1 {
300    }
301
302    static class Concrete1 extends Abstract1 {
303    }
304
305    static class Concrete2 extends Abstract1 implements Interface1 {
306    }
307
308    static class Concrete3 extends Concrete2 {
309    }
310
311    static final class Final1 extends Abstract1 {
312    }
313
314    abstract static class Abstract4 extends Concrete3 {
315    }
316
317    void checkConcreteSubtype(ResolvedJavaType type, ResolvedJavaType expected) {
318        AssumptionResult<ResolvedJavaType> leafConcreteSubtype = type.findLeafConcreteSubtype();
319        if (leafConcreteSubtype == null) {
320            // findLeafConcreteSubtype() is conservative
321        } else {
322            if (expected == null) {
323                assertNull(leafConcreteSubtype);
324            } else {
325                assertTrue(leafConcreteSubtype.getResult().equals(expected));
326            }
327        }
328
329        if (!type.isArray()) {
330            ResolvedJavaType arrayType = type.getArrayClass();
331            AssumptionResult<ResolvedJavaType> arraySubtype = arrayType.findLeafConcreteSubtype();
332            if (arraySubtype != null) {
333                assertEquals(arraySubtype.getResult(), arrayType);
334            } else {
335                // findLeafConcreteSubtype() method is conservative
336            }
337        }
338    }
339
340    @Test
341    public void findLeafConcreteSubtypeTest() {
342        ResolvedJavaType base = metaAccess.lookupJavaType(Base.class);
343        checkConcreteSubtype(base, base);
344
345        ResolvedJavaType a1 = metaAccess.lookupJavaType(Abstract1.class);
346        ResolvedJavaType c1 = metaAccess.lookupJavaType(Concrete1.class);
347
348        checkConcreteSubtype(base, null);
349        checkConcreteSubtype(a1, c1);
350        checkConcreteSubtype(c1, c1);
351
352        ResolvedJavaType i1 = metaAccess.lookupJavaType(Interface1.class);
353        ResolvedJavaType c2 = metaAccess.lookupJavaType(Concrete2.class);
354
355        checkConcreteSubtype(base, null);
356        checkConcreteSubtype(a1, null);
357        checkConcreteSubtype(c1, c1);
358        checkConcreteSubtype(i1, c2);
359        checkConcreteSubtype(c2, c2);
360
361        ResolvedJavaType c3 = metaAccess.lookupJavaType(Concrete3.class);
362        checkConcreteSubtype(c2, null);
363        checkConcreteSubtype(c3, c3);
364
365        ResolvedJavaType a4 = metaAccess.lookupJavaType(Abstract4.class);
366        checkConcreteSubtype(c3, null);
367        checkConcreteSubtype(a4, null);
368
369        ResolvedJavaType a1a = metaAccess.lookupJavaType(Abstract1[].class);
370        checkConcreteSubtype(a1a, null);
371        ResolvedJavaType c1a = metaAccess.lookupJavaType(Concrete1[].class);
372        checkConcreteSubtype(c1a, null);
373        ResolvedJavaType f1a = metaAccess.lookupJavaType(Final1[].class);
374        checkConcreteSubtype(f1a, f1a);
375
376        ResolvedJavaType obja = metaAccess.lookupJavaType(Object[].class);
377        checkConcreteSubtype(obja, null);
378
379        ResolvedJavaType inta = metaAccess.lookupJavaType(int[].class);
380        checkConcreteSubtype(inta, inta);
381    }
382
383    interface NoImplementor {
384    }
385
386    interface SingleImplementorInterface {
387    }
388
389    static class SingleConcreteImplementor implements SingleImplementorInterface {
390    }
391
392    interface SingleAbstractImplementorInterface {
393    }
394
395    abstract static class SingleAbstractImplementor implements SingleAbstractImplementorInterface {
396    }
397
398    interface MultiImplementorInterface {
399    }
400
401    static class ConcreteImplementor1 implements MultiImplementorInterface {
402    }
403
404    static class ConcreteImplementor2 implements MultiImplementorInterface {
405    }
406
407    interface MultipleAbstractImplementorInterface {
408    }
409
410    abstract static class MultiAbstractImplementor1 implements MultipleAbstractImplementorInterface {
411    }
412
413    abstract static class MultiAbstractImplementor2 implements MultipleAbstractImplementorInterface {
414    }
415
416    interface SingleAbstractImplementorInterface2 {
417    }
418
419    interface ExtendedSingleImplementorInterface {
420    }
421
422    abstract static class SingleAbstractImplementor2 implements SingleAbstractImplementorInterface2 {
423    }
424
425    static class ConcreteTransitiveImplementor1 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface {
426    }
427
428    static class ConcreteTransitiveImplementor2 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface {
429    }
430
431    @Test
432    public void getSingleImplementorTest() {
433        ResolvedJavaType iNi = metaAccess.lookupJavaType(NoImplementor.class);
434        assertNull(iNi.getSingleImplementor());
435
436        ResolvedJavaType iSi = metaAccess.lookupJavaType(SingleImplementorInterface.class);
437        ResolvedJavaType cSi = metaAccess.lookupJavaType(SingleConcreteImplementor.class);
438        assertEquals(cSi, iSi.getSingleImplementor());
439
440        ResolvedJavaType iSai = metaAccess.lookupJavaType(SingleAbstractImplementorInterface.class);
441        ResolvedJavaType aSai = metaAccess.lookupJavaType(SingleAbstractImplementor.class);
442        assertEquals(aSai, iSai.getSingleImplementor());
443
444        ResolvedJavaType iMi = metaAccess.lookupJavaType(MultiImplementorInterface.class);
445        metaAccess.lookupJavaType(ConcreteImplementor1.class);
446        metaAccess.lookupJavaType(ConcreteImplementor2.class);
447        assertEquals(iMi, iMi.getSingleImplementor());
448
449        ResolvedJavaType iMai = metaAccess.lookupJavaType(MultipleAbstractImplementorInterface.class);
450        metaAccess.lookupJavaType(MultiAbstractImplementor1.class);
451        metaAccess.lookupJavaType(MultiAbstractImplementor2.class);
452        assertEquals(iMai, iMai.getSingleImplementor());
453
454        ResolvedJavaType iSai2 = metaAccess.lookupJavaType(SingleAbstractImplementorInterface2.class);
455        ResolvedJavaType aSai2 = metaAccess.lookupJavaType(SingleAbstractImplementor2.class);
456        metaAccess.lookupJavaType(ConcreteTransitiveImplementor1.class);
457        metaAccess.lookupJavaType(ConcreteTransitiveImplementor2.class);
458        assertEquals(aSai2, iSai2.getSingleImplementor());
459    }
460
461    @Test(expected = JVMCIError.class)
462    public void getSingleImplementorTestClassReceiver() {
463        ResolvedJavaType base = metaAccess.lookupJavaType(Base.class);
464        base.getSingleImplementor();
465    }
466
467    @Test(expected = JVMCIError.class)
468    public void getSingleImplementorTestPrimitiveReceiver() {
469        ResolvedJavaType primitive = metaAccess.lookupJavaType(int.class);
470        primitive.getSingleImplementor();
471    }
472
473    @Test
474    public void getComponentTypeTest() {
475        for (Class<?> c : classes) {
476            ResolvedJavaType type = metaAccess.lookupJavaType(c);
477            Class<?> expected = c.getComponentType();
478            ResolvedJavaType actual = type.getComponentType();
479            if (expected == null) {
480                assertNull(actual);
481            } else {
482                assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
483            }
484        }
485    }
486
487    @Test
488    public void getArrayClassTest() {
489        for (Class<?> c : classes) {
490            if (c != void.class) {
491                ResolvedJavaType type = metaAccess.lookupJavaType(c);
492                Class<?> expected = getArrayClass(c);
493                ResolvedJavaType actual = type.getArrayClass();
494                assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
495            }
496        }
497    }
498
499    static class Declarations {
500
501        final Method implementation;
502        final Set<Method> declarations;
503
504        public Declarations(Method impl) {
505            this.implementation = impl;
506            declarations = new HashSet<>();
507        }
508    }
509
510    /**
511     * See <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.5">Method
512     * overriding</a>.
513     */
514    static boolean isOverriderOf(Method impl, Method m) {
515        if (!isPrivate(m.getModifiers()) && !isFinal(m.getModifiers())) {
516            if (m.getName().equals(impl.getName())) {
517                if (m.getReturnType() == impl.getReturnType()) {
518                    if (Arrays.equals(m.getParameterTypes(), impl.getParameterTypes())) {
519                        if (isPublic(m.getModifiers()) || isProtected(m.getModifiers())) {
520                            // m is public or protected
521                            return isPublic(impl.getModifiers()) || isProtected(impl.getModifiers());
522                        } else {
523                            // m is package-private
524                            return impl.getDeclaringClass().getPackage() == m.getDeclaringClass().getPackage();
525                        }
526                    }
527                }
528            }
529        }
530        return false;
531    }
532
533    static final Map<Class<?>, VTable> vtables = new HashMap<>();
534
535    static class VTable {
536
537        final Map<NameAndSignature, Method> methods = new HashMap<>();
538    }
539
540    static synchronized VTable getVTable(Class<?> c) {
541        VTable vtable = vtables.get(c);
542        if (vtable == null) {
543            vtable = new VTable();
544            if (c != Object.class) {
545                VTable superVtable = getVTable(c.getSuperclass());
546                vtable.methods.putAll(superVtable.methods);
547            }
548            for (Method m : c.getDeclaredMethods()) {
549                if (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) {
550                    if (isAbstract(m.getModifiers())) {
551                        // A subclass makes a concrete method in a superclass abstract
552                        vtable.methods.remove(new NameAndSignature(m));
553                    } else {
554                        vtable.methods.put(new NameAndSignature(m), m);
555                    }
556                }
557            }
558            vtables.put(c, vtable);
559        }
560        return vtable;
561    }
562
563    static Set<Method> findDeclarations(Method impl, Class<?> c) {
564        Set<Method> declarations = new HashSet<>();
565        NameAndSignature implSig = new NameAndSignature(impl);
566        if (c != null) {
567            for (Method m : c.getDeclaredMethods()) {
568                if (new NameAndSignature(m).equals(implSig)) {
569                    declarations.add(m);
570                    break;
571                }
572            }
573            if (!c.isInterface()) {
574                declarations.addAll(findDeclarations(impl, c.getSuperclass()));
575            }
576            for (Class<?> i : c.getInterfaces()) {
577                declarations.addAll(findDeclarations(impl, i));
578            }
579        }
580        return declarations;
581    }
582
583    private static void checkResolveMethod(ResolvedJavaType type, ResolvedJavaType context, ResolvedJavaMethod decl, ResolvedJavaMethod expected) {
584        ResolvedJavaMethod impl = type.resolveConcreteMethod(decl, context);
585        assertEquals(expected, impl);
586    }
587
588    @Test
589    public void resolveMethodTest() {
590        ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class);
591        for (Class<?> c : classes) {
592            if (c.isInterface() || c.isPrimitive()) {
593                ResolvedJavaType type = metaAccess.lookupJavaType(c);
594                for (Method m : c.getDeclaredMethods()) {
595                    if (JAVA_VERSION <= 1.7D || (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers()))) {
596                        ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m);
597                        ResolvedJavaMethod impl = type.resolveMethod(resolved, context);
598                        ResolvedJavaMethod expected = resolved.isDefault() || resolved.isAbstract() ? resolved : null;
599                        assertEquals(m.toString(), expected, impl);
600                    } else {
601                        // As of JDK 8, interfaces can have static and private methods
602                    }
603                }
604            } else {
605                ResolvedJavaType type = metaAccess.lookupJavaType(c);
606                VTable vtable = getVTable(c);
607                for (Method impl : vtable.methods.values()) {
608                    Set<Method> decls = findDeclarations(impl, c);
609                    for (Method decl : decls) {
610                        ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl);
611                        if (m.isPublic()) {
612                            ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl);
613                            checkResolveMethod(type, context, m, i);
614                        }
615                    }
616                }
617            }
618        }
619    }
620
621    @Test
622    public void resolveConcreteMethodTest() {
623        ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class);
624        for (Class<?> c : classes) {
625            if (c.isInterface() || c.isPrimitive()) {
626                ResolvedJavaType type = metaAccess.lookupJavaType(c);
627                for (Method m : c.getDeclaredMethods()) {
628                    if (JAVA_VERSION <= 1.7D || (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers()))) {
629                        ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m);
630                        ResolvedJavaMethod impl = type.resolveConcreteMethod(resolved, context);
631                        ResolvedJavaMethod expected = resolved.isDefault() ? resolved : null;
632                        assertEquals(m.toString(), expected, impl);
633                    } else {
634                        // As of JDK 8, interfaces can have static and private methods
635                    }
636                }
637            } else {
638                ResolvedJavaType type = metaAccess.lookupJavaType(c);
639                VTable vtable = getVTable(c);
640                for (Method impl : vtable.methods.values()) {
641                    Set<Method> decls = findDeclarations(impl, c);
642                    for (Method decl : decls) {
643                        ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl);
644                        if (m.isPublic()) {
645                            ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl);
646                            checkResolveMethod(type, context, m, i);
647                        }
648                    }
649                }
650                for (Method m : c.getDeclaredMethods()) {
651                    ResolvedJavaMethod impl = type.resolveConcreteMethod(metaAccess.lookupJavaMethod(m), context);
652                    ResolvedJavaMethod expected = isAbstract(m.getModifiers()) ? null : impl;
653                    assertEquals(type + " " + m.toString(), expected, impl);
654                }
655            }
656        }
657    }
658
659    @Test
660    public void findUniqueConcreteMethodTest() throws NoSuchMethodException {
661        ResolvedJavaMethod thisMethod = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("findUniqueConcreteMethodTest"));
662        ResolvedJavaMethod ucm = metaAccess.lookupJavaType(getClass()).findUniqueConcreteMethod(thisMethod).getResult();
663        assertEquals(thisMethod, ucm);
664    }
665
666    public static Set<Field> getInstanceFields(Class<?> c, boolean includeSuperclasses) {
667        if (c.isArray() || c.isPrimitive() || c.isInterface()) {
668            return Collections.emptySet();
669        }
670        Set<Field> result = new HashSet<>();
671        for (Field f : c.getDeclaredFields()) {
672            if (!Modifier.isStatic(f.getModifiers())) {
673                result.add(f);
674            }
675        }
676        if (includeSuperclasses && c != Object.class) {
677            result.addAll(getInstanceFields(c.getSuperclass(), true));
678        }
679        return result;
680    }
681
682    public static Set<Field> getStaticFields(Class<?> c) {
683        Set<Field> result = new HashSet<>();
684        for (Field f : c.getDeclaredFields()) {
685            if (Modifier.isStatic(f.getModifiers())) {
686                result.add(f);
687            }
688        }
689        return result;
690    }
691
692    public boolean fieldsEqual(Field f, ResolvedJavaField rjf) {
693        return rjf.getDeclaringClass().equals(metaAccess.lookupJavaType(f.getDeclaringClass())) && rjf.getName().equals(f.getName()) &&
694                        rjf.getType().resolve(rjf.getDeclaringClass()).equals(metaAccess.lookupJavaType(f.getType()));
695    }
696
697    public ResolvedJavaField lookupField(ResolvedJavaField[] fields, Field key) {
698        for (ResolvedJavaField rf : fields) {
699            if (fieldsEqual(key, rf)) {
700                return rf;
701            }
702        }
703        return null;
704    }
705
706    public Field lookupField(Set<Field> fields, ResolvedJavaField key) {
707        for (Field f : fields) {
708            if (fieldsEqual(f, key)) {
709                return f;
710            }
711        }
712        return null;
713    }
714
715    private static boolean isHiddenFromReflection(ResolvedJavaField f) {
716        if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Throwable.class)) && f.getName().equals("backtrace")) {
717            return true;
718        }
719        if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ConstantPool.class)) && f.getName().equals("constantPoolOop")) {
720            return true;
721        }
722        if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Class.class)) && f.getName().equals("classLoader")) {
723            return true;
724        }
725        return false;
726    }
727
728    @Test
729    public void getInstanceFieldsTest() {
730        for (Class<?> c : classes) {
731            ResolvedJavaType type = metaAccess.lookupJavaType(c);
732            for (boolean includeSuperclasses : new boolean[]{true, false}) {
733                Set<Field> expected = getInstanceFields(c, includeSuperclasses);
734                ResolvedJavaField[] actual = type.getInstanceFields(includeSuperclasses);
735                for (Field f : expected) {
736                    assertNotNull(lookupField(actual, f));
737                }
738                for (ResolvedJavaField rf : actual) {
739                    if (!isHiddenFromReflection(rf)) {
740                        assertEquals(rf.toString(), lookupField(expected, rf) != null, !rf.isInternal());
741                    }
742                }
743
744                // Test stability of getInstanceFields
745                ResolvedJavaField[] actual2 = type.getInstanceFields(includeSuperclasses);
746                assertArrayEquals(actual, actual2);
747            }
748        }
749    }
750
751    @Test
752    public void getStaticFieldsTest() {
753        for (Class<?> c : classes) {
754            ResolvedJavaType type = metaAccess.lookupJavaType(c);
755            Set<Field> expected = getStaticFields(c);
756            ResolvedJavaField[] actual = type.getStaticFields();
757            for (Field f : expected) {
758                assertNotNull(lookupField(actual, f));
759            }
760            for (ResolvedJavaField rf : actual) {
761                if (!isHiddenFromReflection(rf)) {
762                    assertEquals(lookupField(expected, rf) != null, !rf.isInternal());
763                }
764            }
765
766            // Test stability of getStaticFields
767            ResolvedJavaField[] actual2 = type.getStaticFields();
768            assertArrayEquals(actual, actual2);
769        }
770    }
771
772    @Test
773    public void getDeclaredMethodsTest() {
774        for (Class<?> c : classes) {
775            ResolvedJavaType type = metaAccess.lookupJavaType(c);
776            Method[] raw = c.getDeclaredMethods();
777            Set<ResolvedJavaMethod> expected = new HashSet<>();
778            for (Method m : raw) {
779                ResolvedJavaMethod resolvedMethod = metaAccess.lookupJavaMethod(m);
780                assertNotNull(resolvedMethod);
781                expected.add(resolvedMethod);
782            }
783            Set<ResolvedJavaMethod> actual = new HashSet<>(Arrays.asList(type.getDeclaredMethods()));
784            assertEquals(expected, actual);
785        }
786    }
787
788    static class A {
789        static String name = "foo";
790    }
791
792    static class B extends A {
793    }
794
795    static class C {
796    }
797
798    static class D {
799        void foo() {
800            // use of assertions causes the class to have a <clinit>
801            assert getClass() != null;
802        }
803    }
804
805    static class SubD extends D {
806
807    }
808
809    @Test
810    public void getClassInitializerTest() {
811        assertNotNull(metaAccess.lookupJavaType(A.class).getClassInitializer());
812        assertNotNull(metaAccess.lookupJavaType(D.class).getClassInitializer());
813        assertNull(metaAccess.lookupJavaType(B.class).getClassInitializer());
814        assertNull(metaAccess.lookupJavaType(C.class).getClassInitializer());
815        assertNull(metaAccess.lookupJavaType(int.class).getClassInitializer());
816        assertNull(metaAccess.lookupJavaType(void.class).getClassInitializer());
817    }
818
819    @Test
820    public void getAnnotationsTest() {
821        for (Class<?> c : classes) {
822            ResolvedJavaType type = metaAccess.lookupJavaType(c);
823            assertArrayEquals(c.getAnnotations(), type.getAnnotations());
824        }
825    }
826
827    @Test
828    public void getAnnotationTest() {
829        for (Class<?> c : classes) {
830            ResolvedJavaType type = metaAccess.lookupJavaType(c);
831            for (Annotation a : c.getAnnotations()) {
832                assertEquals(a, type.getAnnotation(a.annotationType()));
833            }
834        }
835    }
836
837    @Test
838    public void memberClassesTest() {
839        for (Class<?> c : classes) {
840            ResolvedJavaType type = metaAccess.lookupJavaType(c);
841            assertEquals(c.isLocalClass(), type.isLocal());
842            assertEquals(c.isMemberClass(), type.isMember());
843            Class<?> enclc = c.getEnclosingClass();
844            ResolvedJavaType enclt = type.getEnclosingType();
845            assertFalse(enclc == null ^ enclt == null);
846            if (enclc != null) {
847                assertEquals(enclt, metaAccess.lookupJavaType(enclc));
848            }
849        }
850    }
851
852    @Test
853    public void classFilePathTest() {
854        for (Class<?> c : classes) {
855            ResolvedJavaType type = metaAccess.lookupJavaType(c);
856            URL path = type.getClassFilePath();
857            if (type.isPrimitive() || type.isArray()) {
858                assertEquals(null, path);
859            } else {
860                assertNotNull(path);
861                String pathString = path.getPath();
862                if (type.isLocal() || type.isMember()) {
863                    assertTrue(pathString.indexOf('$') > 0);
864                }
865            }
866        }
867    }
868
869    @Test
870    public void isTrustedInterfaceTypeTest() {
871        for (Class<?> c : classes) {
872            ResolvedJavaType type = metaAccess.lookupJavaType(c);
873            if (TrustedInterface.class.isAssignableFrom(c)) {
874                assertTrue(type.isTrustedInterfaceType());
875            }
876        }
877    }
878
879    @Test
880    public void isLeafTest() {
881        for (Class<?> c : classes) {
882            ResolvedJavaType type = metaAccess.lookupJavaType(c);
883            ResolvedJavaType arrayType = c != void.class ? metaAccess.lookupJavaType(getArrayClass(c)) : null;
884            if (c.isPrimitive()) {
885                assertTrue(type.isLeaf());
886                assertTrue(arrayType == null || arrayType.isLeaf());
887            } else {
888                assertTrue(c.toString(), type.isLeaf() == arrayType.isLeaf());
889                if (!c.isArray()) {
890                    assertTrue(c.toString(), type.isLeaf() == Modifier.isFinal(c.getModifiers()));
891                }
892            }
893        }
894    }
895
896    @Test
897    public void findMethodTest() {
898        try {
899            ResolvedJavaMethod findFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V"));
900            ResolvedJavaMethod expectedFoo = metaAccess.lookupJavaMethod(D.class.getDeclaredMethod("foo"));
901            assertEquals(expectedFoo, findFoo);
902
903            ResolvedJavaMethod wrongReturnTypeFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()I"));
904            assertNull(wrongReturnTypeFoo);
905
906            ResolvedJavaMethod wrongArgumentsFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("(I)V"));
907            assertNull(wrongArgumentsFoo);
908
909            ResolvedJavaMethod wrongNameFoo = metaAccess.lookupJavaType(D.class).findMethod("bar", metaAccess.parseMethodDescriptor("()V"));
910            assertNull(wrongNameFoo);
911
912            ResolvedJavaMethod wrongClassFoo = metaAccess.lookupJavaType(SubD.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V"));
913            assertNull(wrongClassFoo);
914        } catch (NoSuchMethodException | SecurityException e) {
915            throw new RuntimeException(e);
916        }
917    }
918
919    private Method findTestMethod(Method apiMethod) {
920        String testName = apiMethod.getName() + "Test";
921        for (Method m : getClass().getDeclaredMethods()) {
922            if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) {
923                return m;
924            }
925        }
926        return null;
927    }
928
929    // @formatter:off
930    private static final String[] untestedApiMethods = {
931        "initialize",
932        "isPrimitive",
933        "newArray",
934        "getDeclaredConstructors",
935        "isInitialized",
936        "isLinked",
937        "getJavaClass",
938        "getObjectHub",
939        "hasFinalizableSubclass",
940        "hasFinalizer",
941        "getSourceFileName",
942        "getClassFilePath",
943        "isLocal",
944        "isJavaLangObject",
945        "isMember",
946        "getElementalType",
947        "getEnclosingType",
948        "$jacocoInit",
949        "isCpiSet",
950        "getCorrespondingCpi",
951        "setCorrespondingCpi"
952    };
953    // @formatter:on
954
955    /**
956     * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written
957     * for them or are added to {@link #untestedApiMethods}.
958     */
959    @Test
960    public void testCoverage() {
961        Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods));
962        for (Method m : ResolvedJavaType.class.getDeclaredMethods()) {
963            if (findTestMethod(m) == null) {
964                assertTrue("test missing for " + m, known.contains(m.getName()));
965            } else {
966                assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName()));
967            }
968        }
969    }
970}
971