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