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