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