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