1 /*
   2  * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @requires vm.jvmci
  27  * @library ../../../../../
  28  * @compile ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/AnnotationTestInput.java
  29  *          ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/MemberDeleted.java
  30  *          ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/MemberTypeChanged.java
  31  * @clean jdk.internal.vm.test.AnnotationTestInput$Missing
  32  * @compile ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/alt/MemberDeleted.java
  33  *          ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/alt/MemberTypeChanged.java
  34  * @modules java.base/jdk.internal.org.objectweb.asm
  35  *          java.base/jdk.internal.reflect
  36  *          jdk.internal.vm.ci/jdk.vm.ci.meta
  37  *          jdk.internal.vm.ci/jdk.vm.ci.runtime
  38  *          jdk.internal.vm.ci/jdk.vm.ci.common
  39  *          java.base/jdk.internal.misc
  40  *          java.base/jdk.internal.vm
  41  *          java.base/sun.reflect.annotation
  42  * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:-UseJVMCICompiler jdk.vm.ci.runtime.test.TestResolvedJavaType
  43  */
  44 
  45 package jdk.vm.ci.runtime.test;
  46 
  47 import static java.lang.reflect.Modifier.isAbstract;
  48 import static java.lang.reflect.Modifier.isFinal;
  49 import static java.lang.reflect.Modifier.isPrivate;
  50 import static java.lang.reflect.Modifier.isProtected;
  51 import static java.lang.reflect.Modifier.isPublic;
  52 import static java.lang.reflect.Modifier.isStatic;
  53 import static jdk.vm.ci.meta.MetaUtil.internalNameToJava;
  54 import static jdk.vm.ci.meta.MetaUtil.toInternalName;
  55 import static org.junit.Assert.assertArrayEquals;
  56 import static org.junit.Assert.assertEquals;
  57 import static org.junit.Assert.assertFalse;
  58 import static org.junit.Assert.assertNotNull;
  59 import static org.junit.Assert.assertNull;
  60 import static org.junit.Assert.assertTrue;
  61 
  62 import java.io.DataInputStream;
  63 import java.io.IOException;
  64 import java.io.InputStream;
  65 import java.lang.annotation.Annotation;
  66 import java.lang.invoke.MethodHandles.Lookup;
  67 import java.lang.reflect.AccessibleObject;
  68 import java.lang.reflect.AnnotatedElement;
  69 import java.lang.reflect.Array;
  70 import java.lang.reflect.Constructor;
  71 import java.lang.reflect.Field;
  72 import java.lang.reflect.Method;
  73 import java.lang.reflect.Modifier;
  74 import java.util.Arrays;
  75 import java.util.Collections;
  76 import java.util.function.BiConsumer;
  77 import java.util.function.Supplier;
  78 import java.util.HashMap;
  79 import java.util.HashSet;
  80 import java.util.List;
  81 import java.util.Map;
  82 import java.util.Set;
  83 import java.util.stream.Collectors;
  84 import java.util.stream.Stream;
  85 
  86 import org.junit.Assert;
  87 import org.junit.Test;
  88 
  89 import jdk.internal.reflect.ConstantPool;
  90 import jdk.internal.vm.test.AnnotationTestInput;
  91 import jdk.vm.ci.common.JVMCIError;
  92 import jdk.vm.ci.meta.Annotated;
  93 import jdk.vm.ci.meta.AnnotationData;
  94 import jdk.vm.ci.meta.EnumData;
  95 import jdk.vm.ci.meta.Assumptions.AssumptionResult;
  96 import jdk.vm.ci.meta.JavaConstant;
  97 import jdk.vm.ci.meta.JavaKind;
  98 import jdk.vm.ci.meta.JavaType;
  99 import jdk.vm.ci.meta.MetaUtil;
 100 import jdk.vm.ci.meta.ResolvedJavaField;
 101 import jdk.vm.ci.meta.ResolvedJavaMethod;
 102 import jdk.vm.ci.meta.ResolvedJavaType;
 103 import jdk.vm.ci.meta.UnresolvedJavaType;
 104 import sun.reflect.annotation.AnnotationSupport;
 105 
 106 /**
 107  * Tests for {@link ResolvedJavaType}.
 108  */
 109 public class TestResolvedJavaType extends TypeUniverse {
 110     private static final Class<? extends Annotation> SIGNATURE_POLYMORPHIC_CLASS = findPolymorphicSignatureClass();
 111 
 112     public TestResolvedJavaType() {
 113     }
 114 
 115     @Test
 116     public void equalsTest() {
 117         for (ResolvedJavaType t : javaTypes) {
 118             for (ResolvedJavaType that : javaTypes) {
 119                 boolean expect = t == that;
 120                 boolean actual = t.equals(that);
 121                 assertEquals(expect, actual);
 122             }
 123         }
 124     }
 125 
 126     @SuppressWarnings("unchecked")
 127     private static Class<? extends Annotation> findPolymorphicSignatureClass() {
 128         Class<? extends Annotation> signaturePolyAnnotation = null;
 129         try {
 130             for (Class<?> clazz : TestResolvedJavaType.class.getClassLoader().loadClass("java.lang.invoke.MethodHandle").getDeclaredClasses()) {
 131                 if (clazz.getName().endsWith("PolymorphicSignature") && Annotation.class.isAssignableFrom(clazz)) {
 132                     signaturePolyAnnotation = (Class<? extends Annotation>) clazz;
 133                     break;
 134                 }
 135             }
 136         } catch (Throwable e) {
 137             throw new AssertionError("Could not find annotation PolymorphicSignature in java.lang.invoke.MethodHandle", e);
 138         }
 139         assertNotNull(signaturePolyAnnotation);
 140         return signaturePolyAnnotation;
 141     }
 142 
 143     @Test
 144     public void findInstanceFieldWithOffsetTest() {
 145         for (Class<?> c : classes) {
 146             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 147             Set<Field> reflectionFields = getInstanceFields(c, true);
 148             for (Field f : reflectionFields) {
 149                 ResolvedJavaField rf = lookupField(type.getInstanceFields(true), f);
 150                 assertNotNull(rf);
 151                 long offset = isStatic(f.getModifiers()) ? unsafe.staticFieldOffset(f) : unsafe.objectFieldOffset(f);
 152                 ResolvedJavaField result = type.findInstanceFieldWithOffset(offset, rf.getJavaKind());
 153                 assertNotNull(result);
 154                 assertTrue(fieldsEqual(f, result));
 155             }
 156         }
 157     }
 158 
 159     @Test
 160     public void isInterfaceTest() {
 161         for (Class<?> c : classes) {
 162             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 163             boolean expected = c.isInterface();
 164             boolean actual = type.isInterface();
 165             assertEquals(expected, actual);
 166         }
 167     }
 168 
 169     @Test
 170     public void isEnumTest() {
 171         for (Class<?> c : classes) {
 172             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 173             boolean expected = c.isEnum();
 174             boolean actual = type.isEnum();
 175             assertEquals(expected, actual);
 176         }
 177     }
 178 
 179     @Test
 180     public void isInstanceClassTest() {
 181         for (Class<?> c : classes) {
 182             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 183             boolean expected = !c.isArray() && !c.isPrimitive() && !c.isInterface();
 184             boolean actual = type.isInstanceClass();
 185             assertEquals(expected, actual);
 186         }
 187     }
 188 
 189     @Test
 190     public void isArrayTest() {
 191         for (Class<?> c : classes) {
 192             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 193             boolean expected = c.isArray();
 194             boolean actual = type.isArray();
 195             assertEquals(expected, actual);
 196         }
 197     }
 198 
 199     @Test
 200     public void lambdaInternalNameTest() {
 201         // Verify that the last dot in lambda types is properly handled when transitioning from
 202         // internal name to java
 203         // name and vice versa.
 204         Supplier<Runnable> lambda = () -> () -> System.out.println("run");
 205         ResolvedJavaType lambdaType = metaAccess.lookupJavaType(lambda.getClass());
 206         String typeName = lambdaType.getName();
 207         String javaName = lambda.getClass().getName();
 208         assertEquals(typeName, toInternalName(javaName));
 209         assertEquals(javaName, internalNameToJava(typeName, true, true));
 210     }
 211 
 212     @Test
 213     public void getModifiersTest() {
 214         for (Class<?> c : classes) {
 215             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 216             int mask = Modifier.classModifiers() & ~Modifier.STATIC;
 217             int expected = c.getModifiers() & mask;
 218             int actual = type.getModifiers() & mask;
 219             Class<?> elementalType = c;
 220             while (elementalType.isArray()) {
 221                 elementalType = elementalType.getComponentType();
 222             }
 223             if (elementalType.isMemberClass()) {
 224                 // member class get their modifiers from the inner-class attribute in the JVM and
 225                 // from the classfile header in jvmci
 226                 expected &= ~(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED);
 227                 actual &= ~(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED);
 228             }
 229             assertEquals(String.format("%s: 0x%x != 0x%x", type, expected, actual), expected, actual);
 230         }
 231     }
 232 
 233     @Test
 234     public void isAssignableFromTest() {
 235         Class<?>[] all = classes.toArray(new Class<?>[classes.size()]);
 236         for (int i = 0; i < all.length; i++) {
 237             Class<?> c1 = all[i];
 238             for (int j = i; j < all.length; j++) {
 239                 Class<?> c2 = all[j];
 240                 ResolvedJavaType t1 = metaAccess.lookupJavaType(c1);
 241                 ResolvedJavaType t2 = metaAccess.lookupJavaType(c2);
 242                 boolean expected = c1.isAssignableFrom(c2);
 243                 boolean actual = t1.isAssignableFrom(t2);
 244                 assertEquals(expected, actual);
 245                 if (expected && t1 != t2) {
 246                     assertFalse(t2.isAssignableFrom(t1));
 247                 }
 248             }
 249         }
 250     }
 251 
 252     @Test
 253     public void isInstanceTest() {
 254         for (ConstantValue cv : constants()) {
 255             JavaConstant c = cv.value;
 256             if (c.getJavaKind() == JavaKind.Object && !c.isNull()) {
 257                 ResolvedJavaType cType = metaAccess.lookupJavaType(c);
 258                 for (ResolvedJavaType t : javaTypes) {
 259                     if (t.isAssignableFrom(cType)) {
 260                         assertTrue(t.isInstance(c));
 261                     } else {
 262                         assertFalse(t.isInstance(c));
 263                     }
 264                 }
 265             }
 266         }
 267     }
 268 
 269     @Test
 270     public void getSuperclassTest() {
 271         for (Class<?> c : classes) {
 272             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 273             Class<?> expected = c.getSuperclass();
 274             ResolvedJavaType actual = type.getSuperclass();
 275             if (expected == null) {
 276                 assertTrue(actual == null);
 277             } else {
 278                 assertNotNull(actual);
 279                 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
 280             }
 281         }
 282     }
 283 
 284     @Test
 285     public void getInterfacesTest() {
 286         for (Class<?> c : classes) {
 287             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 288             Class<?>[] expected = c.getInterfaces();
 289             ResolvedJavaType[] actual = type.getInterfaces();
 290             assertEquals(expected.length, actual.length);
 291             for (int i = 0; i < expected.length; i++) {
 292                 assertTrue(actual[i].equals(metaAccess.lookupJavaType(expected[i])));
 293             }
 294         }
 295     }
 296 
 297     public Class<?> getSupertype(Class<?> c) {
 298         assert !c.isPrimitive();
 299         if (c.isArray()) {
 300             Class<?> componentType = c.getComponentType();
 301             if (componentType.isPrimitive() || componentType == Object.class) {
 302                 return Object.class;
 303             }
 304             return getArrayClass(getSupertype(componentType));
 305         }
 306         if (c.isInterface()) {
 307             return Object.class;
 308         }
 309         return c.getSuperclass();
 310     }
 311 
 312     public Class<?> findLeastCommonAncestor(Class<?> c1Initial, Class<?> c2Initial) {
 313         if (c1Initial.isPrimitive() || c2Initial.isPrimitive()) {
 314             return null;
 315         } else {
 316             Class<?> c1 = c1Initial;
 317             Class<?> c2 = c2Initial;
 318             while (true) {
 319                 if (c1.isAssignableFrom(c2)) {
 320                     return c1;
 321                 }
 322                 if (c2.isAssignableFrom(c1)) {
 323                     return c2;
 324                 }
 325                 c1 = getSupertype(c1);
 326                 c2 = getSupertype(c2);
 327             }
 328         }
 329     }
 330 
 331     @Test
 332     public void findLeastCommonAncestorTest() {
 333         Class<?>[] all = classes.toArray(new Class<?>[classes.size()]);
 334         for (int i = 0; i < all.length; i++) {
 335             Class<?> c1 = all[i];
 336             for (int j = i; j < all.length; j++) {
 337                 Class<?> c2 = all[j];
 338                 ResolvedJavaType t1 = metaAccess.lookupJavaType(c1);
 339                 ResolvedJavaType t2 = metaAccess.lookupJavaType(c2);
 340                 Class<?> expected = findLeastCommonAncestor(c1, c2);
 341                 ResolvedJavaType actual = t1.findLeastCommonAncestor(t2);
 342                 if (expected == null) {
 343                     assertTrue(actual == null);
 344                 } else {
 345                     assertNotNull(actual);
 346                     assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
 347                 }
 348             }
 349         }
 350     }
 351 
 352     @Test
 353     public void linkTest() {
 354         for (Class<?> c : classes) {
 355             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 356             type.link();
 357         }
 358     }
 359 
 360     private class HidingClassLoader extends ClassLoader {
 361         @Override
 362         protected Class<?> findClass(final String name) throws ClassNotFoundException {
 363             if (name.endsWith("MissingInterface")) {
 364                 throw new ClassNotFoundException("missing");
 365             }
 366             byte[] classData = null;
 367             try {
 368                 InputStream is = HidingClassLoader.class.getResourceAsStream("/" + name.replace('.', '/') + ".class");
 369                 classData = new byte[is.available()];
 370                 new DataInputStream(is).readFully(classData);
 371             } catch (IOException e) {
 372                 Assert.fail("can't access class: " + name);
 373             }
 374 
 375             return defineClass(null, classData, 0, classData.length);
 376         }
 377 
 378         ResolvedJavaType lookupJavaType(String name) throws ClassNotFoundException {
 379             return metaAccess.lookupJavaType(loadClass(name));
 380         }
 381 
 382         HidingClassLoader() {
 383             super(null);
 384         }
 385 
 386     }
 387 
 388     interface MissingInterface {
 389     }
 390 
 391     static class MissingInterfaceImpl implements MissingInterface {
 392     }
 393 
 394     interface SomeInterface {
 395         default MissingInterface someMethod() {
 396             return new MissingInterfaceImpl();
 397         }
 398     }
 399 
 400     static class Wrapper implements SomeInterface {
 401     }
 402 
 403     @Test
 404     public void linkExceptionTest() throws ClassNotFoundException {
 405         HidingClassLoader cl = new HidingClassLoader();
 406         ResolvedJavaType inner = cl.lookupJavaType(Wrapper.class.getName());
 407         assertTrue("expected default methods", inner.hasDefaultMethods());
 408         try {
 409             inner.link();
 410             assertFalse("link should throw an exception", true);
 411         } catch (NoClassDefFoundError e) {
 412         }
 413     }
 414 
 415     @Test
 416     public void hasDefaultMethodsTest() {
 417         for (Class<?> c : classes) {
 418             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 419             assertEquals(hasDefaultMethods(type), type.hasDefaultMethods());
 420         }
 421     }
 422 
 423     @Test
 424     public void declaresDefaultMethodsTest() {
 425         for (Class<?> c : classes) {
 426             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 427             assertEquals(declaresDefaultMethods(type), type.declaresDefaultMethods());
 428         }
 429     }
 430 
 431     private static boolean hasDefaultMethods(ResolvedJavaType type) {
 432         if (!type.isInterface() && type.getSuperclass() != null && hasDefaultMethods(type.getSuperclass())) {
 433             return true;
 434         }
 435         for (ResolvedJavaType iface : type.getInterfaces()) {
 436             if (hasDefaultMethods(iface)) {
 437                 return true;
 438             }
 439         }
 440         return declaresDefaultMethods(type);
 441     }
 442 
 443     static boolean declaresDefaultMethods(ResolvedJavaType type) {
 444         if (!type.isInterface()) {
 445             /* Only interfaces can declare default methods. */
 446             return false;
 447         }
 448         for (ResolvedJavaMethod method : type.getDeclaredMethods()) {
 449             if (method.isDefault()) {
 450                 assert !Modifier.isStatic(method.getModifiers()) : "Default method that is static?";
 451                 return true;
 452             }
 453         }
 454         return false;
 455     }
 456 
 457     private static class Base {
 458     }
 459 
 460     abstract static class Abstract1 extends Base {
 461     }
 462 
 463     interface Interface1 {
 464     }
 465 
 466     static class Concrete1 extends Abstract1 {
 467     }
 468 
 469     static class Concrete2 extends Abstract1 implements Interface1 {
 470     }
 471 
 472     static class Concrete3 extends Concrete2 {
 473     }
 474 
 475     static final class Final1 extends Abstract1 {
 476     }
 477 
 478     abstract static class Abstract4 extends Concrete3 {
 479     }
 480 
 481     void checkConcreteSubtype(ResolvedJavaType type, ResolvedJavaType expected) {
 482         AssumptionResult<ResolvedJavaType> leafConcreteSubtype = type.findLeafConcreteSubtype();
 483         if (leafConcreteSubtype == null) {
 484             // findLeafConcreteSubtype() is conservative
 485         } else {
 486             if (expected == null) {
 487                 assertNull(leafConcreteSubtype);
 488             } else {
 489                 assertTrue(leafConcreteSubtype.getResult().equals(expected));
 490             }
 491             assertTrue(!type.isLeaf() || leafConcreteSubtype.isAssumptionFree());
 492         }
 493 
 494         if (!type.isArray()) {
 495             ResolvedJavaType arrayType = type.getArrayClass();
 496             AssumptionResult<ResolvedJavaType> arraySubtype = arrayType.findLeafConcreteSubtype();
 497             if (arraySubtype != null) {
 498                 assertEquals(arraySubtype.getResult(), arrayType);
 499             } else {
 500                 // findLeafConcreteSubtype() method is conservative
 501             }
 502         }
 503     }
 504 
 505     @Test
 506     public void findLeafConcreteSubtypeTest() {
 507         ResolvedJavaType base = metaAccess.lookupJavaType(Base.class);
 508         checkConcreteSubtype(base, base);
 509 
 510         ResolvedJavaType a1 = metaAccess.lookupJavaType(Abstract1.class);
 511         ResolvedJavaType c1 = metaAccess.lookupJavaType(Concrete1.class);
 512 
 513         checkConcreteSubtype(base, null);
 514         checkConcreteSubtype(a1, c1);
 515         checkConcreteSubtype(c1, c1);
 516 
 517         ResolvedJavaType i1 = metaAccess.lookupJavaType(Interface1.class);
 518         ResolvedJavaType c2 = metaAccess.lookupJavaType(Concrete2.class);
 519 
 520         checkConcreteSubtype(base, null);
 521         checkConcreteSubtype(a1, null);
 522         checkConcreteSubtype(c1, c1);
 523         checkConcreteSubtype(i1, c2);
 524         checkConcreteSubtype(c2, c2);
 525 
 526         ResolvedJavaType c3 = metaAccess.lookupJavaType(Concrete3.class);
 527         checkConcreteSubtype(c2, null);
 528         checkConcreteSubtype(c3, c3);
 529 
 530         ResolvedJavaType a4 = metaAccess.lookupJavaType(Abstract4.class);
 531         checkConcreteSubtype(c3, null);
 532         checkConcreteSubtype(a4, null);
 533 
 534         ResolvedJavaType a1a = metaAccess.lookupJavaType(Abstract1[].class);
 535         checkConcreteSubtype(a1a, null);
 536         ResolvedJavaType i1a = metaAccess.lookupJavaType(Interface1[].class);
 537         checkConcreteSubtype(i1a, null);
 538         ResolvedJavaType c1a = metaAccess.lookupJavaType(Concrete1[].class);
 539         checkConcreteSubtype(c1a, c1a);
 540         ResolvedJavaType f1a = metaAccess.lookupJavaType(Final1[].class);
 541         checkConcreteSubtype(f1a, f1a);
 542 
 543         ResolvedJavaType obja = metaAccess.lookupJavaType(Object[].class);
 544         checkConcreteSubtype(obja, null);
 545 
 546         ResolvedJavaType inta = metaAccess.lookupJavaType(int[].class);
 547         checkConcreteSubtype(inta, inta);
 548     }
 549 
 550     interface NoImplementor {
 551     }
 552 
 553     interface SingleImplementorInterface {
 554     }
 555 
 556     static class SingleConcreteImplementor implements SingleImplementorInterface {
 557     }
 558 
 559     interface SingleAbstractImplementorInterface {
 560     }
 561 
 562     abstract static class SingleAbstractImplementor implements SingleAbstractImplementorInterface {
 563     }
 564 
 565     interface MultiImplementorInterface {
 566     }
 567 
 568     static class ConcreteImplementor1 implements MultiImplementorInterface {
 569     }
 570 
 571     static class ConcreteImplementor2 implements MultiImplementorInterface {
 572     }
 573 
 574     interface MultipleAbstractImplementorInterface {
 575     }
 576 
 577     abstract static class MultiAbstractImplementor1 implements MultipleAbstractImplementorInterface {
 578     }
 579 
 580     abstract static class MultiAbstractImplementor2 implements MultipleAbstractImplementorInterface {
 581     }
 582 
 583     interface SingleAbstractImplementorInterface2 {
 584     }
 585 
 586     interface ExtendedSingleImplementorInterface {
 587     }
 588 
 589     abstract static class SingleAbstractImplementor2 implements SingleAbstractImplementorInterface2 {
 590     }
 591 
 592     static class ConcreteTransitiveImplementor1 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface {
 593     }
 594 
 595     static class ConcreteTransitiveImplementor2 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface {
 596     }
 597 
 598     @Test
 599     public void getSingleImplementorTest() {
 600         ResolvedJavaType iNi = metaAccess.lookupJavaType(NoImplementor.class);
 601         assertNull(iNi.getSingleImplementor());
 602 
 603         ResolvedJavaType iSi = metaAccess.lookupJavaType(SingleImplementorInterface.class);
 604         ResolvedJavaType cSi = metaAccess.lookupJavaType(SingleConcreteImplementor.class);
 605         assertEquals(cSi, iSi.getSingleImplementor());
 606 
 607         ResolvedJavaType iSai = metaAccess.lookupJavaType(SingleAbstractImplementorInterface.class);
 608         ResolvedJavaType aSai = metaAccess.lookupJavaType(SingleAbstractImplementor.class);
 609         assertEquals(aSai, iSai.getSingleImplementor());
 610 
 611         ResolvedJavaType iMi = metaAccess.lookupJavaType(MultiImplementorInterface.class);
 612         metaAccess.lookupJavaType(ConcreteImplementor1.class);
 613         metaAccess.lookupJavaType(ConcreteImplementor2.class);
 614         assertEquals(iMi, iMi.getSingleImplementor());
 615 
 616         ResolvedJavaType iMai = metaAccess.lookupJavaType(MultipleAbstractImplementorInterface.class);
 617         metaAccess.lookupJavaType(MultiAbstractImplementor1.class);
 618         metaAccess.lookupJavaType(MultiAbstractImplementor2.class);
 619         assertEquals(iMai, iMai.getSingleImplementor());
 620 
 621         ResolvedJavaType iSai2 = metaAccess.lookupJavaType(SingleAbstractImplementorInterface2.class);
 622         ResolvedJavaType aSai2 = metaAccess.lookupJavaType(SingleAbstractImplementor2.class);
 623         metaAccess.lookupJavaType(ConcreteTransitiveImplementor1.class);
 624         metaAccess.lookupJavaType(ConcreteTransitiveImplementor2.class);
 625         assertEquals(aSai2, iSai2.getSingleImplementor());
 626 
 627         for (Class<?> c : classes) {
 628             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 629             try {
 630                 type.getSingleImplementor();
 631                 if (!c.isInterface()) {
 632                     throw new AssertionError("Expected exception for calling getSingleImplmentor on " + c.getName());
 633                 }
 634             } catch (JVMCIError e) {
 635                 if (c.isInterface()) {
 636                     throw new AssertionError("Unexpected exception", e);
 637                 }
 638             }
 639         }
 640     }
 641 
 642     @Test(expected = JVMCIError.class)
 643     public void getSingleImplementorTestClassReceiver() {
 644         ResolvedJavaType base = metaAccess.lookupJavaType(Base.class);
 645         base.getSingleImplementor();
 646     }
 647 
 648     @Test(expected = JVMCIError.class)
 649     public void getSingleImplementorTestPrimitiveReceiver() {
 650         ResolvedJavaType primitive = metaAccess.lookupJavaType(int.class);
 651         primitive.getSingleImplementor();
 652     }
 653 
 654     @Test
 655     public void getComponentTypeTest() {
 656         for (Class<?> c : classes) {
 657             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 658             Class<?> expected = c.getComponentType();
 659             ResolvedJavaType actual = type.getComponentType();
 660             if (expected == null) {
 661                 assertNull(actual);
 662             } else {
 663                 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
 664             }
 665         }
 666     }
 667 
 668     @Test
 669     public void getArrayClassTest() {
 670         for (Class<?> c : classes) {
 671             if (c != void.class) {
 672                 ResolvedJavaType type = metaAccess.lookupJavaType(c);
 673                 Class<?> expected = getArrayClass(c);
 674                 ResolvedJavaType actual = type.getArrayClass();
 675                 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
 676             }
 677         }
 678     }
 679 
 680     static class Declarations {
 681 
 682         final Method implementation;
 683         final Set<Method> declarations;
 684 
 685         Declarations(Method impl) {
 686             this.implementation = impl;
 687             declarations = new HashSet<>();
 688         }
 689     }
 690 
 691     /**
 692      * See <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.5">Method
 693      * overriding</a>.
 694      */
 695     static boolean isOverriderOf(Method impl, Method m) {
 696         if (!isPrivate(m.getModifiers()) && !isFinal(m.getModifiers())) {
 697             if (m.getName().equals(impl.getName())) {
 698                 if (m.getReturnType() == impl.getReturnType()) {
 699                     if (Arrays.equals(m.getParameterTypes(), impl.getParameterTypes())) {
 700                         if (isPublic(m.getModifiers()) || isProtected(m.getModifiers())) {
 701                             // m is public or protected
 702                             return isPublic(impl.getModifiers()) || isProtected(impl.getModifiers());
 703                         } else {
 704                             // m is package-private
 705                             return impl.getDeclaringClass().getPackage() == m.getDeclaringClass().getPackage();
 706                         }
 707                     }
 708                 }
 709             }
 710         }
 711         return false;
 712     }
 713 
 714     static final Map<Class<?>, VTable> vtables = new HashMap<>();
 715 
 716     static class VTable {
 717 
 718         final Map<NameAndSignature, Method> methods = new HashMap<>();
 719     }
 720 
 721     static synchronized VTable getVTable(Class<?> c) {
 722         VTable vtable = vtables.get(c);
 723         if (vtable == null) {
 724             vtable = new VTable();
 725             if (c != Object.class) {
 726                 VTable superVtable = getVTable(c.getSuperclass());
 727                 vtable.methods.putAll(superVtable.methods);
 728             }
 729             for (Method m : c.getDeclaredMethods()) {
 730                 if (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) {
 731                     if (isAbstract(m.getModifiers())) {
 732                         // A subclass makes a concrete method in a superclass abstract
 733                         vtable.methods.remove(new NameAndSignature(m));
 734                     } else {
 735                         vtable.methods.put(new NameAndSignature(m), m);
 736                     }
 737                 }
 738             }
 739             vtables.put(c, vtable);
 740         }
 741         return vtable;
 742     }
 743 
 744     static Set<Method> findDeclarations(Method impl, Class<?> c) {
 745         Set<Method> declarations = new HashSet<>();
 746         NameAndSignature implSig = new NameAndSignature(impl);
 747         if (c != null) {
 748             for (Method m : c.getDeclaredMethods()) {
 749                 if (new NameAndSignature(m).equals(implSig)) {
 750                     declarations.add(m);
 751                     break;
 752                 }
 753             }
 754             if (!c.isInterface()) {
 755                 declarations.addAll(findDeclarations(impl, c.getSuperclass()));
 756             }
 757             for (Class<?> i : c.getInterfaces()) {
 758                 declarations.addAll(findDeclarations(impl, i));
 759             }
 760         }
 761         return declarations;
 762     }
 763 
 764     @Test
 765     public void resolveMethodTest() {
 766         ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class);
 767         for (Class<?> c : classes) {
 768             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 769             if (c.isInterface()) {
 770                 for (Method m : c.getDeclaredMethods()) {
 771                     ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m);
 772                     ResolvedJavaMethod impl = type.resolveMethod(resolved, context);
 773                     assertEquals(m.toString(), null, impl);
 774                 }
 775             } else if (c.isPrimitive()) {
 776                 assertEquals("No methods expected", c.getDeclaredMethods().length, 0);
 777             } else {
 778                 VTable vtable = getVTable(c);
 779                 for (Method impl : vtable.methods.values()) {
 780                     Set<Method> decls = findDeclarations(impl, c);
 781                     for (Method decl : decls) {
 782                         ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl);
 783                         if (m.isPublic()) {
 784                             ResolvedJavaMethod resolvedMethod = type.resolveMethod(m, context);
 785                             if (isSignaturePolymorphic(m)) {
 786                                 // Signature polymorphic methods must not be resolved
 787                                 assertNull(resolvedMethod);
 788                             } else {
 789                                 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl);
 790                                 assertEquals(m.toString(), i, resolvedMethod);
 791                             }
 792                         }
 793                     }
 794                 }
 795                 // For backwards compatibility treat constructors as resolvable even though they
 796                 // aren't virtually dispatched.
 797                 ResolvedJavaType declaringClass = metaAccess.lookupJavaType(c);
 798                 for (Constructor<?> m : c.getDeclaredConstructors()) {
 799                     ResolvedJavaMethod decl = metaAccess.lookupJavaMethod(m);
 800                     ResolvedJavaMethod impl = type.resolveMethod(decl, declaringClass);
 801                     assertEquals(m.toString(), decl, impl);
 802                 }
 803                 for (Method m : c.getDeclaredMethods()) {
 804                     if (isStatic(m.getModifiers())) {
 805                         // resolveMethod really shouldn't be called with static methods and the
 806                         // result is is somewhat inconsistent so just ignore them
 807                         continue;
 808                     }
 809                     ResolvedJavaMethod decl = metaAccess.lookupJavaMethod(m);
 810                     ResolvedJavaMethod impl = type.resolveMethod(decl, declaringClass);
 811                     ResolvedJavaMethod expected = isSignaturePolymorphic(decl) ? null : decl;
 812                     assertEquals(m.toString(), expected, impl);
 813                 }
 814             }
 815         }
 816     }
 817 
 818     @Test
 819     public void resolveConcreteMethodTest() {
 820         ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class);
 821         for (Class<?> c : classes) {
 822             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 823             if (c.isInterface()) {
 824                 for (Method m : c.getDeclaredMethods()) {
 825                     ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m);
 826                     ResolvedJavaMethod impl = type.resolveConcreteMethod(resolved, context);
 827                     assertEquals(m.toString(), null, impl);
 828                 }
 829             } else if (c.isPrimitive()) {
 830                 assertEquals("No methods expected", c.getDeclaredMethods().length, 0);
 831             } else {
 832                 VTable vtable = getVTable(c);
 833                 for (Method impl : vtable.methods.values()) {
 834                     Set<Method> decls = findDeclarations(impl, c);
 835                     for (Method decl : decls) {
 836                         ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl);
 837                         if (m.isPublic()) {
 838                             ResolvedJavaMethod resolvedMethod = type.resolveConcreteMethod(m, context);
 839                             if (isSignaturePolymorphic(m)) {
 840                                 // Signature polymorphic methods must not be resolved
 841                                 assertNull(String.format("Got: %s", resolvedMethod), resolvedMethod);
 842                             } else {
 843                                 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl);
 844                                 assertEquals(i, resolvedMethod);
 845                             }
 846                         }
 847                     }
 848                 }
 849                 for (Method m : c.getDeclaredMethods()) {
 850                     ResolvedJavaMethod impl = type.resolveConcreteMethod(metaAccess.lookupJavaMethod(m), context);
 851                     ResolvedJavaMethod expected = isAbstract(m.getModifiers()) ? null : impl;
 852                     assertEquals(type + " " + m.toString(), expected, impl);
 853                 }
 854             }
 855         }
 856     }
 857 
 858     @Test
 859     public void findUniqueConcreteMethodTest() throws NoSuchMethodException {
 860         ResolvedJavaMethod thisMethod = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("findUniqueConcreteMethodTest"));
 861         ResolvedJavaMethod ucm = metaAccess.lookupJavaType(getClass()).findUniqueConcreteMethod(thisMethod).getResult();
 862         assertEquals(thisMethod, ucm);
 863     }
 864 
 865     public static Set<Field> getInstanceFields(Class<?> c, boolean includeSuperclasses) {
 866         if (c.isArray() || c.isPrimitive() || c.isInterface()) {
 867             return Collections.emptySet();
 868         }
 869         Set<Field> result = new HashSet<>();
 870         for (Field f : c.getDeclaredFields()) {
 871             if (!Modifier.isStatic(f.getModifiers())) {
 872                 result.add(f);
 873             }
 874         }
 875         if (includeSuperclasses && c != Object.class) {
 876             result.addAll(getInstanceFields(c.getSuperclass(), true));
 877         }
 878         return result;
 879     }
 880 
 881     public static Set<Field> getStaticFields(Class<?> c) {
 882         Set<Field> result = new HashSet<>();
 883         for (Field f : c.getDeclaredFields()) {
 884             if (Modifier.isStatic(f.getModifiers())) {
 885                 result.add(f);
 886             }
 887         }
 888         return result;
 889     }
 890 
 891     public boolean fieldsEqual(Field f, ResolvedJavaField rjf) {
 892         return rjf.getDeclaringClass().equals(metaAccess.lookupJavaType(f.getDeclaringClass())) && rjf.getName().equals(f.getName()) &&
 893                         rjf.getType().resolve(rjf.getDeclaringClass()).equals(metaAccess.lookupJavaType(f.getType()));
 894     }
 895 
 896     public ResolvedJavaField lookupField(ResolvedJavaField[] fields, Field key) {
 897         for (ResolvedJavaField rf : fields) {
 898             if (fieldsEqual(key, rf)) {
 899                 return rf;
 900             }
 901         }
 902         return null;
 903     }
 904 
 905     public Field lookupField(Set<Field> fields, ResolvedJavaField key) {
 906         for (Field f : fields) {
 907             if (fieldsEqual(f, key)) {
 908                 return f;
 909             }
 910         }
 911         return null;
 912     }
 913 
 914     private static boolean isHiddenFromReflection(ResolvedJavaField f) {
 915         if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Throwable.class)) && f.getName().equals("backtrace")) {
 916             return true;
 917         }
 918         if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ConstantPool.class)) && f.getName().equals("constantPoolOop")) {
 919             return true;
 920         }
 921         if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Class.class))) {
 922             return f.getName().equals("classLoader") || f.getName().equals("classData");
 923         }
 924         if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Lookup.class))) {
 925             return f.getName().equals("allowedModes") || f.getName().equals("lookupClass");
 926         }
 927         if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ClassLoader.class)) ||
 928                         f.getDeclaringClass().equals(metaAccess.lookupJavaType(AccessibleObject.class)) ||
 929                         f.getDeclaringClass().equals(metaAccess.lookupJavaType(Constructor.class)) ||
 930                         f.getDeclaringClass().equals(metaAccess.lookupJavaType(Field.class)) ||
 931                         f.getDeclaringClass().equals(metaAccess.lookupJavaType(Method.class)) ||
 932                         f.getDeclaringClass().equals(metaAccess.lookupJavaType(Module.class))) {
 933             return true;
 934         }
 935         return false;
 936     }
 937 
 938     @Test
 939     public void getInstanceFieldsTest() {
 940         for (Class<?> c : classes) {
 941             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 942             for (boolean includeSuperclasses : new boolean[]{true, false}) {
 943                 Set<Field> expected = getInstanceFields(c, includeSuperclasses);
 944                 ResolvedJavaField[] actual = type.getInstanceFields(includeSuperclasses);
 945                 for (Field f : expected) {
 946                     assertNotNull(lookupField(actual, f));
 947                 }
 948                 for (ResolvedJavaField rf : actual) {
 949                     if (!isHiddenFromReflection(rf)) {
 950                         assertEquals(rf.toString(), lookupField(expected, rf) != null, !rf.isInternal());
 951                     }
 952                 }
 953 
 954                 // Test stability of getInstanceFields
 955                 ResolvedJavaField[] actual2 = type.getInstanceFields(includeSuperclasses);
 956                 assertArrayEquals(actual, actual2);
 957             }
 958         }
 959     }
 960 
 961     @Test
 962     public void getStaticFieldsTest() {
 963         for (Class<?> c : classes) {
 964             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 965             Set<Field> expected = getStaticFields(c);
 966             ResolvedJavaField[] actual = type.getStaticFields();
 967             for (Field f : expected) {
 968                 assertNotNull(lookupField(actual, f));
 969             }
 970             for (ResolvedJavaField rf : actual) {
 971                 if (!isHiddenFromReflection(rf)) {
 972                     assertEquals(lookupField(expected, rf) != null, !rf.isInternal());
 973                 }
 974             }
 975 
 976             // Test stability of getStaticFields
 977             ResolvedJavaField[] actual2 = type.getStaticFields();
 978             assertArrayEquals(actual, actual2);
 979         }
 980     }
 981 
 982     @Test
 983     public void getDeclaredMethodsTest() {
 984         for (Class<?> c : classes) {
 985             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 986             Method[] raw = c.getDeclaredMethods();
 987             Set<ResolvedJavaMethod> expected = new HashSet<>();
 988             for (Method m : raw) {
 989                 ResolvedJavaMethod resolvedMethod = metaAccess.lookupJavaMethod(m);
 990                 assertNotNull(resolvedMethod);
 991                 expected.add(resolvedMethod);
 992             }
 993             Set<ResolvedJavaMethod> actual = new HashSet<>(Arrays.asList(type.getDeclaredMethods()));
 994             assertEquals(expected, actual);
 995         }
 996     }
 997 
 998     static class A {
 999         static String name = "foo";
1000     }
1001 
1002     static class B extends A {
1003     }
1004 
1005     static class C {
1006     }
1007 
1008     static class D {
1009         void foo() {
1010             // use of assertions causes the class to have a <clinit>
1011             assert getClass() != null;
1012         }
1013     }
1014 
1015     static class SubD extends D {
1016 
1017     }
1018 
1019     private static ResolvedJavaMethod getClassInitializer(Class<?> c) {
1020         ResolvedJavaMethod clinit = metaAccess.lookupJavaType(c).getClassInitializer();
1021         if (clinit != null) {
1022             assertEquals(0, clinit.getAnnotations().length);
1023             assertEquals(0, clinit.getDeclaredAnnotations().length);
1024         }
1025         return clinit;
1026     }
1027 
1028     @Test
1029     public void getClassInitializerTest() {
1030         assertNotNull(getClassInitializer(A.class));
1031         assertNotNull(getClassInitializer(D.class));
1032         assertNull(getClassInitializer(B.class));
1033         assertNull(getClassInitializer(C.class));
1034         assertNull(getClassInitializer(int.class));
1035         assertNull(getClassInitializer(void.class));
1036         for (Class<?> c : classes) {
1037             getClassInitializer(c);
1038         }
1039     }
1040 
1041     @Test
1042     public void getAnnotationsTest() {
1043         for (Class<?> c : classes) {
1044             ResolvedJavaType type = metaAccess.lookupJavaType(c);
1045             assertArrayEquals(c.getAnnotations(), type.getAnnotations());
1046         }
1047     }
1048 
1049     @Test
1050     public void getAnnotationTest() {
1051         for (Class<?> c : classes) {
1052             ResolvedJavaType type = metaAccess.lookupJavaType(c);
1053             for (Annotation a : c.getAnnotations()) {
1054                 assertEquals(a, type.getAnnotation(a.annotationType()));
1055             }
1056         }
1057     }
1058 
1059     @Test
1060     public void getSourceFileNameTest() {
1061         Class<?> c = Object.class;
1062         ResolvedJavaType type = metaAccess.lookupJavaType(c);
1063         assertEquals(type.getSourceFileName(), "Object.java");
1064     }
1065 
1066     @Test
1067     public void memberClassesTest() {
1068         for (Class<?> c : classes) {
1069             ResolvedJavaType type = metaAccess.lookupJavaType(c);
1070             assertEquals(c.isLocalClass(), type.isLocal());
1071             assertEquals(c.isMemberClass(), type.isMember());
1072             Class<?> enclc = c.getEnclosingClass();
1073             ResolvedJavaType enclt = type.getEnclosingType();
1074             assertFalse(enclc == null ^ enclt == null);
1075             if (enclc != null) {
1076                 assertEquals(enclt, metaAccess.lookupJavaType(enclc));
1077             }
1078         }
1079     }
1080 
1081     @Test
1082     public void isLeafTest() {
1083         for (Class<?> c : classes) {
1084             ResolvedJavaType type = metaAccess.lookupJavaType(c);
1085             ResolvedJavaType arrayType = c != void.class ? metaAccess.lookupJavaType(getArrayClass(c)) : null;
1086             if (c.isPrimitive()) {
1087                 assertTrue(type.isLeaf());
1088                 assertTrue(arrayType == null || arrayType.isLeaf());
1089             } else {
1090                 assertTrue(c.toString(), type.isLeaf() == arrayType.isLeaf());
1091                 if (!c.isArray()) {
1092                     assertTrue(c.toString(), type.isLeaf() == Modifier.isFinal(c.getModifiers()));
1093                 }
1094             }
1095         }
1096     }
1097 
1098     static class TrivialCloneable implements Cloneable {
1099         @Override
1100         protected Object clone() {
1101             return new TrivialCloneable();
1102         }
1103     }
1104 
1105     @Test
1106     public void isCloneableWithAllocationTest() {
1107         ResolvedJavaType cloneable = metaAccess.lookupJavaType(Cloneable.class);
1108         for (Class<?> c : classes) {
1109             ResolvedJavaType type = metaAccess.lookupJavaType(c);
1110             if (type.isCloneableWithAllocation()) {
1111                 // Only Cloneable types should be allocation cloneable
1112                 assertTrue(c.toString(), cloneable.isAssignableFrom(type));
1113             }
1114         }
1115         /*
1116          * We can't know for sure which types should be allocation cloneable on a particular
1117          * platform but assume that at least totally trivial objects should be.
1118          */
1119         ResolvedJavaType trivialCloneable = metaAccess.lookupJavaType(TrivialCloneable.class);
1120         assertTrue(trivialCloneable.toString(), trivialCloneable.isCloneableWithAllocation());
1121     }
1122 
1123     @Test
1124     public void findMethodTest() {
1125         try {
1126             ResolvedJavaMethod findFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V"));
1127             ResolvedJavaMethod expectedFoo = metaAccess.lookupJavaMethod(D.class.getDeclaredMethod("foo"));
1128             assertEquals(expectedFoo, findFoo);
1129 
1130             ResolvedJavaMethod wrongReturnTypeFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()I"));
1131             assertNull(wrongReturnTypeFoo);
1132 
1133             ResolvedJavaMethod wrongArgumentsFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("(I)V"));
1134             assertNull(wrongArgumentsFoo);
1135 
1136             ResolvedJavaMethod wrongNameFoo = metaAccess.lookupJavaType(D.class).findMethod("bar", metaAccess.parseMethodDescriptor("()V"));
1137             assertNull(wrongNameFoo);
1138 
1139             ResolvedJavaMethod wrongClassFoo = metaAccess.lookupJavaType(SubD.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V"));
1140             assertNull(wrongClassFoo);
1141         } catch (NoSuchMethodException | SecurityException e) {
1142             throw new RuntimeException(e);
1143         }
1144     }
1145 
1146     private Method findTestMethod(Method apiMethod) {
1147         String testName = apiMethod.getName() + "Test";
1148         for (Method m : getClass().getDeclaredMethods()) {
1149             if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) {
1150                 return m;
1151             }
1152         }
1153         return null;
1154     }
1155 
1156     @Test
1157     public void getAnnotationDataTest() throws Exception {
1158         getAnnotationDataTest(AnnotationTestInput.AnnotatedClass.class);
1159         getAnnotationDataTest(int.class);
1160         getAnnotationDataTest(void.class);
1161         for (Class<?> c : classes) {
1162             getAnnotationDataTest(c);
1163         }
1164 
1165         // Primitive classes have no annotations but we cannot directly
1166         // test absence of annotations. Instead, just ensure empty answers
1167         // are returned when looking up an arbitrary annotation type.
1168         Class<?>[] prims = {void.class, byte.class, int.class, double.class, float.class, short.class, char.class, long.class};
1169         ResolvedJavaType overrideType = metaAccess.lookupJavaType(Override.class);
1170         for (Class<?> c : prims) {
1171             ResolvedJavaType type = metaAccess.lookupJavaType(c);
1172             AnnotationData ad = type.getAnnotationData(overrideType);
1173             Assert.assertNull(String.valueOf(ad), ad);
1174             List<AnnotationData> adArray = type.getAnnotationData(overrideType, overrideType);
1175             Assert.assertEquals(0, adArray.size());
1176         }
1177 
1178         // Test that inherited annotations are handled properly.
1179         ResolvedJavaType namedType = metaAccess.lookupJavaType(AnnotationTestInput.Named.class);
1180         AnnotationData ad = metaAccess.lookupJavaType(AnnotationTestInput.OwnName.class).getAnnotationData(namedType);
1181         Assert.assertEquals("NonInheritedValue", ad.get("value", String.class));
1182         ad = metaAccess.lookupJavaType(AnnotationTestInput.InheritedName1.class).getAnnotationData(namedType);
1183         Assert.assertEquals("Super1", ad.get("value", String.class));
1184         ad = metaAccess.lookupJavaType(AnnotationTestInput.InheritedName2.class).getAnnotationData(namedType);
1185         Assert.assertEquals("Super2", ad.get("value", String.class));
1186         ad = metaAccess.lookupJavaType(AnnotationTestInput.InheritedName3.class).getAnnotationData(namedType);
1187         Assert.assertEquals("Super1", ad.get("value", String.class));
1188     }
1189 
1190     // @formatter:off
1191     private static final String[] untestedApiMethods = {
1192         "initialize",
1193         "isPrimitive",
1194         "newArray",
1195         "getDeclaredConstructors",
1196         "isInitialized",
1197         "isLinked",
1198         "getJavaClass",
1199         "getObjectHub",
1200         "getHostClass",
1201         "hasFinalizableSubclass",
1202         "hasFinalizer",
1203         "isLocal",
1204         "isJavaLangObject",
1205         "isMember",
1206         "getElementalType",
1207         "getEnclosingType",
1208         "lookupType",
1209         "resolveField",
1210         "$jacocoInit"
1211     };
1212     // @formatter:on
1213 
1214     /**
1215      * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written
1216      * for them or are added to {@link #untestedApiMethods}.
1217      */
1218     @Test
1219     public void testCoverage() {
1220         Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods));
1221         for (Method m : ResolvedJavaType.class.getDeclaredMethods()) {
1222             if (findTestMethod(m) == null) {
1223                 assertTrue("test missing for " + m, known.contains(m.getName()));
1224             } else {
1225                 assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName()));
1226             }
1227         }
1228     }
1229 
1230     private static boolean isSignaturePolymorphic(ResolvedJavaMethod method) {
1231         return method.getAnnotation(SIGNATURE_POLYMORPHIC_CLASS) != null;
1232     }
1233 
1234     /**
1235      * Tests that {@link AnnotationData} obtained from a {@link Class}, {@link Method} or
1236      * {@link Field} matches {@link AnnotatedElement#getAnnotations()} for the corresponding JVMCI
1237      * object.
1238      *
1239      * @param annotated a {@link Class}, {@link Method} or {@link Field} object
1240      */
1241     public static void getAnnotationDataTest(AnnotatedElement annotated) throws Exception {
1242         testGetAnnotationData(annotated, List.of(annotated.getAnnotations()));
1243     }
1244 
1245     private static void testGetAnnotationData(AnnotatedElement annotated, List<Annotation> annotations) throws AssertionError {
1246         for (Annotation a : annotations) {
1247             AnnotationData ad = toAnnotated(annotated).getAnnotationData(metaAccess.lookupJavaType(a.annotationType()));
1248             assertAnnotationsEquals(a, ad);
1249 
1250             // Check that encoding/decoding produces a stable result
1251             AnnotationData ad2 = toAnnotated(annotated).getAnnotationData(metaAccess.lookupJavaType(a.annotationType()));
1252             assertEquals(ad, ad2);
1253         }
1254         if (annotations.size() < 2) {
1255             return;
1256         }
1257         ResolvedJavaType type1 = metaAccess.lookupJavaType(annotations.get(0).annotationType());
1258         ResolvedJavaType type2 = metaAccess.lookupJavaType(annotations.get(1).annotationType());
1259         for (int i = 2; i < annotations.size(); i++) {
1260 
1261             ResolvedJavaType[] types = annotations.//
1262                             subList(2, i + 1).//
1263                             stream().map(a -> metaAccess.lookupJavaType(a.annotationType())).//
1264                             toArray(ResolvedJavaType[]::new);
1265             List<AnnotationData> annotationData = toAnnotated(annotated).getAnnotationData(type1, type2, types);
1266             assertEquals(2 + types.length, annotationData.size());
1267 
1268             for (int j = 0; j < annotationData.size(); j++) {
1269                 Annotation a = annotations.get(j);
1270                 AnnotationData ad = annotationData.get(j);
1271                 assertAnnotationsEquals(a, ad);
1272             }
1273         }
1274     }
1275 
1276     private static Annotated toAnnotated(AnnotatedElement element) {
1277         if (element instanceof Class<?> t) {
1278             return metaAccess.lookupJavaType(t);
1279         } else if (element instanceof Method m) {
1280             return metaAccess.lookupJavaMethod(m);
1281         } else {
1282             Field f = (Field) element;
1283             return metaAccess.lookupJavaField(f);
1284         }
1285     }
1286 
1287     private static UnresolvedJavaType asType(Class<?> valueType) {
1288         return UnresolvedJavaType.create(MetaUtil.toInternalName(valueType.getName()));
1289     }
1290 
1291     private static void assertAnnotationsEquals(Annotation a, AnnotationData ad) {
1292         Map<String, Object> values = AnnotationSupport.memberValues(a);
1293         for (Map.Entry<String, Object> e : values.entrySet()) {
1294             String name = e.getKey();
1295             Object aValue = e.getValue();
1296             Object adValue;
1297             try {
1298                 adValue = ad.get(name, Object.class);
1299             } catch (IllegalArgumentException ex) {
1300                 assertEquals(aValue.toString(), ex.getMessage());
1301                 continue;
1302             }
1303             try {
1304                 assertAnnotationElementsEqual(aValue, adValue);
1305             } catch (ClassCastException ex) {
1306                 throw new AssertionError(a.getClass().getName() + "." + name + " has wrong type: " + adValue.getClass().getName(), ex);
1307             }
1308         }
1309     }
1310 
1311     private static void assertAnnotationElementsEqual(Object aValue, Object adValue) {
1312         Class<?> valueType = aValue.getClass();
1313         if (valueType.isEnum()) {
1314             assertEnumObjectsEquals(aValue, adValue);
1315         } else if (aValue instanceof Class) {
1316             assertClassObjectsEquals(aValue, adValue);
1317         } else if (aValue instanceof Annotation) {
1318             assertAnnotationObjectsEquals(aValue, adValue);
1319         } else if (valueType.isArray()) {
1320             List<?> adList = (List<?>) adValue;
1321             int length = Array.getLength(aValue);
1322             assertEquals(length, adList.size());
1323             for (int i = 0; i < length; i++) {
1324                 assertAnnotationElementsEqual(Array.get(aValue, i), adList.get(i));
1325             }
1326         } else {
1327             assertEquals(aValue.getClass(), adValue.getClass());
1328             assertEquals(aValue, adValue);
1329         }
1330     }
1331 
1332     private static void assertClassObjectsEquals(Object aValue, Object adValue) {
1333         String aName = ((Class<?>) aValue).getName();
1334         String adName = ((JavaType) adValue).toClassName();
1335         assertEquals(aName, adName);
1336     }
1337 
1338     private static void assertEnumObjectsEquals(Object aValue, Object adValue) {
1339         EnumData adEnum = (EnumData) adValue;
1340         String adEnumName = adEnum.getName();
1341         String aEnumName = ((Enum<?>) aValue).name();
1342         assertEquals(adEnumName, aEnumName);
1343     }
1344 
1345     private static void assertAnnotationObjectsEquals(Object aValue, Object adValue) {
1346         Annotation aAnnotation = (Annotation) aValue;
1347         AnnotationData adAnnotation = (AnnotationData) adValue;
1348         assertAnnotationsEquals(aAnnotation, adAnnotation);
1349     }
1350 
1351     private static void assertArraysEqual(Object aValue, Object adValue, int length, BiConsumer<Object, Object> assertEqualty) {
1352         Object[] aArray = (Object[]) aValue;
1353         Object[] adArray = (Object[]) adValue;
1354         for (int i = 0; i < length; i++) {
1355             assertEqualty.accept(aArray[i], adArray[i]);
1356         }
1357     }
1358 }