1 /*
   2  * Copyright (c) 2012, 2021, 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.jvmci
  27  * @library ../../../../../
  28  * @modules java.base/jdk.internal.org.objectweb.asm
  29  *          java.base/jdk.internal.reflect
  30  *          jdk.internal.vm.ci/jdk.vm.ci.meta
  31  *          jdk.internal.vm.ci/jdk.vm.ci.runtime
  32  *          jdk.internal.vm.ci/jdk.vm.ci.common
  33  *          java.base/jdk.internal.misc
  34  * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:-UseJVMCICompiler 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 jdk.vm.ci.meta.MetaUtil.internalNameToJava;
  46 import static jdk.vm.ci.meta.MetaUtil.toInternalName;
  47 import static org.junit.Assert.assertArrayEquals;
  48 import static org.junit.Assert.assertEquals;
  49 import static org.junit.Assert.assertFalse;
  50 import static org.junit.Assert.assertNotNull;
  51 import static org.junit.Assert.assertNull;
  52 import static org.junit.Assert.assertTrue;
  53 
  54 import java.io.DataInputStream;
  55 import java.io.IOException;
  56 import java.io.InputStream;
  57 import java.lang.annotation.Annotation;
  58 import java.lang.invoke.MethodHandles.Lookup;
  59 import java.lang.reflect.AccessibleObject;
  60 import java.lang.reflect.Constructor;
  61 import java.lang.reflect.Field;
  62 import java.lang.reflect.Method;
  63 import java.lang.reflect.Modifier;
  64 import java.util.Arrays;
  65 import java.util.Collections;
  66 import java.util.function.Supplier;
  67 import java.util.HashMap;
  68 import java.util.HashSet;
  69 import java.util.Map;
  70 import java.util.Set;
  71 
  72 import org.junit.Assert;
  73 import org.junit.Test;
  74 
  75 import jdk.internal.org.objectweb.asm.*;
  76 import jdk.internal.reflect.ConstantPool;
  77 import jdk.vm.ci.common.JVMCIError;
  78 import jdk.vm.ci.meta.Assumptions.AssumptionResult;
  79 import jdk.vm.ci.meta.JavaConstant;
  80 import jdk.vm.ci.meta.JavaKind;
  81 import jdk.vm.ci.meta.ResolvedJavaField;
  82 import jdk.vm.ci.meta.ResolvedJavaMethod;
  83 import jdk.vm.ci.meta.ResolvedJavaType;
  84 
  85 /**
  86  * Tests for {@link ResolvedJavaType}.
  87  */
  88 public class TestResolvedJavaType extends TypeUniverse {
  89     private static final Class<? extends Annotation> SIGNATURE_POLYMORPHIC_CLASS = findPolymorphicSignatureClass();
  90 
  91     public TestResolvedJavaType() {
  92     }
  93 
  94     @Test
  95     public void equalsTest() {
  96         for (ResolvedJavaType t : javaTypes) {
  97             for (ResolvedJavaType that : javaTypes) {
  98                 boolean expect = t == that;
  99                 boolean actual = t.equals(that);
 100                 assertEquals(expect, actual);
 101             }
 102         }
 103     }
 104 
 105     @SuppressWarnings("unchecked")
 106     private static Class<? extends Annotation> findPolymorphicSignatureClass() {
 107         Class<? extends Annotation> signaturePolyAnnotation = null;
 108         try {
 109             for (Class<?> clazz : TestResolvedJavaType.class.getClassLoader().loadClass("java.lang.invoke.MethodHandle").getDeclaredClasses()) {
 110                 if (clazz.getName().endsWith("PolymorphicSignature") && Annotation.class.isAssignableFrom(clazz)) {
 111                     signaturePolyAnnotation = (Class<? extends Annotation>) clazz;
 112                     break;
 113                 }
 114             }
 115         } catch (Throwable e) {
 116             throw new AssertionError("Could not find annotation PolymorphicSignature in java.lang.invoke.MethodHandle", e);
 117         }
 118         assertNotNull(signaturePolyAnnotation);
 119         return signaturePolyAnnotation;
 120     }
 121 
 122     @Test
 123     public void findInstanceFieldWithOffsetTest() {
 124         for (Class<?> c : classes) {
 125             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 126             Set<Field> reflectionFields = getInstanceFields(c, true);
 127             for (Field f : reflectionFields) {
 128                 ResolvedJavaField rf = lookupField(type.getInstanceFields(true), f);
 129                 assertNotNull(rf);
 130                 long offset = isStatic(f.getModifiers()) ? unsafe.staticFieldOffset(f) : unsafe.objectFieldOffset(f);
 131                 ResolvedJavaField result = type.findInstanceFieldWithOffset(offset, rf.getJavaKind());
 132                 assertNotNull(result);
 133                 assertTrue(fieldsEqual(f, result));
 134             }
 135         }
 136     }
 137 
 138     @Test
 139     public void isInterfaceTest() {
 140         for (Class<?> c : classes) {
 141             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 142             boolean expected = c.isInterface();
 143             boolean actual = type.isInterface();
 144             assertEquals(expected, actual);
 145         }
 146     }
 147 
 148     @Test
 149     public void isEnumTest() {
 150         for (Class<?> c : classes) {
 151             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 152             boolean expected = c.isEnum();
 153             boolean actual = type.isEnum();
 154             assertEquals(expected, actual);
 155         }
 156     }
 157 
 158     @Test
 159     public void isInstanceClassTest() {
 160         for (Class<?> c : classes) {
 161             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 162             boolean expected = !c.isArray() && !c.isPrimitive() && !c.isInterface();
 163             boolean actual = type.isInstanceClass();
 164             assertEquals(expected, actual);
 165         }
 166     }
 167 
 168     @Test
 169     public void isArrayTest() {
 170         for (Class<?> c : classes) {
 171             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 172             boolean expected = c.isArray();
 173             boolean actual = type.isArray();
 174             assertEquals(expected, actual);
 175         }
 176     }
 177 
 178     @Test
 179     public void lambdaInternalNameTest() {
 180         // Verify that the last dot in lambda types is properly handled when transitioning from internal name to java
 181         // name and vice versa.
 182         Supplier<Runnable> lambda = () -> () -> System.out.println("run");
 183         ResolvedJavaType lambdaType = metaAccess.lookupJavaType(lambda.getClass());
 184         String typeName = lambdaType.getName();
 185         String javaName = lambda.getClass().getName();
 186         assertEquals(typeName, toInternalName(javaName));
 187         assertEquals(javaName, internalNameToJava(typeName, true, true));
 188     }
 189 
 190     @Test
 191     public void getModifiersTest() {
 192         for (Class<?> c : classes) {
 193             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 194             int mask = Modifier.classModifiers() & ~Modifier.STATIC;
 195             int expected = c.getModifiers() & mask;
 196             int actual = type.getModifiers() & mask;
 197             Class<?> elementalType = c;
 198             while (elementalType.isArray()) {
 199                 elementalType = elementalType.getComponentType();
 200             }
 201             if (elementalType.isMemberClass()) {
 202                 // member class get their modifiers from the inner-class attribute in the JVM and
 203                 // from the classfile header in jvmci
 204                 expected &= ~(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED);
 205                 actual &= ~(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED);
 206             }
 207             assertEquals(String.format("%s: 0x%x != 0x%x", type, expected, actual), expected, actual);
 208         }
 209     }
 210 
 211     @Test
 212     public void isAssignableFromTest() {
 213         Class<?>[] all = classes.toArray(new Class<?>[classes.size()]);
 214         for (int i = 0; i < all.length; i++) {
 215             Class<?> c1 = all[i];
 216             for (int j = i; j < all.length; j++) {
 217                 Class<?> c2 = all[j];
 218                 ResolvedJavaType t1 = metaAccess.lookupJavaType(c1);
 219                 ResolvedJavaType t2 = metaAccess.lookupJavaType(c2);
 220                 boolean expected = c1.isAssignableFrom(c2);
 221                 boolean actual = t1.isAssignableFrom(t2);
 222                 assertEquals(expected, actual);
 223                 if (expected && t1 != t2) {
 224                     assertFalse(t2.isAssignableFrom(t1));
 225                 }
 226             }
 227         }
 228     }
 229 
 230     @Test
 231     public void isInstanceTest() {
 232         for (ConstantValue cv : constants()) {
 233             JavaConstant c = cv.value;
 234             if (c.getJavaKind() == JavaKind.Object && !c.isNull()) {
 235                 ResolvedJavaType cType = metaAccess.lookupJavaType(c);
 236                 for (ResolvedJavaType t : javaTypes) {
 237                     if (t.isAssignableFrom(cType)) {
 238                         assertTrue(t.isInstance(c));
 239                     } else {
 240                         assertFalse(t.isInstance(c));
 241                     }
 242                 }
 243             }
 244         }
 245     }
 246 
 247     @Test
 248     public void getSuperclassTest() {
 249         for (Class<?> c : classes) {
 250             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 251             Class<?> expected = c.getSuperclass();
 252             ResolvedJavaType actual = type.getSuperclass();
 253             if (expected == null) {
 254                 assertTrue(actual == null);
 255             } else {
 256                 assertNotNull(actual);
 257                 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
 258             }
 259         }
 260     }
 261 
 262     @Test
 263     public void getInterfacesTest() {
 264         for (Class<?> c : classes) {
 265             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 266             Class<?>[] expected = c.getInterfaces();
 267             ResolvedJavaType[] actual = type.getInterfaces();
 268             assertEquals(expected.length, actual.length);
 269             for (int i = 0; i < expected.length; i++) {
 270                 assertTrue(actual[i].equals(metaAccess.lookupJavaType(expected[i])));
 271             }
 272         }
 273     }
 274 
 275     public Class<?> getSupertype(Class<?> c) {
 276         assert !c.isPrimitive();
 277         if (c.isArray()) {
 278             Class<?> componentType = c.getComponentType();
 279             if (componentType.isPrimitive() || componentType == Object.class) {
 280                 return Object.class;
 281             }
 282             return getArrayClass(getSupertype(componentType));
 283         }
 284         if (c.isInterface()) {
 285             return Object.class;
 286         }
 287         return c.getSuperclass();
 288     }
 289 
 290     public Class<?> findLeastCommonAncestor(Class<?> c1Initial, Class<?> c2Initial) {
 291         if (c1Initial.isPrimitive() || c2Initial.isPrimitive()) {
 292             return null;
 293         } else {
 294             Class<?> c1 = c1Initial;
 295             Class<?> c2 = c2Initial;
 296             while (true) {
 297                 if (c1.isAssignableFrom(c2)) {
 298                     return c1;
 299                 }
 300                 if (c2.isAssignableFrom(c1)) {
 301                     return c2;
 302                 }
 303                 c1 = getSupertype(c1);
 304                 c2 = getSupertype(c2);
 305             }
 306         }
 307     }
 308 
 309     @Test
 310     public void findLeastCommonAncestorTest() {
 311         Class<?>[] all = classes.toArray(new Class<?>[classes.size()]);
 312         for (int i = 0; i < all.length; i++) {
 313             Class<?> c1 = all[i];
 314             for (int j = i; j < all.length; j++) {
 315                 Class<?> c2 = all[j];
 316                 ResolvedJavaType t1 = metaAccess.lookupJavaType(c1);
 317                 ResolvedJavaType t2 = metaAccess.lookupJavaType(c2);
 318                 Class<?> expected = findLeastCommonAncestor(c1, c2);
 319                 ResolvedJavaType actual = t1.findLeastCommonAncestor(t2);
 320                 if (expected == null) {
 321                     assertTrue(actual == null);
 322                 } else {
 323                     assertNotNull(actual);
 324                     assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
 325                 }
 326             }
 327         }
 328     }
 329 
 330     @Test
 331     public void linkTest() {
 332         for (Class<?> c : classes) {
 333             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 334             type.link();
 335         }
 336     }
 337 
 338     private class HidingClassLoader extends ClassLoader {
 339         @Override
 340         protected Class<?> findClass(final String name) throws ClassNotFoundException {
 341             if (name.endsWith("MissingInterface")) {
 342                 throw new ClassNotFoundException("missing");
 343             }
 344             byte[] classData = null;
 345             try {
 346                 InputStream is = HidingClassLoader.class.getResourceAsStream("/" + name.replace('.', '/') + ".class");
 347                 classData = new byte[is.available()];
 348                 new DataInputStream(is).readFully(classData);
 349             } catch (IOException e) {
 350                 Assert.fail("can't access class: " + name);
 351             }
 352 
 353             return defineClass(null, classData, 0, classData.length);
 354         }
 355 
 356         ResolvedJavaType lookupJavaType(String name) throws ClassNotFoundException {
 357             return metaAccess.lookupJavaType(loadClass(name));
 358         }
 359 
 360         HidingClassLoader() {
 361             super(null);
 362         }
 363 
 364     }
 365 
 366     interface MissingInterface {
 367     }
 368 
 369     static class MissingInterfaceImpl implements MissingInterface {
 370     }
 371 
 372     interface SomeInterface {
 373         default MissingInterface someMethod() {
 374             return new MissingInterfaceImpl();
 375         }
 376     }
 377 
 378     static class Wrapper implements SomeInterface {
 379     }
 380 
 381     @Test
 382     public void linkExceptionTest() throws ClassNotFoundException {
 383         HidingClassLoader cl = new HidingClassLoader();
 384         ResolvedJavaType inner = cl.lookupJavaType(Wrapper.class.getName());
 385         assertTrue("expected default methods", inner.hasDefaultMethods());
 386         try {
 387             inner.link();
 388             assertFalse("link should throw an exception", true);
 389         } catch (NoClassDefFoundError e) {
 390         }
 391     }
 392 
 393     @Test
 394     public void hasDefaultMethodsTest() {
 395         for (Class<?> c : classes) {
 396             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 397             assertEquals(hasDefaultMethods(type), type.hasDefaultMethods());
 398         }
 399     }
 400 
 401     @Test
 402     public void declaresDefaultMethodsTest() {
 403         for (Class<?> c : classes) {
 404             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 405             assertEquals(declaresDefaultMethods(type), type.declaresDefaultMethods());
 406         }
 407     }
 408 
 409     private static boolean hasDefaultMethods(ResolvedJavaType type) {
 410         if (!type.isInterface() && type.getSuperclass() != null && hasDefaultMethods(type.getSuperclass())) {
 411             return true;
 412         }
 413         for (ResolvedJavaType iface : type.getInterfaces()) {
 414             if (hasDefaultMethods(iface)) {
 415                 return true;
 416             }
 417         }
 418         return declaresDefaultMethods(type);
 419     }
 420 
 421     static boolean declaresDefaultMethods(ResolvedJavaType type) {
 422         if (!type.isInterface()) {
 423             /* Only interfaces can declare default methods. */
 424             return false;
 425         }
 426         for (ResolvedJavaMethod method : type.getDeclaredMethods()) {
 427             if (method.isDefault()) {
 428                 assert !Modifier.isStatic(method.getModifiers()) : "Default method that is static?";
 429                 return true;
 430             }
 431         }
 432         return false;
 433     }
 434 
 435     private static class Base {
 436     }
 437 
 438     abstract static class Abstract1 extends Base {
 439     }
 440 
 441     interface Interface1 {
 442     }
 443 
 444     static class Concrete1 extends Abstract1 {
 445     }
 446 
 447     static class Concrete2 extends Abstract1 implements Interface1 {
 448     }
 449 
 450     static class Concrete3 extends Concrete2 {
 451     }
 452 
 453     static final class Final1 extends Abstract1 {
 454     }
 455 
 456     abstract static class Abstract4 extends Concrete3 {
 457     }
 458 
 459     void checkConcreteSubtype(ResolvedJavaType type, ResolvedJavaType expected) {
 460         AssumptionResult<ResolvedJavaType> leafConcreteSubtype = type.findLeafConcreteSubtype();
 461         if (leafConcreteSubtype == null) {
 462             // findLeafConcreteSubtype() is conservative
 463         } else {
 464             if (expected == null) {
 465                 assertNull(leafConcreteSubtype);
 466             } else {
 467                 assertTrue(leafConcreteSubtype.getResult().equals(expected));
 468             }
 469             assertTrue(!type.isLeaf() || leafConcreteSubtype.isAssumptionFree());
 470         }
 471 
 472         if (!type.isArray()) {
 473             ResolvedJavaType arrayType = type.getArrayClass();
 474             AssumptionResult<ResolvedJavaType> arraySubtype = arrayType.findLeafConcreteSubtype();
 475             if (arraySubtype != null) {
 476                 assertEquals(arraySubtype.getResult(), arrayType);
 477             } else {
 478                 // findLeafConcreteSubtype() method is conservative
 479             }
 480         }
 481     }
 482 
 483     @Test
 484     public void findLeafConcreteSubtypeTest() {
 485         ResolvedJavaType base = metaAccess.lookupJavaType(Base.class);
 486         checkConcreteSubtype(base, base);
 487 
 488         ResolvedJavaType a1 = metaAccess.lookupJavaType(Abstract1.class);
 489         ResolvedJavaType c1 = metaAccess.lookupJavaType(Concrete1.class);
 490 
 491         checkConcreteSubtype(base, null);
 492         checkConcreteSubtype(a1, c1);
 493         checkConcreteSubtype(c1, c1);
 494 
 495         ResolvedJavaType i1 = metaAccess.lookupJavaType(Interface1.class);
 496         ResolvedJavaType c2 = metaAccess.lookupJavaType(Concrete2.class);
 497 
 498         checkConcreteSubtype(base, null);
 499         checkConcreteSubtype(a1, null);
 500         checkConcreteSubtype(c1, c1);
 501         checkConcreteSubtype(i1, c2);
 502         checkConcreteSubtype(c2, c2);
 503 
 504         ResolvedJavaType c3 = metaAccess.lookupJavaType(Concrete3.class);
 505         checkConcreteSubtype(c2, null);
 506         checkConcreteSubtype(c3, c3);
 507 
 508         ResolvedJavaType a4 = metaAccess.lookupJavaType(Abstract4.class);
 509         checkConcreteSubtype(c3, null);
 510         checkConcreteSubtype(a4, null);
 511 
 512         ResolvedJavaType a1a = metaAccess.lookupJavaType(Abstract1[].class);
 513         checkConcreteSubtype(a1a, null);
 514         ResolvedJavaType i1a = metaAccess.lookupJavaType(Interface1[].class);
 515         checkConcreteSubtype(i1a, null);
 516         ResolvedJavaType c1a = metaAccess.lookupJavaType(Concrete1[].class);
 517         checkConcreteSubtype(c1a, c1a);
 518         ResolvedJavaType f1a = metaAccess.lookupJavaType(Final1[].class);
 519         checkConcreteSubtype(f1a, f1a);
 520 
 521         ResolvedJavaType obja = metaAccess.lookupJavaType(Object[].class);
 522         checkConcreteSubtype(obja, null);
 523 
 524         ResolvedJavaType inta = metaAccess.lookupJavaType(int[].class);
 525         checkConcreteSubtype(inta, inta);
 526     }
 527 
 528     interface NoImplementor {
 529     }
 530 
 531     interface SingleImplementorInterface {
 532     }
 533 
 534     static class SingleConcreteImplementor implements SingleImplementorInterface {
 535     }
 536 
 537     interface SingleAbstractImplementorInterface {
 538     }
 539 
 540     abstract static class SingleAbstractImplementor implements SingleAbstractImplementorInterface {
 541     }
 542 
 543     interface MultiImplementorInterface {
 544     }
 545 
 546     static class ConcreteImplementor1 implements MultiImplementorInterface {
 547     }
 548 
 549     static class ConcreteImplementor2 implements MultiImplementorInterface {
 550     }
 551 
 552     interface MultipleAbstractImplementorInterface {
 553     }
 554 
 555     abstract static class MultiAbstractImplementor1 implements MultipleAbstractImplementorInterface {
 556     }
 557 
 558     abstract static class MultiAbstractImplementor2 implements MultipleAbstractImplementorInterface {
 559     }
 560 
 561     interface SingleAbstractImplementorInterface2 {
 562     }
 563 
 564     interface ExtendedSingleImplementorInterface {
 565     }
 566 
 567     abstract static class SingleAbstractImplementor2 implements SingleAbstractImplementorInterface2 {
 568     }
 569 
 570     static class ConcreteTransitiveImplementor1 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface {
 571     }
 572 
 573     static class ConcreteTransitiveImplementor2 extends SingleAbstractImplementor2 implements ExtendedSingleImplementorInterface {
 574     }
 575 
 576     @Test
 577     public void getSingleImplementorTest() {
 578         ResolvedJavaType iNi = metaAccess.lookupJavaType(NoImplementor.class);
 579         assertNull(iNi.getSingleImplementor());
 580 
 581         ResolvedJavaType iSi = metaAccess.lookupJavaType(SingleImplementorInterface.class);
 582         ResolvedJavaType cSi = metaAccess.lookupJavaType(SingleConcreteImplementor.class);
 583         assertEquals(cSi, iSi.getSingleImplementor());
 584 
 585         ResolvedJavaType iSai = metaAccess.lookupJavaType(SingleAbstractImplementorInterface.class);
 586         ResolvedJavaType aSai = metaAccess.lookupJavaType(SingleAbstractImplementor.class);
 587         assertEquals(aSai, iSai.getSingleImplementor());
 588 
 589         ResolvedJavaType iMi = metaAccess.lookupJavaType(MultiImplementorInterface.class);
 590         metaAccess.lookupJavaType(ConcreteImplementor1.class);
 591         metaAccess.lookupJavaType(ConcreteImplementor2.class);
 592         assertEquals(iMi, iMi.getSingleImplementor());
 593 
 594         ResolvedJavaType iMai = metaAccess.lookupJavaType(MultipleAbstractImplementorInterface.class);
 595         metaAccess.lookupJavaType(MultiAbstractImplementor1.class);
 596         metaAccess.lookupJavaType(MultiAbstractImplementor2.class);
 597         assertEquals(iMai, iMai.getSingleImplementor());
 598 
 599         ResolvedJavaType iSai2 = metaAccess.lookupJavaType(SingleAbstractImplementorInterface2.class);
 600         ResolvedJavaType aSai2 = metaAccess.lookupJavaType(SingleAbstractImplementor2.class);
 601         metaAccess.lookupJavaType(ConcreteTransitiveImplementor1.class);
 602         metaAccess.lookupJavaType(ConcreteTransitiveImplementor2.class);
 603         assertEquals(aSai2, iSai2.getSingleImplementor());
 604 
 605         for (Class<?> c : classes) {
 606             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 607             try {
 608                 type.getSingleImplementor();
 609                 if (!c.isInterface()) {
 610                     throw new AssertionError("Expected exception for calling getSingleImplmentor on " + c.getName());
 611                 }
 612             } catch (JVMCIError e) {
 613                 if (c.isInterface()) {
 614                     throw new AssertionError("Unexpected exception", e);
 615                 }
 616             }
 617         }
 618     }
 619 
 620     @Test(expected = JVMCIError.class)
 621     public void getSingleImplementorTestClassReceiver() {
 622         ResolvedJavaType base = metaAccess.lookupJavaType(Base.class);
 623         base.getSingleImplementor();
 624     }
 625 
 626     @Test(expected = JVMCIError.class)
 627     public void getSingleImplementorTestPrimitiveReceiver() {
 628         ResolvedJavaType primitive = metaAccess.lookupJavaType(int.class);
 629         primitive.getSingleImplementor();
 630     }
 631 
 632     @Test
 633     public void getComponentTypeTest() {
 634         for (Class<?> c : classes) {
 635             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 636             Class<?> expected = c.getComponentType();
 637             ResolvedJavaType actual = type.getComponentType();
 638             if (expected == null) {
 639                 assertNull(actual);
 640             } else {
 641                 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
 642             }
 643         }
 644     }
 645 
 646     @Test
 647     public void getArrayClassTest() {
 648         for (Class<?> c : classes) {
 649             if (c != void.class) {
 650                 ResolvedJavaType type = metaAccess.lookupJavaType(c);
 651                 Class<?> expected = getArrayClass(c);
 652                 ResolvedJavaType actual = type.getArrayClass();
 653                 assertTrue(actual.equals(metaAccess.lookupJavaType(expected)));
 654             }
 655         }
 656     }
 657 
 658     static class Declarations {
 659 
 660         final Method implementation;
 661         final Set<Method> declarations;
 662 
 663         Declarations(Method impl) {
 664             this.implementation = impl;
 665             declarations = new HashSet<>();
 666         }
 667     }
 668 
 669     /**
 670      * See <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.5">Method
 671      * overriding</a>.
 672      */
 673     static boolean isOverriderOf(Method impl, Method m) {
 674         if (!isPrivate(m.getModifiers()) && !isFinal(m.getModifiers())) {
 675             if (m.getName().equals(impl.getName())) {
 676                 if (m.getReturnType() == impl.getReturnType()) {
 677                     if (Arrays.equals(m.getParameterTypes(), impl.getParameterTypes())) {
 678                         if (isPublic(m.getModifiers()) || isProtected(m.getModifiers())) {
 679                             // m is public or protected
 680                             return isPublic(impl.getModifiers()) || isProtected(impl.getModifiers());
 681                         } else {
 682                             // m is package-private
 683                             return impl.getDeclaringClass().getPackage() == m.getDeclaringClass().getPackage();
 684                         }
 685                     }
 686                 }
 687             }
 688         }
 689         return false;
 690     }
 691 
 692     static final Map<Class<?>, VTable> vtables = new HashMap<>();
 693 
 694     static class VTable {
 695 
 696         final Map<NameAndSignature, Method> methods = new HashMap<>();
 697     }
 698 
 699     static synchronized VTable getVTable(Class<?> c) {
 700         VTable vtable = vtables.get(c);
 701         if (vtable == null) {
 702             vtable = new VTable();
 703             if (c != Object.class) {
 704                 VTable superVtable = getVTable(c.getSuperclass());
 705                 vtable.methods.putAll(superVtable.methods);
 706             }
 707             for (Method m : c.getDeclaredMethods()) {
 708                 if (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) {
 709                     if (isAbstract(m.getModifiers())) {
 710                         // A subclass makes a concrete method in a superclass abstract
 711                         vtable.methods.remove(new NameAndSignature(m));
 712                     } else {
 713                         vtable.methods.put(new NameAndSignature(m), m);
 714                     }
 715                 }
 716             }
 717             vtables.put(c, vtable);
 718         }
 719         return vtable;
 720     }
 721 
 722     static Set<Method> findDeclarations(Method impl, Class<?> c) {
 723         Set<Method> declarations = new HashSet<>();
 724         NameAndSignature implSig = new NameAndSignature(impl);
 725         if (c != null) {
 726             for (Method m : c.getDeclaredMethods()) {
 727                 if (new NameAndSignature(m).equals(implSig)) {
 728                     declarations.add(m);
 729                     break;
 730                 }
 731             }
 732             if (!c.isInterface()) {
 733                 declarations.addAll(findDeclarations(impl, c.getSuperclass()));
 734             }
 735             for (Class<?> i : c.getInterfaces()) {
 736                 declarations.addAll(findDeclarations(impl, i));
 737             }
 738         }
 739         return declarations;
 740     }
 741 
 742     @Test
 743     public void resolveMethodTest() {
 744         ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class);
 745         for (Class<?> c : classes) {
 746             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 747             if (c.isInterface()) {
 748                 for (Method m : c.getDeclaredMethods()) {
 749                     ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m);
 750                     ResolvedJavaMethod impl = type.resolveMethod(resolved, context);
 751                     assertEquals(m.toString(), null, impl);
 752                 }
 753             } else if (c.isPrimitive()) {
 754                 assertEquals("No methods expected", c.getDeclaredMethods().length, 0);
 755             } else {
 756                 VTable vtable = getVTable(c);
 757                 for (Method impl : vtable.methods.values()) {
 758                     Set<Method> decls = findDeclarations(impl, c);
 759                     for (Method decl : decls) {
 760                         ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl);
 761                         if (m.isPublic()) {
 762                             ResolvedJavaMethod resolvedMethod = type.resolveMethod(m, context);
 763                             if (isSignaturePolymorphic(m)) {
 764                                 // Signature polymorphic methods must not be resolved
 765                                 assertNull(resolvedMethod);
 766                             } else {
 767                                 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl);
 768                                 assertEquals(m.toString(), i, resolvedMethod);
 769                             }
 770                         }
 771                     }
 772                 }
 773                 // For backwards compatibility treat constructors as resolvable even though they
 774                 // aren't virtually dispatched.
 775                 ResolvedJavaType declaringClass = metaAccess.lookupJavaType(c);
 776                 for (Constructor<?> m : c.getDeclaredConstructors()) {
 777                     ResolvedJavaMethod decl = metaAccess.lookupJavaMethod(m);
 778                     ResolvedJavaMethod impl = type.resolveMethod(decl, declaringClass);
 779                     assertEquals(m.toString(), decl, impl);
 780                 }
 781                 for (Method m : c.getDeclaredMethods()) {
 782                     if (isStatic(m.getModifiers())) {
 783                         // resolveMethod really shouldn't be called with static methods and the
 784                         // result is is somewhat inconsistent so just ignore them
 785                         continue;
 786                     }
 787                     ResolvedJavaMethod decl = metaAccess.lookupJavaMethod(m);
 788                     ResolvedJavaMethod impl = type.resolveMethod(decl, declaringClass);
 789                     ResolvedJavaMethod expected = isSignaturePolymorphic(decl) ? null : decl;
 790                     assertEquals(m.toString(), expected, impl);
 791                 }
 792             }
 793         }
 794     }
 795 
 796     @Test
 797     public void resolveConcreteMethodTest() {
 798         ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class);
 799         for (Class<?> c : classes) {
 800             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 801             if (c.isInterface()) {
 802                 for (Method m : c.getDeclaredMethods()) {
 803                     ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m);
 804                     ResolvedJavaMethod impl = type.resolveConcreteMethod(resolved, context);
 805                     assertEquals(m.toString(), null, impl);
 806                 }
 807             } else if (c.isPrimitive()) {
 808                 assertEquals("No methods expected", c.getDeclaredMethods().length, 0);
 809             } else {
 810                 VTable vtable = getVTable(c);
 811                 for (Method impl : vtable.methods.values()) {
 812                     Set<Method> decls = findDeclarations(impl, c);
 813                     for (Method decl : decls) {
 814                         ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl);
 815                         if (m.isPublic()) {
 816                             ResolvedJavaMethod resolvedMethod = type.resolveConcreteMethod(m, context);
 817                             if (isSignaturePolymorphic(m)) {
 818                                 // Signature polymorphic methods must not be resolved
 819                                 assertNull(String.format("Got: %s", resolvedMethod), resolvedMethod);
 820                             } else {
 821                                 ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl);
 822                                 assertEquals(i, resolvedMethod);
 823                             }
 824                         }
 825                     }
 826                 }
 827                 for (Method m : c.getDeclaredMethods()) {
 828                     ResolvedJavaMethod impl = type.resolveConcreteMethod(metaAccess.lookupJavaMethod(m), context);
 829                     ResolvedJavaMethod expected = isAbstract(m.getModifiers()) ? null : impl;
 830                     assertEquals(type + " " + m.toString(), expected, impl);
 831                 }
 832             }
 833         }
 834     }
 835 
 836     @Test
 837     public void findUniqueConcreteMethodTest() throws NoSuchMethodException {
 838         ResolvedJavaMethod thisMethod = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("findUniqueConcreteMethodTest"));
 839         ResolvedJavaMethod ucm = metaAccess.lookupJavaType(getClass()).findUniqueConcreteMethod(thisMethod).getResult();
 840         assertEquals(thisMethod, ucm);
 841     }
 842 
 843     public static Set<Field> getInstanceFields(Class<?> c, boolean includeSuperclasses) {
 844         if (c.isArray() || c.isPrimitive() || c.isInterface()) {
 845             return Collections.emptySet();
 846         }
 847         Set<Field> result = new HashSet<>();
 848         for (Field f : c.getDeclaredFields()) {
 849             if (!Modifier.isStatic(f.getModifiers())) {
 850                 result.add(f);
 851             }
 852         }
 853         if (includeSuperclasses && c != Object.class) {
 854             result.addAll(getInstanceFields(c.getSuperclass(), true));
 855         }
 856         return result;
 857     }
 858 
 859     public static Set<Field> getStaticFields(Class<?> c) {
 860         Set<Field> result = new HashSet<>();
 861         for (Field f : c.getDeclaredFields()) {
 862             if (Modifier.isStatic(f.getModifiers())) {
 863                 result.add(f);
 864             }
 865         }
 866         return result;
 867     }
 868 
 869     public boolean fieldsEqual(Field f, ResolvedJavaField rjf) {
 870         return rjf.getDeclaringClass().equals(metaAccess.lookupJavaType(f.getDeclaringClass())) && rjf.getName().equals(f.getName()) &&
 871                         rjf.getType().resolve(rjf.getDeclaringClass()).equals(metaAccess.lookupJavaType(f.getType()));
 872     }
 873 
 874     public ResolvedJavaField lookupField(ResolvedJavaField[] fields, Field key) {
 875         for (ResolvedJavaField rf : fields) {
 876             if (fieldsEqual(key, rf)) {
 877                 return rf;
 878             }
 879         }
 880         return null;
 881     }
 882 
 883     public Field lookupField(Set<Field> fields, ResolvedJavaField key) {
 884         for (Field f : fields) {
 885             if (fieldsEqual(f, key)) {
 886                 return f;
 887             }
 888         }
 889         return null;
 890     }
 891 
 892     private static boolean isHiddenFromReflection(ResolvedJavaField f) {
 893         if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Throwable.class)) && f.getName().equals("backtrace")) {
 894             return true;
 895         }
 896         if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ConstantPool.class)) && f.getName().equals("constantPoolOop")) {
 897             return true;
 898         }
 899         if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Class.class))) {
 900             return f.getName().equals("classLoader") || f.getName().equals("classData");
 901         }
 902         if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Lookup.class))) {
 903             return f.getName().equals("allowedModes") || f.getName().equals("lookupClass");
 904         }
 905         if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ClassLoader.class)) ||
 906             f.getDeclaringClass().equals(metaAccess.lookupJavaType(AccessibleObject.class)) ||
 907             f.getDeclaringClass().equals(metaAccess.lookupJavaType(Constructor.class)) ||
 908             f.getDeclaringClass().equals(metaAccess.lookupJavaType(Field.class)) ||
 909             f.getDeclaringClass().equals(metaAccess.lookupJavaType(Method.class)) ||
 910             f.getDeclaringClass().equals(metaAccess.lookupJavaType(Module.class))) {
 911             return true;
 912         }
 913         return false;
 914     }
 915 
 916     @Test
 917     public void getInstanceFieldsTest() {
 918         for (Class<?> c : classes) {
 919             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 920             for (boolean includeSuperclasses : new boolean[]{true, false}) {
 921                 Set<Field> expected = getInstanceFields(c, includeSuperclasses);
 922                 ResolvedJavaField[] actual = type.getInstanceFields(includeSuperclasses);
 923                 for (Field f : expected) {
 924                     assertNotNull(lookupField(actual, f));
 925                 }
 926                 for (ResolvedJavaField rf : actual) {
 927                     if (!isHiddenFromReflection(rf)) {
 928                         assertEquals(rf.toString(), lookupField(expected, rf) != null, !rf.isInternal());
 929                     }
 930                 }
 931 
 932                 // Test stability of getInstanceFields
 933                 ResolvedJavaField[] actual2 = type.getInstanceFields(includeSuperclasses);
 934                 assertArrayEquals(actual, actual2);
 935             }
 936         }
 937     }
 938 
 939     @Test
 940     public void getStaticFieldsTest() {
 941         for (Class<?> c : classes) {
 942             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 943             Set<Field> expected = getStaticFields(c);
 944             ResolvedJavaField[] actual = type.getStaticFields();
 945             for (Field f : expected) {
 946                 assertNotNull(lookupField(actual, f));
 947             }
 948             for (ResolvedJavaField rf : actual) {
 949                 if (!isHiddenFromReflection(rf)) {
 950                     assertEquals(lookupField(expected, rf) != null, !rf.isInternal());
 951                 }
 952             }
 953 
 954             // Test stability of getStaticFields
 955             ResolvedJavaField[] actual2 = type.getStaticFields();
 956             assertArrayEquals(actual, actual2);
 957         }
 958     }
 959 
 960     @Test
 961     public void getDeclaredMethodsTest() {
 962         for (Class<?> c : classes) {
 963             ResolvedJavaType type = metaAccess.lookupJavaType(c);
 964             Method[] raw = c.getDeclaredMethods();
 965             Set<ResolvedJavaMethod> expected = new HashSet<>();
 966             for (Method m : raw) {
 967                 ResolvedJavaMethod resolvedMethod = metaAccess.lookupJavaMethod(m);
 968                 assertNotNull(resolvedMethod);
 969                 expected.add(resolvedMethod);
 970             }
 971             Set<ResolvedJavaMethod> actual = new HashSet<>(Arrays.asList(type.getDeclaredMethods()));
 972             assertEquals(expected, actual);
 973         }
 974     }
 975 
 976     static class A {
 977         static String name = "foo";
 978     }
 979 
 980     static class B extends A {
 981     }
 982 
 983     static class C {
 984     }
 985 
 986     static class D {
 987         void foo() {
 988             // use of assertions causes the class to have a <clinit>
 989             assert getClass() != null;
 990         }
 991     }
 992 
 993     static class SubD extends D {
 994 
 995     }
 996 
 997     private static ResolvedJavaMethod getClassInitializer(Class<?> c) {
 998         ResolvedJavaMethod clinit = metaAccess.lookupJavaType(c).getClassInitializer();
 999         if (clinit != null) {
1000             assertEquals(0, clinit.getAnnotations().length);
1001             assertEquals(0, clinit.getDeclaredAnnotations().length);
1002         }
1003         return clinit;
1004     }
1005 
1006     @Test
1007     public void getClassInitializerTest() {
1008         assertNotNull(getClassInitializer(A.class));
1009         assertNotNull(getClassInitializer(D.class));
1010         assertNull(getClassInitializer(B.class));
1011         assertNull(getClassInitializer(C.class));
1012         assertNull(getClassInitializer(int.class));
1013         assertNull(getClassInitializer(void.class));
1014         for (Class<?> c : classes) {
1015             getClassInitializer(c);
1016         }
1017     }
1018 
1019     @Test
1020     public void getAnnotationsTest() {
1021         for (Class<?> c : classes) {
1022             ResolvedJavaType type = metaAccess.lookupJavaType(c);
1023             assertArrayEquals(c.getAnnotations(), type.getAnnotations());
1024         }
1025     }
1026 
1027     @Test
1028     public void getAnnotationTest() {
1029         for (Class<?> c : classes) {
1030             ResolvedJavaType type = metaAccess.lookupJavaType(c);
1031             for (Annotation a : c.getAnnotations()) {
1032                 assertEquals(a, type.getAnnotation(a.annotationType()));
1033             }
1034         }
1035     }
1036 
1037     @Test
1038     public void getSourceFileNameTest() {
1039         Class<?> c = Object.class;
1040         ResolvedJavaType type = metaAccess.lookupJavaType(c);
1041         assertEquals(type.getSourceFileName(), "Object.java");
1042     }
1043 
1044     @Test
1045     public void memberClassesTest() {
1046         for (Class<?> c : classes) {
1047             ResolvedJavaType type = metaAccess.lookupJavaType(c);
1048             assertEquals(c.isLocalClass(), type.isLocal());
1049             assertEquals(c.isMemberClass(), type.isMember());
1050             Class<?> enclc = c.getEnclosingClass();
1051             ResolvedJavaType enclt = type.getEnclosingType();
1052             assertFalse(enclc == null ^ enclt == null);
1053             if (enclc != null) {
1054                 assertEquals(enclt, metaAccess.lookupJavaType(enclc));
1055             }
1056         }
1057     }
1058 
1059     @Test
1060     public void isLeafTest() {
1061         for (Class<?> c : classes) {
1062             ResolvedJavaType type = metaAccess.lookupJavaType(c);
1063             ResolvedJavaType arrayType = c != void.class ? metaAccess.lookupJavaType(getArrayClass(c)) : null;
1064             if (c.isPrimitive()) {
1065                 assertTrue(type.isLeaf());
1066                 assertTrue(arrayType == null || arrayType.isLeaf());
1067             } else {
1068                 assertTrue(c.toString(), type.isLeaf() == arrayType.isLeaf());
1069                 if (!c.isArray()) {
1070                     assertTrue(c.toString(), type.isLeaf() == Modifier.isFinal(c.getModifiers()));
1071                 }
1072             }
1073         }
1074     }
1075 
1076     static class TrivialCloneable implements Cloneable {
1077         @Override
1078         protected Object clone() {
1079             return new TrivialCloneable();
1080         }
1081     }
1082 
1083     @Test
1084     public void isCloneableWithAllocationTest() {
1085         ResolvedJavaType cloneable = metaAccess.lookupJavaType(Cloneable.class);
1086         for (Class<?> c : classes) {
1087             ResolvedJavaType type = metaAccess.lookupJavaType(c);
1088             if (type.isCloneableWithAllocation()) {
1089                 // Only Cloneable types should be allocation cloneable
1090                 assertTrue(c.toString(), cloneable.isAssignableFrom(type));
1091             }
1092         }
1093         /*
1094          * We can't know for sure which types should be allocation cloneable on a particular
1095          * platform but assume that at least totally trivial objects should be.
1096          */
1097         ResolvedJavaType trivialCloneable = metaAccess.lookupJavaType(TrivialCloneable.class);
1098         assertTrue(trivialCloneable.toString(), trivialCloneable.isCloneableWithAllocation());
1099     }
1100 
1101     @Test
1102     public void findMethodTest() {
1103         try {
1104             ResolvedJavaMethod findFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V"));
1105             ResolvedJavaMethod expectedFoo = metaAccess.lookupJavaMethod(D.class.getDeclaredMethod("foo"));
1106             assertEquals(expectedFoo, findFoo);
1107 
1108             ResolvedJavaMethod wrongReturnTypeFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("()I"));
1109             assertNull(wrongReturnTypeFoo);
1110 
1111             ResolvedJavaMethod wrongArgumentsFoo = metaAccess.lookupJavaType(D.class).findMethod("foo", metaAccess.parseMethodDescriptor("(I)V"));
1112             assertNull(wrongArgumentsFoo);
1113 
1114             ResolvedJavaMethod wrongNameFoo = metaAccess.lookupJavaType(D.class).findMethod("bar", metaAccess.parseMethodDescriptor("()V"));
1115             assertNull(wrongNameFoo);
1116 
1117             ResolvedJavaMethod wrongClassFoo = metaAccess.lookupJavaType(SubD.class).findMethod("foo", metaAccess.parseMethodDescriptor("()V"));
1118             assertNull(wrongClassFoo);
1119         } catch (NoSuchMethodException | SecurityException e) {
1120             throw new RuntimeException(e);
1121         }
1122     }
1123 
1124     private Method findTestMethod(Method apiMethod) {
1125         String testName = apiMethod.getName() + "Test";
1126         for (Method m : getClass().getDeclaredMethods()) {
1127             if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) {
1128                 return m;
1129             }
1130         }
1131         return null;
1132     }
1133 
1134     // @formatter:off
1135     private static final String[] untestedApiMethods = {
1136         "initialize",
1137         "isPrimitive",
1138         "newArray",
1139         "getDeclaredConstructors",
1140         "isInitialized",
1141         "isLinked",
1142         "getJavaClass",
1143         "getObjectHub",
1144         "getHostClass",
1145         "hasFinalizableSubclass",
1146         "hasFinalizer",
1147         "isLocal",
1148         "isJavaLangObject",
1149         "isMember",
1150         "getElementalType",
1151         "getEnclosingType",
1152         "lookupType",
1153         "resolveField",
1154         "$jacocoInit"
1155     };
1156     // @formatter:on
1157 
1158     /**
1159      * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written
1160      * for them or are added to {@link #untestedApiMethods}.
1161      */
1162     @Test
1163     public void testCoverage() {
1164         Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods));
1165         for (Method m : ResolvedJavaType.class.getDeclaredMethods()) {
1166             if (findTestMethod(m) == null) {
1167                 assertTrue("test missing for " + m, known.contains(m.getName()));
1168             } else {
1169                 assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName()));
1170             }
1171         }
1172     }
1173 
1174     private static boolean isSignaturePolymorphic(ResolvedJavaMethod method) {
1175         return method.getAnnotation(SIGNATURE_POLYMORPHIC_CLASS) != null;
1176     }
1177 }