Skip to content

Comments

New function pointer APIs#123819

Open
jgh07 wants to merge 56 commits intodotnet:mainfrom
jgh07:issue-75348
Open

New function pointer APIs#123819
jgh07 wants to merge 56 commits intodotnet:mainfrom
jgh07:issue-75348

Conversation

@jgh07
Copy link
Contributor

@jgh07 jgh07 commented Jan 30, 2026

This implements #75348.

MakeFunctionPointerType is only implemented for CoreCLR, a Mono implementation will follow.

Copilot AI review requested due to automatic review settings January 30, 2026 22:34
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Jan 30, 2026
@dotnet-policy-service
Copy link
Contributor

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

Copy link
Contributor

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 implements new function pointer APIs as proposed in issue #75348, enabling better support for function pointer types in IL generation and reflection scenarios.

Changes:

  • Adds Type.MakeFunctionPointerSignatureType and Type.MakeModifiedSignatureType static factory methods for creating signature types
  • Implements ILGenerator.EmitCalli(Type functionPointerType) overload in ILGeneratorImpl for modern function pointer calling conventions
  • Adds new internal SignatureFunctionPointerType and SignatureModifiedType classes to represent these signature types

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
System.Runtime.cs Adds public API surface for new function pointer and modified signature type factory methods
Type.cs Implements MakeFunctionPointerSignatureType and MakeModifiedSignatureType factory methods with XML documentation
SignatureFunctionPointerType.cs New internal class representing function pointer signature types with support for calling conventions
SignatureModifiedType.cs New internal class representing types with required/optional custom modifiers
SignatureType.cs Changes UnderlyingSystemType from sealed to virtual to allow SignatureModifiedType to override it
ILGenerator.cs Adds new EmitCalli overload accepting function pointer Type with XML documentation
ILGeneratorImpl.cs Implements the new EmitCalli overload with stack tracking and signature token generation
SignatureHelper.cs Makes WriteSignatureForFunctionPointerType internal and adds logic to unwrap SignatureTypes
ModuleBuilderImpl.cs Adds GetFunctionPointerSignatureToken method to generate standalone signatures for calli instructions
DynamicILGenerator.cs Adds stub TODO implementation for EmitCalli - not yet functional
System.Reflection.Emit.ILGeneration.cs Adds public API surface for new EmitCalli overload
Strings.resx Adds error message for invalid function pointer type arguments
System.Private.CoreLib.Shared.projitems Adds new SignatureFunctionPointerType and SignatureModifiedType to project, plus unrelated whitespace fix
SignatureTypes.cs Adds tests for the new MakeFunctionPointerSignatureType and MakeModifiedSignatureType methods
AssemblySaveILGeneratorTests.cs Adds end-to-end test for EmitCalli with function pointer types
Utilities.cs Adds ClassWithFunctionPointer test helper class
Comments suppressed due to low confidence (1)

src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs:6

  • This using directive appears to be unused. There are no references to System.IO types in this file. Consider removing it to keep the code clean.
using System.Reflection.Metadata;

Copilot AI review requested due to automatic review settings February 2, 2026 13:32
Copy link
Contributor

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 21 out of 21 changed files in this pull request and generated 7 comments.

Copilot AI review requested due to automatic review settings February 2, 2026 17:42
Copy link
Contributor

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 24 out of 24 changed files in this pull request and generated 3 comments.

@jkotas
Copy link
Member

jkotas commented Feb 22, 2026

I think the calling conventions specified via the explicit arg should always win. I do not think we should be smart about merging. We can throw an exception for now and see whether it causes issues in practices. We can replace the exception with merging later if it proves to be necessary. So something like:

If there are modopt calling conventions on the return type, they have to match the (non-built in subset of) calling conventions passed via the explicit arg. If they do not match, throw an exception.
If there are no modopt calling conventions on the return type and there are calling conventions passed in via the explicit arg, wrap the return type to inject the calling conventions as needed.

Copilot AI review requested due to automatic review settings February 22, 2026 14:02
Copy link
Contributor

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 35 out of 35 changed files in this pull request and generated 3 comments.

Comment on lines +1376 to +1384
internal int GetFunctionPointerSignatureToken(Type functionPointerType)
{
BlobBuilder blobBuilder = new();
SignatureTypeEncoder encoder = new(blobBuilder);
MetadataSignatureHelper.WriteSignatureForFunctionPointerType(encoder, functionPointerType, this);

byte[] blob = blobBuilder.ToArray()[1..]; // Strip away ELEMENT_TYPE_FNPTR
return MetadataTokens.GetToken(_metadataBuilder.AddStandaloneSignature(_metadataBuilder.GetOrAddBlob(blob)));
}
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

GetFunctionPointerSignatureToken currently materializes the full blob to an array and then slices it ([1..]) to remove the leading ELEMENT_TYPE_FNPTR byte, which results in extra allocations (ToArray plus the slice copy). Consider emitting the standalone method signature blob without the ELEMENT_TYPE_FNPTR prefix in the first place (e.g., using a MethodSignatureEncoder / BlobEncoder directly, or writing into a second BlobBuilder starting at offset 1) to avoid the extra allocations when emitting many calli signatures.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings February 22, 2026 19:06
Copy link
Contributor

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 35 out of 35 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings February 23, 2026 16:32
Copy link
Contributor

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 36 out of 36 changed files in this pull request and generated 3 comments.

Comment on lines +676 to +678
foreach (Type callConv in callingConventions)
ArgumentNullException.ThrowIfNull(callConv, nameof(callingConventions));

Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

MakeFunctionPointerSignatureType currently only null-checks the callingConventions array elements, but doesn’t validate that they are actually calling-convention marker types (e.g., System.Runtime.CompilerServices.CallConv*). As-is, callers can pass arbitrary types (or signature types) and get a Type whose GetFunctionPointerCallingConventions() returns values that cannot be meaningfully encoded/round-tripped as calling conventions. Consider validating each entry (e.g., FullName starts with CallingConventionTypePrefix, not HasElementType, not ContainsGenericParameters, and not IsSignatureType) and throwing ArgumentException for invalid entries.

Suggested change
foreach (Type callConv in callingConventions)
ArgumentNullException.ThrowIfNull(callConv, nameof(callingConventions));
foreach (Type callConv in callingConventions)
{
ArgumentNullException.ThrowIfNull(callConv, nameof(callingConventions));
if (callConv.HasElementType ||
callConv.ContainsGenericParameters ||
callConv.IsSignatureType ||
callConv.FullName is not string fullName ||
!fullName.StartsWith(CallingConventionTypePrefix, StringComparison.Ordinal))
{
throw new ArgumentException(
"Each calling convention type must be a non-generic, non-signature, non-element type whose full name starts with System.Runtime.CompilerServices.CallConv.",
nameof(callingConventions));
}
}

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

@jgh07 jgh07 Feb 23, 2026

Choose a reason for hiding this comment

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

@jkotas Should we add this check after all? Since the modopt validation logic also assumes that the callingConventions elements adhere to this.

Copy link
Member

Choose a reason for hiding this comment

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

I guess we can add it. It is easier to relax the validation after the fact if it proves to be problematic.

Copilot AI review requested due to automatic review settings February 23, 2026 17:12
Copy link
Contributor

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 36 out of 36 changed files in this pull request and generated 2 comments.

Comment on lines +710 to +716
else if (callConvInModOpts)
{
if (callConvIndex != callingConventions.Length)
throw new ArgumentException(SR.FunctionPointer_CallingConventionsUnbalanced, nameof(returnType));

callConvsFinished = true;
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
else if (callConvInModOpts)
{
if (callConvIndex != callingConventions.Length)
throw new ArgumentException(SR.FunctionPointer_CallingConventionsUnbalanced, nameof(returnType));
callConvsFinished = true;
}

I think it is ok to allow calling conventions to be interleaved with other modopts.

Copy link
Member

Choose a reason for hiding this comment

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

I think we can use callConvIndex != 0 instead of callConvInModOpts, and delete callConvInModOpts if you implement this suggestion.

Comment on lines +697 to +703
if (callConvsFinished)
throw new ArgumentException(SR.FunctionPointer_CallingConventionsUnbalanced, nameof(returnType));

callConvInModOpts = true;

if (builtInCallConv && IsBuiltInCallingConvention(typeName))
throw new ArgumentException(SR.FunctionPointer_CallingConventionsUnbalanced, nameof(returnType));
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (callConvsFinished)
throw new ArgumentException(SR.FunctionPointer_CallingConventionsUnbalanced, nameof(returnType));
callConvInModOpts = true;
if (builtInCallConv && IsBuiltInCallingConvention(typeName))
throw new ArgumentException(SR.FunctionPointer_CallingConventionsUnbalanced, nameof(returnType));
if (builtInCallConv)
throw new ArgumentException(SR.FunctionPointer_CallingConventionsUnbalanced, nameof(returnType));

I do not think we need to re-check the typeName here. If we have a built-in callconv, we cannot have any callconvs in modopts.

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

Labels

area-System.Reflection 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.

5 participants