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