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