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