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