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