Skip to content

Conversation

jgh07
Copy link

@jgh07 jgh07 commented Sep 21, 2025

This fixes #111003. The current implementation is a work in progress, only managed function pointers with no modopts are supported.

I imagine adding support for unmanaged calling conventions and custom modifiers will be a bit more challenging, since I cannot use Type.GetFunctionPointerCallingConventions et al.

@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Sep 21, 2025
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-reflection-emit
See info in area-owners.md if you want to be subscribed.

@jgh07
Copy link
Author

jgh07 commented Sep 21, 2025

@jgh07 please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.

@dotnet-policy-service agree [company="{your company}"]

Options:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@dotnet-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@dotnet-policy-service agree company="Microsoft"

Contributor License Agreement

@dotnet-policy-service agree

@jgh07
Copy link
Author

jgh07 commented Oct 13, 2025

I've now added support for unmanaged calling conventions and custom modifiers, altough there are still some limitations which I outlined in the original issue (#111003).

Fully supporting all possible variants of function pointer types will require updates of the reflection API, as far as I'm concerned. Still, my implementation should support everything that C# currently supports, which is why I am opening the PR for review.

I understand if this PR cannot be merged until there is comprehensive support for the feature, altough in my opinion it is already 90% of the way there in terms of support for real-life scenarios.

@jgh07 jgh07 marked this pull request as ready for review October 13, 2025 16:56
@Copilot Copilot AI review requested due to automatic review settings October 13, 2025 16:56
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for function pointer types in System.Reflection.Emit by implementing signature generation for function pointers. The implementation currently supports managed function pointers without custom modifiers (modopt), with unmanaged calling conventions and custom modifiers planned for future work.

Key changes:

  • Added function pointer type detection and handling in signature generation
  • Implemented calling convention mapping for function pointers
  • Added support for custom modifiers on function pointer return types and parameters

@jgh07 jgh07 requested a review from Copilot October 13, 2025 17:00
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

@jkotas
Copy link
Member

jkotas commented Oct 13, 2025

Could you please add tests as well?

@jgh07
Copy link
Author

jgh07 commented Oct 13, 2025

Could you please add tests as well?

Done.

{
foreach (Type conv in conventions)
{
if (conv == typeof(CallConvCdecl))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PersistedAssemblyBuilder should not assume that the types are from the runtime reflection type universe. This should match the types by name to handle any type universes.

Here is an example of prior art:

protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
{
// Handle pseudo custom attributes
switch (con.ReflectedType!.FullName)
{
case "System.Runtime.InteropServices.StructLayoutAttribute":
ParseStructLayoutAttribute(con, binaryAttribute);
return;
case "System.Runtime.CompilerServices.SpecialNameAttribute":
_attributes |= TypeAttributes.SpecialName;
return;
case "System.SerializableAttribute":
#pragma warning disable SYSLIB0050 // 'TypeAttributes.Serializable' is obsolete: 'Formatter-based serialization is obsolete and should not be used'.
_attributes |= TypeAttributes.Serializable;
#pragma warning restore SYSLIB0050
return;
case "System.Runtime.InteropServices.ComImportAttribute":
_attributes |= TypeAttributes.Import;
return;
case "System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeImportAttribute":
_attributes |= TypeAttributes.WindowsRuntime;
return;
case "System.Security.SuppressUnmanagedCodeSecurityAttribute": // It says has no effect in .NET Core, maybe remove?
_attributes |= TypeAttributes.HasSecurity;
break;
}

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be useful to have tests that validate one assembly can reference function pointer field in other assembly?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. I modified my repro from the original issue and added it as a test.

@jgh07 jgh07 requested a review from jkotas October 16, 2025 16:35
@jkotas
Copy link
Member

jkotas commented Oct 17, 2025

The new test is failing:

   System.Reflection.Emit.Tests.AssemblySaveTypeBuilderTests.ConsumeFunctionPointerFields [FAIL]
      System.IO.FileNotFoundException : Could not load file or assembly 'Assembly1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
      
      Stack Trace:
           at Program.Main()
           at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
           at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)

// }

TempFile assembly2Path = TempFile.Create();
Assembly assembly1FromDisk = Assembly.LoadFile(assembly1Path.Path);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use TestAssemblyLoadContext, similar to

TestAssemblyLoadContext tlc = new TestAssemblyLoadContext();
tlc.LoadFromAssemblyPath(file.Path);
Type typeFromDisk = tlc.LoadFromAssemblyPath(file2.Path).GetType("Type2");

Copy link
Author

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™.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.Reflection.Emit community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ILGenerator does not properly handle function pointers

2 participants