-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Support function pointer types in System.Reflection.Emit
#119935
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 10 commits
2ceb67e
34b17f9
1df4ed5
3c6a62e
3b8c5a5
1968af0
3d9357c
07adab0
0b61dda
b322aa8
6d7dbee
848b86b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -7,6 +7,8 @@ | |||||||
using System.Linq; | ||||||||
using System.Reflection.Metadata; | ||||||||
using System.Reflection.PortableExecutable; | ||||||||
using System.Runtime.CompilerServices; | ||||||||
using System.Runtime.InteropServices; | ||||||||
using Xunit; | ||||||||
|
||||||||
namespace System.Reflection.Emit.Tests | ||||||||
|
@@ -789,6 +791,143 @@ public void CreateGenericTypeFromMetadataLoadContextSignatureTypes() | |||||||
Assert.Equal("ValueTypeChildren", fields[1].Name); | ||||||||
Assert.True(fields[1].FieldType.GetGenericArguments()[0].IsValueType); | ||||||||
} | ||||||||
|
||||||||
[Fact] | ||||||||
public void SaveFunctionPointerFields() | ||||||||
{ | ||||||||
using TempFile file = TempFile.Create(); | ||||||||
using MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver()); | ||||||||
|
||||||||
PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyAndModule(out ModuleBuilder mb); | ||||||||
TypeBuilder tb = mb.DefineType("TestType", TypeAttributes.Public | TypeAttributes.Class); | ||||||||
|
||||||||
// delegate*<int, int> | ||||||||
Type funcPtr1 = typeof(delegate*<int, int>); | ||||||||
tb.DefineField("FuncPtr1", funcPtr1, FieldAttributes.Public | FieldAttributes.Static); | ||||||||
|
||||||||
// delegate* unmanaged[Cdecl]<int, float, double> | ||||||||
Type funcPtr4 = new ModifiedTypeHelpers.FunctionPointer( | ||||||||
typeof(delegate* unmanaged[Cdecl]<int, float, double>), | ||||||||
[typeof(CallConvCdecl)]); | ||||||||
tb.DefineField("FuncPtr2", funcPtr4, FieldAttributes.Public | FieldAttributes.Static); | ||||||||
|
||||||||
// delegate* unmanaged[Stdcall]<string, in int, void> | ||||||||
Type funcPtr5 = new ModifiedTypeHelpers.FunctionPointer( | ||||||||
typeof(delegate* unmanaged[Stdcall]<string, in int, void>), | ||||||||
[typeof(CallConvStdcall)], | ||||||||
customParameterTypes: [typeof(string), new ModifiedTypeHelpers.ModifiedType(typeof(int).MakeByRefType(), [typeof(InAttribute)], [])]); | ||||||||
tb.DefineField("FuncPtr3", funcPtr5, FieldAttributes.Public | FieldAttributes.Static); | ||||||||
|
||||||||
tb.CreateType(); | ||||||||
ab.Save(file.Path); | ||||||||
|
||||||||
Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); | ||||||||
Type testType = assemblyFromDisk.Modules.First().GetType("TestType"); | ||||||||
Assert.NotNull(testType); | ||||||||
|
||||||||
FieldInfo field1 = testType.GetField("FuncPtr1"); | ||||||||
Assert.NotNull(field1); | ||||||||
Assert.True(field1.FieldType.IsFunctionPointer); | ||||||||
Assert.False(field1.FieldType.IsUnmanagedFunctionPointer); | ||||||||
Type[] paramTypes1 = field1.FieldType.GetFunctionPointerParameterTypes(); | ||||||||
Assert.Equal(1, paramTypes1.Length); | ||||||||
Assert.Equal(typeof(int).FullName, paramTypes1[0].FullName); | ||||||||
Assert.Equal(typeof(int).FullName, field1.FieldType.GetFunctionPointerReturnType().FullName); | ||||||||
|
||||||||
FieldInfo field2 = testType.GetField("FuncPtr2"); | ||||||||
Type field2Type = field2.GetModifiedFieldType(); | ||||||||
Assert.NotNull(field2); | ||||||||
Assert.True(field2Type.IsFunctionPointer); | ||||||||
Assert.True(field2Type.IsUnmanagedFunctionPointer); | ||||||||
Type[] paramTypes2 = field2Type.GetFunctionPointerParameterTypes(); | ||||||||
Assert.Equal(2, paramTypes2.Length); | ||||||||
Assert.Equal(typeof(int).FullName, paramTypes2[0].FullName); | ||||||||
Assert.Equal(typeof(float).FullName, paramTypes2[1].FullName); | ||||||||
Assert.Equal(typeof(double).FullName, field2Type.GetFunctionPointerReturnType().FullName); | ||||||||
Type[] callingConventions2 = field2Type.GetFunctionPointerCallingConventions(); | ||||||||
Assert.Contains(callingConventions2, t => t.FullName == typeof(CallConvCdecl).FullName); | ||||||||
|
||||||||
FieldInfo field3 = testType.GetField("FuncPtr3"); | ||||||||
Type field3Type = field3.GetModifiedFieldType(); | ||||||||
Assert.NotNull(field3); | ||||||||
Assert.True(field3Type.IsFunctionPointer); | ||||||||
Assert.True(field3Type.IsUnmanagedFunctionPointer); | ||||||||
Type[] paramTypes3 = field3Type.GetFunctionPointerParameterTypes(); | ||||||||
Assert.Equal(2, paramTypes3.Length); | ||||||||
Assert.Equal(typeof(string).FullName, paramTypes3[0].FullName); | ||||||||
Assert.Equal(typeof(int).MakeByRefType().FullName, paramTypes3[1].FullName); | ||||||||
Assert.Contains(paramTypes3[1].GetRequiredCustomModifiers(), t => t.FullName == typeof(InAttribute).FullName); | ||||||||
Assert.Equal(typeof(void).FullName, field3Type.GetFunctionPointerReturnType().FullName); | ||||||||
Type[] callingConventions3 = field3Type.GetFunctionPointerCallingConventions(); | ||||||||
Assert.Contains(callingConventions3, t => t.FullName == typeof(CallConvStdcall).FullName); | ||||||||
jkotas marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
} | ||||||||
|
||||||||
[Fact] | ||||||||
public void ConsumeFunctionPointerFields() | ||||||||
{ | ||||||||
// public unsafe class Container | ||||||||
// { | ||||||||
// public static delegate*<int, int, int> Method; | ||||||||
// | ||||||||
// public static int Add(int a, int b) => a + b; | ||||||||
// public static void Init() => Method = &Add; | ||||||||
// } | ||||||||
|
||||||||
TempFile assembly1Path = TempFile.Create(); | ||||||||
PersistedAssemblyBuilder assembly1 = new(new AssemblyName("Assembly1"), typeof(object).Assembly); | ||||||||
ModuleBuilder mod1 = assembly1.DefineDynamicModule("Module1"); | ||||||||
TypeBuilder containerType = mod1.DefineType("Container", TypeAttributes.Public | TypeAttributes.Class); | ||||||||
FieldBuilder methodField = containerType.DefineField("Method", typeof(delegate*<int, int, int>), FieldAttributes.Public | FieldAttributes.Static); | ||||||||
MethodBuilder addMethod = containerType.DefineMethod("Add", MethodAttributes.Public | MethodAttributes.Static); | ||||||||
addMethod.SetParameters(typeof(int), typeof(int)); | ||||||||
addMethod.SetReturnType(typeof(int)); | ||||||||
ILGenerator addMethodIL = addMethod.GetILGenerator(); | ||||||||
addMethodIL.Emit(OpCodes.Ldarg_0); | ||||||||
addMethodIL.Emit(OpCodes.Ldarg_1); | ||||||||
addMethodIL.Emit(OpCodes.Add); | ||||||||
addMethodIL.Emit(OpCodes.Ret); | ||||||||
MethodBuilder initMethod = containerType.DefineMethod("Init", MethodAttributes.Public | MethodAttributes.Static); | ||||||||
initMethod.SetReturnType(typeof(void)); | ||||||||
ILGenerator initMethodIL = initMethod.GetILGenerator(); | ||||||||
initMethodIL.Emit(OpCodes.Ldftn, addMethod); | ||||||||
initMethodIL.Emit(OpCodes.Stsfld, methodField); | ||||||||
initMethodIL.Emit(OpCodes.Ret); | ||||||||
containerType.CreateType(); | ||||||||
assembly1.Save(assembly1Path.Path); | ||||||||
|
||||||||
// class Program | ||||||||
// { | ||||||||
// public static int Main() | ||||||||
// { | ||||||||
// Container.Init(); | ||||||||
// return Container.Method(2, 3); | ||||||||
// } | ||||||||
// } | ||||||||
|
||||||||
TempFile assembly2Path = TempFile.Create(); | ||||||||
Assembly assembly1FromDisk = Assembly.LoadFile(assembly1Path.Path); | ||||||||
|
TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); | |
tlc.LoadFromAssemblyPath(file.Path); | |
Type typeFromDisk = tlc.LoadFromAssemblyPath(file2.Path).GetType("Type2"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in the newest commit.
Hope the test runs now, it worked on my machine™.
Uh oh!
There was an error while loading. Please reload this page.