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