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