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