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