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