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