| File: Internal\Runtime\TypeLoader\TypeBuilderState.cs | Web Access |
| Project: src\runtime\src\coreclr\nativeaot\System.Private.TypeLoader\src\System.Private.TypeLoader.csproj (System.Private.TypeLoader) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Diagnostics; using Internal.NativeFormat; using Internal.TypeSystem; namespace Internal.Runtime.TypeLoader { internal struct NativeLayoutInfo { public uint Offset; public NativeFormatModuleInfo Module; public NativeReader Reader; public NativeLayoutInfoLoadContext LoadContext; } // // TypeBuilder per-Type state. It is attached to each TypeDesc that gets involved in type building. // internal class TypeBuilderState { public TypeBuilderState(TypeDesc typeBeingBuilt) { TypeBeingBuilt = typeBeingBuilt; } public readonly TypeDesc TypeBeingBuilt; // // We cache and try to reuse the most recently used TypeSystemContext. The TypeSystemContext is used by not just type builder itself, // but also in several random other places in reflection. There can be multiple ResolutionContexts in flight at any given moment. // This check ensures that the RuntimeTypeHandle cache in the current resolution context is refreshed in case there were new // types built using a different TypeSystemContext in the meantime. // NOTE: For correctness, this value must be recomputed every time the context is recycled. This requires flushing the TypeBuilderState // from each type that has one if context is recycled. // public bool AttemptedAndFailedToRetrieveTypeHandle; public bool NeedsTypeHandle; public bool HasBeenPrepared; public RuntimeTypeHandle HalfBakedRuntimeTypeHandle; public IntPtr HalfBakedDictionary; private bool _templateComputed; private bool _nativeLayoutTokenComputed; private TypeDesc _templateType; public TypeDesc TemplateType { get { if (!_templateComputed) { // Multidimensional arrays don't implement generic interfaces and are special cases. They use // typeof(object[,]) as their template. if (TypeBeingBuilt.IsMdArray) { _templateType = TypeBeingBuilt.Context.ResolveRuntimeTypeHandle(typeof(object[,]).TypeHandle); _templateTypeLoaderNativeLayout = false; _nativeLayoutComputed = _nativeLayoutTokenComputed = _templateComputed = true; return _templateType; } // Arrays of pointers don't implement generic interfaces and are special cases. They use // typeof(char*[]) as their template. if (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType is TypeDesc elementType && (elementType.IsPointer || elementType.IsFunctionPointer)) { _templateType = TypeBeingBuilt.Context.ResolveRuntimeTypeHandle(typeof(char*[]).TypeHandle); _templateTypeLoaderNativeLayout = false; _nativeLayoutComputed = _nativeLayoutTokenComputed = _templateComputed = true; return _templateType; } // Locate the template type and native layout info _templateType = TemplateLocator.TryGetTypeTemplate(TypeBeingBuilt, ref _nativeLayoutInfo); Debug.Assert(_templateType == null || !_templateType.RuntimeTypeHandle.IsNull()); _templateTypeLoaderNativeLayout = true; _templateComputed = true; if (_templateType != null) _nativeLayoutTokenComputed = true; } return _templateType; } } private bool _nativeLayoutComputed; private bool _templateTypeLoaderNativeLayout; private NativeLayoutInfo _nativeLayoutInfo; private void EnsureNativeLayoutInfoComputed() { if (!_nativeLayoutComputed) { if (!_nativeLayoutTokenComputed) { if (!_templateComputed) { // Attempt to compute native layout through as a non-ReadyToRun template object _ = this.TemplateType; } _nativeLayoutTokenComputed = true; } if (_nativeLayoutInfo.Module != null) { FinishInitNativeLayoutInfo(TypeBeingBuilt, ref _nativeLayoutInfo); } _nativeLayoutComputed = true; } } /// <summary> /// Initialize the Reader and LoadContext fields of the native layout info /// </summary> /// <param name="type"></param> /// <param name="nativeLayoutInfo"></param> private static void FinishInitNativeLayoutInfo(TypeDesc type, ref NativeLayoutInfo nativeLayoutInfo) { var nativeLayoutInfoLoadContext = new NativeLayoutInfoLoadContext(); nativeLayoutInfoLoadContext._typeSystemContext = type.Context; nativeLayoutInfoLoadContext._module = nativeLayoutInfo.Module; if (type is DefType) { nativeLayoutInfoLoadContext._typeArgumentHandles = ((DefType)type).Instantiation; } else if (type is ArrayType) { nativeLayoutInfoLoadContext._typeArgumentHandles = new Instantiation(new TypeDesc[] { ((ArrayType)type).ElementType }); } else { Debug.Assert(false); } nativeLayoutInfoLoadContext._methodArgumentHandles = new Instantiation(null); nativeLayoutInfo.Reader = TypeLoaderEnvironment.GetNativeLayoutInfoReader(nativeLayoutInfo.Module.Handle); nativeLayoutInfo.LoadContext = nativeLayoutInfoLoadContext; } public NativeLayoutInfo NativeLayoutInfo { get { EnsureNativeLayoutInfoComputed(); return _nativeLayoutInfo; } } public NativeParser GetParserForNativeLayoutInfo() { EnsureNativeLayoutInfoComputed(); if (_templateTypeLoaderNativeLayout) return new NativeParser(_nativeLayoutInfo.Reader, _nativeLayoutInfo.Offset); else return default(NativeParser); } // RuntimeInterfaces is the full list of interfaces that the type implements. It can include private internal implementation // detail interfaces that nothing is known about. public DefType[] RuntimeInterfaces { get { // Generic Type Definitions have no runtime interfaces if (TypeBeingBuilt.IsGenericDefinition) return null; return TypeBeingBuilt.RuntimeInterfaces; } } private bool? _hasDictionarySlotInVTable; private bool ComputeHasDictionarySlotInVTable() { if (!TypeBeingBuilt.IsGeneric() && !(TypeBeingBuilt is ArrayType)) return false; // Generic interfaces always have a dictionary slot if (TypeBeingBuilt.IsInterface) return true; return TypeBeingBuilt.CanShareNormalGenericCode(); } public bool HasDictionarySlotInVTable { get { _hasDictionarySlotInVTable ??= ComputeHasDictionarySlotInVTable(); return _hasDictionarySlotInVTable.Value; } } private bool? _hasDictionaryInVTable; private bool ComputeHasDictionaryInVTable() { if (!HasDictionarySlotInVTable) return false; if (TypeBeingBuilt.RetrieveRuntimeTypeHandleIfPossible()) { // Type was already constructed return TypeBeingBuilt.RuntimeTypeHandle.GetDictionary() != IntPtr.Zero; } else { // Type is being newly constructed Debug.Assert(TemplateType != null); NativeParser parser = GetParserForNativeLayoutInfo(); // Template type loader case var dictionaryLayoutParser = parser.GetParserForBagElementKind(BagElementKind.DictionaryLayout); return !dictionaryLayoutParser.IsNull; } } public bool HasDictionaryInVTable { get { _hasDictionaryInVTable ??= ComputeHasDictionaryInVTable(); return _hasDictionaryInVTable.Value; } } private ushort? _numVTableSlots; private ushort ComputeNumVTableSlots() { if (TypeBeingBuilt.RetrieveRuntimeTypeHandleIfPossible()) { unsafe { return TypeBeingBuilt.RuntimeTypeHandle.ToEETypePtr()->NumVtableSlots; } } else { // Template type loader case unsafe { if (TypeBeingBuilt.IsPointer || TypeBeingBuilt.IsByRef || TypeBeingBuilt.IsFunctionPointer) { // Pointers and byrefs don't have vtable slots return 0; } if (TypeBeingBuilt.IsMdArray || (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType is TypeDesc elementType && (elementType.IsPointer || elementType.IsFunctionPointer))) { // MDArray types and pointer arrays have the same vtable as the System.Array type they "derive" from. // They do not implement the generic interfaces that make this interesting for normal arrays. return TypeBeingBuilt.BaseType.GetRuntimeTypeHandle().ToEETypePtr()->NumVtableSlots; } else { Debug.Assert(TypeBeingBuilt.IsTemplateCanonical()); TypeDesc templateType = TypeBeingBuilt.ComputeTemplate(false); Debug.Assert(templateType != null); // Canonical template type loader case return templateType.GetRuntimeTypeHandle().ToEETypePtr()->NumVtableSlots; } } } } public ushort NumVTableSlots { get { _numVTableSlots ??= ComputeNumVTableSlots(); return _numVTableSlots.Value; } } public GenericTypeDictionary Dictionary; public int NonGcDataSize; public int GcDataSize; public int ThreadDataSize; public bool HasStaticConstructor => ClassConstructorPointer.HasValue; public IntPtr? ClassConstructorPointer; public IntPtr GcStaticDesc; public IntPtr ThreadStaticDesc; public uint ThreadStaticOffset; public GenericVariance[] GenericVarianceFlags; // Sentinel static to allow us to initialize _instanceLayout to something // and then detect that InstanceGCLayout should return null private static readonly bool[] s_emptyLayout = []; private bool[] _instanceGCLayout; /// <summary> /// The instance gc layout of a dynamically laid out type. /// null if one of the following is true /// 1) For an array type: /// - the type is a reference array /// 2) For a generic type: /// - the type has no GC instance fields /// - the type already has a type handle /// - the type has a non-universal canonical template /// - the type has already been constructed /// /// If the type is a valuetype array, this is the layout of the valuetype held in the array if the type has GC reference fields /// Otherwise, it is the layout of the fields in the type. /// </summary> public bool[] InstanceGCLayout { get { if (_instanceGCLayout == null) { if (TypeBeingBuilt is ArrayType) { if (!IsArrayOfReferenceTypes) { ArrayType arrayType = (ArrayType)TypeBeingBuilt; TypeBuilder.GCLayout elementGcLayout = GetFieldGCLayout(arrayType.ElementType); if (!elementGcLayout.IsNone) { _instanceGCLayout = elementGcLayout.AsBitfield(); } } else { // Array of reference type returns null _instanceGCLayout = s_emptyLayout; } } else { Debug.Assert(TypeBeingBuilt.RetrieveRuntimeTypeHandleIfPossible() || (TypeBeingBuilt is PointerType) || (TypeBeingBuilt is ByRefType) || (TypeBeingBuilt is FunctionPointerType) || TypeBeingBuilt.IsTemplateCanonical()); _instanceGCLayout = s_emptyLayout; } } if (_instanceGCLayout == s_emptyLayout) return null; else return _instanceGCLayout; } } private bool _staticGCLayoutPrepared; /// <summary> /// Prepare the StaticGCLayout/ThreadStaticGCLayout/GcStaticDesc/ThreadStaticDesc fields by /// reading native layout or metadata as appropriate. This method should only be called for types which /// are actually to be created. /// </summary> public void PrepareStaticGCLayout() { if (!_staticGCLayoutPrepared) { _staticGCLayoutPrepared = true; DefType defType = TypeBeingBuilt as DefType; if (defType == null) { // Array/pointer types do not have static fields } else if (defType.IsTemplateCanonical()) { // Canonical templates get their layout directly from the NativeLayoutInfo. // Parse it and pull that info out here. NativeParser typeInfoParser = GetParserForNativeLayoutInfo(); BagElementKind kind; while ((kind = typeInfoParser.GetBagElementKind()) != BagElementKind.End) { switch (kind) { case BagElementKind.GcStaticDesc: GcStaticDesc = NativeLayoutInfo.LoadContext.GetGCStaticInfo(typeInfoParser.GetUnsigned()); break; case BagElementKind.ThreadStaticDesc: ThreadStaticDesc = NativeLayoutInfo.LoadContext.GetGCStaticInfo(typeInfoParser.GetUnsigned()); break; default: typeInfoParser.SkipInteger(); break; } } } else { // SUPPORTS_NATIVE_METADATA_TYPE_LOADING // We land here instead of throwing MissingTemplateException. We really should throw. } } } /// <summary> /// Get the GC layout of a type when used as a field. /// NOTE: if the fieldtype is a reference type, this function will return GCLayout.None /// Consumers of the api must handle that special case. /// </summary> private static unsafe TypeBuilder.GCLayout GetFieldGCLayout(TypeDesc fieldType) { if (!fieldType.IsValueType) { Debug.Assert(!fieldType.IsByRef); if (fieldType.IsPointer || fieldType.IsFunctionPointer) return TypeBuilder.GCLayout.None; else return TypeBuilder.GCLayout.SingleReference; } // Is this a type that already exists? If so, get its gclayout from the MethodTable directly if (fieldType.RetrieveRuntimeTypeHandleIfPossible()) { return new TypeBuilder.GCLayout(fieldType.RuntimeTypeHandle); } // The type of the field must be a valuetype that is dynamically being constructed Debug.Assert(fieldType.IsTemplateCanonical()); // Pull the GC Desc from the canonical instantiation TypeDesc templateType = fieldType.ComputeTemplate(); bool success = templateType.RetrieveRuntimeTypeHandleIfPossible(); Debug.Assert(success); return new TypeBuilder.GCLayout(templateType.RuntimeTypeHandle); } public bool IsArrayOfReferenceTypes { get { ArrayType typeAsArrayType = TypeBeingBuilt as ArrayType; if (typeAsArrayType != null) return !typeAsArrayType.ParameterType.IsValueType && !typeAsArrayType.ParameterType.IsPointer && !typeAsArrayType.ParameterType.IsFunctionPointer; else return false; } } // Rank for arrays, -1 is used for an SzArray, and a positive number for a multidimensional array. public int? ArrayRank { get { if (!TypeBeingBuilt.IsArray) return null; else if (TypeBeingBuilt.IsSzArray) return -1; else { Debug.Assert(TypeBeingBuilt.IsMdArray); return ((ArrayType)TypeBeingBuilt).Rank; } } } } }