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[2];
 289                 types[0] = runtime().getJavaLangCloneable();
 290                 types[1] = runtime().getJavaLangSerializable();
 291                 this.interfaces = types;
 292             } else {
 293                 interfaces = runtime().compilerToVm.getInterfaces(this);
 294             }
 295         }
 296         return interfaces;
 297     }
 298 
 299     @Override
 300     public HotSpotResolvedObjectTypeImpl getSingleImplementor() {
 301         if (!isInterface()) {
 302             throw new JVMCIError("Cannot call getSingleImplementor() on a non-interface type: %s", this);
 303         }
 304         return compilerToVM().getImplementor(this);
 305     }
 306 
 307     @Override
 308     public HotSpotResolvedObjectTypeImpl getSupertype() {
 309         ResolvedJavaType component = getComponentType();
 310         if (component != null) {
 311             if (component.equals(getJavaLangObject()) || component.isPrimitive()) {
 312                 return getJavaLangObject();
 313             }
 314             HotSpotResolvedObjectTypeImpl supertype = ((HotSpotResolvedObjectTypeImpl) component).getSupertype();
 315             return (HotSpotResolvedObjectTypeImpl) supertype.getArrayClass();
 316         }
 317         if (isInterface()) {
 318             return getJavaLangObject();
 319         }
 320         return getSuperclass();
 321     }
 322 
 323     @Override
 324     public HotSpotResolvedObjectType findLeastCommonAncestor(ResolvedJavaType otherType) {
 325         if (otherType.isPrimitive()) {
 326             return null;
 327         } else {
 328             HotSpotResolvedObjectTypeImpl t1 = this;
 329             HotSpotResolvedObjectTypeImpl t2 = (HotSpotResolvedObjectTypeImpl) otherType;
 330             while (true) {
 331                 if (t1.isAssignableFrom(t2)) {
 332                     return t1;
 333                 }
 334                 if (t2.isAssignableFrom(t1)) {
 335                     return t2;
 336                 }
 337                 t1 = t1.getSupertype();
 338                 t2 = t2.getSupertype();
 339             }
 340         }
 341     }
 342 
 343     @Override
 344     public AssumptionResult<Boolean> hasFinalizableSubclass() {
 345         assert !isArray();
 346         if (!compilerToVM().hasFinalizableSubclass(this)) {
 347             return new AssumptionResult<>(false, new NoFinalizableSubclass(this));
 348         }
 349         return new AssumptionResult<>(true);
 350     }
 351 
 352     @Override
 353     public boolean hasFinalizer() {
 354         return (getAccessFlags() & config().jvmAccHasFinalizer) != 0;
 355     }
 356 
 357     @Override
 358     public boolean isArray() {
 359         return layoutHelper() < config().klassLayoutHelperNeutralValue;
 360     }
 361 
 362     @Override
 363     public boolean isEnum() {
 364         HotSpotResolvedObjectTypeImpl superclass = getSuperclass();
 365         return superclass != null && superclass.equals(runtime().getJavaLangEnum());
 366     }
 367 
 368     @Override
 369     public boolean isInitialized() {
 370         return isArray() ? true : getInitState() == config().instanceKlassStateFullyInitialized;
 371     }
 372 
 373     @Override
 374     public boolean isBeingInitialized() {
 375         return isArray() ? false : getInitState() == config().instanceKlassStateBeingInitialized;
 376     }
 377 
 378     @Override
 379     public boolean isLinked() {
 380         return isArray() ? true : getInitState() >= config().instanceKlassStateLinked;
 381     }
 382 
 383     @Override
 384     public void link() {
 385         if (!isLinked()) {
 386             runtime().compilerToVm.ensureLinked(this);
 387         }
 388     }
 389 
 390     @Override
 391     public boolean hasDefaultMethods() {
 392         HotSpotVMConfig config = config();
 393         int miscFlags = UNSAFE.getChar(getMetaspaceKlass() + config.instanceKlassMiscFlagsOffset);
 394         return (miscFlags & config.jvmMiscFlagsHasDefaultMethods) != 0;
 395     }
 396 
 397     @Override
 398     public boolean declaresDefaultMethods() {
 399         HotSpotVMConfig config = config();
 400         int miscFlags = UNSAFE.getChar(getMetaspaceKlass() + config.instanceKlassMiscFlagsOffset);
 401         return (miscFlags & config.jvmMiscFlagsDeclaresDefaultMethods) != 0;
 402     }
 403 
 404     /**
 405      * Returns the value of the state field {@code InstanceKlass::_init_state} of the metaspace
 406      * klass.
 407      *
 408      * @return state field value of this type
 409      */
 410     private int getInitState() {
 411         assert !isArray() : "_init_state only exists in InstanceKlass";
 412         return UNSAFE.getByte(getMetaspaceKlass() + config().instanceKlassInitStateOffset) & 0xFF;
 413     }
 414 
 415     @Override
 416     public void initialize() {
 417         if (!isInitialized()) {
 418             runtime().compilerToVm.ensureInitialized(this);
 419             assert isInitialized() || isBeingInitialized();
 420         }
 421     }
 422 
 423     @Override
 424     public boolean isInstance(JavaConstant obj) {
 425         if (obj.getJavaKind() == JavaKind.Object && !obj.isNull()) {
 426             return runtime().reflection.isInstance(this, (HotSpotObjectConstantImpl) obj);
 427         }
 428         return false;
 429     }
 430 
 431     @Override
 432     public boolean isInstanceClass() {
 433         return !isArray() && !isInterface();
 434     }
 435 
 436     @Override
 437     public boolean isInterface() {
 438         return (getAccessFlags() & config().jvmAccInterface) != 0;
 439     }
 440 
 441     @Override
 442     public boolean isAssignableFrom(ResolvedJavaType other) {
 443         assert other != null;
 444         if (other instanceof HotSpotResolvedObjectTypeImpl) {
 445             HotSpotResolvedObjectTypeImpl otherType = (HotSpotResolvedObjectTypeImpl) other;
 446             return runtime().reflection.isAssignableFrom(this, otherType);
 447         }
 448         return false;
 449     }
 450 
 451     @Override
 452     public boolean isJavaLangObject() {
 453         return getName().equals("Ljava/lang/Object;");
 454     }
 455 
 456     @Override
 457     public JavaKind getJavaKind() {
 458         return JavaKind.Object;
 459     }
 460 
 461     @Override
 462     public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) {
 463         assert !callerType.isArray();
 464         if (isInterface()) {
 465             // Methods can only be resolved against concrete types
 466             return null;
 467         }
 468         if (method.isConcrete() && method.getDeclaringClass().equals(this) && method.isPublic() && !isSignaturePolymorphicHolder(method.getDeclaringClass())) {
 469             return method;
 470         }
 471         if (!method.getDeclaringClass().isAssignableFrom(this)) {
 472             return null;
 473         }
 474         if (method.isConstructor()) {
 475             // Constructor calls should have been checked in the verifier and method's
 476             // declaring class is assignable from this (see above) so treat it as resolved.
 477             return method;
 478         }
 479         HotSpotResolvedJavaMethodImpl hotSpotMethod = (HotSpotResolvedJavaMethodImpl) method;
 480         HotSpotResolvedObjectTypeImpl hotSpotCallerType = (HotSpotResolvedObjectTypeImpl) callerType;
 481         return compilerToVM().resolveMethod(this, hotSpotMethod, hotSpotCallerType);
 482     }
 483 
 484     @Override
 485     public HotSpotConstantPool getConstantPool() {
 486         if (constantPool == null || !isArray() && UNSAFE.getAddress(getMetaspaceKlass() + config().instanceKlassConstantsOffset) != constantPool.getMetaspaceConstantPool()) {
 487             /*
 488              * If the pointer to the ConstantPool has changed since this was last read refresh the
 489              * HotSpotConstantPool wrapper object. This ensures that uses of the constant pool are
 490              * operating on the latest one and that HotSpotResolvedJavaMethodImpls will be able to
 491              * use the shared copy instead of creating their own instance.
 492              */
 493             constantPool = compilerToVM().getConstantPool(this);
 494         }
 495         return constantPool;
 496     }
 497 
 498     /**
 499      * Gets the instance size of this type. If an instance of this type cannot be fast path
 500      * allocated, then the returned value is negative (its absolute value gives the size). Must not
 501      * be called if this is an array or interface type.
 502      */
 503     @Override
 504     public int instanceSize() {
 505         assert !isArray();
 506         assert !isInterface();
 507 
 508         HotSpotVMConfig config = config();
 509         final int layoutHelper = layoutHelper();
 510         assert layoutHelper > config.klassLayoutHelperNeutralValue : "must be instance";
 511 
 512         // See: Klass::layout_helper_size_in_bytes
 513         int size = layoutHelper & ~config.klassLayoutHelperInstanceSlowPathBit;
 514 
 515         // See: Klass::layout_helper_needs_slow_path
 516         boolean needsSlowPath = (layoutHelper & config.klassLayoutHelperInstanceSlowPathBit) != 0;
 517 
 518         return needsSlowPath ? -size : size;
 519     }
 520 
 521     @Override
 522     public int layoutHelper() {
 523         HotSpotVMConfig config = config();
 524         assert getMetaspaceKlass() != 0 : getName();
 525         return UNSAFE.getInt(getMetaspaceKlass() + config.klassLayoutHelperOffset);
 526     }
 527 
 528     @Override
 529     public long getFingerprint() {
 530         return compilerToVM().getFingerprint(getMetaspaceKlass());
 531     }
 532 
 533     synchronized HotSpotResolvedJavaMethod createMethod(long metaspaceHandle) {
 534         long metaspaceMethod = UNSAFE.getLong(metaspaceHandle);
 535         // Maintain cache as array.
 536         if (methodCacheArray == null) {
 537             methodCacheArray = new HotSpotResolvedJavaMethodImpl[METHOD_CACHE_ARRAY_CAPACITY];
 538         }
 539 
 540         int i = 0;
 541         for (; i < methodCacheArray.length; ++i) {
 542             HotSpotResolvedJavaMethodImpl curMethod = methodCacheArray[i];
 543             if (curMethod == null) {
 544                 HotSpotResolvedJavaMethodImpl newMethod = new HotSpotResolvedJavaMethodImpl(this, metaspaceHandle);
 545                 methodCacheArray[i] = newMethod;
 546                 return newMethod;
 547             } else if (curMethod.getMetaspaceMethod() == metaspaceMethod) {
 548                 return curMethod;
 549             }
 550         }
 551 
 552         // Fall-back to hash table.
 553         if (methodCacheHashMap == null) {
 554             methodCacheHashMap = new HashMap<>();
 555         }
 556 
 557         HotSpotResolvedJavaMethodImpl lookupResult = methodCacheHashMap.get(metaspaceMethod);
 558         if (lookupResult == null) {
 559             HotSpotResolvedJavaMethodImpl newMethod = new HotSpotResolvedJavaMethodImpl(this, metaspaceHandle);
 560             methodCacheHashMap.put(metaspaceMethod, newMethod);
 561             return newMethod;
 562         } else {
 563             return lookupResult;
 564         }
 565     }
 566 
 567     @Override
 568     public int getVtableLength() {
 569         HotSpotVMConfig config = config();
 570         if (isInterface() || isArray()) {
 571             /* Everything has the core vtable of java.lang.Object */
 572             return config.baseVtableLength();
 573         }
 574         int result = UNSAFE.getInt(getMetaspaceKlass() + config.klassVtableLengthOffset) / (config.vtableEntrySize / config.heapWordSize);
 575         assert result >= config.baseVtableLength() : UNSAFE.getInt(getMetaspaceKlass() + config.klassVtableLengthOffset) + " " + config.vtableEntrySize;
 576         return result;
 577     }
 578 
 579     HotSpotResolvedJavaField createField(JavaType type, long offset, int rawFlags, int index) {
 580         return new HotSpotResolvedJavaFieldImpl(this, type, offset, rawFlags, index);
 581     }
 582 
 583     @Override
 584     public AssumptionResult<ResolvedJavaMethod> findUniqueConcreteMethod(ResolvedJavaMethod method) {
 585         HotSpotResolvedJavaMethod hmethod = (HotSpotResolvedJavaMethod) method;
 586         HotSpotResolvedObjectType declaredHolder = hmethod.getDeclaringClass();
 587         /*
 588          * Sometimes the receiver type in the graph hasn't stabilized to a subtype of declared
 589          * holder, usually because of phis, so make sure that the type is related to the declared
 590          * type before using it for lookup. Unlinked types should also be ignored because we can't
 591          * resolve the proper method to invoke. Generally unlinked types in invokes should result in
 592          * a deopt instead since they can't really be used if they aren't linked yet.
 593          */
 594         if (!declaredHolder.isAssignableFrom(this) || this.isArray() || this.equals(declaredHolder) || !isLinked() || isInterface()) {
 595             if (hmethod.canBeStaticallyBound()) {
 596                 // No assumptions are required.
 597                 return new AssumptionResult<>(hmethod);
 598             }
 599             ResolvedJavaMethod result = hmethod.uniqueConcreteMethod(declaredHolder);
 600             if (result != null) {
 601                 return new AssumptionResult<>(result, new ConcreteMethod(method, declaredHolder, result));
 602             }
 603             return null;
 604         }
 605         /*
 606          * The holder may be a subtype of the declaredHolder so make sure to resolve the method to
 607          * the correct method for the subtype.
 608          */
 609         HotSpotResolvedJavaMethod resolvedMethod = (HotSpotResolvedJavaMethod) resolveMethod(hmethod, this);
 610         if (resolvedMethod == null) {
 611             // The type isn't known to implement the method.
 612             return null;
 613         }
 614         if (resolvedMethod.canBeStaticallyBound()) {
 615             // No assumptions are required.
 616             return new AssumptionResult<>(resolvedMethod);
 617         }
 618 
 619         ResolvedJavaMethod result = resolvedMethod.uniqueConcreteMethod(this);
 620         if (result != null) {
 621             return new AssumptionResult<>(result, new ConcreteMethod(method, this, result));
 622         }
 623         return null;
 624     }
 625 
 626     FieldInfo createFieldInfo(int index) {
 627         return new FieldInfo(index);
 628     }
 629 
 630     public void ensureInitialized() {
 631         runtime().compilerToVm.ensureInitialized(this);
 632     }
 633 
 634     @Override
 635     public boolean equals(Object obj) {
 636         if (obj == this) {
 637             return true;
 638         }
 639         if (!(obj instanceof HotSpotResolvedObjectTypeImpl)) {
 640             return false;
 641         }
 642         HotSpotResolvedObjectTypeImpl that = (HotSpotResolvedObjectTypeImpl) obj;
 643         return getMetaspaceKlass() == that.getMetaspaceKlass();
 644     }
 645 
 646     @Override
 647     JavaConstant getJavaMirror() {
 648         return mirror;
 649     }
 650 
 651     /**
 652      * This class represents the field information for one field contained in the fields array of an
 653      * {@code InstanceKlass}. The implementation is similar to the native {@code FieldInfo} class.
 654      */
 655     class FieldInfo {
 656         /**
 657          * Native pointer into the array of Java shorts.
 658          */
 659         private final long metaspaceData;
 660 
 661         /**
 662          * Creates a field info for the field in the fields array at index {@code index}.
 663          *
 664          * @param index index to the fields array
 665          */
 666         FieldInfo(int index) {
 667             HotSpotVMConfig config = config();
 668             // Get Klass::_fields
 669             final long metaspaceFields = UNSAFE.getAddress(getMetaspaceKlass() + config.instanceKlassFieldsOffset);
 670             assert config.fieldInfoFieldSlots == 6 : "revisit the field parsing code";
 671             int offset = config.fieldInfoFieldSlots * Short.BYTES * index;
 672             metaspaceData = metaspaceFields + config.arrayU2DataOffset + offset;
 673         }
 674 
 675         private int getAccessFlags() {
 676             return readFieldSlot(config().fieldInfoAccessFlagsOffset);
 677         }
 678 
 679         private int getNameIndex() {
 680             return readFieldSlot(config().fieldInfoNameIndexOffset);
 681         }
 682 
 683         private int getSignatureIndex() {
 684             return readFieldSlot(config().fieldInfoSignatureIndexOffset);
 685         }
 686 
 687         public int getOffset() {
 688             HotSpotVMConfig config = config();
 689             final int lowPacked = readFieldSlot(config.fieldInfoLowPackedOffset);
 690             final int highPacked = readFieldSlot(config.fieldInfoHighPackedOffset);
 691             final int offset = ((highPacked << Short.SIZE) | lowPacked) >> config.fieldInfoTagSize;
 692             return offset;
 693         }
 694 
 695         /**
 696          * Helper method to read an entry (slot) from the field array. Currently field info is laid
 697          * on top an array of Java shorts.
 698          */
 699         private int readFieldSlot(int index) {
 700             int offset = Short.BYTES * index;
 701             return UNSAFE.getChar(metaspaceData + offset);
 702         }
 703 
 704         /**
 705          * Returns the name of this field as a {@link String}. If the field is an internal field the
 706          * name index is pointing into the vmSymbols table.
 707          */
 708         public String getName() {
 709             final int nameIndex = getNameIndex();
 710             return isInternal() ? config().symbolAt(nameIndex) : getConstantPool().lookupUtf8(nameIndex);
 711         }
 712 
 713         /**
 714          * Returns the signature of this field as {@link String}. If the field is an internal field
 715          * the signature index is pointing into the vmSymbols table.
 716          */
 717         public String getSignature() {
 718             final int signatureIndex = getSignatureIndex();
 719             return isInternal() ? config().symbolAt(signatureIndex) : getConstantPool().lookupUtf8(signatureIndex);
 720         }
 721 
 722         public JavaType getType() {
 723             String signature = getSignature();
 724             return runtime().lookupType(signature, HotSpotResolvedObjectTypeImpl.this, false);
 725         }
 726 
 727         private boolean isInternal() {
 728             return (getAccessFlags() & config().jvmAccFieldInternal) != 0;
 729         }
 730 
 731         public boolean isStatic() {
 732             return Modifier.isStatic(getAccessFlags());
 733         }
 734 
 735         public boolean hasGenericSignature() {
 736             return (getAccessFlags() & config().jvmAccFieldHasGenericSignature) != 0;
 737         }
 738     }
 739 
 740     static class SortByOffset implements Comparator<ResolvedJavaField> {
 741         public int compare(ResolvedJavaField a, ResolvedJavaField b) {
 742             return a.getOffset() - b.getOffset();
 743         }
 744     }
 745 
 746     @Override
 747     public ResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) {
 748         if (instanceFields == null) {
 749             if (isArray() || isInterface()) {
 750                 instanceFields = NO_FIELDS;
 751             } else {
 752                 HotSpotResolvedJavaField[] prepend = NO_FIELDS;
 753                 if (getSuperclass() != null) {
 754                     prepend = (HotSpotResolvedJavaField[]) getSuperclass().getInstanceFields(true);
 755                 }
 756                 instanceFields = getFields(false, prepend);
 757             }
 758         }
 759         if (!includeSuperclasses && getSuperclass() != null) {
 760             int superClassFieldCount = getSuperclass().getInstanceFields(true).length;
 761             if (superClassFieldCount == instanceFields.length) {
 762                 // This class does not have any instance fields of its own.
 763                 return NO_FIELDS;
 764             } else if (superClassFieldCount != 0) {
 765                 // Fields of the current class can be interleaved with fields of its super-classes
 766                 // but the array of fields to be returned must be sorted by increasing offset
 767                 // This code populates the array, then applies the sorting function
 768                 HotSpotResolvedJavaField[] result = new HotSpotResolvedJavaField[instanceFields.length - superClassFieldCount];
 769                 int i = 0;
 770                 for (HotSpotResolvedJavaField f : instanceFields) {
 771                     if (f.getDeclaringClass() == this) {
 772                         result[i++] = f;
 773                     }
 774                 }
 775                 Arrays.sort(result, fieldSortingMethod);
 776                 return result;
 777             } else {
 778                 // The super classes of this class do not have any instance fields.
 779             }
 780         }
 781         return instanceFields;
 782     }
 783 
 784     @Override
 785     public ResolvedJavaField[] getStaticFields() {
 786         if (isArray()) {
 787             return new HotSpotResolvedJavaField[0];
 788         } else {
 789             return getFields(true, NO_FIELDS);
 790         }
 791     }
 792 
 793     /**
 794      * Gets the instance or static fields of this class.
 795      *
 796      * @param retrieveStaticFields specifies whether to return instance or static fields
 797      * @param prepend an array to be prepended to the returned result
 798      */
 799     private HotSpotResolvedJavaField[] getFields(boolean retrieveStaticFields, HotSpotResolvedJavaField[] prepend) {
 800         HotSpotVMConfig config = config();
 801         final long metaspaceFields = UNSAFE.getAddress(getMetaspaceKlass() + config.instanceKlassFieldsOffset);
 802         int metaspaceFieldsLength = UNSAFE.getInt(metaspaceFields + config.arrayU1LengthOffset);
 803         int resultCount = 0;
 804         int index = 0;
 805         for (int i = 0; i < metaspaceFieldsLength; i += config.fieldInfoFieldSlots, index++) {
 806             FieldInfo field = new FieldInfo(index);
 807             if (field.hasGenericSignature()) {
 808                 metaspaceFieldsLength--;
 809             }
 810 
 811             if (field.isStatic() == retrieveStaticFields) {
 812                 resultCount++;
 813             }
 814         }
 815 
 816         if (resultCount == 0) {
 817             return prepend;
 818         }
 819 
 820         int prependLength = prepend.length;
 821         resultCount += prependLength;
 822 
 823         HotSpotResolvedJavaField[] result = new HotSpotResolvedJavaField[resultCount];
 824         if (prependLength != 0) {
 825             System.arraycopy(prepend, 0, result, 0, prependLength);
 826         }
 827 
 828         // Fields of the current class can be interleaved with fields of its super-classes
 829         // but the array of fields to be returned must be sorted by increasing offset
 830         // This code populates the array, then applies the sorting function
 831         int resultIndex = prependLength;
 832         for (int i = 0; i < index; ++i) {
 833             FieldInfo field = new FieldInfo(i);
 834             if (field.isStatic() == retrieveStaticFields) {
 835                 int offset = field.getOffset();
 836                 HotSpotResolvedJavaField resolvedJavaField = createField(field.getType(), offset, field.getAccessFlags(), i);
 837                 result[resultIndex++] = resolvedJavaField;
 838             }
 839         }
 840         Arrays.sort(result, fieldSortingMethod);
 841         return result;
 842     }
 843 
 844     @Override
 845     public String getSourceFileName() {
 846         if (isArray()) {
 847             return null;
 848         }
 849         return getConstantPool().getSourceFileName();
 850     }
 851 
 852     @Override
 853     public Annotation[] getAnnotations() {
 854         return runtime().reflection.getAnnotations(this);
 855     }
 856 
 857     @Override
 858     public Annotation[] getDeclaredAnnotations() {
 859         return runtime().reflection.getDeclaredAnnotations(this);
 860     }
 861 
 862     @Override
 863     public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
 864         return runtime().reflection.getAnnotation(this, annotationClass);
 865     }
 866 
 867     /**
 868      * Performs a fast-path check that this type is resolved in the context of a given accessing
 869      * class. A negative result does not mean this type is not resolved with respect to
 870      * {@code accessingClass}. That can only be determined by
 871      * {@linkplain HotSpotJVMCIRuntime#lookupType(String, HotSpotResolvedObjectType, boolean)
 872      * re-resolving} the type.
 873      */
 874     @Override
 875     public boolean isDefinitelyResolvedWithRespectTo(ResolvedJavaType accessingClass) {
 876         assert accessingClass != null;
 877         ResolvedJavaType elementType = getElementalType();
 878         if (elementType.isPrimitive()) {
 879             // Primitive type resolution is context free.
 880             return true;
 881         }
 882         if (elementType.getName().startsWith("Ljava/") && hasSameClassLoader(runtime().getJavaLangObject())) {
 883             // Classes in a java.* package defined by the boot class loader are always resolved.
 884             return true;
 885         }
 886         HotSpotResolvedObjectTypeImpl otherMirror = ((HotSpotResolvedObjectTypeImpl) accessingClass);
 887         return hasSameClassLoader(otherMirror);
 888     }
 889 
 890     private boolean hasSameClassLoader(HotSpotResolvedObjectTypeImpl otherMirror) {
 891         return UnsafeAccess.UNSAFE.getAddress(getMetaspaceKlass() + config().classLoaderDataOffset) == UnsafeAccess.UNSAFE.getAddress(
 892                         otherMirror.getMetaspaceKlass() + config().classLoaderDataOffset);
 893     }
 894 
 895     @Override
 896     public ResolvedJavaType resolve(ResolvedJavaType accessingClass) {
 897         if (isDefinitelyResolvedWithRespectTo(requireNonNull(accessingClass))) {
 898             return this;
 899         }
 900         HotSpotResolvedObjectTypeImpl accessingType = (HotSpotResolvedObjectTypeImpl) accessingClass;
 901         return (ResolvedJavaType) runtime().lookupType(getName(), accessingType, true);
 902     }
 903 
 904     /**
 905      * Gets the metaspace Klass boxed in a {@link JavaConstant}.
 906      */
 907     @Override
 908     public Constant klass() {
 909         return HotSpotMetaspaceConstantImpl.forMetaspaceObject(this, false);
 910     }
 911 
 912     @Override
 913     public boolean isPrimaryType() {
 914         return config().secondarySuperCacheOffset != superCheckOffset();
 915     }
 916 
 917     @Override
 918     public int superCheckOffset() {
 919         HotSpotVMConfig config = config();
 920         return UNSAFE.getInt(getMetaspaceKlass() + config.superCheckOffsetOffset);
 921     }
 922 
 923     @Override
 924     public long prototypeMarkWord() {
 925         HotSpotVMConfig config = config();
 926         return config.prototypeMarkWord();
 927     }
 928 
 929     @Override
 930     public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedEntryKind) {
 931         ResolvedJavaField[] declaredFields = getInstanceFields(true);
 932         return findFieldWithOffset(offset, expectedEntryKind, declaredFields);
 933     }
 934 
 935     public ResolvedJavaField findStaticFieldWithOffset(long offset, JavaKind expectedEntryKind) {
 936         ResolvedJavaField[] declaredFields = getStaticFields();
 937         return findFieldWithOffset(offset, expectedEntryKind, declaredFields);
 938     }
 939 
 940     private static ResolvedJavaField findFieldWithOffset(long offset, JavaKind expectedEntryKind, ResolvedJavaField[] declaredFields) {
 941         for (ResolvedJavaField field : declaredFields) {
 942             long resolvedFieldOffset = field.getOffset();
 943             // @formatter:off
 944             if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN &&
 945                     expectedEntryKind.isPrimitive() &&
 946                     !expectedEntryKind.equals(JavaKind.Void) &&
 947                     field.getJavaKind().isPrimitive()) {
 948                 resolvedFieldOffset +=
 949                         field.getJavaKind().getByteCount() -
 950                                 Math.min(field.getJavaKind().getByteCount(), 4 + expectedEntryKind.getByteCount());
 951             }
 952             if (resolvedFieldOffset == offset) {
 953                 return field;
 954             }
 955             // @formatter:on
 956         }
 957         return null;
 958     }
 959 
 960     @Override
 961     public boolean isLocal() {
 962         return runtime().reflection.isLocalClass(this);
 963     }
 964 
 965     @Override
 966     public boolean isMember() {
 967         return runtime().reflection.isMemberClass(this);
 968     }
 969 
 970     @Override
 971     public HotSpotResolvedObjectType getEnclosingType() {
 972         return runtime().reflection.getEnclosingClass(this);
 973     }
 974 
 975     @Override
 976     public ResolvedJavaMethod[] getDeclaredConstructors() {
 977         return runtime().compilerToVm.getDeclaredConstructors(this);
 978     }
 979 
 980     @Override
 981     public ResolvedJavaMethod[] getDeclaredMethods() {
 982         return runtime().compilerToVm.getDeclaredMethods(this);
 983     }
 984 
 985     @Override
 986     public ResolvedJavaMethod getClassInitializer() {
 987         if (!isArray()) {
 988             return compilerToVM().getClassInitializer(this);
 989         }
 990         return null;
 991     }
 992 
 993     @Override
 994     public String toString() {
 995         return "HotSpotType<" + getName() + ", resolved>";
 996     }
 997 
 998     @Override
 999     public ResolvedJavaType lookupType(UnresolvedJavaType unresolvedJavaType, boolean resolve) {
1000         JavaType javaType = HotSpotJVMCIRuntime.runtime().lookupType(unresolvedJavaType.getName(), this, resolve);
1001         if (javaType instanceof ResolvedJavaType) {
1002             return (ResolvedJavaType) javaType;
1003         }
1004         return null;
1005     }
1006 
1007     @Override
1008     public ResolvedJavaField resolveField(UnresolvedJavaField unresolvedJavaField, ResolvedJavaType accessingClass) {
1009         for (ResolvedJavaField field : getInstanceFields(false)) {
1010             if (field.getName().equals(unresolvedJavaField.getName())) {
1011                 return field;
1012             }
1013         }
1014         for (ResolvedJavaField field : getStaticFields()) {
1015             if (field.getName().equals(unresolvedJavaField.getName())) {
1016                 return field;
1017             }
1018         }
1019         throw new InternalError(unresolvedJavaField.toString());
1020     }
1021 
1022     @Override
1023     public boolean isCloneableWithAllocation() {
1024         return (getAccessFlags() & config().jvmAccIsCloneableFast) != 0;
1025     }
1026 
1027     private int getMiscFlags() {
1028         return UNSAFE.getInt(getMetaspaceKlass() + config().instanceKlassMiscFlagsOffset);
1029     }
1030 
1031 }