Skip to content

Commit

Permalink
Merge pull request #539 from Washi1337/feature/is-ref-or-contains-refs
Browse files Browse the repository at this point in the history
Add TypeMemoryLayout::IsReferenceOrContainsReferences.
  • Loading branch information
Washi1337 authored Mar 24, 2024
2 parents 8b2318d + 51c6d80 commit e20aafe
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 25 deletions.
10 changes: 10 additions & 0 deletions src/AsmResolver.DotNet/Memory/MemoryLayoutAttributes.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Runtime.CompilerServices;

namespace AsmResolver.DotNet.Memory
{
Expand Down Expand Up @@ -27,5 +28,14 @@ public enum MemoryLayoutAttributes
/// Indicates the type layout depends on the bitness of the environment.
/// </summary>
IsPlatformDependent = 0b10,

/// <summary>
/// Indicates the type is a managed reference or contains managed references that are tracked by the
/// garbage collector.
/// </summary>
/// <remarks>
/// This is an equivalent to <see cref="RuntimeHelpers.IsReferenceOrContainsReferences{T}"/>.
/// </remarks>
IsReferenceOrContainsReferences = 0b100
}
}
15 changes: 15 additions & 0 deletions src/AsmResolver.DotNet/Memory/TypeMemoryLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;

namespace AsmResolver.DotNet.Memory
{
Expand Down Expand Up @@ -81,6 +82,20 @@ public MemoryLayoutAttributes Attributes
/// </summary>
public bool IsPlatformDependent => (Attributes & MemoryLayoutAttributes.IsPlatformDependent) != 0;

/// <summary>
/// Gets a value indicating whether the type is a managed reference or contains managed references that are
/// tracked by the garbage collector.
/// </summary>
/// <remarks>
/// This is an equivalent to <see cref="RuntimeHelpers.IsReferenceOrContainsReferences{T}"/>.
/// </remarks>
public bool IsReferenceOrContainsReferences => (Attributes & MemoryLayoutAttributes.IsReferenceOrContainsReferences) != 0;

/// <summary>
/// Gets all fields stored in the type.
/// </summary>
public IEnumerable<FieldDefinition> GetFields() => _fields.Keys;

private IEnumerable<FieldMemoryLayout> GetOrderedFields()
{
return _fields.Values
Expand Down
70 changes: 45 additions & 25 deletions src/AsmResolver.DotNet/Memory/TypeMemoryLayoutDetector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,44 +51,46 @@ public TypeMemoryLayoutDetector(GenericContext currentGenericContext, bool is32B

/// <inheritdoc />
public TypeMemoryLayout VisitArrayType(ArrayTypeSignature signature) =>
CreatePointerLayout(signature);
CreateReferenceLayout(signature);

/// <inheritdoc />
public TypeMemoryLayout VisitBoxedType(BoxedTypeSignature signature) =>
CreatePointerLayout(signature);
CreateReferenceLayout(signature);

/// <inheritdoc />
public TypeMemoryLayout VisitByReferenceType(ByReferenceTypeSignature signature) =>
CreatePointerLayout(signature);
CreateReferenceLayout(signature);

/// <inheritdoc />
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);
}
Expand Down Expand Up @@ -138,7 +140,7 @@ public TypeMemoryLayout VisitSentinelType(SentinelTypeSignature signature)

/// <inheritdoc />
public TypeMemoryLayout VisitSzArrayType(SzArrayTypeSignature signature)
=> CreatePointerLayout(signature);
=> CreateReferenceLayout(signature);

/// <inheritdoc />
public TypeMemoryLayout VisitTypeDefOrRef(TypeDefOrRefSignature signature)
Expand Down Expand Up @@ -177,7 +179,7 @@ private TypeMemoryLayout VisitTypeDefinition(TypeDefinition type)
{
return type.IsValueType
? VisitValueTypeDefinition(type)
: CreatePointerLayout(type);
: CreateReferenceLayout(type);
}

private TypeMemoryLayout VisitValueTypeDefinition(TypeDefinition type)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);

Expand All @@ -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);
}
}
}
37 changes: 37 additions & 0 deletions test/AsmResolver.DotNet.Tests/Memory/MiscellaneousStructTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -86,5 +87,41 @@ public void DetermineNestedPlatformDependentSize()
Assert.True(layout.IsPlatformDependent);
Assert.Equal((uint) Unsafe.SizeOf<NestedPlatformDependentStruct>(), 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<TypeDefinition>(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<TypeDefinition>(type.MetadataToken)
.MakeGenericInstanceType(paramType, paramType);

var layout = t.GetImpliedMemoryLayout(false);
Assert.Equal(expected, layout.IsReferenceOrContainsReferences);
}
}
}

0 comments on commit e20aafe

Please sign in to comment.