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