001/*
002 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package jdk.internal.jvmci.hotspot;
024
025import static java.util.Objects.*;
026import static jdk.internal.jvmci.common.UnsafeAccess.*;
027import static jdk.internal.jvmci.hotspot.HotSpotJVMCIRuntime.*;
028
029import java.lang.annotation.*;
030import java.lang.reflect.*;
031import java.net.*;
032import java.nio.*;
033import java.util.*;
034
035import jdk.internal.jvmci.common.*;
036import jdk.internal.jvmci.meta.*;
037import jdk.internal.jvmci.meta.Assumptions.*;
038
039/**
040 * Implementation of {@link JavaType} for resolved non-primitive HotSpot classes.
041 */
042public final class HotSpotResolvedObjectTypeImpl extends HotSpotResolvedJavaType implements HotSpotResolvedObjectType, HotSpotProxified {
043
044    /**
045     * The Java class this type represents.
046     */
047    private final Class<?> javaClass;
048
049    private HashMap<Long, HotSpotResolvedJavaField> fieldCache;
050    private HashMap<Long, HotSpotResolvedJavaMethod> methodCache;
051    private HotSpotResolvedJavaField[] instanceFields;
052    private HotSpotResolvedObjectTypeImpl[] interfaces;
053    private ConstantPool constantPool;
054    private HotSpotResolvedObjectType arrayOfType;
055
056    /**
057     * Gets the JVMCI mirror for a {@link Class} object.
058     *
059     * @return the {@link HotSpotResolvedJavaType} corresponding to {@code javaClass}
060     */
061    public static HotSpotResolvedObjectTypeImpl fromObjectClass(Class<?> javaClass) {
062        return (HotSpotResolvedObjectTypeImpl) runtime().fromClass(javaClass);
063    }
064
065    /**
066     * Gets the JVMCI mirror from a HotSpot metaspace Klass native object.
067     *
068     * @param metaspaceKlass a metaspace Klass object
069     * @return the {@link ResolvedJavaType} corresponding to {@code metaspaceKlass}
070     */
071    public static HotSpotResolvedObjectTypeImpl fromMetaspaceKlass(long metaspaceKlass) {
072        assert metaspaceKlass != 0;
073        Class<?> javaClass = runtime().getCompilerToVM().getJavaMirror(metaspaceKlass);
074        assert javaClass != null;
075        return fromObjectClass(javaClass);
076    }
077
078    /**
079     * Creates the JVMCI mirror for a {@link Class} object.
080     *
081     * <p>
082     * <b>NOTE</b>: Creating an instance of this class does not install the mirror for the
083     * {@link Class} type. Use {@link #fromObjectClass(Class)} or {@link #fromMetaspaceKlass(long)}
084     * instead.
085     * </p>
086     *
087     * @param javaClass the Class to create the mirror for
088     */
089    public HotSpotResolvedObjectTypeImpl(Class<?> javaClass) {
090        super(getSignatureName(javaClass));
091        this.javaClass = javaClass;
092        assert getName().charAt(0) != '[' || isArray() : getName();
093    }
094
095    /**
096     * Returns the name of this type as it would appear in a signature.
097     */
098    private static String getSignatureName(Class<?> javaClass) {
099        if (javaClass.isArray()) {
100            return javaClass.getName().replace('.', '/');
101        }
102        return "L" + javaClass.getName().replace('.', '/') + ";";
103    }
104
105    /**
106     * Gets the metaspace Klass for this type.
107     */
108    public long getMetaspaceKlass() {
109        if (HotSpotJVMCIRuntime.getHostWordKind() == Kind.Long) {
110            return unsafe.getLong(javaClass, (long) runtime().getConfig().klassOffset);
111        }
112        return unsafe.getInt(javaClass, (long) runtime().getConfig().klassOffset) & 0xFFFFFFFFL;
113    }
114
115    @Override
116    public int getModifiers() {
117        if (isArray()) {
118            return (getElementalType().getModifiers() & (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED)) | Modifier.FINAL | Modifier.ABSTRACT;
119        } else {
120            return getAccessFlags() & ModifiersProvider.jvmClassModifiers();
121        }
122    }
123
124    public int getAccessFlags() {
125        HotSpotVMConfig config = runtime().getConfig();
126        return unsafe.getInt(getMetaspaceKlass() + config.klassAccessFlagsOffset);
127    }
128
129    @Override
130    public HotSpotResolvedObjectType getArrayClass() {
131        if (arrayOfType == null) {
132            arrayOfType = fromObjectClass(Array.newInstance(mirror(), 0).getClass());
133        }
134        return arrayOfType;
135    }
136
137    @Override
138    public ResolvedJavaType getComponentType() {
139        Class<?> javaComponentType = mirror().getComponentType();
140        return javaComponentType == null ? null : fromClass(javaComponentType);
141    }
142
143    @Override
144    public AssumptionResult<ResolvedJavaType> findLeafConcreteSubtype() {
145        HotSpotVMConfig config = runtime().getConfig();
146        if (isArray()) {
147            return getElementalType().isLeaf() ? new AssumptionResult<>(this) : null;
148        } else if (isInterface()) {
149            HotSpotResolvedObjectTypeImpl implementor = getSingleImplementor();
150            /*
151             * If the implementor field contains itself that indicates that the interface has more
152             * than one implementors (see: InstanceKlass::add_implementor).
153             */
154            if (implementor == null || implementor.equals(this)) {
155                return null;
156            }
157
158            assert !implementor.isInterface();
159            if (implementor.isAbstract() || !implementor.isLeafClass()) {
160                AssumptionResult<ResolvedJavaType> leafConcreteSubtype = implementor.findLeafConcreteSubtype();
161                if (leafConcreteSubtype != null) {
162                    assert !leafConcreteSubtype.getResult().equals(implementor);
163                    AssumptionResult<ResolvedJavaType> newResult = new AssumptionResult<>(leafConcreteSubtype.getResult(), new ConcreteSubtype(this, implementor));
164                    // Accumulate leaf assumptions and return the combined result.
165                    newResult.add(leafConcreteSubtype);
166                    return newResult;
167                }
168                return null;
169            }
170
171            return new AssumptionResult<>(implementor, new LeafType(implementor), new ConcreteSubtype(this, implementor));
172        } else {
173            HotSpotResolvedObjectTypeImpl type = this;
174            while (type.isAbstract()) {
175                long subklass = type.getSubklass();
176                if (subklass == 0 || unsafe.getAddress(subklass + config.nextSiblingOffset) != 0) {
177                    return null;
178                }
179                type = fromMetaspaceKlass(subklass);
180            }
181            if (type.isAbstract() || type.isInterface() || !type.isLeafClass()) {
182                return null;
183            }
184            if (this.isAbstract()) {
185                return new AssumptionResult<>(type, new LeafType(type), new ConcreteSubtype(this, type));
186            } else {
187                assert this.equals(type);
188                return new AssumptionResult<>(type, new LeafType(type));
189            }
190        }
191    }
192
193    /**
194     * Returns if type {@code type} is a leaf class. This is the case if the
195     * {@code Klass::_subklass} field of the underlying class is zero.
196     *
197     * @return true if the type is a leaf class
198     */
199    private boolean isLeafClass() {
200        return getSubklass() == 0;
201    }
202
203    /**
204     * Returns the {@code Klass::_subklass} field of the underlying metaspace klass for the given
205     * type {@code type}.
206     *
207     * @return value of the subklass field as metaspace klass pointer
208     */
209    private long getSubklass() {
210        return unsafe.getAddress(getMetaspaceKlass() + runtime().getConfig().subklassOffset);
211    }
212
213    @Override
214    public HotSpotResolvedObjectTypeImpl getSuperclass() {
215        Class<?> javaSuperclass = mirror().getSuperclass();
216        return javaSuperclass == null ? null : (HotSpotResolvedObjectTypeImpl) fromObjectClass(javaSuperclass);
217    }
218
219    @Override
220    public HotSpotResolvedObjectTypeImpl[] getInterfaces() {
221        if (interfaces == null) {
222            Class<?>[] javaInterfaces = mirror().getInterfaces();
223            HotSpotResolvedObjectTypeImpl[] result = new HotSpotResolvedObjectTypeImpl[javaInterfaces.length];
224            for (int i = 0; i < javaInterfaces.length; i++) {
225                result[i] = fromObjectClass(javaInterfaces[i]);
226            }
227            interfaces = result;
228        }
229        return interfaces;
230    }
231
232    @Override
233    public HotSpotResolvedObjectTypeImpl getSingleImplementor() {
234        if (!isInterface()) {
235            throw new JVMCIError("Cannot call getSingleImplementor() on a non-interface type: %s", this);
236        }
237        final long implementorMetaspaceKlass = runtime().getCompilerToVM().getKlassImplementor(getMetaspaceKlass());
238
239        // No implementor.
240        if (implementorMetaspaceKlass == 0) {
241            return null;
242        }
243
244        return fromMetaspaceKlass(implementorMetaspaceKlass);
245    }
246
247    public HotSpotResolvedObjectTypeImpl getSupertype() {
248        if (isArray()) {
249            ResolvedJavaType componentType = getComponentType();
250            if (mirror() == Object[].class || componentType.isPrimitive()) {
251                return fromObjectClass(Object.class);
252            }
253            return (HotSpotResolvedObjectTypeImpl) ((HotSpotResolvedObjectTypeImpl) componentType).getSupertype().getArrayClass();
254        }
255        if (isInterface()) {
256            return fromObjectClass(Object.class);
257        }
258        return getSuperclass();
259    }
260
261    @Override
262    public HotSpotResolvedObjectType findLeastCommonAncestor(ResolvedJavaType otherType) {
263        if (otherType.isPrimitive()) {
264            return null;
265        } else {
266            HotSpotResolvedObjectTypeImpl t1 = this;
267            HotSpotResolvedObjectTypeImpl t2 = (HotSpotResolvedObjectTypeImpl) otherType;
268            while (true) {
269                if (t1.isAssignableFrom(t2)) {
270                    return t1;
271                }
272                if (t2.isAssignableFrom(t1)) {
273                    return t2;
274                }
275                t1 = t1.getSupertype();
276                t2 = t2.getSupertype();
277            }
278        }
279    }
280
281    @Override
282    public HotSpotResolvedObjectType asExactType() {
283        return isLeaf() ? this : null;
284    }
285
286    @Override
287    public JavaConstant getJavaClass() {
288        return HotSpotObjectConstantImpl.forObject(mirror());
289    }
290
291    @Override
292    public JavaConstant getObjectHub() {
293        return klass();
294    }
295
296    @Override
297    public AssumptionResult<Boolean> hasFinalizableSubclass() {
298        assert !isArray();
299        if (!runtime().getCompilerToVM().hasFinalizableSubclass(getMetaspaceKlass())) {
300            return new AssumptionResult<>(false, new NoFinalizableSubclass(this));
301        }
302        return new AssumptionResult<>(true);
303    }
304
305    @Override
306    public boolean hasFinalizer() {
307        HotSpotVMConfig config = runtime().getConfig();
308        return (getAccessFlags() & config.klassHasFinalizerFlag) != 0;
309    }
310
311    @Override
312    public boolean isPrimitive() {
313        return false;
314    }
315
316    @Override
317    public boolean isArray() {
318        return mirror().isArray();
319    }
320
321    @Override
322    public boolean isInitialized() {
323        return isArray() ? true : getInitState() == runtime().getConfig().instanceKlassStateFullyInitialized;
324    }
325
326    @Override
327    public boolean isLinked() {
328        return isArray() ? true : getInitState() >= runtime().getConfig().instanceKlassStateLinked;
329    }
330
331    /**
332     * Returns the value of the state field {@code InstanceKlass::_init_state} of the metaspace
333     * klass.
334     *
335     * @return state field value of this type
336     */
337    private int getInitState() {
338        assert !isArray() : "_init_state only exists in InstanceKlass";
339        return unsafe.getByte(getMetaspaceKlass() + runtime().getConfig().instanceKlassInitStateOffset) & 0xFF;
340    }
341
342    @Override
343    public void initialize() {
344        if (!isInitialized()) {
345            unsafe.ensureClassInitialized(mirror());
346            assert isInitialized();
347        }
348    }
349
350    @Override
351    public boolean isInstance(JavaConstant obj) {
352        if (obj.getKind() == Kind.Object && !obj.isNull()) {
353            return mirror().isInstance(((HotSpotObjectConstantImpl) obj).object());
354        }
355        return false;
356    }
357
358    @Override
359    public boolean isInstanceClass() {
360        return !isArray() && !isInterface();
361    }
362
363    @Override
364    public boolean isInterface() {
365        return mirror().isInterface();
366    }
367
368    @Override
369    public boolean isAssignableFrom(ResolvedJavaType other) {
370        assert other != null;
371        if (other instanceof HotSpotResolvedObjectTypeImpl) {
372            HotSpotResolvedObjectTypeImpl otherType = (HotSpotResolvedObjectTypeImpl) other;
373            return mirror().isAssignableFrom(otherType.mirror());
374        }
375        return false;
376    }
377
378    @Override
379    public boolean isJavaLangObject() {
380        return javaClass.equals(Object.class);
381    }
382
383    @Override
384    public Kind getKind() {
385        return Kind.Object;
386    }
387
388    @Override
389    public ResolvedJavaMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) {
390        ResolvedJavaMethod resolvedMethod = resolveMethod(method, callerType);
391        if (resolvedMethod == null || resolvedMethod.isAbstract()) {
392            return null;
393        }
394        return resolvedMethod;
395    }
396
397    @Override
398    public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) {
399        assert !callerType.isArray();
400        if (method.isConcrete() && method.getDeclaringClass().equals(this) && method.isPublic()) {
401            return method;
402        }
403        if (!method.getDeclaringClass().isAssignableFrom(this)) {
404            return null;
405        }
406        HotSpotResolvedJavaMethodImpl hotSpotMethod = (HotSpotResolvedJavaMethodImpl) method;
407        HotSpotResolvedObjectTypeImpl hotSpotCallerType = (HotSpotResolvedObjectTypeImpl) callerType;
408        final long resolvedMetaspaceMethod = runtime().getCompilerToVM().resolveMethod(getMetaspaceKlass(), hotSpotMethod.getMetaspaceMethod(), hotSpotCallerType.getMetaspaceKlass());
409        if (resolvedMetaspaceMethod == 0) {
410            return null;
411        }
412        return HotSpotResolvedJavaMethodImpl.fromMetaspace(resolvedMetaspaceMethod);
413    }
414
415    public ConstantPool constantPool() {
416        if (constantPool == null) {
417            final long metaspaceConstantPool = unsafe.getAddress(getMetaspaceKlass() + runtime().getConfig().instanceKlassConstantsOffset);
418            constantPool = new HotSpotConstantPool(metaspaceConstantPool);
419        }
420        return constantPool;
421    }
422
423    /**
424     * Gets the instance size of this type. If an instance of this type cannot be fast path
425     * allocated, then the returned value is negative (its absolute value gives the size). Must not
426     * be called if this is an array or interface type.
427     */
428    public int instanceSize() {
429        assert !isArray();
430        assert !isInterface();
431
432        HotSpotVMConfig config = runtime().getConfig();
433        final int layoutHelper = layoutHelper();
434        assert layoutHelper > config.klassLayoutHelperNeutralValue : "must be instance";
435
436        // See: Klass::layout_helper_size_in_bytes
437        int size = layoutHelper & ~config.klassLayoutHelperInstanceSlowPathBit;
438
439        // See: Klass::layout_helper_needs_slow_path
440        boolean needsSlowPath = (layoutHelper & config.klassLayoutHelperInstanceSlowPathBit) != 0;
441
442        return needsSlowPath ? -size : size;
443    }
444
445    public int layoutHelper() {
446        HotSpotVMConfig config = runtime().getConfig();
447        return unsafe.getInt(getMetaspaceKlass() + config.klassLayoutHelperOffset);
448    }
449
450    public synchronized HotSpotResolvedJavaMethod createMethod(long metaspaceMethod) {
451        HotSpotResolvedJavaMethod method = null;
452        if (methodCache == null) {
453            methodCache = new HashMap<>(8);
454        } else {
455            method = methodCache.get(metaspaceMethod);
456        }
457        if (method == null) {
458            method = new HotSpotResolvedJavaMethodImpl(this, metaspaceMethod);
459            methodCache.put(metaspaceMethod, method);
460        }
461        return method;
462    }
463
464    public int getVtableLength() {
465        HotSpotVMConfig config = runtime().getConfig();
466        if (isInterface() || isArray()) {
467            /* Everything has the core vtable of java.lang.Object */
468            return config.baseVtableLength;
469        }
470        int result = unsafe.getInt(getMetaspaceKlass() + config.instanceKlassVtableLengthOffset) / (config.vtableEntrySize / config.heapWordSize);
471        assert result >= config.baseVtableLength : unsafe.getInt(getMetaspaceKlass() + config.instanceKlassVtableLengthOffset) + " " + config.vtableEntrySize;
472        return result;
473    }
474
475    public synchronized HotSpotResolvedJavaField createField(String fieldName, JavaType type, long offset, int rawFlags) {
476        HotSpotResolvedJavaField result = null;
477
478        final int flags = rawFlags & ModifiersProvider.jvmFieldModifiers();
479
480        final long id = offset + ((long) flags << 32);
481
482        // (thomaswue) Must cache the fields, because the local load elimination only works if the
483        // objects from two field lookups are identical.
484        if (fieldCache == null) {
485            fieldCache = new HashMap<>(8);
486        } else {
487            result = fieldCache.get(id);
488        }
489
490        if (result == null) {
491            result = new HotSpotResolvedJavaFieldImpl(this, fieldName, type, offset, rawFlags);
492            fieldCache.put(id, result);
493        } else {
494            assert result.getName().equals(fieldName);
495            // assert result.getType().equals(type);
496            assert result.offset() == offset;
497            assert result.getModifiers() == flags;
498        }
499
500        return result;
501    }
502
503    @Override
504    public AssumptionResult<ResolvedJavaMethod> findUniqueConcreteMethod(ResolvedJavaMethod method) {
505        HotSpotResolvedJavaMethod hmethod = (HotSpotResolvedJavaMethod) method;
506        HotSpotResolvedObjectType declaredHolder = hmethod.getDeclaringClass();
507        /*
508         * Sometimes the receiver type in the graph hasn't stabilized to a subtype of declared
509         * holder, usually because of phis, so make sure that the type is related to the declared
510         * type before using it for lookup. Unlinked types should also be ignored because we can't
511         * resolve the proper method to invoke. Generally unlinked types in invokes should result in
512         * a deopt instead since they can't really be used if they aren't linked yet.
513         */
514        if (!declaredHolder.isAssignableFrom(this) || this.isArray() || this.equals(declaredHolder) || !isLinked() || isInterface()) {
515            ResolvedJavaMethod result = hmethod.uniqueConcreteMethod(declaredHolder);
516            if (result != null) {
517                return new AssumptionResult<>(result, new ConcreteMethod(method, declaredHolder, result));
518            }
519            return null;
520        }
521        /*
522         * The holder may be a subtype of the declaredHolder so make sure to resolve the method to
523         * the correct method for the subtype.
524         */
525        HotSpotResolvedJavaMethod resolvedMethod = (HotSpotResolvedJavaMethod) resolveMethod(hmethod, this);
526        if (resolvedMethod == null) {
527            // The type isn't known to implement the method.
528            return null;
529        }
530
531        ResolvedJavaMethod result = resolvedMethod.uniqueConcreteMethod(this);
532        if (result != null) {
533            return new AssumptionResult<>(result, new ConcreteMethod(method, this, result));
534        }
535        return null;
536    }
537
538    /**
539     * This class represents the field information for one field contained in the fields array of an
540     * {@code InstanceKlass}. The implementation is similar to the native {@code FieldInfo} class.
541     */
542    private class FieldInfo {
543        /**
544         * Native pointer into the array of Java shorts.
545         */
546        private final long metaspaceData;
547
548        /**
549         * Creates a field info for the field in the fields array at index {@code index}.
550         *
551         * @param index index to the fields array
552         */
553        public FieldInfo(int index) {
554            HotSpotVMConfig config = runtime().getConfig();
555            // Get Klass::_fields
556            final long metaspaceFields = unsafe.getAddress(getMetaspaceKlass() + config.instanceKlassFieldsOffset);
557            assert config.fieldInfoFieldSlots == 6 : "revisit the field parsing code";
558            metaspaceData = metaspaceFields + config.arrayU2DataOffset + config.fieldInfoFieldSlots * Short.BYTES * index;
559        }
560
561        private int getAccessFlags() {
562            return readFieldSlot(runtime().getConfig().fieldInfoAccessFlagsOffset);
563        }
564
565        private int getNameIndex() {
566            return readFieldSlot(runtime().getConfig().fieldInfoNameIndexOffset);
567        }
568
569        private int getSignatureIndex() {
570            return readFieldSlot(runtime().getConfig().fieldInfoSignatureIndexOffset);
571        }
572
573        public int getOffset() {
574            HotSpotVMConfig config = runtime().getConfig();
575            final int lowPacked = readFieldSlot(config.fieldInfoLowPackedOffset);
576            final int highPacked = readFieldSlot(config.fieldInfoHighPackedOffset);
577            final int offset = ((highPacked << Short.SIZE) | lowPacked) >> config.fieldInfoTagSize;
578            return offset;
579        }
580
581        /**
582         * Helper method to read an entry (slot) from the field array. Currently field info is laid
583         * on top an array of Java shorts.
584         */
585        private int readFieldSlot(int index) {
586            return unsafe.getChar(metaspaceData + Short.BYTES * index);
587        }
588
589        /**
590         * Returns the name of this field as a {@link String}. If the field is an internal field the
591         * name index is pointing into the vmSymbols table.
592         */
593        public String getName() {
594            final int nameIndex = getNameIndex();
595            return isInternal() ? HotSpotVmSymbols.symbolAt(nameIndex) : constantPool().lookupUtf8(nameIndex);
596        }
597
598        /**
599         * Returns the signature of this field as {@link String}. If the field is an internal field
600         * the signature index is pointing into the vmSymbols table.
601         */
602        public String getSignature() {
603            final int signatureIndex = getSignatureIndex();
604            return isInternal() ? HotSpotVmSymbols.symbolAt(signatureIndex) : constantPool().lookupUtf8(signatureIndex);
605        }
606
607        public JavaType getType() {
608            String signature = getSignature();
609            return runtime().lookupType(signature, HotSpotResolvedObjectTypeImpl.this, false);
610        }
611
612        private boolean isInternal() {
613            return (getAccessFlags() & runtime().getConfig().jvmAccFieldInternal) != 0;
614        }
615
616        public boolean isStatic() {
617            return Modifier.isStatic(getAccessFlags());
618        }
619
620        public boolean hasGenericSignature() {
621            return (getAccessFlags() & runtime().getConfig().jvmAccFieldHasGenericSignature) != 0;
622        }
623    }
624
625    private static class OffsetComparator implements java.util.Comparator<HotSpotResolvedJavaField> {
626        @Override
627        public int compare(HotSpotResolvedJavaField o1, HotSpotResolvedJavaField o2) {
628            return o1.offset() - o2.offset();
629        }
630    }
631
632    @Override
633    public ResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) {
634        if (instanceFields == null) {
635            if (isArray() || isInterface()) {
636                instanceFields = new HotSpotResolvedJavaField[0];
637            } else {
638                final int fieldCount = getFieldCount();
639                ArrayList<HotSpotResolvedJavaField> fieldsArray = new ArrayList<>(fieldCount);
640
641                for (int i = 0; i < fieldCount; i++) {
642                    FieldInfo field = new FieldInfo(i);
643
644                    // We are only interested in instance fields.
645                    if (!field.isStatic()) {
646                        HotSpotResolvedJavaField resolvedJavaField = createField(field.getName(), field.getType(), field.getOffset(), field.getAccessFlags());
647                        fieldsArray.add(resolvedJavaField);
648                    }
649                }
650
651                fieldsArray.sort(new OffsetComparator());
652
653                HotSpotResolvedJavaField[] myFields = fieldsArray.toArray(new HotSpotResolvedJavaField[0]);
654
655                if (mirror() != Object.class) {
656                    HotSpotResolvedJavaField[] superFields = (HotSpotResolvedJavaField[]) getSuperclass().getInstanceFields(true);
657                    HotSpotResolvedJavaField[] fields = Arrays.copyOf(superFields, superFields.length + myFields.length);
658                    System.arraycopy(myFields, 0, fields, superFields.length, myFields.length);
659                    instanceFields = fields;
660                } else {
661                    assert myFields.length == 0 : "java.lang.Object has fields!";
662                    instanceFields = myFields;
663                }
664
665            }
666        }
667        if (!includeSuperclasses) {
668            int myFieldsStart = 0;
669            while (myFieldsStart < instanceFields.length && !instanceFields[myFieldsStart].getDeclaringClass().equals(this)) {
670                myFieldsStart++;
671            }
672            if (myFieldsStart == 0) {
673                return instanceFields;
674            }
675            if (myFieldsStart == instanceFields.length) {
676                return new HotSpotResolvedJavaField[0];
677            }
678            return Arrays.copyOfRange(instanceFields, myFieldsStart, instanceFields.length);
679        }
680        return instanceFields;
681    }
682
683    @Override
684    public ResolvedJavaField[] getStaticFields() {
685        if (isArray()) {
686            return new HotSpotResolvedJavaField[0];
687        } else {
688            final int fieldCount = getFieldCount();
689            ArrayList<HotSpotResolvedJavaField> fieldsArray = new ArrayList<>(fieldCount);
690
691            for (int i = 0; i < fieldCount; i++) {
692                FieldInfo field = new FieldInfo(i);
693
694                // We are only interested in static fields.
695                if (field.isStatic()) {
696                    HotSpotResolvedJavaField resolvedJavaField = createField(field.getName(), field.getType(), field.getOffset(), field.getAccessFlags());
697                    fieldsArray.add(resolvedJavaField);
698                }
699            }
700
701            fieldsArray.sort(new OffsetComparator());
702            return fieldsArray.toArray(new HotSpotResolvedJavaField[fieldsArray.size()]);
703        }
704    }
705
706    /**
707     * Returns the actual field count of this class's internal {@code InstanceKlass::_fields} array
708     * by walking the array and discounting the generic signature slots at the end of the array.
709     *
710     * <p>
711     * See {@code FieldStreamBase::init_generic_signature_start_slot}
712     */
713    private int getFieldCount() {
714        HotSpotVMConfig config = runtime().getConfig();
715        final long metaspaceFields = unsafe.getAddress(getMetaspaceKlass() + config.instanceKlassFieldsOffset);
716        int metaspaceFieldsLength = unsafe.getInt(metaspaceFields + config.arrayU1LengthOffset);
717        int fieldCount = 0;
718
719        for (int i = 0, index = 0; i < metaspaceFieldsLength; i += config.fieldInfoFieldSlots, index++) {
720            FieldInfo field = new FieldInfo(index);
721            if (field.hasGenericSignature()) {
722                metaspaceFieldsLength--;
723            }
724            fieldCount++;
725        }
726        return fieldCount;
727    }
728
729    @Override
730    public Class<?> mirror() {
731        return javaClass;
732    }
733
734    @Override
735    public String getSourceFileName() {
736        HotSpotVMConfig config = runtime().getConfig();
737        final int sourceFileNameIndex = unsafe.getChar(getMetaspaceKlass() + config.instanceKlassSourceFileNameIndexOffset);
738        if (sourceFileNameIndex == 0) {
739            return null;
740        }
741        return constantPool().lookupUtf8(sourceFileNameIndex);
742    }
743
744    @Override
745    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
746        return mirror().getAnnotation(annotationClass);
747    }
748
749    /**
750     * Performs a fast-path check that this type is resolved in the context of a given accessing
751     * class. A negative result does not mean this type is not resolved with respect to
752     * {@code accessingClass}. That can only be determined by
753     * {@linkplain HotSpotJVMCIRuntime#lookupType(String, HotSpotResolvedObjectType, boolean)
754     * re-resolving} the type.
755     */
756    public boolean isDefinitelyResolvedWithRespectTo(ResolvedJavaType accessingClass) {
757        assert accessingClass != null;
758        ResolvedJavaType elementType = getElementalType();
759        if (elementType.isPrimitive()) {
760            // Primitive type resolution is context free.
761            return true;
762        }
763        if (elementType.getName().startsWith("Ljava/")) {
764            // Classes in a java.* package can only be defined by the
765            // boot class loader. This is enforced by ClassLoader.preDefineClass()
766            assert mirror().getClassLoader() == null;
767            return true;
768        }
769        ClassLoader thisCl = mirror().getClassLoader();
770        ClassLoader accessingClassCl = ((HotSpotResolvedObjectTypeImpl) accessingClass).mirror().getClassLoader();
771        return thisCl == accessingClassCl;
772    }
773
774    @Override
775    public ResolvedJavaType resolve(ResolvedJavaType accessingClass) {
776        if (isDefinitelyResolvedWithRespectTo(requireNonNull(accessingClass))) {
777            return this;
778        }
779        HotSpotResolvedObjectTypeImpl accessingType = (HotSpotResolvedObjectTypeImpl) accessingClass;
780        return (ResolvedJavaType) runtime().lookupType(getName(), accessingType, true);
781    }
782
783    /**
784     * Gets the metaspace Klass boxed in a {@link JavaConstant}.
785     */
786    public JavaConstant klass() {
787        return HotSpotMetaspaceConstantImpl.forMetaspaceObject(runtime().getHostJVMCIBackend().getTarget().wordKind, getMetaspaceKlass(), this, false);
788    }
789
790    public boolean isPrimaryType() {
791        return runtime().getConfig().secondarySuperCacheOffset != superCheckOffset();
792    }
793
794    public int superCheckOffset() {
795        HotSpotVMConfig config = runtime().getConfig();
796        return unsafe.getInt(getMetaspaceKlass() + config.superCheckOffsetOffset);
797    }
798
799    public long prototypeMarkWord() {
800        HotSpotVMConfig config = runtime().getConfig();
801        if (isArray()) {
802            return config.arrayPrototypeMarkWord();
803        } else {
804            return unsafe.getAddress(getMetaspaceKlass() + config.prototypeMarkWordOffset);
805        }
806    }
807
808    @Override
809    public ResolvedJavaField findInstanceFieldWithOffset(long offset, Kind expectedEntryKind) {
810        ResolvedJavaField[] declaredFields = getInstanceFields(true);
811        for (ResolvedJavaField field : declaredFields) {
812            HotSpotResolvedJavaField resolvedField = (HotSpotResolvedJavaField) field;
813            long resolvedFieldOffset = resolvedField.offset();
814            // @formatter:off
815            if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN  &&
816                            expectedEntryKind.isPrimitive() &&
817                            !expectedEntryKind.equals(Kind.Void) &&
818                            resolvedField.getKind().isPrimitive()) {
819                resolvedFieldOffset +=
820                                resolvedField.getKind().getByteCount() -
821                                Math.min(resolvedField.getKind().getByteCount(), 4 + expectedEntryKind.getByteCount());
822            }
823            if (resolvedFieldOffset == offset) {
824                return field;
825            }
826            // @formatter:on
827        }
828        return null;
829    }
830
831    @Override
832    public URL getClassFilePath() {
833        Class<?> cls = mirror();
834        return cls.getResource(MetaUtil.getSimpleName(cls, true).replace('.', '$') + ".class");
835    }
836
837    @Override
838    public boolean isLocal() {
839        return mirror().isLocalClass();
840    }
841
842    @Override
843    public boolean isMember() {
844        return mirror().isMemberClass();
845    }
846
847    @Override
848    public HotSpotResolvedObjectTypeImpl getEnclosingType() {
849        final Class<?> encl = mirror().getEnclosingClass();
850        return encl == null ? null : fromObjectClass(encl);
851    }
852
853    @Override
854    public ResolvedJavaMethod[] getDeclaredConstructors() {
855        Constructor<?>[] constructors = mirror().getDeclaredConstructors();
856        ResolvedJavaMethod[] result = new ResolvedJavaMethod[constructors.length];
857        for (int i = 0; i < constructors.length; i++) {
858            result[i] = runtime().getHostJVMCIBackend().getMetaAccess().lookupJavaMethod(constructors[i]);
859            assert result[i].isConstructor();
860        }
861        return result;
862    }
863
864    @Override
865    public ResolvedJavaMethod[] getDeclaredMethods() {
866        Method[] methods = mirror().getDeclaredMethods();
867        ResolvedJavaMethod[] result = new ResolvedJavaMethod[methods.length];
868        for (int i = 0; i < methods.length; i++) {
869            result[i] = runtime().getHostJVMCIBackend().getMetaAccess().lookupJavaMethod(methods[i]);
870            assert !result[i].isConstructor();
871        }
872        return result;
873    }
874
875    public ResolvedJavaMethod getClassInitializer() {
876        final long metaspaceMethod = runtime().getCompilerToVM().getClassInitializer(getMetaspaceKlass());
877        if (metaspaceMethod != 0L) {
878            return createMethod(metaspaceMethod);
879        }
880        return null;
881    }
882
883    @Override
884    public String toString() {
885        return "HotSpotType<" + getName() + ", resolved>";
886    }
887
888    @Override
889    public boolean isTrustedInterfaceType() {
890        return TrustedInterface.class.isAssignableFrom(mirror());
891    }
892}