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