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