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.jvmci
  27  * @library ../../../../../
  28  * @modules java.base/jdk.internal.reflect
  29  *          jdk.internal.vm.ci/jdk.vm.ci.meta
  30  *          jdk.internal.vm.ci/jdk.vm.ci.runtime
  31  *          jdk.internal.vm.ci/jdk.vm.ci.common
  32  *          java.base/jdk.internal.misc
  33  * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -Djvmci.Compiler=null jdk.vm.ci.runtime.test.TestResolvedJavaType
  34  */
  35 
  36 package jdk.vm.ci.runtime.test;
  37 
  38 import static java.lang.reflect.Modifier.isAbstract;
  39 import static java.lang.reflect.Modifier.isFinal;
  40 import static java.lang.reflect.Modifier.isPrivate;
  41 import static java.lang.reflect.Modifier.isProtected;
  42 import static java.lang.reflect.Modifier.isPublic;
  43 import static java.lang.reflect.Modifier.isStatic;
  44 import static org.junit.Assert.assertArrayEquals;
  45 import static org.junit.Assert.assertEquals;
  46 import static org.junit.Assert.assertFalse;
  47 import static org.junit.Assert.assertNotNull;
  48 import static org.junit.Assert.assertNull;
  49 import static org.junit.Assert.assertTrue;
  50 
  51 import java.lang.annotation.Annotation;
  52 import java.lang.reflect.Field;
  53 import java.lang.reflect.Method;
  54 import java.lang.reflect.Modifier;
  55 import java.util.Arrays;
  56 import java.util.Collections;
  57 import java.util.HashMap;
  58 import java.util.HashSet;
  59 import java.util.Map;
  60 import java.util.Set;
  61 
  62 import org.junit.Test;
  63 
  64 import jdk.internal.reflect.ConstantPool;
  65 import jdk.vm.ci.common.JVMCIError;
  66 import jdk.vm.ci.meta.Assumptions.AssumptionResult;
  67 import jdk.vm.ci.meta.JavaConstant;
  68 import jdk.vm.ci.meta.JavaKind;
  69 import jdk.vm.ci.meta.ResolvedJavaField;
  70 import jdk.vm.ci.meta.ResolvedJavaMethod;
  71 import jdk.vm.ci.meta.ResolvedJavaType;
  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 mask = Modifier.classModifiers() & ~Modifier.STATIC;
 150             int expected = c.getModifiers() & mask;
 151             int actual = type.getModifiers() & mask;
 152             Class<?> elementalType = c;
 153             while (elementalType.isArray()) {
 154                 elementalType = elementalType.getComponentType();
 155             }
 156             if (elementalType.isMemberClass()) {
 157                 // member class get their modifiers from the inner-class attribute in the JVM and
 158                 // from the classfile header in jvmci
 159                 expected &= ~(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED);
 160                 actual &= ~(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED);
 161             }
 162             assertEquals(String.format("%s: 0x%x != 0x%x", type, expected, actual), expected, actual);
 163         }
 164     }
 165 
 166     @Test
 167     public void isAssignableFromTest() {
 168         Class<?>[] all = classes.toArray(new Class<?>[classes.size()]);
 169         for (int i = 0; i < all.length; i++) {
 170             Class<?> c1 = all[i];
 171             for (int j = i; j < all.length; j++) {
 172                 Class<?> c2 = all[j];
 173                 ResolvedJavaType t1 = metaAccess.lookupJavaType(c1);
 174                 ResolvedJavaType t2 = metaAccess.lookupJavaType(c2);
 175                 boolean expected = c1.isAssignableFrom(c2);
 176                 boolean actual = t1.isAssignableFrom(t2);
 177                 assertEquals(expected, actual);
 178                 if (expected && t1 != t2) {
 179                     assertFalse(t2.isAssignableFrom(t1));
 180                 }
 181             }
 182         }
 183     }
 184 
 185     @Test
 186     public void isInstanceTest() {
 187         for (ConstantValue cv : constants()) {
 188             JavaConstant c = cv.value;
 189             if (c.getJavaKind() == JavaKind.Object && !c.isNull()) {
 190                 ResolvedJavaType cType = metaAccess.lookupJavaType(c);
 191                 for (ResolvedJavaType t : javaTypes) {
 192                     if (t.isAssignableFrom(cType)) {
 193                         assertTrue(t.isInstance(c));
 194                     } else {
 195                         assertFalse(t.isInstance(c));
 196                     }
 197                 }
 198             }
 199         }
 200     }
 201 
 202     @Test
 203     public void getSuperclassTest() {
 204         for (Class<?> c : classes) {
 205             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 206             Class<?> expected = c.getSuperclass();
 207             ResolvedJavaType actual = type.getSuperclass();
 208             if (expected == null) {
 209                 assertTrue(actual == null);
 210             } else {
 211                 assertNotNull(actual);
 212                 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
 213             }
 214         }
 215     }
 216 
 217     @Test
 218     public void getInterfacesTest() {
 219         for (Class<?> c : classes) {
 220             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 221             Class<?>[] expected = c.getInterfaces();
 222             ResolvedJavaType[] actual = type.getInterfaces();
 223             assertEquals(expected.length, actual.length);
 224             for (int i = 0; i < expected.length; i++) {
 225                 assertTrue(actual[i].equals(metaAccess.lookupJavaType(expected[i])));
 226             }
 227         }
 228     }
 229 
 230     public Class<?> getSupertype(Class<?> c) {
 231         assert !c.isPrimitive();
 232         if (c.isArray()) {
 233             Class<?> componentType = c.getComponentType();
 234             if (componentType.isPrimitive() || componentType == Object.class) {
 235                 return Object.class;
 236             }
 237             return getArrayClass(getSupertype(componentType));
 238         }
 239         if (c.isInterface()) {
 240             return Object.class;
 241         }
 242         return c.getSuperclass();
 243     }
 244 
 245     public Class<?> findLeastCommonAncestor(Class<?> c1Initial, Class<?> c2Initial) {
 246         if (c1Initial.isPrimitive() || c2Initial.isPrimitive()) {
 247             return null;
 248         } else {
 249             Class<?> c1 = c1Initial;
 250             Class<?> c2 = c2Initial;
 251             while (true) {
 252                 if (c1.isAssignableFrom(c2)) {
 253                     return c1;
 254                 }
 255                 if (c2.isAssignableFrom(c1)) {
 256                     return c2;
 257                 }
 258                 c1 = getSupertype(c1);
 259                 c2 = getSupertype(c2);
 260             }
 261         }
 262     }
 263 
 264     @Test
 265     public void findLeastCommonAncestorTest() {
 266         Class<?>[] all = classes.toArray(new Class<?>[classes.size()]);
 267         for (int i = 0; i < all.length; i++) {
 268             Class<?> c1 = all[i];
 269             for (int j = i; j < all.length; j++) {
 270                 Class<?> c2 = all[j];
 271                 ResolvedJavaType t1 = metaAccess.lookupJavaType(c1);
 272                 ResolvedJavaType t2 = metaAccess.lookupJavaType(c2);
 273                 Class<?> expected = findLeastCommonAncestor(c1, c2);
 274                 ResolvedJavaType actual = t1.findLeastCommonAncestor(t2);
 275                 if (expected == null) {
 276                     assertTrue(actual == null);
 277                 } else {
 278                     assertNotNull(actual);
 279                     assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
 280                 }
 281             }
 282         }
 283     }
 284 
 285     private static class Base {
 286     }
 287 
 288     abstract static class Abstract1 extends Base {
 289     }
 290 
 291     interface Interface1 {
 292     }
 293 
 294     static class Concrete1 extends Abstract1 {
 295     }
 296 
 297     static class Concrete2 extends Abstract1 implements Interface1 {
 298     }
 299 
 300     static class Concrete3 extends Concrete2 {
 301     }
 302 
 303     static final class Final1 extends Abstract1 {
 304     }
 305 
 306     abstract static class Abstract4 extends Concrete3 {
 307     }
 308 
 309     void checkConcreteSubtype(ResolvedJavaType type, ResolvedJavaType expected) {
 310         AssumptionResult<ResolvedJavaType> leafConcreteSubtype = type.findLeafConcreteSubtype();
 311         if (leafConcreteSubtype == null) {
 312             // findLeafConcreteSubtype() is conservative
 313         } else {
 314             if (expected == null) {
 315                 assertNull(leafConcreteSubtype);
 316             } else {
 317                 assertTrue(leafConcreteSubtype.getResult().equals(expected));
 318             }
 319             assertTrue(!type.isLeaf() || leafConcreteSubtype.isAssumptionFree());
 320         }
 321 
 322         if (!type.isArray()) {
 323             ResolvedJavaType arrayType = type.getArrayClass();
 324             AssumptionResult<ResolvedJavaType> arraySubtype = arrayType.findLeafConcreteSubtype();
 325             if (arraySubtype != null) {
 326                 assertEquals(arraySubtype.getResult(), arrayType);
 327             } else {
 328                 // findLeafConcreteSubtype() method is conservative
 329             }
 330         }
 331     }
 332 
 333     @Test
 334     public void findLeafConcreteSubtypeTest() {
 335         ResolvedJavaType base = metaAccess.lookupJavaType(Base.class);
 336         checkConcreteSubtype(base, base);
 337 
 338         ResolvedJavaType a1 = metaAccess.lookupJavaType(Abstract1.class);
 339         ResolvedJavaType c1 = metaAccess.lookupJavaType(Concrete1.class);
 340 
 341         checkConcreteSubtype(base, null);
 342         checkConcreteSubtype(a1, c1);
 343         checkConcreteSubtype(c1, c1);
 344 
 345         ResolvedJavaType i1 = metaAccess.lookupJavaType(Interface1.class);
 346         ResolvedJavaType c2 = metaAccess.lookupJavaType(Concrete2.class);
 347 
 348         checkConcreteSubtype(base, null);
 349         checkConcreteSubtype(a1, null);
 350         checkConcreteSubtype(c1, c1);
 351         checkConcreteSubtype(i1, c2);
 352         checkConcreteSubtype(c2, c2);
 353 
 354         ResolvedJavaType c3 = metaAccess.lookupJavaType(Concrete3.class);
 355         checkConcreteSubtype(c2, null);
 356         checkConcreteSubtype(c3, c3);
 357 
 358         ResolvedJavaType a4 = metaAccess.lookupJavaType(Abstract4.class);
 359         checkConcreteSubtype(c3, null);
 360         checkConcreteSubtype(a4, null);
 361 
 362         ResolvedJavaType a1a = metaAccess.lookupJavaType(Abstract1[].class);
 363         checkConcreteSubtype(a1a, null);
 364         ResolvedJavaType i1a = metaAccess.lookupJavaType(Interface1[].class);
 365         checkConcreteSubtype(i1a, null);
 366         ResolvedJavaType c1a = metaAccess.lookupJavaType(Concrete1[].class);
 367         checkConcreteSubtype(c1a, c1a);
 368         ResolvedJavaType f1a = metaAccess.lookupJavaType(Final1[].class);
 369         checkConcreteSubtype(f1a, f1a);
 370 
 371         ResolvedJavaType obja = metaAccess.lookupJavaType(Object[].class);
 372         checkConcreteSubtype(obja, null);
 373 
 374         ResolvedJavaType inta = metaAccess.lookupJavaType(int[].class);
 375         checkConcreteSubtype(inta, inta);
 376     }
 377 
 378     interface NoImplementor {
 379     }
 380 
 381     interface SingleImplementorInterface {
 382     }
 383 
 384     static class SingleConcreteImplementor implements SingleImplementorInterface {
 385     }
 386 
 387     interface SingleAbstractImplementorInterface {
 388     }
 389 
 390     abstract static class SingleAbstractImplementor implements SingleAbstractImplementorInterface {
 391     }
 392 
 393     interface MultiImplementorInterface {
 394     }
 395 
 396     static class ConcreteImplementor1 implements MultiImplementorInterface {
 397     }
 398 
 399     static class ConcreteImplementor2 implements MultiImplementorInterface {
 400     }
 401 
 402     interface MultipleAbstractImplementorInterface {
 403     }
 404 
 405     abstract static class MultiAbstractImplementor1 implements MultipleAbstractImplementorInterface {
 406     }
 407 
 408     abstract static class MultiAbstractImplementor2 implements MultipleAbstractImplementorInterface {
 409     }
 410 
 411     interface SingleAbstractImplementorInterface2 {
 412     }
 413 
 414     interface ExtendedSingleImplementorInterface {
 415     }
 416 
 417     abstract static class SingleAbstractImplementor2 implements SingleAbstractImplementorInterface2 {
 418     }
 419 
 420     static class ConcreteTransitiveImplementor1 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface {
 421     }
 422 
 423     static class ConcreteTransitiveImplementor2 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface {
 424     }
 425 
 426     @Test
 427     public void getSingleImplementorTest() {
 428         ResolvedJavaType iNi = metaAccess.lookupJavaType(NoImplementor.class);
 429         assertNull(iNi.getSingleImplementor());
 430 
 431         ResolvedJavaType iSi = metaAccess.lookupJavaType(SingleImplementorInterface.class);
 432         ResolvedJavaType cSi = metaAccess.lookupJavaType(SingleConcreteImplementor.class);
 433         assertEquals(cSi, iSi.getSingleImplementor());
 434 
 435         ResolvedJavaType iSai = metaAccess.lookupJavaType(SingleAbstractImplementorInterface.class);
 436         ResolvedJavaType aSai = metaAccess.lookupJavaType(SingleAbstractImplementor.class);
 437         assertEquals(aSai, iSai.getSingleImplementor());
 438 
 439         ResolvedJavaType iMi = metaAccess.lookupJavaType(MultiImplementorInterface.class);
 440         metaAccess.lookupJavaType(ConcreteImplementor1.class);
 441         metaAccess.lookupJavaType(ConcreteImplementor2.class);
 442         assertEquals(iMi, iMi.getSingleImplementor());
 443 
 444         ResolvedJavaType iMai = metaAccess.lookupJavaType(MultipleAbstractImplementorInterface.class);
 445         metaAccess.lookupJavaType(MultiAbstractImplementor1.class);
 446         metaAccess.lookupJavaType(MultiAbstractImplementor2.class);
 447         assertEquals(iMai, iMai.getSingleImplementor());
 448 
 449         ResolvedJavaType iSai2 = metaAccess.lookupJavaType(SingleAbstractImplementorInterface2.class);
 450         ResolvedJavaType aSai2 = metaAccess.lookupJavaType(SingleAbstractImplementor2.class);
 451         metaAccess.lookupJavaType(ConcreteTransitiveImplementor1.class);
 452         metaAccess.lookupJavaType(ConcreteTransitiveImplementor2.class);
 453         assertEquals(aSai2, iSai2.getSingleImplementor());
 454     }
 455 
 456     @Test(expected = JVMCIError.class)
 457     public void getSingleImplementorTestClassReceiver() {
 458         ResolvedJavaType base = metaAccess.lookupJavaType(Base.class);
 459         base.getSingleImplementor();
 460     }
 461 
 462     @Test(expected = JVMCIError.class)
 463     public void getSingleImplementorTestPrimitiveReceiver() {
 464         ResolvedJavaType primitive = metaAccess.lookupJavaType(int.class);
 465         primitive.getSingleImplementor();
 466     }
 467 
 468     @Test
 469     public void getComponentTypeTest() {
 470         for (Class<?> c : classes) {
 471             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 472             Class<?> expected = c.getComponentType();
 473             ResolvedJavaType actual = type.getComponentType();
 474             if (expected == null) {
 475                 assertNull(actual);
 476             } else {
 477                 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
 478             }
 479         }
 480     }
 481 
 482     @Test
 483     public void getArrayClassTest() {
 484         for (Class<?> c : classes) {
 485             if (c != void.class) {
 486                 ResolvedJavaType type = metaAccess.lookupJavaType(c);
 487                 Class<?> expected = getArrayClass(c);
 488                 ResolvedJavaType actual = type.getArrayClass();
 489                 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
 490             }
 491         }
 492     }
 493 
 494     static class Declarations {
 495 
 496         final Method implementation;
 497         final Set<Method> declarations;
 498 
 499         Declarations(Method impl) {
 500             this.implementation = impl;
 501             declarations = new HashSet<>();
 502         }
 503     }
 504 
 505     /**
 506      * See <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.5">Method
 507      * overriding</a>.
 508      */
 509     static boolean isOverriderOf(Method impl, Method m) {
 510         if (!isPrivate(m.getModifiers()) && !isFinal(m.getModifiers())) {
 511             if (m.getName().equals(impl.getName())) {
 512                 if (m.getReturnType() == impl.getReturnType()) {
 513                     if (Arrays.equals(m.getParameterTypes(), impl.getParameterTypes())) {
 514                         if (isPublic(m.getModifiers()) || isProtected(m.getModifiers())) {
 515                             // m is public or protected
 516                             return isPublic(impl.getModifiers()) || isProtected(impl.getModifiers());
 517                         } else {
 518                             // m is package-private
 519                             return impl.getDeclaringClass().getPackage() == m.getDeclaringClass().getPackage();
 520                         }
 521                     }
 522                 }
 523             }
 524         }
 525         return false;
 526     }
 527 
 528     static final Map<Class<?>, VTable> vtables = new HashMap<>();
 529 
 530     static class VTable {
 531 
 532         final Map<NameAndSignature, Method> methods = new HashMap<>();
 533     }
 534 
 535     static synchronized VTable getVTable(Class<?> c) {
 536         VTable vtable = vtables.get(c);
 537         if (vtable == null) {
 538             vtable = new VTable();
 539             if (c != Object.class) {
 540                 VTable superVtable = getVTable(c.getSuperclass());
 541                 vtable.methods.putAll(superVtable.methods);
 542             }
 543             for (Method m : c.getDeclaredMethods()) {
 544                 if (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) {
 545                     if (isAbstract(m.getModifiers())) {
 546                         // A subclass makes a concrete method in a superclass abstract
 547                         vtable.methods.remove(new NameAndSignature(m));
 548                     } else {
 549                         vtable.methods.put(new NameAndSignature(m), m);
 550                     }
 551                 }
 552             }
 553             vtables.put(c, vtable);
 554         }
 555         return vtable;
 556     }
 557 
 558     static Set<Method> findDeclarations(Method impl, Class<?> c) {
 559         Set<Method> declarations = new HashSet<>();
 560         NameAndSignature implSig = new NameAndSignature(impl);
 561         if (c != null) {
 562             for (Method m : c.getDeclaredMethods()) {
 563                 if (new NameAndSignature(m).equals(implSig)) {
 564                     declarations.add(m);
 565                     break;
 566                 }
 567             }
 568             if (!c.isInterface()) {
 569                 declarations.addAll(findDeclarations(impl, c.getSuperclass()));
 570             }
 571             for (Class<?> i : c.getInterfaces()) {
 572                 declarations.addAll(findDeclarations(impl, i));
 573             }
 574         }
 575         return declarations;
 576     }
 577 
 578     @Test
 579     public void resolveMethodTest() {
 580         ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class);
 581         for (Class<?> c : classes) {
 582             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 583             if (c.isInterface()) {
 584                 for (Method m : c.getDeclaredMethods()) {
 585                     ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m);
 586                     ResolvedJavaMethod impl = type.resolveMethod(resolved, context);
 587                     assertEquals(m.toString(), null, impl);
 588                 }
 589             } else if (c.isPrimitive()) {
 590                 assertEquals("No methods expected", c.getDeclaredMethods().length, 0);
 591             } else {
 592                 VTable vtable = getVTable(c);
 593                 for (Method impl : vtable.methods.values()) {
 594                     Set<Method> decls = findDeclarations(impl, c);
 595                     for (Method decl : decls) {
 596                         ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl);
 597                         if (m.isPublic()) {
 598                             ResolvedJavaMethod resolvedmethod = type.resolveMethod(m, context);
 599                             if (isSignaturePolymorphic(m)) {
 600                                 // Signature polymorphic methods must not be resolved
 601                                 assertNull(resolvedmethod);
 602                             } else {
 603                                 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl);
 604                                 assertEquals(m.toString(), i, resolvedmethod);
 605                             }
 606                         }
 607                     }
 608                 }
 609             }
 610         }
 611     }
 612 
 613     @Test
 614     public void resolveConcreteMethodTest() {
 615         ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class);
 616         for (Class<?> c : classes) {
 617             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 618             if (c.isInterface()) {
 619                 for (Method m : c.getDeclaredMethods()) {
 620                     ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m);
 621                     ResolvedJavaMethod impl = type.resolveConcreteMethod(resolved, context);
 622                     assertEquals(m.toString(), null, impl);
 623                 }
 624             } else if (c.isPrimitive()) {
 625                 assertEquals("No methods expected", c.getDeclaredMethods().length, 0);
 626             } else {
 627                 VTable vtable = getVTable(c);
 628                 for (Method impl : vtable.methods.values()) {
 629                     Set<Method> decls = findDeclarations(impl, c);
 630                     for (Method decl : decls) {
 631                         ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl);
 632                         if (m.isPublic()) {
 633                             ResolvedJavaMethod resolvedMethod = type.resolveConcreteMethod(m, context);
 634                             if (isSignaturePolymorphic(m)) {
 635                                 // Signature polymorphic methods must not be resolved
 636                                 assertNull(String.format("Got: %s", resolvedMethod), resolvedMethod);
 637                             } else {
 638                                 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl);
 639                                 assertEquals(i, resolvedMethod);
 640                             }
 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 
 963     private static boolean isSignaturePolymorphic(ResolvedJavaMethod method) {
 964         return method.getAnnotation(SIGNATURE_POLYMORPHIC_CLASS) != null;
 965     }
 966 }