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