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