From 51c6d8010c7a519f0ead7e7e01cbb9524dabeb49 Mon Sep 17 00:00:00 2001 From: Washi Date: Sat, 16 Mar 2024 21:51:59 +0100 Subject: [PATCH] Add TypeMemoryLayout::IsReferenceOrContainsReferences. --- .../Memory/MemoryLayoutAttributes.cs | 10 +++ .../Memory/TypeMemoryLayout.cs | 15 ++++ .../Memory/TypeMemoryLayoutDetector.cs | 70 ++++++++++++------- .../Memory/MiscellaneousStructTest.cs | 37 ++++++++++ 4 files changed, 107 insertions(+), 25 deletions(-) diff --git a/src/AsmResolver.DotNet/Memory/MemoryLayoutAttributes.cs b/src/AsmResolver.DotNet/Memory/MemoryLayoutAttributes.cs index e9acbbabc..0cce21259 100644 --- a/src/AsmResolver.DotNet/Memory/MemoryLayoutAttributes.cs +++ b/src/AsmResolver.DotNet/Memory/MemoryLayoutAttributes.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace AsmResolver.DotNet.Memory { @@ -27,5 +28,14 @@ public enum MemoryLayoutAttributes /// Indicates the type layout depends on the bitness of the environment. /// IsPlatformDependent = 0b10, + + /// + /// Indicates the type is a managed reference or contains managed references that are tracked by the + /// garbage collector. + /// + /// + /// This is an equivalent to . + /// + IsReferenceOrContainsReferences = 0b100 } } diff --git a/src/AsmResolver.DotNet/Memory/TypeMemoryLayout.cs b/src/AsmResolver.DotNet/Memory/TypeMemoryLayout.cs index 152405cc5..9fd373ed6 100644 --- a/src/AsmResolver.DotNet/Memory/TypeMemoryLayout.cs +++ b/src/AsmResolver.DotNet/Memory/TypeMemoryLayout.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.CompilerServices; namespace AsmResolver.DotNet.Memory { @@ -81,6 +82,20 @@ public MemoryLayoutAttributes Attributes /// public bool IsPlatformDependent => (Attributes & MemoryLayoutAttributes.IsPlatformDependent) != 0; + /// + /// Gets a value indicating whether the type is a managed reference or contains managed references that are + /// tracked by the garbage collector. + /// + /// + /// This is an equivalent to . + /// + public bool IsReferenceOrContainsReferences => (Attributes & MemoryLayoutAttributes.IsReferenceOrContainsReferences) != 0; + + /// + /// Gets all fields stored in the type. + /// + public IEnumerable GetFields() => _fields.Keys; + private IEnumerable GetOrderedFields() { return _fields.Values diff --git a/src/AsmResolver.DotNet/Memory/TypeMemoryLayoutDetector.cs b/src/AsmResolver.DotNet/Memory/TypeMemoryLayoutDetector.cs index 3dae49f64..b3795126a 100644 --- a/src/AsmResolver.DotNet/Memory/TypeMemoryLayoutDetector.cs +++ b/src/AsmResolver.DotNet/Memory/TypeMemoryLayoutDetector.cs @@ -51,44 +51,46 @@ public TypeMemoryLayoutDetector(GenericContext currentGenericContext, bool is32B /// public TypeMemoryLayout VisitArrayType(ArrayTypeSignature signature) => - CreatePointerLayout(signature); + CreateReferenceLayout(signature); /// public TypeMemoryLayout VisitBoxedType(BoxedTypeSignature signature) => - CreatePointerLayout(signature); + CreateReferenceLayout(signature); /// public TypeMemoryLayout VisitByReferenceType(ByReferenceTypeSignature signature) => - CreatePointerLayout(signature); + CreateReferenceLayout(signature); /// public TypeMemoryLayout VisitCorLibType(CorLibTypeSignature signature) { - (int elementSize, bool isPlatformDependent) = signature.ElementType switch + (int elementSize, bool isPlatformDependent, bool isReference) = signature.ElementType switch { - ElementType.Boolean => (sizeof(bool), false), - ElementType.Char => (sizeof(char), false), - ElementType.I1 => (sizeof(sbyte), false), - ElementType.U1 => (sizeof(byte), false), - ElementType.I2 => (sizeof(short), false), - ElementType.U2 => (sizeof(ushort), false), - ElementType.I4 => (sizeof(int), false), - ElementType.U4 => (sizeof(uint), false), - ElementType.I8 => (sizeof(long), false), - ElementType.U8 => (sizeof(ulong), false), - ElementType.R4 => (sizeof(float), false), - ElementType.R8 => (sizeof(double), false), - ElementType.String => (PointerSize, true), - ElementType.I => (PointerSize, true), - ElementType.U => (PointerSize, true), - ElementType.Object => (PointerSize, true), - ElementType.TypedByRef => (PointerSize * 2, true), + ElementType.Boolean => (sizeof(bool), false, false), + ElementType.Char => (sizeof(char), false, false), + ElementType.I1 => (sizeof(sbyte), false, false), + ElementType.U1 => (sizeof(byte), false, false), + ElementType.I2 => (sizeof(short), false, false), + ElementType.U2 => (sizeof(ushort), false, false), + ElementType.I4 => (sizeof(int), false, false), + ElementType.U4 => (sizeof(uint), false, false), + ElementType.I8 => (sizeof(long), false, false), + ElementType.U8 => (sizeof(ulong), false, false), + ElementType.R4 => (sizeof(float), false, false), + ElementType.R8 => (sizeof(double), false, false), + ElementType.String => (PointerSize, true, true), + ElementType.I => (PointerSize, true, false), + ElementType.U => (PointerSize, true, false), + ElementType.Object => (PointerSize, true, true), + ElementType.TypedByRef => (PointerSize * 2, true, false), _ => throw new ArgumentOutOfRangeException(nameof(signature)) }; var attributes = _defaultAttributes; if (isPlatformDependent) attributes |= MemoryLayoutAttributes.IsPlatformDependent; + if (isReference) + attributes |= MemoryLayoutAttributes.IsReferenceOrContainsReferences; return new TypeMemoryLayout(signature, (uint) elementSize, attributes); } @@ -138,7 +140,7 @@ public TypeMemoryLayout VisitSentinelType(SentinelTypeSignature signature) /// public TypeMemoryLayout VisitSzArrayType(SzArrayTypeSignature signature) - => CreatePointerLayout(signature); + => CreateReferenceLayout(signature); /// public TypeMemoryLayout VisitTypeDefOrRef(TypeDefOrRefSignature signature) @@ -177,7 +179,7 @@ private TypeMemoryLayout VisitTypeDefinition(TypeDefinition type) { return type.IsValueType ? VisitValueTypeDefinition(type) - : CreatePointerLayout(type); + : CreateReferenceLayout(type); } private TypeMemoryLayout VisitValueTypeDefinition(TypeDefinition type) @@ -232,6 +234,8 @@ private TypeMemoryLayout InferSequentialLayout(TypeDefinition type, uint alignme var contentsLayout = field.Signature.FieldType.AcceptVisitor(this); if (contentsLayout.IsPlatformDependent) result.Attributes |= MemoryLayoutAttributes.IsPlatformDependent; + if (contentsLayout.IsReferenceOrContainsReferences) + result.Attributes |= MemoryLayoutAttributes.IsReferenceOrContainsReferences; // Fields are aligned to the alignment of the type, unless the field is smaller. In such a case, the // field is aligned to its own field size. @@ -278,6 +282,8 @@ private TypeMemoryLayout InferExplicitLayout(TypeDefinition type, uint alignment var contentsLayout = field.Signature.FieldType.AcceptVisitor(this); if (contentsLayout.IsPlatformDependent) result.Attributes |= MemoryLayoutAttributes.IsPlatformDependent; + if (contentsLayout.IsReferenceOrContainsReferences) + result.Attributes |= MemoryLayoutAttributes.IsReferenceOrContainsReferences; result[field] = new FieldMemoryLayout(field, offset, contentsLayout); @@ -288,7 +294,21 @@ private TypeMemoryLayout InferExplicitLayout(TypeDefinition type, uint alignment return result; } - private TypeMemoryLayout CreatePointerLayout(ITypeDescriptor type) => - new(type, (uint) PointerSize, _defaultAttributes | MemoryLayoutAttributes.IsPlatformDependent); + private TypeMemoryLayout CreateReferenceLayout(ITypeDescriptor type) + { + var attributes = _defaultAttributes + | MemoryLayoutAttributes.IsPlatformDependent + | MemoryLayoutAttributes.IsReferenceOrContainsReferences; + + return new TypeMemoryLayout(type, (uint)PointerSize, attributes); + } + + private TypeMemoryLayout CreatePointerLayout(ITypeDescriptor type) + { + var attributes = _defaultAttributes + | MemoryLayoutAttributes.IsPlatformDependent; + + return new TypeMemoryLayout(type, (uint)PointerSize, attributes); + } } } diff --git a/test/AsmResolver.DotNet.Tests/Memory/MiscellaneousStructTest.cs b/test/AsmResolver.DotNet.Tests/Memory/MiscellaneousStructTest.cs index e1132dbd2..4e09940b8 100644 --- a/test/AsmResolver.DotNet.Tests/Memory/MiscellaneousStructTest.cs +++ b/test/AsmResolver.DotNet.Tests/Memory/MiscellaneousStructTest.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using AsmResolver.DotNet.Memory; +using AsmResolver.PE.DotNet.Metadata.Tables.Rows; using Xunit; // Ignore unused field warnings. @@ -86,5 +87,41 @@ public void DetermineNestedPlatformDependentSize() Assert.True(layout.IsPlatformDependent); Assert.Equal((uint) Unsafe.SizeOf(), layout.Size); } + + private struct ManagedStruct + { + public string ManagedField; + } + + [Theory] + [InlineData(typeof(SequentialTestStructs.EmptyStruct), false)] + [InlineData(typeof(Struct1), false)] + [InlineData(typeof(NestedPlatformDependentStruct), false)] + [InlineData(typeof(MiscellaneousStructTest), true)] + [InlineData(typeof(ManagedStruct), true)] + public void DetermineNonGenericIsReferenceOrContainsReferences(Type type, bool expected) + { + var module = ModuleDefinition.FromFile(type.Assembly.Location); + var t = module.LookupMember(type.MetadataToken); + + var layout = t.GetImpliedMemoryLayout(false); + Assert.Equal(expected, layout.IsReferenceOrContainsReferences); + } + + [Theory] + [InlineData(ElementType.I4, false)] + [InlineData(ElementType.String, true)] + public void DetermineGenericIsReferenceOrContainsReferences(ElementType elementType, bool expected) + { + var type = typeof(SequentialTestStructs.GenericStruct<,>); + var module = ModuleDefinition.FromFile(type.Assembly.Location); + + var paramType = module.CorLibTypeFactory.FromElementType(elementType)!; + var t = module.LookupMember(type.MetadataToken) + .MakeGenericInstanceType(paramType, paramType); + + var layout = t.GetImpliedMemoryLayout(false); + Assert.Equal(expected, layout.IsReferenceOrContainsReferences); + } } }