Skip to content

Commit

Permalink
Merge pull request #204 from MonoMod/byreflike-sizeof
Browse files Browse the repository at this point in the history
Properly support Extensions.GetManagedSize() for byreflike types
nike4613 authored Dec 12, 2024
2 parents c52e274 + 6e94f66 commit 26a0a4d
Showing 5 changed files with 56 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/MonoMod.Core/MonoMod.Core.csproj
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
</Description>
<PackageTags>RuntimeDetour;detour;detours;$(PackageTags)</PackageTags>

<VersionPrefix>1.2.1</VersionPrefix>
<VersionPrefix>1.2.2</VersionPrefix>
<PackageValidationBaselineVersion>1.0.0</PackageValidationBaselineVersion>

<MMIncludeTFMChecks>true</MMIncludeTFMChecks>
2 changes: 1 addition & 1 deletion src/MonoMod.RuntimeDetour/MonoMod.RuntimeDetour.csproj
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@

<MMIncludeTFMChecks>true</MMIncludeTFMChecks>

<VersionPrefix>25.2.1</VersionPrefix>
<VersionPrefix>25.2.2</VersionPrefix>
<PackageValidationBaselineVersion>25.0.0</PackageValidationBaselineVersion>
</PropertyGroup>

35 changes: 35 additions & 0 deletions src/MonoMod.UnitTest/SizeofByRefLikeTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using MonoMod.Utils;
using System;
using Xunit;
using Xunit.Abstractions;

namespace MonoMod.UnitTest
{
public unsafe class SizeofByRefLikeTest : TestBase
{
public SizeofByRefLikeTest(ITestOutputHelper helper) : base(helper)
{
}

[Theory]
[InlineData(typeof(int))]
[InlineData(typeof(string))]
[InlineData(typeof(SizeofByRefLikeTest))]
[InlineData(typeof(ReadOnlySpan<char>))]
[InlineData(typeof(ReadOnlyMemory<char>))]
[InlineData(typeof(Span<byte>))]
[InlineData(typeof(void*))]
[InlineData(typeof(int*))]
[InlineData(typeof(int), true)]
[InlineData(typeof(string), true)]
[InlineData(typeof(object), true)]
public void SizeofDoesNotThrow(Type t, bool byref = false)
{
if (byref)
{
t = t.MakeByRefType();
}
_ = t.GetManagedSize();
}
}
}
19 changes: 18 additions & 1 deletion src/MonoMod.Utils/Extensions.Unsafe.cs
Original file line number Diff line number Diff line change
@@ -26,7 +26,9 @@ public static partial class Extensions
/// <param name="t">The type to get the size from.</param>
/// <returns>The managed type size.</returns>
public static int GetManagedSize(this Type t)
=> _GetManagedSizeCache.GetOrAdd(Helpers.ThrowIfNull(t), ComputeManagedSize);
=> Helpers.ThrowIfNull(t).IsByRef || t.IsPointer
? IntPtr.Size
: _GetManagedSizeCache.GetOrAdd(Helpers.ThrowIfNull(t), ComputeManagedSize);

private static int ComputeManagedSize(Type t)
{
@@ -36,9 +38,24 @@ private static int ComputeManagedSize(Type t)
_GetManagedSizeHelper = szHelper = typeof(Unsafe).GetMethod(nameof(Unsafe.SizeOf))!;
}

if (t.IsByRef || t.IsPointer || t.IsByRefLike())
{
// cannot instantiate a generic method on a byref, byreflike, or pointer type
return GenerateAndInvokeSizeofHelper(t);
}

return szHelper.MakeGenericMethod(t).CreateDelegate<Func<int>>()();
}

private static int GenerateAndInvokeSizeofHelper(Type t)
{
using var dmd = new DynamicMethodDefinition($"SizeOf<{t}>", typeof(int), []);
var il = dmd.GetILProcessor();
il.Emit(OpCodes.Sizeof, il.Import(t));
il.Emit(OpCodes.Ret);
return (int)dmd.Generate().Invoke(null, null)!;
}

/// <summary>
/// Get a type which matches what the method should receive via ldarg.0
/// </summary>
2 changes: 1 addition & 1 deletion src/MonoMod.Utils/MonoMod.Utils.csproj
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
<!-- Disable "Dispose objects before losing scope" because it utterly chokes on this project -->
<NoWarn>$(NoWarn);CA2000</NoWarn>

<VersionPrefix>25.0.7</VersionPrefix>
<VersionPrefix>25.0.8</VersionPrefix>
<PackageValidationBaselineVersion>25.0.0</PackageValidationBaselineVersion>
</PropertyGroup>

0 comments on commit 26a0a4d

Please sign in to comment.