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