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