-
Notifications
You must be signed in to change notification settings - Fork 5.3k
New function pointer APIs #123819
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?
New function pointer APIs #123819
Conversation
…, ILGeneratorImpl.EmitCalli(Type)
|
Tagging subscribers to this area: @dotnet/area-system-reflection |
| public abstract void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type? returnType, Type[]? parameterTypes); | ||
|
|
||
| /// <summary> | ||
| /// Puts a <see cref="OpCodes.Calli"/> instruction onto the Microsoft intermediate language (MSIL) stream, |
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.
I kept this description largely the same as the existing overloads. I think it might make more sense to update it though, since as far as I'm concerned the term MSIL was dropped in favor of CIL.
| public sealed override int GetHashCode() => base.GetHashCode(); | ||
| #endif | ||
| public sealed override Type UnderlyingSystemType => this; // Equals(Type) depends on this. | ||
| public override Type UnderlyingSystemType => this; // Equals(Type) depends on this. |
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.
This feels very wrong, and I hope there is a better way.
The comment is there for a reason, and it does indeed break Equals(Type) for SignatureModifiedType, but I honestly couldn't think of a better way to make the original, non-signature type available.
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.
I cannot think of anything better.
| WriteSignatureForType(retTypeEncoder.Type(), returnType, module); | ||
| Type returnTypeToWrite = returnType; | ||
| if (returnTypeToWrite.IsSignatureType) | ||
| returnTypeToWrite = returnTypeToWrite.UnderlyingSystemType; |
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.
Not a no-op because of my override hack in SignatureModifiedType. I'd love to remove this in favor of a better solution.
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.
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.MakeFunctionPointerSignatureTypeandType.MakeModifiedSignatureTypestatic 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;
| public override Type[] GetFunctionPointerCallingConventions() => _callingConventions; | ||
| public override Type[] GetFunctionPointerParameterTypes() => _parameterTypes; |
Copilot
AI
Jan 30, 2026
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.
These methods return the internal arrays directly without cloning, which allows callers to mutate the internal state. This violates encapsulation and could lead to unexpected behavior. These methods should return clones of the arrays, similar to how SignatureConstructedGenericType.GenericTypeArguments clones the array before returning it.
| public override Type[] GetFunctionPointerCallingConventions() => _callingConventions; | |
| public override Type[] GetFunctionPointerParameterTypes() => _parameterTypes; | |
| public override Type[] GetFunctionPointerCallingConventions() => (Type[])_callingConventions.Clone(); | |
| public override Type[] GetFunctionPointerParameterTypes() => (Type[])_parameterTypes.Clone(); |
| public override Type[] GetRequiredCustomModifiers() => _requiredCustomModifiers; | ||
| public override Type[] GetOptionalCustomModifiers() => _optionalCustomModifiers; |
Copilot
AI
Jan 30, 2026
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.
This method returns the internal arrays directly without cloning, which allows callers to mutate the internal state. This violates encapsulation and could lead to unexpected behavior. These methods should return clones of the arrays, similar to how SignatureConstructedGenericType.GenericTypeArguments clones the array before returning it.
| public override Type[] GetRequiredCustomModifiers() => _requiredCustomModifiers; | |
| public override Type[] GetOptionalCustomModifiers() => _optionalCustomModifiers; | |
| public override Type[] GetRequiredCustomModifiers() => (Type[])_requiredCustomModifiers.Clone(); | |
| public override Type[] GetOptionalCustomModifiers() => (Type[])_optionalCustomModifiers.Clone(); |
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
Show resolved
Hide resolved
| /// <returns>A <see cref="Type"/> object representing the constructed function pointer signature.</returns> | ||
| public static Type MakeFunctionPointerSignatureType(Type? returnType, Type[]? parameterTypes, bool isUnmanaged = false, Type[]? callingConventions = null) | ||
| { | ||
| returnType ??= typeof(void); |
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.
MakeFunctionPointerSignatureType is meant to work for System.Type from any reflection universe. I do not think we want to be substituting null with typeof(void). It would make it tied to runtime reflection universe.
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.
We may want to make the returnType argument non-nullable to avoid this substitution.
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.
I agree, I think that's sensible.
This implements #75348. It is not yet ready to be merged, but I'd appreciate a preliminary review before I tackle the remaining issues.
Type.MakeFunctionPointerSignatureTypeandType.MakeModifiedSignatureTypeare implemented.Type.MakeFunctionPointerTypeis not implemented yet. I am still debating wheter to include it in this PR.EmitCalliis currently only implemented onILGeneratorImpl.DynamicILGeneratoris a wholly different code base, therefore I cannot make use of the helpers I added in Support function pointer types inSystem.Reflection.Emit#119935. I might have to reimplement parts of the logic I did for that PR.