Skip to content

Commit

Permalink
[mono] Use underlying type in RuntimeHelpers.GetSpanDataFrom (#87019)
Browse files Browse the repository at this point in the history
* [mono] Use underlying type in RuntimeHelpers.GetSpanDataFrom

Make it work correctly for spans of enums

Fixes #86865

Note that in net8 RuntimeHelpers.CreateSpan<T> is an intrinsic, so
GetSpanDataFrom is never called directly.

But in net7 CreateSpan is not intrinsified on Mono, so the underlying
method really does get called.

* test: Print all hidden field names if we can't find the right one
  • Loading branch information
lambdageek committed Jun 3, 2023
1 parent 9d55017 commit 75a1806
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/mono/mono/metadata/icall.c
Expand Up @@ -985,7 +985,7 @@ ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetSpanDataFrom (MonoCl
return NULL;
}

MonoType *type = targetTypeHandle;
MonoType *type = mono_type_get_underlying_type (targetTypeHandle);
if (MONO_TYPE_IS_REFERENCE (type) || type->type == MONO_TYPE_VALUETYPE) {
mono_error_set_argument (error, "array", "Cannot initialize array of non-primitive type");
return NULL;
Expand Down
70 changes: 70 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_86865/test86865.cs
@@ -0,0 +1,70 @@
using System;
using System.Reflection;

namespace test86865;

public class test86865
{
public static int Main()
{

// Regression test for https://github.com/dotnet/runtime/issues/86865
// Verify that the RuntimeHelpers.GetSpanDataFrom method underlying RuntimeHelpers.CreateSpan<T>
// works correctly with enums.

ReadOnlySpan<MyEnum> myEnums = new[]
{
MyEnum.A,
MyEnum.B,
MyEnum.C,
MyEnum.B,
MyEnum.C,
};

if (string.Join(", ", myEnums.ToArray()) != "A, B, C, B, C")
return 1;

var types = new Type[] {
typeof(RuntimeFieldHandle),
typeof(RuntimeTypeHandle),
typeof(int).MakeByRefType(),
};
var mi = typeof(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod("GetSpanDataFrom", BindingFlags.Static | BindingFlags.NonPublic, types);
if (mi == null)
return 2;

var pid = typeof(MyEnum).Assembly.GetType("<PrivateImplementationDetails>");
if (pid == null)
return 3;

var fi = pid.GetField("0B77DC554B4A81403D62BE25FB5404020AD451151D4203D544BF60E3FEDBD8AE4", BindingFlags.Static | BindingFlags.NonPublic);
if (fi == null)
{
Console.WriteLine("Could not find the expected array data in <PrivateImplementationDetails>. The available static non-public fields are:");
foreach (var f in pid.GetFields(BindingFlags.Static | BindingFlags.NonPublic)) {
Console.WriteLine($" - '{f}'");
}
return 4;
}

var parms = new object[] {
fi.FieldHandle,
typeof(MyEnum).TypeHandle,
new int()
};
var result = mi.Invoke(null, parms);
if (result == null)
return 6;
if ((int)parms[2] != myEnums.Length)
return 7;

return 100;
}
}

enum MyEnum
{
A,
B,
C
}
9 changes: 9 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_86865/test86865.csproj
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Include="test86865.cs" />
</ItemGroup>
</Project>

0 comments on commit 75a1806

Please sign in to comment.