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         }
 331 
 332         if (!type.isArray()) {
 333             ResolvedJavaType arrayType = type.getArrayClass();
 334             AssumptionResult<ResolvedJavaType> arraySubtype = arrayType.findLeafConcreteSubtype();
 335             if (arraySubtype != null) {
 336                 assertEquals(arraySubtype.getResult(), arrayType);
 337             } else {
 338                 // findLeafConcreteSubtype() method is conservative
 339             }
 340         }
 341     }
 342 
 343     @Test
 344     public void findLeafConcreteSubtypeTest() {
 345         ResolvedJavaType base = metaAccess.lookupJavaType(Base.class);
 346         checkConcreteSubtype(base, base);
 347 
 348         ResolvedJavaType a1 = metaAccess.lookupJavaType(Abstract1.class);
 349         ResolvedJavaType c1 = metaAccess.lookupJavaType(Concrete1.class);
 350 
 351         checkConcreteSubtype(base, null);
 352         checkConcreteSubtype(a1, c1);
 353         checkConcreteSubtype(c1, c1);
 354 
 355         ResolvedJavaType i1 = metaAccess.lookupJavaType(Interface1.class);
 356         ResolvedJavaType c2 = metaAccess.lookupJavaType(Concrete2.class);
 357 
 358         checkConcreteSubtype(base, null);
 359         checkConcreteSubtype(a1, null);
 360         checkConcreteSubtype(c1, c1);
 361         checkConcreteSubtype(i1, c2);
 362         checkConcreteSubtype(c2, c2);
 363 
 364         ResolvedJavaType c3 = metaAccess.lookupJavaType(Concrete3.class);
 365         checkConcreteSubtype(c2, null);
 366         checkConcreteSubtype(c3, c3);
 367 
 368         ResolvedJavaType a4 = metaAccess.lookupJavaType(Abstract4.class);
 369         checkConcreteSubtype(c3, null);
 370         checkConcreteSubtype(a4, null);
 371 
 372         ResolvedJavaType a1a = metaAccess.lookupJavaType(Abstract1[].class);
 373         checkConcreteSubtype(a1a, null);
 374         ResolvedJavaType c1a = metaAccess.lookupJavaType(Concrete1[].class);
 375         checkConcreteSubtype(c1a, null);
 376         ResolvedJavaType f1a = metaAccess.lookupJavaType(Final1[].class);
 377         checkConcreteSubtype(f1a, f1a);
 378 
 379         ResolvedJavaType obja = metaAccess.lookupJavaType(Object[].class);
 380         checkConcreteSubtype(obja, null);
 381 
 382         ResolvedJavaType inta = metaAccess.lookupJavaType(int[].class);
 383         checkConcreteSubtype(inta, inta);
 384     }
 385 
 386     interface NoImplementor {
 387     }
 388 
 389     interface SingleImplementorInterface {
 390     }
 391 
 392     static class SingleConcreteImplementor implements SingleImplementorInterface {
 393     }
 394 
 395     interface SingleAbstractImplementorInterface {
 396     }
 397 
 398     abstract static class SingleAbstractImplementor implements SingleAbstractImplementorInterface {
 399     }
 400 
 401     interface MultiImplementorInterface {
 402     }
 403 
 404     static class ConcreteImplementor1 implements MultiImplementorInterface {
 405     }
 406 
 407     static class ConcreteImplementor2 implements MultiImplementorInterface {
 408     }
 409 
 410     interface MultipleAbstractImplementorInterface {
 411     }
 412 
 413     abstract static class MultiAbstractImplementor1 implements MultipleAbstractImplementorInterface {
 414     }
 415 
 416     abstract static class MultiAbstractImplementor2 implements MultipleAbstractImplementorInterface {
 417     }
 418 
 419     interface SingleAbstractImplementorInterface2 {
 420     }
 421 
 422     interface ExtendedSingleImplementorInterface {
 423     }
 424 
 425     abstract static class SingleAbstractImplementor2 implements SingleAbstractImplementorInterface2 {
 426     }
 427 
 428     static class ConcreteTransitiveImplementor1 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface {
 429     }
 430 
 431     static class ConcreteTransitiveImplementor2 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface {
 432     }
 433 
 434     @Test
 435     public void getSingleImplementorTest() {
 436         ResolvedJavaType iNi = metaAccess.lookupJavaType(NoImplementor.class);
 437         assertNull(iNi.getSingleImplementor());
 438 
 439         ResolvedJavaType iSi = metaAccess.lookupJavaType(SingleImplementorInterface.class);
 440         ResolvedJavaType cSi = metaAccess.lookupJavaType(SingleConcreteImplementor.class);
 441         assertEquals(cSi, iSi.getSingleImplementor());
 442 
 443         ResolvedJavaType iSai = metaAccess.lookupJavaType(SingleAbstractImplementorInterface.class);
 444         ResolvedJavaType aSai = metaAccess.lookupJavaType(SingleAbstractImplementor.class);
 445         assertEquals(aSai, iSai.getSingleImplementor());
 446 
 447         ResolvedJavaType iMi = metaAccess.lookupJavaType(MultiImplementorInterface.class);
 448         metaAccess.lookupJavaType(ConcreteImplementor1.class);
 449         metaAccess.lookupJavaType(ConcreteImplementor2.class);
 450         assertEquals(iMi, iMi.getSingleImplementor());
 451 
 452         ResolvedJavaType iMai = metaAccess.lookupJavaType(MultipleAbstractImplementorInterface.class);
 453         metaAccess.lookupJavaType(MultiAbstractImplementor1.class);
 454         metaAccess.lookupJavaType(MultiAbstractImplementor2.class);
 455         assertEquals(iMai, iMai.getSingleImplementor());
 456 
 457         ResolvedJavaType iSai2 = metaAccess.lookupJavaType(SingleAbstractImplementorInterface2.class);
 458         ResolvedJavaType aSai2 = metaAccess.lookupJavaType(SingleAbstractImplementor2.class);
 459         metaAccess.lookupJavaType(ConcreteTransitiveImplementor1.class);
 460         metaAccess.lookupJavaType(ConcreteTransitiveImplementor2.class);
 461         assertEquals(aSai2, iSai2.getSingleImplementor());
 462     }
 463 
 464     @Test(expected = JVMCIError.class)
 465     public void getSingleImplementorTestClassReceiver() {
 466         ResolvedJavaType base = metaAccess.lookupJavaType(Base.class);
 467         base.getSingleImplementor();
 468     }
 469 
 470     @Test(expected = JVMCIError.class)
 471     public void getSingleImplementorTestPrimitiveReceiver() {
 472         ResolvedJavaType primitive = metaAccess.lookupJavaType(int.class);
 473         primitive.getSingleImplementor();
 474     }
 475 
 476     @Test
 477     public void getComponentTypeTest() {
 478         for (Class<?> c : classes) {
 479             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 480             Class<?> expected = c.getComponentType();
 481             ResolvedJavaType actual = type.getComponentType();
 482             if (expected == null) {
 483                 assertNull(actual);
 484             } else {
 485                 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
 486             }
 487         }
 488     }
 489 
 490     @Test
 491     public void getArrayClassTest() {
 492         for (Class<?> c : classes) {
 493             if (c != void.class) {
 494                 ResolvedJavaType type = metaAccess.lookupJavaType(c);
 495                 Class<?> expected = getArrayClass(c);
 496                 ResolvedJavaType actual = type.getArrayClass();
 497                 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
 498             }
 499         }
 500     }
 501 
 502     static class Declarations {
 503 
 504         final Method implementation;
 505         final Set<Method> declarations;
 506 
 507         Declarations(Method impl) {
 508             this.implementation = impl;
 509             declarations = new HashSet<>();
 510         }
 511     }
 512 
 513     /**
 514      * See <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.5">Method
 515      * overriding</a>.
 516      */
 517     static boolean isOverriderOf(Method impl, Method m) {
 518         if (!isPrivate(m.getModifiers()) && !isFinal(m.getModifiers())) {
 519             if (m.getName().equals(impl.getName())) {
 520                 if (m.getReturnType() == impl.getReturnType()) {
 521                     if (Arrays.equals(m.getParameterTypes(), impl.getParameterTypes())) {
 522                         if (isPublic(m.getModifiers()) || isProtected(m.getModifiers())) {
 523                             // m is public or protected
 524                             return isPublic(impl.getModifiers()) || isProtected(impl.getModifiers());
 525                         } else {
 526                             // m is package-private
 527                             return impl.getDeclaringClass().getPackage() == m.getDeclaringClass().getPackage();
 528                         }
 529                     }
 530                 }
 531             }
 532         }
 533         return false;
 534     }
 535 
 536     static final Map<Class<?>, VTable> vtables = new HashMap<>();
 537 
 538     static class VTable {
 539 
 540         final Map<NameAndSignature, Method> methods = new HashMap<>();
 541     }
 542 
 543     static synchronized VTable getVTable(Class<?> c) {
 544         VTable vtable = vtables.get(c);
 545         if (vtable == null) {
 546             vtable = new VTable();
 547             if (c != Object.class) {
 548                 VTable superVtable = getVTable(c.getSuperclass());
 549                 vtable.methods.putAll(superVtable.methods);
 550             }
 551             for (Method m : c.getDeclaredMethods()) {
 552                 if (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) {
 553                     if (isAbstract(m.getModifiers())) {
 554                         // A subclass makes a concrete method in a superclass abstract
 555                         vtable.methods.remove(new NameAndSignature(m));
 556                     } else {
 557                         vtable.methods.put(new NameAndSignature(m), m);
 558                     }
 559                 }
 560             }
 561             vtables.put(c, vtable);
 562         }
 563         return vtable;
 564     }
 565 
 566     static Set<Method> findDeclarations(Method impl, Class<?> c) {
 567         Set<Method> declarations = new HashSet<>();
 568         NameAndSignature implSig = new NameAndSignature(impl);
 569         if (c != null) {
 570             for (Method m : c.getDeclaredMethods()) {
 571                 if (new NameAndSignature(m).equals(implSig)) {
 572                     declarations.add(m);
 573                     break;
 574                 }
 575             }
 576             if (!c.isInterface()) {
 577                 declarations.addAll(findDeclarations(impl, c.getSuperclass()));
 578             }
 579             for (Class<?> i : c.getInterfaces()) {
 580                 declarations.addAll(findDeclarations(impl, i));
 581             }
 582         }
 583         return declarations;
 584     }
 585 
 586     @Test
 587     public void resolveMethodTest() {
 588         ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class);
 589         for (Class<?> c : classes) {
 590             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 591             if (c.isInterface()) {
 592                 for (Method m : c.getDeclaredMethods()) {
 593                     ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m);
 594                     ResolvedJavaMethod impl = type.resolveMethod(resolved, context);
 595                     assertEquals(m.toString(), null, impl);
 596                 }
 597             } else if (c.isPrimitive()) {
 598                 assertEquals("No methods expected", c.getDeclaredMethods().length, 0);
 599             } else {
 600                 VTable vtable = getVTable(c);
 601                 for (Method impl : vtable.methods.values()) {
 602                     Set<Method> decls = findDeclarations(impl, c);
 603                     for (Method decl : decls) {
 604                         ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl);
 605                         if (m.isPublic()) {
 606                             ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl);
 607                             assertEquals(m.toString(), i, type.resolveMethod(m, context));
 608                         }
 609                     }
 610                 }
 611             }
 612         }
 613     }
 614 
 615     @Test
 616     public void resolveConcreteMethodTest() {
 617         ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class);
 618         for (Class<?> c : classes) {
 619             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 620             if (c.isInterface()) {
 621                 for (Method m : c.getDeclaredMethods()) {
 622                     ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m);
 623                     ResolvedJavaMethod impl = type.resolveConcreteMethod(resolved, context);
 624                     assertEquals(m.toString(), null, impl);
 625                 }
 626             } else if (c.isPrimitive()) {
 627                 assertEquals("No methods expected", c.getDeclaredMethods().length, 0);
 628             } else {
 629                 VTable vtable = getVTable(c);
 630                 for (Method impl : vtable.methods.values()) {
 631                     Set<Method> decls = findDeclarations(impl, c);
 632                     for (Method decl : decls) {
 633                         ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl);
 634                         if (m.isPublic()) {
 635                             ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl);
 636                             assertEquals(i, type.resolveConcreteMethod(m, context));
 637                         }
 638                     }
 639                 }
 640                 for (Method m : c.getDeclaredMethods()) {
 641                     ResolvedJavaMethod impl = type.resolveConcreteMethod(metaAccess.lookupJavaMethod(m), context);
 642                     ResolvedJavaMethod expected = isAbstract(m.getModifiers()) ? null : impl;
 643                     assertEquals(type + " " + m.toString(), expected, impl);
 644                 }
 645             }
 646         }
 647     }
 648 
 649     @Test
 650     public void findUniqueConcreteMethodTest() throws NoSuchMethodException {
 651         ResolvedJavaMethod thisMethod = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("findUniqueConcreteMethodTest"));
 652         ResolvedJavaMethod ucm = metaAccess.lookupJavaType(getClass()).findUniqueConcreteMethod(thisMethod).getResult();
 653         assertEquals(thisMethod, ucm);
 654     }
 655 
 656     public static Set<Field> getInstanceFields(Class<?> c, boolean includeSuperclasses) {
 657         if (c.isArray() || c.isPrimitive() || c.isInterface()) {
 658             return Collections.emptySet();
 659         }
 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         if (includeSuperclasses && c != Object.class) {
 667             result.addAll(getInstanceFields(c.getSuperclass(), true));
 668         }
 669         return result;
 670     }
 671 
 672     public static Set<Field> getStaticFields(Class<?> c) {
 673         Set<Field> result = new HashSet<>();
 674         for (Field f : c.getDeclaredFields()) {
 675             if (Modifier.isStatic(f.getModifiers())) {
 676                 result.add(f);
 677             }
 678         }
 679         return result;
 680     }
 681 
 682     public boolean fieldsEqual(Field f, ResolvedJavaField rjf) {
 683         return rjf.getDeclaringClass().equals(metaAccess.lookupJavaType(f.getDeclaringClass())) && rjf.getName().equals(f.getName()) &&
 684                         rjf.getType().resolve(rjf.getDeclaringClass()).equals(metaAccess.lookupJavaType(f.getType()));
 685     }
 686 
 687     public ResolvedJavaField lookupField(ResolvedJavaField[] fields, Field key) {
 688         for (ResolvedJavaField rf : fields) {
 689             if (fieldsEqual(key, rf)) {
 690                 return rf;
 691             }
 692         }
 693         return null;
 694     }
 695 
 696     public Field lookupField(Set<Field> fields, ResolvedJavaField key) {
 697         for (Field f : fields) {
 698             if (fieldsEqual(f, key)) {
 699                 return f;
 700             }
 701         }
 702         return null;
 703     }
 704 
 705     private static boolean isHiddenFromReflection(ResolvedJavaField f) {
 706         if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Throwable.class)) && f.getName().equals("backtrace")) {
 707             return true;
 708         }
 709         if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ConstantPool.class)) && f.getName().equals("constantPoolOop")) {
 710             return true;
 711         }
 712         if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Class.class)) && f.getName().equals("classLoader")) {
 713             return true;
 714         }
 715         return false;
 716     }
 717 
 718     @Test
 719     public void getInstanceFieldsTest() {
 720         for (Class<?> c : classes) {
 721             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 722             for (boolean includeSuperclasses : new boolean[]{true, false}) {
 723                 Set<Field> expected = getInstanceFields(c, includeSuperclasses);
 724                 ResolvedJavaField[] actual = type.getInstanceFields(includeSuperclasses);
 725                 for (Field f : expected) {
 726                     assertNotNull(lookupField(actual, f));
 727                 }
 728                 for (ResolvedJavaField rf : actual) {
 729                     if (!isHiddenFromReflection(rf)) {
 730                         assertEquals(rf.toString(), lookupField(expected, rf) != null, !rf.isInternal());
 731                     }
 732                 }
 733 
 734                 // Test stability of getInstanceFields
 735                 ResolvedJavaField[] actual2 = type.getInstanceFields(includeSuperclasses);
 736                 assertArrayEquals(actual, actual2);
 737             }
 738         }
 739     }
 740 
 741     @Test
 742     public void getStaticFieldsTest() {
 743         for (Class<?> c : classes) {
 744             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 745             Set<Field> expected = getStaticFields(c);
 746             ResolvedJavaField[] actual = type.getStaticFields();
 747             for (Field f : expected) {
 748                 assertNotNull(lookupField(actual, f));
 749             }
 750             for (ResolvedJavaField rf : actual) {
 751                 if (!isHiddenFromReflection(rf)) {
 752                     assertEquals(lookupField(expected, rf) != null, !rf.isInternal());
 753                 }
 754             }
 755 
 756             // Test stability of getStaticFields
 757             ResolvedJavaField[] actual2 = type.getStaticFields();
 758             assertArrayEquals(actual, actual2);
 759         }
 760     }
 761 
 762     @Test
 763     public void getDeclaredMethodsTest() {
 764         for (Class<?> c : classes) {
 765             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 766             Method[] raw = c.getDeclaredMethods();
 767             Set<ResolvedJavaMethod> expected = new HashSet<>();
 768             for (Method m : raw) {
 769                 ResolvedJavaMethod resolvedMethod = metaAccess.lookupJavaMethod(m);
 770                 assertNotNull(resolvedMethod);
 771                 expected.add(resolvedMethod);
 772             }
 773             Set<ResolvedJavaMethod> actual = new HashSet<>(Arrays.asList(type.getDeclaredMethods()));
 774             assertEquals(expected, actual);
 775         }
 776     }
 777 
 778     static class A {
 779         static String name = "foo";
 780     }
 781 
 782     static class B extends A {
 783     }
 784 
 785     static class C {
 786     }
 787 
 788     static class D {
 789         void foo() {
 790             // use of assertions causes the class to have a <clinit>
 791             assert getClass() != null;
 792         }
 793     }
 794 
 795     static class SubD extends D {
 796 
 797     }
 798 
 799     @Test
 800     public void getClassInitializerTest() {
 801         assertNotNull(metaAccess.lookupJavaType(A.class).getClassInitializer());
 802         assertNotNull(metaAccess.lookupJavaType(D.class).getClassInitializer());
 803         assertNull(metaAccess.lookupJavaType(B.class).getClassInitializer());
 804         assertNull(metaAccess.lookupJavaType(C.class).getClassInitializer());
 805         assertNull(metaAccess.lookupJavaType(int.class).getClassInitializer());
 806         assertNull(metaAccess.lookupJavaType(void.class).getClassInitializer());
 807     }
 808 
 809     @Test
 810     public void getAnnotationsTest() {
 811         for (Class<?> c : classes) {
 812             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 813             assertArrayEquals(c.getAnnotations(), type.getAnnotations());
 814         }
 815     }
 816 
 817     @Test
 818     public void getAnnotationTest() {
 819         for (Class<?> c : classes) {
 820             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 821             for (Annotation a : c.getAnnotations()) {
 822                 assertEquals(a, type.getAnnotation(a.annotationType()));
 823             }
 824         }
 825     }
 826 
 827     @Test
 828     public void memberClassesTest() {
 829         for (Class<?> c : classes) {
 830             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 831             assertEquals(c.isLocalClass(), type.isLocal());
 832             assertEquals(c.isMemberClass(), type.isMember());
 833             Class<?> enclc = c.getEnclosingClass();
 834             ResolvedJavaType enclt = type.getEnclosingType();
 835             assertFalse(enclc == null ^ enclt == null);
 836             if (enclc != null) {
 837                 assertEquals(enclt, metaAccess.lookupJavaType(enclc));
 838             }
 839         }
 840     }
 841 
 842     @Test
 843     public void isTrustedInterfaceTypeTest() {
 844         for (Class<?> c : classes) {
 845             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 846             if (TrustedInterface.class.isAssignableFrom(c)) {
 847                 assertTrue(type.isTrustedInterfaceType());
 848             }
 849         }
 850     }
 851 
 852     @Test
 853     public void isLeafTest() {
 854         for (Class<?> c : classes) {
 855             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 856             ResolvedJavaType arrayType = c != void.class ? metaAccess.lookupJavaType(getArrayClass(c)) : null;
 857             if (c.isPrimitive()) {
 858                 assertTrue(type.isLeaf());
 859                 assertTrue(arrayType == null || arrayType.isLeaf());
 860             } else {
 861                 assertTrue(c.toString(), type.isLeaf() == arrayType.isLeaf());
 862                 if (!c.isArray()) {
 863                     assertTrue(c.toString(), type.isLeaf() == Modifier.isFinal(c.getModifiers()));
 864                 }
 865             }
 866         }
 867     }
 868 
 869     @Test
 870     public void findMethodTest() {
 871         try {
 872             ResolvedJavaMethod findFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V"));
 873             ResolvedJavaMethod expectedFoo = metaAccess.lookupJavaMethod(D.class.getDeclaredMethod("foo"));
 874             assertEquals(expectedFoo, findFoo);
 875 
 876             ResolvedJavaMethod wrongReturnTypeFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()I"));
 877             assertNull(wrongReturnTypeFoo);
 878 
 879             ResolvedJavaMethod wrongArgumentsFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("(I)V"));
 880             assertNull(wrongArgumentsFoo);
 881 
 882             ResolvedJavaMethod wrongNameFoo = metaAccess.lookupJavaType(D.class).findMethod("bar", metaAccess.parseMethodDescriptor("()V"));
 883             assertNull(wrongNameFoo);
 884 
 885             ResolvedJavaMethod wrongClassFoo = metaAccess.lookupJavaType(SubD.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V"));
 886             assertNull(wrongClassFoo);
 887         } catch (NoSuchMethodException | SecurityException e) {
 888             throw new RuntimeException(e);
 889         }
 890     }
 891 
 892     private Method findTestMethod(Method apiMethod) {
 893         String testName = apiMethod.getName() + "Test";
 894         for (Method m : getClass().getDeclaredMethods()) {
 895             if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) {
 896                 return m;
 897             }
 898         }
 899         return null;
 900     }
 901 
 902     // @formatter:off
 903     private static final String[] untestedApiMethods = {
 904         "initialize",
 905         "isPrimitive",
 906         "newArray",
 907         "getDeclaredConstructors",
 908         "isInitialized",
 909         "isLinked",
 910         "getJavaClass",
 911         "getObjectHub",
 912         "hasFinalizableSubclass",
 913         "hasFinalizer",
 914         "getSourceFileName",
 915         "getClassFilePath",
 916         "isLocal",
 917         "isJavaLangObject",
 918         "isMember",
 919         "getElementalType",
 920         "getEnclosingType",
 921         "$jacocoInit",
 922         "isCpiSet",
 923         "getCorrespondingCpi",
 924         "setCorrespondingCpi"
 925     };
 926     // @formatter:on
 927 
 928     /**
 929      * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written
 930      * for them or are added to {@link #untestedApiMethods}.
 931      */
 932     @Test
 933     public void testCoverage() {
 934         Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods));
 935         for (Method m : ResolvedJavaType.class.getDeclaredMethods()) {
 936             if (findTestMethod(m) == null) {
 937                 assertTrue("test missing for " + m, known.contains(m.getName()));
 938             } else {
 939                 assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName()));
 940             }
 941         }
 942     }
 943 }