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             return true;
 777         }
 778         return false;
 779     }
 780 
 781     @Test
 782     public void getInstanceFieldsTest() {
 783         for (Class<?> c : classes) {
 784             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 785             for (boolean includeSuperclasses : new boolean[]{true, false}) {
 786                 Set<Field> expected = getInstanceFields(c, includeSuperclasses);
 787                 ResolvedJavaField[] actual = type.getInstanceFields(includeSuperclasses);
 788                 for (Field f : expected) {
 789                     assertNotNull(lookupField(actual, f));
 790                 }
 791                 for (ResolvedJavaField rf : actual) {
 792                     if (!isHiddenFromReflection(rf)) {
 793                         assertEquals(rf.toString(), lookupField(expected, rf) != null, !rf.isInternal());
 794                     }
 795                 }
 796 
 797                 // Test stability of getInstanceFields
 798                 ResolvedJavaField[] actual2 = type.getInstanceFields(includeSuperclasses);
 799                 assertArrayEquals(actual, actual2);
 800             }
 801         }
 802     }
 803 
 804     @Test
 805     public void getStaticFieldsTest() {
 806         for (Class<?> c : classes) {
 807             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 808             Set<Field> expected = getStaticFields(c);
 809             ResolvedJavaField[] actual = type.getStaticFields();
 810             for (Field f : expected) {
 811                 assertNotNull(lookupField(actual, f));
 812             }
 813             for (ResolvedJavaField rf : actual) {
 814                 if (!isHiddenFromReflection(rf)) {
 815                     assertEquals(lookupField(expected, rf) != null, !rf.isInternal());
 816                 }
 817             }
 818 
 819             // Test stability of getStaticFields
 820             ResolvedJavaField[] actual2 = type.getStaticFields();
 821             assertArrayEquals(actual, actual2);
 822         }
 823     }
 824 
 825     @Test
 826     public void getDeclaredMethodsTest() {
 827         for (Class<?> c : classes) {
 828             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 829             Method[] raw = c.getDeclaredMethods();
 830             Set<ResolvedJavaMethod> expected = new HashSet<>();
 831             for (Method m : raw) {
 832                 ResolvedJavaMethod resolvedMethod = metaAccess.lookupJavaMethod(m);
 833                 assertNotNull(resolvedMethod);
 834                 expected.add(resolvedMethod);
 835             }
 836             Set<ResolvedJavaMethod> actual = new HashSet<>(Arrays.asList(type.getDeclaredMethods()));
 837             assertEquals(expected, actual);
 838         }
 839     }
 840 
 841     static class A {
 842         static String name = "foo";
 843     }
 844 
 845     static class B extends A {
 846     }
 847 
 848     static class C {
 849     }
 850 
 851     static class D {
 852         void foo() {
 853             // use of assertions causes the class to have a <clinit>
 854             assert getClass() != null;
 855         }
 856     }
 857 
 858     static class SubD extends D {
 859 
 860     }
 861 
 862     private static ResolvedJavaMethod getClassInitializer(Class<?> c) {
 863         ResolvedJavaMethod clinit = metaAccess.lookupJavaType(c).getClassInitializer();
 864         if (clinit != null) {
 865             assertEquals(0, clinit.getAnnotations().length);
 866             assertEquals(0, clinit.getDeclaredAnnotations().length);
 867         }
 868         return clinit;
 869     }
 870 
 871     @Test
 872     public void getClassInitializerTest() {
 873         assertNotNull(getClassInitializer(A.class));
 874         assertNotNull(getClassInitializer(D.class));
 875         assertNull(getClassInitializer(B.class));
 876         assertNull(getClassInitializer(C.class));
 877         assertNull(getClassInitializer(int.class));
 878         assertNull(getClassInitializer(void.class));
 879         for (Class<?> c : classes) {
 880             getClassInitializer(c);
 881         }
 882     }
 883 
 884     @Test
 885     public void getAnnotationsTest() {
 886         for (Class<?> c : classes) {
 887             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 888             assertArrayEquals(c.getAnnotations(), type.getAnnotations());
 889         }
 890     }
 891 
 892     @Test
 893     public void getAnnotationTest() {
 894         for (Class<?> c : classes) {
 895             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 896             for (Annotation a : c.getAnnotations()) {
 897                 assertEquals(a, type.getAnnotation(a.annotationType()));
 898             }
 899         }
 900     }
 901 
 902     @Test
 903     public void memberClassesTest() {
 904         for (Class<?> c : classes) {
 905             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 906             assertEquals(c.isLocalClass(), type.isLocal());
 907             assertEquals(c.isMemberClass(), type.isMember());
 908             Class<?> enclc = c.getEnclosingClass();
 909             ResolvedJavaType enclt = type.getEnclosingType();
 910             assertFalse(enclc == null ^ enclt == null);
 911             if (enclc != null) {
 912                 assertEquals(enclt, metaAccess.lookupJavaType(enclc));
 913             }
 914         }
 915     }
 916 
 917     @Test
 918     public void isLeafTest() {
 919         for (Class<?> c : classes) {
 920             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 921             ResolvedJavaType arrayType = c != void.class ? metaAccess.lookupJavaType(getArrayClass(c)) : null;
 922             if (c.isPrimitive()) {
 923                 assertTrue(type.isLeaf());
 924                 assertTrue(arrayType == null || arrayType.isLeaf());
 925             } else {
 926                 assertTrue(c.toString(), type.isLeaf() == arrayType.isLeaf());
 927                 if (!c.isArray()) {
 928                     assertTrue(c.toString(), type.isLeaf() == Modifier.isFinal(c.getModifiers()));
 929                 }
 930             }
 931         }
 932     }
 933 
 934     static class TrivialCloneable implements Cloneable {
 935         @Override
 936         protected Object clone() {
 937             return new TrivialCloneable();
 938         }
 939     }
 940 
 941     @Test
 942     public void isCloneableWithAllocationTest() {
 943         ResolvedJavaType cloneable = metaAccess.lookupJavaType(Cloneable.class);
 944         for (Class<?> c : classes) {
 945             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 946             if (type.isCloneableWithAllocation()) {
 947                 // Only Cloneable types should be allocation cloneable
 948                 assertTrue(c.toString(), cloneable.isAssignableFrom(type));
 949             }
 950         }
 951         /*
 952          * We can't know for sure which types should be allocation cloneable on a particular
 953          * platform but assume that at least totally trivial objects should be.
 954          */
 955         ResolvedJavaType trivialCloneable = metaAccess.lookupJavaType(TrivialCloneable.class);
 956         assertTrue(trivialCloneable.toString(), trivialCloneable.isCloneableWithAllocation());
 957     }
 958 
 959     @Test
 960     public void findMethodTest() {
 961         try {
 962             ResolvedJavaMethod findFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V"));
 963             ResolvedJavaMethod expectedFoo = metaAccess.lookupJavaMethod(D.class.getDeclaredMethod("foo"));
 964             assertEquals(expectedFoo, findFoo);
 965 
 966             ResolvedJavaMethod wrongReturnTypeFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()I"));
 967             assertNull(wrongReturnTypeFoo);
 968 
 969             ResolvedJavaMethod wrongArgumentsFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("(I)V"));
 970             assertNull(wrongArgumentsFoo);
 971 
 972             ResolvedJavaMethod wrongNameFoo = metaAccess.lookupJavaType(D.class).findMethod("bar", metaAccess.parseMethodDescriptor("()V"));
 973             assertNull(wrongNameFoo);
 974 
 975             ResolvedJavaMethod wrongClassFoo = metaAccess.lookupJavaType(SubD.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V"));
 976             assertNull(wrongClassFoo);
 977         } catch (NoSuchMethodException | SecurityException e) {
 978             throw new RuntimeException(e);
 979         }
 980     }
 981 
 982     private Method findTestMethod(Method apiMethod) {
 983         String testName = apiMethod.getName() + "Test";
 984         for (Method m : getClass().getDeclaredMethods()) {
 985             if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) {
 986                 return m;
 987             }
 988         }
 989         return null;
 990     }
 991 
 992     // @formatter:off
 993     private static final String[] untestedApiMethods = {
 994         "initialize",
 995         "isPrimitive",
 996         "newArray",
 997         "getDeclaredConstructors",
 998         "isInitialized",
 999         "isLinked",
1000         "getJavaClass",
1001         "getObjectHub",
1002         "hasFinalizableSubclass",
1003         "hasFinalizer",
1004         "getSourceFileName",
1005         "isLocal",
1006         "isJavaLangObject",
1007         "isMember",
1008         "getElementalType",
1009         "getEnclosingType",
1010         "lookupType",
1011         "resolveField",
1012         "$jacocoInit"
1013     };
1014     // @formatter:on
1015 
1016     /**
1017      * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written
1018      * for them or are added to {@link #untestedApiMethods}.
1019      */
1020     @Test
1021     public void testCoverage() {
1022         Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods));
1023         for (Method m : ResolvedJavaType.class.getDeclaredMethods()) {
1024             if (findTestMethod(m) == null) {
1025                 assertTrue("test missing for " + m, known.contains(m.getName()));
1026             } else {
1027                 assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName()));
1028             }
1029         }
1030     }
1031 
1032     private static boolean isSignaturePolymorphic(ResolvedJavaMethod method) {
1033         return method.getAnnotation(SIGNATURE_POLYMORPHIC_CLASS) != null;
1034     }
1035 }