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             assertEquals(expected.length, actual.length);


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