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