1 /*
   2  * Copyright (c) 2011, 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 package jdk.vm.ci.hotspot;
  24 
  25 import static java.util.Objects.requireNonNull;
  26 import static jdk.vm.ci.hotspot.CompilerToVM.compilerToVM;
  27 import static jdk.vm.ci.hotspot.HotSpotConstantPool.isSignaturePolymorphicHolder;
  28 import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
  29 import static jdk.vm.ci.hotspot.HotSpotModifiers.jvmClassModifiers;
  30 import static jdk.vm.ci.hotspot.HotSpotVMConfig.config;
  31 import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;
  32 
  33 import java.lang.annotation.Annotation;
  34 import java.lang.reflect.Field;
  35 import java.lang.reflect.Modifier;
  36 import java.nio.ByteOrder;
  37 import java.util.Arrays;
  38 import java.util.Comparator;
  39 import java.util.HashMap;
  40 
  41 import jdk.vm.ci.common.JVMCIError;
  42 import jdk.vm.ci.meta.Assumptions.AssumptionResult;
  43 import jdk.vm.ci.meta.Assumptions.ConcreteMethod;
  44 import jdk.vm.ci.meta.Assumptions.ConcreteSubtype;
  45 import jdk.vm.ci.meta.Assumptions.LeafType;
  46 import jdk.vm.ci.meta.Assumptions.NoFinalizableSubclass;
  47 import jdk.vm.ci.meta.Constant;
  48 import jdk.vm.ci.meta.JavaConstant;
  49 import jdk.vm.ci.meta.JavaKind;
  50 import jdk.vm.ci.meta.JavaType;
  51 import jdk.vm.ci.meta.ResolvedJavaField;
  52 import jdk.vm.ci.meta.ResolvedJavaMethod;
  53 import jdk.vm.ci.meta.ResolvedJavaType;
  54 import jdk.vm.ci.meta.UnresolvedJavaField;
  55 import jdk.vm.ci.meta.UnresolvedJavaType;
  56 
  57 /**
  58  * Implementation of {@link JavaType} for resolved non-primitive HotSpot classes. This class is not
  59  * an {@link MetaspaceHandleObject} because it doesn't have to be scanned for GC. It's liveness is
  60  * maintained by a reference to the {@link Class} instance.
  61  */
  62 final class HotSpotResolvedObjectTypeImpl extends HotSpotResolvedJavaType implements HotSpotResolvedObjectType, MetaspaceObject {
  63 
  64     private static final HotSpotResolvedJavaField[] NO_FIELDS = new HotSpotResolvedJavaField[0];
  65     private static final int METHOD_CACHE_ARRAY_CAPACITY = 8;
  66     private static final SortByOffset fieldSortingMethod = new SortByOffset();
  67 
  68     /**
  69      * The Java class this type represents.
  70      */
  71     private final long metadataPointer;
  72 
  73     private HotSpotResolvedJavaMethodImpl[] methodCacheArray;
  74     private HashMap<Long, HotSpotResolvedJavaMethodImpl> methodCacheHashMap;
  75     private volatile HotSpotResolvedJavaField[] instanceFields;
  76     private volatile HotSpotResolvedObjectTypeImpl[] interfaces;
  77     private HotSpotConstantPool constantPool;
  78     private final JavaConstant mirror;
  79     private HotSpotResolvedObjectTypeImpl superClass;
  80     private HotSpotResolvedJavaType componentType;
  81 
  82     /**
  83      * Managed exclusively by {@link HotSpotJDKReflection#getField}.
  84      */
  85     HashMap<HotSpotResolvedJavaFieldImpl, Field> reflectionFieldCache;
  86 
  87     static HotSpotResolvedObjectTypeImpl getJavaLangObject() {
  88         return runtime().getJavaLangObject();
  89     }
  90 
  91     /**
  92      * Gets the JVMCI mirror from a HotSpot type.
  93      *
  94      * Called from the VM.
  95      *
  96      * @param klassPointer a native pointer to the Klass*
  97      * @return the {@link ResolvedJavaType} corresponding to {@code javaClass}
  98      */
  99     @SuppressWarnings("unused")
 100     @VMEntryPoint
 101     private static HotSpotResolvedObjectTypeImpl fromMetaspace(long klassPointer, String signature) {
 102         return runtime().fromMetaspace(klassPointer, signature);
 103     }
 104 
 105     /**
 106      * Creates the JVMCI mirror for a {@link Class} object.
 107      *
 108      * <b>NOTE</b>: Creating an instance of this class does not install the mirror for the
 109      * {@link Class} type.
 110      * </p>
 111      *
 112      * @param metadataPointer the Klass* to create the mirror for
 113      */
 114     @SuppressWarnings("try")
 115     HotSpotResolvedObjectTypeImpl(long metadataPointer, String name) {
 116         super(name);
 117         assert metadataPointer != 0;
 118         this.metadataPointer = metadataPointer;
 119 
 120         // The mirror object must be in the global scope since
 121         // this object will be cached in HotSpotJVMCIRuntime.resolvedJavaTypes
 122         // and live across more than one compilation.
 123         try (HotSpotObjectConstantScope global = HotSpotObjectConstantScope.enterGlobalScope()) {
 124             this.mirror = runtime().compilerToVm.getJavaMirror(this);
 125             assert getName().charAt(0) != '[' || isArray() : getName();
 126         }
 127     }
 128 
 129     /**
 130      * Gets the metaspace Klass for this type.
 131      */
 132     long getMetaspaceKlass() {
 133         long metaspacePointer = getMetaspacePointer();
 134         if (metaspacePointer == 0) {
 135             throw new NullPointerException("Klass* is null");
 136         }
 137         return metaspacePointer;
 138     }
 139 
 140     @Override
 141     public long getMetaspacePointer() {
 142         return metadataPointer;
 143     }
 144 
 145     @Override
 146     public int getModifiers() {
 147         if (isArray()) {
 148             return (getElementalType().getModifiers() & (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED)) | Modifier.FINAL | Modifier.ABSTRACT;
 149         } else {
 150             return getAccessFlags() & jvmClassModifiers();
 151         }
 152     }
 153 
 154     public int getAccessFlags() {
 155         HotSpotVMConfig config = config();
 156         return UNSAFE.getInt(getMetaspaceKlass() + config.klassAccessFlagsOffset);
 157     }
 158 
 159     @Override
 160     public ResolvedJavaType getComponentType() {
 161         if (componentType == null) {
 162             if (isArray()) {
 163                 componentType = runtime().compilerToVm.getComponentType(this);
 164             } else {
 165                 componentType = this;
 166             }
 167         }
 168         return this.equals(componentType) ? null : componentType;
 169     }
 170 
 171     @Override
 172     public AssumptionResult<ResolvedJavaType> findLeafConcreteSubtype() {
 173         if (isLeaf()) {
 174             // No assumptions are required.
 175             return new AssumptionResult<>(this);
 176         }
 177         HotSpotVMConfig config = config();
 178         if (isArray()) {
 179             ResolvedJavaType elementalType = getElementalType();
 180             AssumptionResult<ResolvedJavaType> elementType = elementalType.findLeafConcreteSubtype();
 181             if (elementType != null && elementType.getResult().equals(elementalType)) {
 182                 /*
 183                  * If the elementType is leaf then the array is leaf under the same assumptions but
 184                  * only if the element type is exactly the leaf type. The element type can be
 185                  * abstract even if there is only one implementor of the abstract type.
 186                  */
 187                 AssumptionResult<ResolvedJavaType> result = new AssumptionResult<>(this);
 188                 result.add(elementType);
 189                 return result;
 190             }
 191             return null;
 192         } else if (isInterface()) {
 193             HotSpotResolvedObjectTypeImpl implementor = getSingleImplementor();
 194             /*
 195              * If the implementor field contains itself that indicates that the interface has more
 196              * than one implementors (see: InstanceKlass::add_implementor).
 197              */
 198             if (implementor == null || implementor.equals(this)) {
 199                 return null;
 200             }
 201 
 202             assert !implementor.isInterface();
 203             if (implementor.isAbstract() || !implementor.isLeafClass()) {
 204                 AssumptionResult<ResolvedJavaType> leafConcreteSubtype = implementor.findLeafConcreteSubtype();
 205                 if (leafConcreteSubtype != null) {
 206                     assert !leafConcreteSubtype.getResult().equals(implementor);
 207                     AssumptionResult<ResolvedJavaType> newResult = new AssumptionResult<>(leafConcreteSubtype.getResult(), new ConcreteSubtype(this, implementor));
 208                     // Accumulate leaf assumptions and return the combined result.
 209                     newResult.add(leafConcreteSubtype);
 210                     return newResult;
 211                 }
 212                 return null;
 213             }
 214             return concreteSubtype(implementor);
 215         } else {
 216             HotSpotResolvedObjectTypeImpl type = this;
 217             while (type.isAbstract()) {
 218                 HotSpotResolvedObjectTypeImpl subklass = type.getSubklass();
 219                 if (subklass == null || UNSAFE.getAddress(subklass.getMetaspaceKlass() + config.nextSiblingOffset) != 0) {
 220                     return null;
 221                 }
 222                 type = subklass;
 223             }
 224             if (type.isAbstract() || type.isInterface() || !type.isLeafClass()) {
 225                 return null;
 226             }
 227             if (this.isAbstract()) {
 228                 return concreteSubtype(type);
 229             } else {
 230                 assert this.equals(type);
 231                 return new AssumptionResult<>(type, new LeafType(type));
 232             }
 233         }
 234     }
 235 
 236     private AssumptionResult<ResolvedJavaType> concreteSubtype(HotSpotResolvedObjectTypeImpl type) {
 237         if (type.isLeaf()) {
 238             return new AssumptionResult<>(type, new ConcreteSubtype(this, type));
 239         } else {
 240             return new AssumptionResult<>(type, new LeafType(type), new ConcreteSubtype(this, type));
 241         }
 242     }
 243 
 244     /**
 245      * Returns if type {@code type} is a leaf class. This is the case if the
 246      * {@code Klass::_subklass} field of the underlying class is zero.
 247      *
 248      * @return true if the type is a leaf class
 249      */
 250     private boolean isLeafClass() {
 251         return UNSAFE.getLong(this.getMetaspaceKlass() + config().subklassOffset) == 0;
 252     }
 253 
 254     /**
 255      * Returns the {@code Klass::_subklass} field of the underlying metaspace klass for the given
 256      * type {@code type}.
 257      *
 258      * @return value of the subklass field as metaspace klass pointer
 259      */
 260     private HotSpotResolvedObjectTypeImpl getSubklass() {
 261         return compilerToVM().getResolvedJavaType(this, config().subklassOffset, false);
 262     }
 263 
 264     @Override
 265     public HotSpotResolvedObjectTypeImpl getSuperclass() {
 266         if (isInterface()) {
 267             return null;
 268         }
 269         HotSpotResolvedObjectTypeImpl javaLangObject = runtime().getJavaLangObject();
 270         if (this.equals(javaLangObject)) {
 271             return null;
 272         }
 273         if (isArray()) {
 274             return javaLangObject;
 275         }
 276 
 277         // Cache result of native call
 278         if (superClass == null) {
 279             superClass = compilerToVM().getResolvedJavaType(this, config().superOffset, false);
 280         }
 281         return superClass;
 282     }
 283 
 284     @Override
 285     public HotSpotResolvedObjectTypeImpl[] getInterfaces() {
 286         if (interfaces == null) {
 287             if (isArray()) {
 288                 HotSpotResolvedObjectTypeImpl[] types = new HotSpotResolvedObjectTypeImpl[3];
 289                 types[0] = runtime().getJavaLangCloneable();
 290                 types[1] = runtime().getJavaLangSerializable();
 291                 types[2] = runtime().getJavaLangIdentityObject();
 292                 this.interfaces = types;
 293             } else {
 294                 interfaces = runtime().compilerToVm.getInterfaces(this);
 295             }
 296         }
 297         return interfaces;
 298     }
 299 
 300     @Override
 301     public HotSpotResolvedObjectTypeImpl getSingleImplementor() {
 302         if (!isInterface()) {
 303             throw new JVMCIError("Cannot call getSingleImplementor() on a non-interface type: %s", this);
 304         }
 305         return compilerToVM().getImplementor(this);
 306     }
 307 
 308     @Override
 309     public HotSpotResolvedObjectTypeImpl getSupertype() {
 310         ResolvedJavaType component = getComponentType();
 311         if (component != null) {
 312             if (component.equals(getJavaLangObject()) || component.isPrimitive()) {
 313                 return getJavaLangObject();
 314             }
 315             HotSpotResolvedObjectTypeImpl supertype = ((HotSpotResolvedObjectTypeImpl) component).getSupertype();
 316             return (HotSpotResolvedObjectTypeImpl) supertype.getArrayClass();
 317         }
 318         if (isInterface()) {
 319             return getJavaLangObject();
 320         }
 321         return getSuperclass();
 322     }
 323 
 324     @Override
 325     public HotSpotResolvedObjectType findLeastCommonAncestor(ResolvedJavaType otherType) {
 326         if (otherType.isPrimitive()) {
 327             return null;
 328         } else {
 329             HotSpotResolvedObjectTypeImpl t1 = this;
 330             HotSpotResolvedObjectTypeImpl t2 = (HotSpotResolvedObjectTypeImpl) otherType;
 331             while (true) {
 332                 if (t1.isAssignableFrom(t2)) {
 333                     return t1;
 334                 }
 335                 if (t2.isAssignableFrom(t1)) {
 336                     return t2;
 337                 }
 338                 t1 = t1.getSupertype();
 339                 t2 = t2.getSupertype();
 340             }
 341         }
 342     }
 343 
 344     @Override
 345     public AssumptionResult<Boolean> hasFinalizableSubclass() {
 346         assert !isArray();
 347         if (!compilerToVM().hasFinalizableSubclass(this)) {
 348             return new AssumptionResult<>(false, new NoFinalizableSubclass(this));
 349         }
 350         return new AssumptionResult<>(true);
 351     }
 352 
 353     @Override
 354     public boolean hasFinalizer() {
 355         return (getAccessFlags() & config().jvmAccHasFinalizer) != 0;
 356     }
 357 
 358     @Override
 359     public boolean isArray() {
 360         return layoutHelper() < config().klassLayoutHelperNeutralValue;
 361     }
 362 
 363     @Override
 364     public boolean isEnum() {
 365         HotSpotResolvedObjectTypeImpl superclass = getSuperclass();
 366         return superclass != null && superclass.equals(runtime().getJavaLangEnum());
 367     }
 368 
 369     @Override
 370     public boolean isInitialized() {
 371         return isArray() ? true : getInitState() == config().instanceKlassStateFullyInitialized;
 372     }
 373 
 374     @Override
 375     public boolean isBeingInitialized() {
 376         return isArray() ? false : getInitState() == config().instanceKlassStateBeingInitialized;
 377     }
 378 
 379     @Override
 380     public boolean isLinked() {
 381         return isArray() ? true : getInitState() >= config().instanceKlassStateLinked;
 382     }
 383 
 384     @Override
 385     public void link() {
 386         if (!isLinked()) {
 387             runtime().compilerToVm.ensureLinked(this);
 388         }
 389     }
 390 
 391     @Override
 392     public boolean hasDefaultMethods() {
 393         HotSpotVMConfig config = config();
 394         int miscFlags = UNSAFE.getChar(getMetaspaceKlass() + config.instanceKlassMiscFlagsOffset);
 395         return (miscFlags & config.jvmMiscFlagsHasDefaultMethods) != 0;
 396     }
 397 
 398     @Override
 399     public boolean declaresDefaultMethods() {
 400         HotSpotVMConfig config = config();
 401         int miscFlags = UNSAFE.getChar(getMetaspaceKlass() + config.instanceKlassMiscFlagsOffset);
 402         return (miscFlags & config.jvmMiscFlagsDeclaresDefaultMethods) != 0;
 403     }
 404 
 405     /**
 406      * Returns the value of the state field {@code InstanceKlass::_init_state} of the metaspace
 407      * klass.
 408      *
 409      * @return state field value of this type
 410      */
 411     private int getInitState() {
 412         assert !isArray() : "_init_state only exists in InstanceKlass";
 413         return UNSAFE.getByte(getMetaspaceKlass() + config().instanceKlassInitStateOffset) & 0xFF;
 414     }
 415 
 416     @Override
 417     public void initialize() {
 418         if (!isInitialized()) {
 419             runtime().compilerToVm.ensureInitialized(this);
 420             assert isInitialized() || isBeingInitialized();
 421         }
 422     }
 423 
 424     @Override
 425     public boolean isInstance(JavaConstant obj) {
 426         if (obj.getJavaKind() == JavaKind.Object && !obj.isNull()) {
 427             return runtime().reflection.isInstance(this, (HotSpotObjectConstantImpl) obj);
 428         }
 429         return false;
 430     }
 431 
 432     @Override
 433     public boolean isInstanceClass() {
 434         return !isArray() && !isInterface();
 435     }
 436 
 437     @Override
 438     public boolean isInterface() {
 439         return (getAccessFlags() & config().jvmAccInterface) != 0;
 440     }
 441 
 442     @Override
 443     public boolean isAssignableFrom(ResolvedJavaType other) {
 444         assert other != null;
 445         if (other instanceof HotSpotResolvedObjectTypeImpl) {
 446             HotSpotResolvedObjectTypeImpl otherType = (HotSpotResolvedObjectTypeImpl) other;
 447             return runtime().reflection.isAssignableFrom(this, otherType);
 448         }
 449         return false;
 450     }
 451 
 452     @Override
 453     public boolean isJavaLangObject() {
 454         return getName().equals("Ljava/lang/Object;");
 455     }
 456 
 457     @Override
 458     public JavaKind getJavaKind() {
 459         return JavaKind.Object;
 460     }
 461 
 462     @Override
 463     public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) {
 464         assert !callerType.isArray();
 465         if (isInterface()) {
 466             // Methods can only be resolved against concrete types
 467             return null;
 468         }
 469         if (method.isConcrete() && method.getDeclaringClass().equals(this) && method.isPublic() && !isSignaturePolymorphicHolder(method.getDeclaringClass())) {
 470             return method;
 471         }
 472         if (!method.getDeclaringClass().isAssignableFrom(this)) {
 473             return null;
 474         }
 475         if (method.isConstructor()) {
 476             // Constructor calls should have been checked in the verifier and method's
 477             // declaring class is assignable from this (see above) so treat it as resolved.
 478             return method;
 479         }
 480         HotSpotResolvedJavaMethodImpl hotSpotMethod = (HotSpotResolvedJavaMethodImpl) method;
 481         HotSpotResolvedObjectTypeImpl hotSpotCallerType = (HotSpotResolvedObjectTypeImpl) callerType;
 482         return compilerToVM().resolveMethod(this, hotSpotMethod, hotSpotCallerType);
 483     }
 484 
 485     @Override
 486     public HotSpotConstantPool getConstantPool() {
 487         if (constantPool == null || !isArray() && UNSAFE.getAddress(getMetaspaceKlass() + config().instanceKlassConstantsOffset) != constantPool.getMetaspaceConstantPool()) {
 488             /*
 489              * If the pointer to the ConstantPool has changed since this was last read refresh the
 490              * HotSpotConstantPool wrapper object. This ensures that uses of the constant pool are
 491              * operating on the latest one and that HotSpotResolvedJavaMethodImpls will be able to
 492              * use the shared copy instead of creating their own instance.
 493              */
 494             constantPool = compilerToVM().getConstantPool(this);
 495         }
 496         return constantPool;
 497     }
 498 
 499     /**
 500      * Gets the instance size of this type. If an instance of this type cannot be fast path
 501      * allocated, then the returned value is negative (its absolute value gives the size). Must not
 502      * be called if this is an array or interface type.
 503      */
 504     @Override
 505     public int instanceSize() {
 506         assert !isArray();
 507         assert !isInterface();
 508 
 509         HotSpotVMConfig config = config();
 510         final int layoutHelper = layoutHelper();
 511         assert layoutHelper > config.klassLayoutHelperNeutralValue : "must be instance";
 512 
 513         // See: Klass::layout_helper_size_in_bytes
 514         int size = layoutHelper & ~config.klassLayoutHelperInstanceSlowPathBit;
 515 
 516         // See: Klass::layout_helper_needs_slow_path
 517         boolean needsSlowPath = (layoutHelper & config.klassLayoutHelperInstanceSlowPathBit) != 0;
 518 
 519         return needsSlowPath ? -size : size;
 520     }
 521 
 522     @Override
 523     public int layoutHelper() {
 524         HotSpotVMConfig config = config();
 525         assert getMetaspaceKlass() != 0 : getName();
 526         return UNSAFE.getInt(getMetaspaceKlass() + config.klassLayoutHelperOffset);
 527     }
 528 
 529     @Override
 530     public long getFingerprint() {
 531         return compilerToVM().getFingerprint(getMetaspaceKlass());
 532     }
 533 
 534     synchronized HotSpotResolvedJavaMethod createMethod(long metaspaceHandle) {
 535         long metaspaceMethod = UNSAFE.getLong(metaspaceHandle);
 536         // Maintain cache as array.
 537         if (methodCacheArray == null) {
 538             methodCacheArray = new HotSpotResolvedJavaMethodImpl[METHOD_CACHE_ARRAY_CAPACITY];
 539         }
 540 
 541         int i = 0;
 542         for (; i < methodCacheArray.length; ++i) {
 543             HotSpotResolvedJavaMethodImpl curMethod = methodCacheArray[i];
 544             if (curMethod == null) {
 545                 HotSpotResolvedJavaMethodImpl newMethod = new HotSpotResolvedJavaMethodImpl(this, metaspaceHandle);
 546                 methodCacheArray[i] = newMethod;
 547                 return newMethod;
 548             } else if (curMethod.getMetaspaceMethod() == metaspaceMethod) {
 549                 return curMethod;
 550             }
 551         }
 552 
 553         // Fall-back to hash table.
 554         if (methodCacheHashMap == null) {
 555             methodCacheHashMap = new HashMap<>();
 556         }
 557 
 558         HotSpotResolvedJavaMethodImpl lookupResult = methodCacheHashMap.get(metaspaceMethod);
 559         if (lookupResult == null) {
 560             HotSpotResolvedJavaMethodImpl newMethod = new HotSpotResolvedJavaMethodImpl(this, metaspaceHandle);
 561             methodCacheHashMap.put(metaspaceMethod, newMethod);
 562             return newMethod;
 563         } else {
 564             return lookupResult;
 565         }
 566     }
 567 
 568     @Override
 569     public int getVtableLength() {
 570         HotSpotVMConfig config = config();
 571         if (isInterface() || isArray()) {
 572             /* Everything has the core vtable of java.lang.Object */
 573             return config.baseVtableLength();
 574         }
 575         int result = UNSAFE.getInt(getMetaspaceKlass() + config.klassVtableLengthOffset) / (config.vtableEntrySize / config.heapWordSize);
 576         assert result >= config.baseVtableLength() : UNSAFE.getInt(getMetaspaceKlass() + config.klassVtableLengthOffset) + " " + config.vtableEntrySize;
 577         return result;
 578     }
 579 
 580     HotSpotResolvedJavaField createField(JavaType type, long offset, int rawFlags, int index) {
 581         return new HotSpotResolvedJavaFieldImpl(this, type, offset, rawFlags, index);
 582     }
 583 
 584     @Override
 585     public AssumptionResult<ResolvedJavaMethod> findUniqueConcreteMethod(ResolvedJavaMethod method) {
 586         HotSpotResolvedJavaMethod hmethod = (HotSpotResolvedJavaMethod) method;
 587         HotSpotResolvedObjectType declaredHolder = hmethod.getDeclaringClass();
 588         /*
 589          * Sometimes the receiver type in the graph hasn't stabilized to a subtype of declared
 590          * holder, usually because of phis, so make sure that the type is related to the declared
 591          * type before using it for lookup. Unlinked types should also be ignored because we can't
 592          * resolve the proper method to invoke. Generally unlinked types in invokes should result in
 593          * a deopt instead since they can't really be used if they aren't linked yet.
 594          */
 595         if (!declaredHolder.isAssignableFrom(this) || this.isArray() || this.equals(declaredHolder) || !isLinked() || isInterface()) {
 596             if (hmethod.canBeStaticallyBound()) {
 597                 // No assumptions are required.
 598                 return new AssumptionResult<>(hmethod);
 599             }
 600             ResolvedJavaMethod result = hmethod.uniqueConcreteMethod(declaredHolder);
 601             if (result != null) {
 602                 return new AssumptionResult<>(result, new ConcreteMethod(method, declaredHolder, result));
 603             }
 604             return null;
 605         }
 606         /*
 607          * The holder may be a subtype of the declaredHolder so make sure to resolve the method to
 608          * the correct method for the subtype.
 609          */
 610         HotSpotResolvedJavaMethod resolvedMethod = (HotSpotResolvedJavaMethod) resolveMethod(hmethod, this);
 611         if (resolvedMethod == null) {
 612             // The type isn't known to implement the method.
 613             return null;
 614         }
 615         if (resolvedMethod.canBeStaticallyBound()) {
 616             // No assumptions are required.
 617             return new AssumptionResult<>(resolvedMethod);
 618         }
 619 
 620         ResolvedJavaMethod result = resolvedMethod.uniqueConcreteMethod(this);
 621         if (result != null) {
 622             return new AssumptionResult<>(result, new ConcreteMethod(method, this, result));
 623         }
 624         return null;
 625     }
 626 
 627     FieldInfo createFieldInfo(int index) {
 628         return new FieldInfo(index);
 629     }
 630 
 631     public void ensureInitialized() {
 632         runtime().compilerToVm.ensureInitialized(this);
 633     }
 634 
 635     @Override
 636     public boolean equals(Object obj) {
 637         if (obj == this) {
 638             return true;
 639         }
 640         if (!(obj instanceof HotSpotResolvedObjectTypeImpl)) {
 641             return false;
 642         }
 643         HotSpotResolvedObjectTypeImpl that = (HotSpotResolvedObjectTypeImpl) obj;
 644         return getMetaspaceKlass() == that.getMetaspaceKlass();
 645     }
 646 
 647     @Override
 648     JavaConstant getJavaMirror() {
 649         return mirror;
 650     }
 651 
 652     /**
 653      * This class represents the field information for one field contained in the fields array of an
 654      * {@code InstanceKlass}. The implementation is similar to the native {@code FieldInfo} class.
 655      */
 656     class FieldInfo {
 657         /**
 658          * Native pointer into the array of Java shorts.
 659          */
 660         private final long metaspaceData;
 661 
 662         /**
 663          * Creates a field info for the field in the fields array at index {@code index}.
 664          *
 665          * @param index index to the fields array
 666          */
 667         FieldInfo(int index) {
 668             HotSpotVMConfig config = config();
 669             // Get Klass::_fields
 670             final long metaspaceFields = UNSAFE.getAddress(getMetaspaceKlass() + config.instanceKlassFieldsOffset);
 671             assert config.fieldInfoFieldSlots == 6 : "revisit the field parsing code";
 672             int offset = config.fieldInfoFieldSlots * Short.BYTES * index;
 673             metaspaceData = metaspaceFields + config.arrayU2DataOffset + offset;
 674         }
 675 
 676         private int getAccessFlags() {
 677             return readFieldSlot(config().fieldInfoAccessFlagsOffset);
 678         }
 679 
 680         private int getNameIndex() {
 681             return readFieldSlot(config().fieldInfoNameIndexOffset);
 682         }
 683 
 684         private int getSignatureIndex() {
 685             return readFieldSlot(config().fieldInfoSignatureIndexOffset);
 686         }
 687 
 688         public int getOffset() {
 689             HotSpotVMConfig config = config();
 690             final int lowPacked = readFieldSlot(config.fieldInfoLowPackedOffset);
 691             final int highPacked = readFieldSlot(config.fieldInfoHighPackedOffset);
 692             final int offset = ((highPacked << Short.SIZE) | lowPacked) >> config.fieldInfoTagSize;
 693             return offset;
 694         }
 695 
 696         /**
 697          * Helper method to read an entry (slot) from the field array. Currently field info is laid
 698          * on top an array of Java shorts.
 699          */
 700         private int readFieldSlot(int index) {
 701             int offset = Short.BYTES * index;
 702             return UNSAFE.getChar(metaspaceData + offset);
 703         }
 704 
 705         /**
 706          * Returns the name of this field as a {@link String}. If the field is an internal field the
 707          * name index is pointing into the vmSymbols table.
 708          */
 709         public String getName() {
 710             final int nameIndex = getNameIndex();
 711             return isInternal() ? config().symbolAt(nameIndex) : getConstantPool().lookupUtf8(nameIndex);
 712         }
 713 
 714         /**
 715          * Returns the signature of this field as {@link String}. If the field is an internal field
 716          * the signature index is pointing into the vmSymbols table.
 717          */
 718         public String getSignature() {
 719             final int signatureIndex = getSignatureIndex();
 720             return isInternal() ? config().symbolAt(signatureIndex) : getConstantPool().lookupUtf8(signatureIndex);
 721         }
 722 
 723         public JavaType getType() {
 724             String signature = getSignature();
 725             return runtime().lookupType(signature, HotSpotResolvedObjectTypeImpl.this, false);
 726         }
 727 
 728         private boolean isInternal() {
 729             return (getAccessFlags() & config().jvmAccFieldInternal) != 0;
 730         }
 731 
 732         public boolean isStatic() {
 733             return Modifier.isStatic(getAccessFlags());
 734         }
 735 
 736         public boolean hasGenericSignature() {
 737             return (getAccessFlags() & config().jvmAccFieldHasGenericSignature) != 0;
 738         }
 739     }
 740 
 741     static class SortByOffset implements Comparator<ResolvedJavaField> {
 742         public int compare(ResolvedJavaField a, ResolvedJavaField b) {
 743             return a.getOffset() - b.getOffset();
 744         }
 745     }
 746 
 747     @Override
 748     public ResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) {
 749         if (instanceFields == null) {
 750             if (isArray() || isInterface()) {
 751                 instanceFields = NO_FIELDS;
 752             } else {
 753                 HotSpotResolvedJavaField[] prepend = NO_FIELDS;
 754                 if (getSuperclass() != null) {
 755                     prepend = (HotSpotResolvedJavaField[]) getSuperclass().getInstanceFields(true);
 756                 }
 757                 instanceFields = getFields(false, prepend);
 758             }
 759         }
 760         if (!includeSuperclasses && getSuperclass() != null) {
 761             int superClassFieldCount = getSuperclass().getInstanceFields(true).length;
 762             if (superClassFieldCount == instanceFields.length) {
 763                 // This class does not have any instance fields of its own.
 764                 return NO_FIELDS;
 765             } else if (superClassFieldCount != 0) {
 766                 // Fields of the current class can be interleaved with fields of its super-classes
 767                 // but the array of fields to be returned must be sorted by increasing offset
 768                 // This code populates the array, then applies the sorting function
 769                 HotSpotResolvedJavaField[] result = new HotSpotResolvedJavaField[instanceFields.length - superClassFieldCount];
 770                 int i = 0;
 771                 for (HotSpotResolvedJavaField f : instanceFields) {
 772                     if (f.getDeclaringClass() == this) {
 773                         result[i++] = f;
 774                     }
 775                 }
 776                 Arrays.sort(result, fieldSortingMethod);
 777                 return result;
 778             } else {
 779                 // The super classes of this class do not have any instance fields.
 780             }
 781         }
 782         return instanceFields;
 783     }
 784 
 785     @Override
 786     public ResolvedJavaField[] getStaticFields() {
 787         if (isArray()) {
 788             return new HotSpotResolvedJavaField[0];
 789         } else {
 790             return getFields(true, NO_FIELDS);
 791         }
 792     }
 793 
 794     /**
 795      * Gets the instance or static fields of this class.
 796      *
 797      * @param retrieveStaticFields specifies whether to return instance or static fields
 798      * @param prepend an array to be prepended to the returned result
 799      */
 800     private HotSpotResolvedJavaField[] getFields(boolean retrieveStaticFields, HotSpotResolvedJavaField[] prepend) {
 801         HotSpotVMConfig config = config();
 802         final long metaspaceFields = UNSAFE.getAddress(getMetaspaceKlass() + config.instanceKlassFieldsOffset);
 803         int metaspaceFieldsLength = UNSAFE.getInt(metaspaceFields + config.arrayU1LengthOffset);
 804         int resultCount = 0;
 805         int index = 0;
 806         for (int i = 0; i < metaspaceFieldsLength; i += config.fieldInfoFieldSlots, index++) {
 807             FieldInfo field = new FieldInfo(index);
 808             if (field.hasGenericSignature()) {
 809                 metaspaceFieldsLength--;
 810             }
 811 
 812             if (field.isStatic() == retrieveStaticFields) {
 813                 resultCount++;
 814             }
 815         }
 816 
 817         if (resultCount == 0) {
 818             return prepend;
 819         }
 820 
 821         int prependLength = prepend.length;
 822         resultCount += prependLength;
 823 
 824         HotSpotResolvedJavaField[] result = new HotSpotResolvedJavaField[resultCount];
 825         if (prependLength != 0) {
 826             System.arraycopy(prepend, 0, result, 0, prependLength);
 827         }
 828 
 829         // Fields of the current class can be interleaved with fields of its super-classes
 830         // but the array of fields to be returned must be sorted by increasing offset
 831         // This code populates the array, then applies the sorting function
 832         int resultIndex = prependLength;
 833         for (int i = 0; i < index; ++i) {
 834             FieldInfo field = new FieldInfo(i);
 835             if (field.isStatic() == retrieveStaticFields) {
 836                 int offset = field.getOffset();
 837                 HotSpotResolvedJavaField resolvedJavaField = createField(field.getType(), offset, field.getAccessFlags(), i);
 838                 result[resultIndex++] = resolvedJavaField;
 839             }
 840         }
 841         Arrays.sort(result, fieldSortingMethod);
 842         return result;
 843     }
 844 
 845     @Override
 846     public String getSourceFileName() {
 847         if (isArray()) {
 848             return null;
 849         }
 850         return getConstantPool().getSourceFileName();
 851     }
 852 
 853     @Override
 854     public Annotation[] getAnnotations() {
 855         return runtime().reflection.getAnnotations(this);
 856     }
 857 
 858     @Override
 859     public Annotation[] getDeclaredAnnotations() {
 860         return runtime().reflection.getDeclaredAnnotations(this);
 861     }
 862 
 863     @Override
 864     public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
 865         return runtime().reflection.getAnnotation(this, annotationClass);
 866     }
 867 
 868     /**
 869      * Performs a fast-path check that this type is resolved in the context of a given accessing
 870      * class. A negative result does not mean this type is not resolved with respect to
 871      * {@code accessingClass}. That can only be determined by
 872      * {@linkplain HotSpotJVMCIRuntime#lookupType(String, HotSpotResolvedObjectType, boolean)
 873      * re-resolving} the type.
 874      */
 875     @Override
 876     public boolean isDefinitelyResolvedWithRespectTo(ResolvedJavaType accessingClass) {
 877         assert accessingClass != null;
 878         ResolvedJavaType elementType = getElementalType();
 879         if (elementType.isPrimitive()) {
 880             // Primitive type resolution is context free.
 881             return true;
 882         }
 883         if (elementType.getName().startsWith("Ljava/") && hasSameClassLoader(runtime().getJavaLangObject())) {
 884             // Classes in a java.* package defined by the boot class loader are always resolved.
 885             return true;
 886         }
 887         HotSpotResolvedObjectTypeImpl otherMirror = ((HotSpotResolvedObjectTypeImpl) accessingClass);
 888         return hasSameClassLoader(otherMirror);
 889     }
 890 
 891     private boolean hasSameClassLoader(HotSpotResolvedObjectTypeImpl otherMirror) {
 892         return UnsafeAccess.UNSAFE.getAddress(getMetaspaceKlass() + config().classLoaderDataOffset) == UnsafeAccess.UNSAFE.getAddress(
 893                         otherMirror.getMetaspaceKlass() + config().classLoaderDataOffset);
 894     }
 895 
 896     @Override
 897     public ResolvedJavaType resolve(ResolvedJavaType accessingClass) {
 898         if (isDefinitelyResolvedWithRespectTo(requireNonNull(accessingClass))) {
 899             return this;
 900         }
 901         HotSpotResolvedObjectTypeImpl accessingType = (HotSpotResolvedObjectTypeImpl) accessingClass;
 902         return (ResolvedJavaType) runtime().lookupType(getName(), accessingType, true);
 903     }
 904 
 905     /**
 906      * Gets the metaspace Klass boxed in a {@link JavaConstant}.
 907      */
 908     @Override
 909     public Constant klass() {
 910         return HotSpotMetaspaceConstantImpl.forMetaspaceObject(this, false);
 911     }
 912 
 913     @Override
 914     public boolean isPrimaryType() {
 915         return config().secondarySuperCacheOffset != superCheckOffset();
 916     }
 917 
 918     @Override
 919     public int superCheckOffset() {
 920         HotSpotVMConfig config = config();
 921         return UNSAFE.getInt(getMetaspaceKlass() + config.superCheckOffsetOffset);
 922     }
 923 
 924     @Override
 925     public long prototypeMarkWord() {
 926         HotSpotVMConfig config = config();
 927         return config.prototypeMarkWord();
 928     }
 929 
 930     @Override
 931     public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedEntryKind) {
 932         ResolvedJavaField[] declaredFields = getInstanceFields(true);
 933         return findFieldWithOffset(offset, expectedEntryKind, declaredFields);
 934     }
 935 
 936     public ResolvedJavaField findStaticFieldWithOffset(long offset, JavaKind expectedEntryKind) {
 937         ResolvedJavaField[] declaredFields = getStaticFields();
 938         return findFieldWithOffset(offset, expectedEntryKind, declaredFields);
 939     }
 940 
 941     private static ResolvedJavaField findFieldWithOffset(long offset, JavaKind expectedEntryKind, ResolvedJavaField[] declaredFields) {
 942         for (ResolvedJavaField field : declaredFields) {
 943             long resolvedFieldOffset = field.getOffset();
 944             // @formatter:off
 945             if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN &&
 946                     expectedEntryKind.isPrimitive() &&
 947                     !expectedEntryKind.equals(JavaKind.Void) &&
 948                     field.getJavaKind().isPrimitive()) {
 949                 resolvedFieldOffset +=
 950                         field.getJavaKind().getByteCount() -
 951                                 Math.min(field.getJavaKind().getByteCount(), 4 + expectedEntryKind.getByteCount());
 952             }
 953             if (resolvedFieldOffset == offset) {
 954                 return field;
 955             }
 956             // @formatter:on
 957         }
 958         return null;
 959     }
 960 
 961     @Override
 962     public boolean isLocal() {
 963         return runtime().reflection.isLocalClass(this);
 964     }
 965 
 966     @Override
 967     public boolean isMember() {
 968         return runtime().reflection.isMemberClass(this);
 969     }
 970 
 971     @Override
 972     public HotSpotResolvedObjectType getEnclosingType() {
 973         return runtime().reflection.getEnclosingClass(this);
 974     }
 975 
 976     @Override
 977     public ResolvedJavaMethod[] getDeclaredConstructors() {
 978         return runtime().compilerToVm.getDeclaredConstructors(this);
 979     }
 980 
 981     @Override
 982     public ResolvedJavaMethod[] getDeclaredMethods() {
 983         return runtime().compilerToVm.getDeclaredMethods(this);
 984     }
 985 
 986     @Override
 987     public ResolvedJavaMethod getClassInitializer() {
 988         if (!isArray()) {
 989             return compilerToVM().getClassInitializer(this);
 990         }
 991         return null;
 992     }
 993 
 994     @Override
 995     public String toString() {
 996         return "HotSpotType<" + getName() + ", resolved>";
 997     }
 998 
 999     @Override
1000     public ResolvedJavaType lookupType(UnresolvedJavaType unresolvedJavaType, boolean resolve) {
1001         JavaType javaType = HotSpotJVMCIRuntime.runtime().lookupType(unresolvedJavaType.getName(), this, resolve);
1002         if (javaType instanceof ResolvedJavaType) {
1003             return (ResolvedJavaType) javaType;
1004         }
1005         return null;
1006     }
1007 
1008     @Override
1009     public ResolvedJavaField resolveField(UnresolvedJavaField unresolvedJavaField, ResolvedJavaType accessingClass) {
1010         for (ResolvedJavaField field : getInstanceFields(false)) {
1011             if (field.getName().equals(unresolvedJavaField.getName())) {
1012                 return field;
1013             }
1014         }
1015         for (ResolvedJavaField field : getStaticFields()) {
1016             if (field.getName().equals(unresolvedJavaField.getName())) {
1017                 return field;
1018             }
1019         }
1020         throw new InternalError(unresolvedJavaField.toString());
1021     }
1022 
1023     @Override
1024     public boolean isCloneableWithAllocation() {
1025         return (getAccessFlags() & config().jvmAccIsCloneableFast) != 0;
1026     }
1027 
1028     private int getMiscFlags() {
1029         return UNSAFE.getInt(getMetaspaceKlass() + config().instanceKlassMiscFlagsOffset);
1030     }
1031 
1032 }