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