diff --git a/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs index bd3a854f39a64f..719908065cbe5d 100644 --- a/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs +++ b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs @@ -4,16 +4,18 @@ /*============================================================ ** ** -** Private version of List for internal System.Private.CoreLib use. This -** permits sharing more source between BCL and System.Private.CoreLib (as well as the -** fact that List is just a useful class in general.) +** Private version of List for internal System.Private.TypeLoader use. Type +** loader itself can't use generic types without [ForceDictionaryLookups], and +** we don't want to annotate List with it because it would impact size and +** performance for general usages. ** ** This does not strive to implement the full api surface area ** (but any portion it does implement should match the real List's ** behavior.) ** -** This file is a subset of System.Collections\System\Collections\Generics\List.cs -** and should be kept in sync with that file. +** This file is a subset of +** src\libraries\System.Private.CoreLib\src\System\Collections\Generics\List.cs +** and should be kept in sync with that file when necessary. ** ===========================================================*/ @@ -32,16 +34,14 @@ namespace System.Collections.Generic // Data size is smaller because there will be minimal virtual function table. // Code size is smaller because only functions called will be in the binary. [DebuggerDisplay("Count = {Count}")] -#if TYPE_LOADER_IMPLEMENTATION [System.Runtime.CompilerServices.ForceDictionaryLookups] -#endif internal class LowLevelList { private const int _defaultCapacity = 4; protected T[] _items; protected int _size; - protected int _version; + // No _version field because no IEnumerable support #pragma warning disable CA1825 // avoid the extra generic instantiation for Array.Empty() private static readonly T[] s_emptyArray = new T[0]; @@ -55,60 +55,6 @@ public LowLevelList() _items = s_emptyArray; } - // Constructs a List with a given initial capacity. The list is - // initially empty, but will have room for the given number of elements - // before any reallocations are required. - // - public LowLevelList(int capacity) - { - ArgumentOutOfRangeException.ThrowIfNegative(capacity); - - if (capacity == 0) - _items = s_emptyArray; - else - _items = new T[capacity]; - } - - // Constructs a List, copying the contents of the given collection. The - // size and capacity of the new list will both be equal to the size of the - // given collection. - // - public LowLevelList(IEnumerable collection) - { - ArgumentNullException.ThrowIfNull(collection); - - ICollection? c = collection as ICollection; - if (c != null) - { - int count = c.Count; - if (count == 0) - { - _items = s_emptyArray; - } - else - { - _items = new T[count]; - c.CopyTo(_items, 0); - _size = count; - } - } - else - { - _size = 0; - _items = s_emptyArray; - // This enumerable could be empty. Let Add allocate a new array, if needed. - // Note it will also go to _defaultCapacity first, not 1, then 2, etc. - - using (IEnumerator en = collection.GetEnumerator()) - { - while (en.MoveNext()) - { - Add(en.Current); - } - } - } - } - // Gets and sets the capacity of this list. The capacity is the size of // the internal array used to hold items. When set, the internal // array of the list is reallocated to the given capacity. @@ -163,7 +109,6 @@ public T this[int index] { ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)_size, nameof(index)); _items[index] = value; - _version++; } } @@ -176,7 +121,6 @@ public void Add(T item) { if (_size == _items.Length) EnsureCapacity(_size + 1); _items[_size++] = item; - _version++; } // Ensures that the capacity of this list is at least the given minimum diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeSearcher.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeSearcher.cs index 89ede0e6d92fe8..4720d0caf8a2e3 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeSearcher.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeSearcher.cs @@ -120,7 +120,7 @@ private IEnumerable GetMatchingCustomAttributesIterator(E e return rawPassesFilter(attributeType); }; - LowLevelList immediateResults = new LowLevelList(); + List immediateResults = new List(); foreach (CustomAttributeData cad in GetDeclaredCustomAttributes(element)) { if (passesFilter(cad.AttributeType)) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs index c0bceb7963c8ad..96e2b8b1cbd449 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs @@ -51,7 +51,7 @@ public override int GetHashCode() private static uint s_genericFunctionPointerNextIndex; private const uint c_genericDictionaryChunkSize = 1024; - private static LowLevelList s_genericFunctionPointerCollection = new LowLevelList(); + private static readonly List s_genericFunctionPointerCollection = new List(); private static LowLevelDictionary s_genericFunctionPointerDictionary = new LowLevelDictionary(); public static unsafe IntPtr GetGenericMethodFunctionPointer(IntPtr canonFunctionPointer, IntPtr instantiationArgument) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 72df9073427635..6b769818faf42e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -329,9 +329,6 @@ Utilities\LockFreeReaderHashtableOfPointers.cs - - System\Collections\Generic\LowLevelList.cs - System\Collections\Generic\LowLevelDictionary.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/NamespaceChain.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/NamespaceChain.cs index ff812387e04ddc..ce53181af13e72 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/NamespaceChain.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/NamespaceChain.cs @@ -22,7 +22,7 @@ internal NamespaceChain(MetadataReader reader, NamespaceDefinitionHandle innerMo NamespaceDefinition currentNamespaceDefinition = innerMostNamespaceHandle.GetNamespaceDefinition(reader); ConstantStringValueHandle currentNameHandle = currentNamespaceDefinition.Name; Handle currentNamespaceHandle; - LowLevelList names = new LowLevelList(); + List names = new List(); for (; ; ) { string name = currentNameHandle.GetStringOrNull(reader); diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.ManifestResources.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.ManifestResources.cs index 182a3333d6a9d0..2b3a0e624f94c0 100644 --- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.ManifestResources.cs +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.ManifestResources.cs @@ -22,7 +22,7 @@ internal sealed partial class ExecutionEnvironmentImplementation : ExecutionEnvi { public sealed override ManifestResourceInfo GetManifestResourceInfo(Assembly assembly, string resourceName) { - LowLevelList resourceInfos = GetExtractedResources(assembly); + List resourceInfos = GetExtractedResources(assembly); for (int i = 0; i < resourceInfos.Count; i++) { if (resourceName == resourceInfos[i].Name) @@ -35,7 +35,7 @@ public sealed override ManifestResourceInfo GetManifestResourceInfo(Assembly ass public sealed override string[] GetManifestResourceNames(Assembly assembly) { - LowLevelList resourceInfos = GetExtractedResources(assembly); + List resourceInfos = GetExtractedResources(assembly); string[] names = new string[resourceInfos.Count]; for (int i = 0; i < resourceInfos.Count; i++) { @@ -50,7 +50,7 @@ public sealed override Stream GetManifestResourceStream(Assembly assembly, strin // This was most likely an embedded resource which the toolchain should have embedded // into an assembly. - LowLevelList resourceInfos = GetExtractedResources(assembly); + List resourceInfos = GetExtractedResources(assembly); for (int i = 0; i < resourceInfos.Count; i++) { ResourceInfo resourceInfo = resourceInfos[i]; @@ -78,17 +78,17 @@ private static unsafe UnmanagedMemoryStream ReadResourceFromBlob(ResourceInfo re return new UnmanagedMemoryStream(pBlob + resourceInfo.Index, resourceInfo.Length); } - private static LowLevelList GetExtractedResources(Assembly assembly) + private static List GetExtractedResources(Assembly assembly) { - LowLevelDictionary> extractedResourceDictionary = ExtractedResourceDictionary; + Dictionary> extractedResourceDictionary = ExtractedResourceDictionary; string assemblyName = assembly.GetName().FullName; - LowLevelList resourceInfos; + List resourceInfos; if (!extractedResourceDictionary.TryGetValue(assemblyName, out resourceInfos)) - return new LowLevelList(); + return new List(); return resourceInfos; } - private static LowLevelDictionary> ExtractedResourceDictionary + private static Dictionary> ExtractedResourceDictionary { get { @@ -97,7 +97,7 @@ private static LowLevelDictionary> ExtractedR // Lazily create the extracted resource dictionary. If two threads race here, we may construct two dictionaries // and overwrite one - this is ok since the dictionaries are read-only once constructed and they contain the identical data. - LowLevelDictionary> dict = new LowLevelDictionary>(); + Dictionary> dict = new Dictionary>(); foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) { @@ -120,10 +120,10 @@ private static LowLevelDictionary> ExtractedR ResourceInfo resourceInfo = new ResourceInfo(resourceName, resourceOffset, resourceLength, module); - LowLevelList assemblyResources; + List assemblyResources; if (!dict.TryGetValue(assemblyName, out assemblyResources)) { - assemblyResources = new LowLevelList(); + assemblyResources = new List(); dict[assemblyName] = assemblyResources; } @@ -144,7 +144,7 @@ private static LowLevelDictionary> ExtractedR /// The dictionary's key is a Fusion-style assembly name. /// The dictionary's value is a list of (resourcename,index) tuples. /// - private static volatile LowLevelDictionary> s_extractedResourceDictionary; + private static volatile Dictionary> s_extractedResourceDictionary; private struct ResourceInfo { diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj index 86fb64fa51852c..6f76fdb352a3fa 100644 --- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj @@ -56,9 +56,6 @@ System\NotImplemented.cs - - System\Collections\Generic\LowLevelList.cs - System\Collections\Generic\LowLevelDictionary.cs diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs index 9a5995e17fb9ef..a4474f8d7f4b07 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs @@ -457,9 +457,9 @@ private static unsafe int GetInstanceGCDescSize(TypeBuilderState state, MethodTa } } - private static bool IsAllGCPointers(LowLevelList bitfield) + private static bool IsAllGCPointers(bool[] bitfield) { - int count = bitfield.Count; + int count = bitfield.Length; Debug.Assert(count > 0); for (int i = 0; i < count; i++) @@ -471,7 +471,7 @@ private static bool IsAllGCPointers(LowLevelList bitfield) return true; } - private static unsafe int CreateArrayGCDesc(LowLevelList bitfield, int rank, bool isSzArray, void* gcdesc) + private static unsafe int CreateArrayGCDesc(bool[] bitfield, int rank, bool isSzArray, void* gcdesc) { if (bitfield == null) return 0; @@ -495,7 +495,7 @@ private static unsafe int CreateArrayGCDesc(LowLevelList bitfield, int ran int first = -1; int last = 0; short numPtrs = 0; - while (i < bitfield.Count) + while (i < bitfield.Length) { if (bitfield[i]) { @@ -513,7 +513,7 @@ private static unsafe int CreateArrayGCDesc(LowLevelList bitfield, int ran numSeries++; numPtrs = 0; - while ((i < bitfield.Count) && (bitfield[i])) + while ((i < bitfield.Length) && (bitfield[i])) { numPtrs++; i++; @@ -531,7 +531,7 @@ private static unsafe int CreateArrayGCDesc(LowLevelList bitfield, int ran { if (numSeries > 0) { - *ptr-- = (short)((first + bitfield.Count - last) * IntPtr.Size); + *ptr-- = (short)((first + bitfield.Length - last) * IntPtr.Size); *ptr-- = numPtrs; *(void**)gcdesc = (void*)-numSeries; @@ -542,7 +542,7 @@ private static unsafe int CreateArrayGCDesc(LowLevelList bitfield, int ran return numSeries; } - private static unsafe int CreateGCDesc(LowLevelList bitfield, int size, bool isValueType, bool isStatic, void* gcdesc) + private static unsafe int CreateGCDesc(bool[] bitfield, int size, bool isValueType, bool isStatic, void* gcdesc) { int offs = 0; // if this type is a class we have to account for the gcdesc. @@ -558,7 +558,7 @@ private static unsafe int CreateGCDesc(LowLevelList bitfield, int size, bo int numSeries = 0; int i = 0; - while (i < bitfield.Count) + while (i < bitfield.Length) { if (bitfield[i]) { @@ -566,7 +566,7 @@ private static unsafe int CreateGCDesc(LowLevelList bitfield, int size, bo int seriesOffset = i * IntPtr.Size + offs; int seriesSize = 0; - while ((i < bitfield.Count) && (bitfield[i])) + while ((i < bitfield.Length) && (bitfield[i])) { seriesSize += IntPtr.Size; i++; diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs index 48bb6c91b6169d..3af4f4451db7c0 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs @@ -13,18 +13,6 @@ namespace Internal.Runtime.TypeLoader { - internal static class LowLevelListExtensions - { - public static void Expand(this LowLevelList list, int count) - { - if (list.Capacity < count) - list.Capacity = count; - - while (list.Count < count) - list.Add(default(T)); - } - } - internal class TypeBuilder { public TypeBuilder() @@ -447,17 +435,17 @@ internal void ParseNativeLayoutInfo(TypeBuilderState state, TypeDesc type) /// internal unsafe struct GCLayout { - private LowLevelList _bitfield; + private bool[] _bitfield; private unsafe void* _gcdesc; private int _size; private bool _isReferenceTypeGCLayout; public static GCLayout None { get { return default(GCLayout); } } - public static GCLayout SingleReference { get; } = new GCLayout(new LowLevelList(new bool[1] { true }), false); + public static GCLayout SingleReference { get; } = new GCLayout([true], false); public bool IsNone { get { return _bitfield == null && _gcdesc == null; } } - public GCLayout(LowLevelList bitfield, bool isReferenceTypeGCLayout) + public GCLayout(bool[] bitfield, bool isReferenceTypeGCLayout) { Debug.Assert(bitfield != null); @@ -473,7 +461,7 @@ public GCLayout(RuntimeTypeHandle rtth) Debug.Assert(MethodTable != null); _bitfield = null; - _isReferenceTypeGCLayout = false; // This field is only used for the LowLevelList path + _isReferenceTypeGCLayout = false; // This field is only used for the bool[] path _gcdesc = MethodTable->ContainsGCPointers ? (void**)MethodTable - 1 : null; _size = (int)MethodTable->BaseSize; } @@ -481,29 +469,21 @@ public GCLayout(RuntimeTypeHandle rtth) /// /// Writes this layout to the given bitfield. /// - /// The bitfield to write a layout to (may be null, at which - /// point it will be created and assigned). - /// The offset at which we need to write the bitfield. - public void WriteToBitfield(LowLevelList bitfield, int offset) + /// The result bitfield. + public bool[] WriteToBitfield() { - ArgumentNullException.ThrowIfNull(bitfield); - - if (IsNone) - return; - // Ensure exactly one of these two are set. + Debug.Assert(!IsNone); Debug.Assert(_gcdesc != null ^ _bitfield != null); if (_bitfield != null) - MergeBitfields(bitfield, offset); + return MergeBitfields(); else - WriteGCDescToBitfield(bitfield, offset); + return WriteGCDescToBitfield(); } - private unsafe void WriteGCDescToBitfield(LowLevelList bitfield, int offset) + private unsafe bool[] WriteGCDescToBitfield() { - int startIndex = offset / IntPtr.Size; - void** ptr = (void**)_gcdesc; Debug.Assert(_gcdesc != null); @@ -512,8 +492,8 @@ private unsafe void WriteGCDescToBitfield(LowLevelList bitfield, int offse Debug.Assert(count >= 0); // Ensure capacity for the values we are about to write - int capacity = startIndex + _size / IntPtr.Size - 2; - bitfield.Expand(capacity); + int capacity = _size / IntPtr.Size - 2; + bool[] bitfield = new bool[capacity]; while (count-- >= 0) { @@ -524,35 +504,35 @@ private unsafe void WriteGCDescToBitfield(LowLevelList bitfield, int offse Debug.Assert(offs >= 0); for (int i = 0; i < len; i++) - bitfield[startIndex + offs + i] = true; + bitfield[offs + i] = true; } + + return bitfield; } - private void MergeBitfields(LowLevelList outputBitfield, int offset) + private bool[] MergeBitfields() { - int startIndex = offset / IntPtr.Size; - // These routines represent the GC layout after the MethodTable pointer - // in an object, but the LowLevelList bitfield logically contains - // the EETypepointer if it is describing a reference type. So, skip the - // first value. + // in an object, but the bool[] bitfield logically contains the EETypepointer + // if it is describing a reference type. So, skip the first value. int itemsToSkip = _isReferenceTypeGCLayout ? 1 : 0; // Assert that we only skip a non-reported pointer. Debug.Assert(itemsToSkip == 0 || _bitfield[0] == false); // Ensure capacity for the values we are about to write - int capacity = startIndex + _bitfield.Count - itemsToSkip; - outputBitfield.Expand(capacity); - + int capacity = _bitfield.Length - itemsToSkip; + bool[] outputBitfield = new bool[capacity]; - for (int i = itemsToSkip; i < _bitfield.Count; i++) + for (int i = itemsToSkip; i < _bitfield.Length; i++) { // We should never overwrite a TRUE value in the table. - Debug.Assert(!outputBitfield[startIndex + i - itemsToSkip] || _bitfield[i]); + Debug.Assert(!outputBitfield[i - itemsToSkip] || _bitfield[i]); - outputBitfield[startIndex + i - itemsToSkip] = _bitfield[i]; + outputBitfield[i - itemsToSkip] = _bitfield[i]; } + + return outputBitfield; } } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs index 39915c06576a15..5d903501773f84 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs @@ -306,9 +306,11 @@ public ushort NumVTableSlots // Sentinel static to allow us to initialize _instanceLayout to something // and then detect that InstanceGCLayout should return null - private static LowLevelList s_emptyLayout = new LowLevelList(); +#pragma warning disable CA1825 // Can't use generic Array.Empty within type loader + private static bool[] s_emptyLayout = new bool[0]; +#pragma warning restore CA1825 - private LowLevelList _instanceGCLayout; + private bool[] _instanceGCLayout; /// /// The instance gc layout of a dynamically laid out type. @@ -324,14 +326,12 @@ public ushort NumVTableSlots /// 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. /// - public LowLevelList InstanceGCLayout + public bool[] InstanceGCLayout { get { if (_instanceGCLayout == null) { - LowLevelList instanceGCLayout; - if (TypeBeingBuilt is ArrayType) { if (!IsArrayOfReferenceTypes) @@ -340,9 +340,7 @@ public LowLevelList InstanceGCLayout TypeBuilder.GCLayout elementGcLayout = GetFieldGCLayout(arrayType.ElementType); if (!elementGcLayout.IsNone) { - instanceGCLayout = new LowLevelList(); - elementGcLayout.WriteToBitfield(instanceGCLayout, 0); - _instanceGCLayout = instanceGCLayout; + _instanceGCLayout = elementGcLayout.WriteToBitfield(); } } else