-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Background and motivation
.NET has supported reflection introspection of function pointer types since .NET 8. This feature works well for existing type definitions, but currently there is no built-in way to construct System.Type instances representing arbitrary unmanaged function pointer types. This has implications in runtime code generation (System.Reflection.Emit), where it is currently difficult and unergonomic to define fields or locals of function pointer types.
Similarly, the EmitCalli method on ILGenerator is inadequately equipped for modern combinations of calling conventions on function pointers, due to the lack of flexibility of the CallingConvention enumeration. The proposed new overload modernizes this API and ensures it remains future-proof as new calling conventions get added to the runtime.
API Proposal
namespace System;
public abstract class Type
{
// Existing API
public static Type MakeGenericSignatureType(Type genericTypeDefinition, params System.Type[] typeArguments);
// Create function pointer signature type. It has functionality limited to what's required to represent signatures like other signature types.
+ public static Type MakeFunctionPointerSignatureType(Type? returnType, Type[]? parameterTypes, bool isUnmanaged = false, Type[]? callingConventions = null);
// Create modified signature type. It has functionality limited to what's required to represent signatures like other signature types.
+ public static Type MakeModifiedSignatureType(Type type, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers);
// Existing API
public virtual Type MakeGenericType(params Type[] typeArguments) => throw new NotSupportedException(SR.NotSupported_SubclassOverride);
// Create full-featured function pointer type from the given type universe (`this` is the function pointer return type)
+ public virtual Type MakeFunctionPointerType(Type[]? parameterTypes, bool isUnmanaged = false) => throw new NotSupportedException(SR.NotSupported_SubclassOverride);
}
namespace System.Reflection.Emit;
public abstract class ILGenerator
{
// Existing APIs
public abstract void Emit(OpCode opcode, SignatureHelper signature);
public abstract void EmitCalli(OpCode opcode, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes, Type[]? optionalParameterTypes);
public abstract void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type? returnType, Type[]? parameterTypes);
// Emit calli opcode with the signature specified by the function pointer type
// Q: Should the signature have OpCode opcode argument for consistency with the existing APIs even though the only valid opcode is OpCode.Calli
+ public virtual void EmitCalli(System.Type functionPointerType) => throw new NotImplementedException();
}API usage
Type fieldType = Type.MakeFunctionPointerSignatureType(
returnType: typeof(bool),
parameterTypes: [typeof(int), typeof(string)],
isUnmanaged: true,
callingConventions: [typeof(CallConvCDecl), typeof(CallConvSuppressGCTransition)]);
TypeBuilder tb = ...;
tb.DefineField("MyField", fieldType, ...);
// ...
ILGenerator il = ...;
il.EmitCalli(fieldType);Notes
- The new overload of
EmitCalliwill not be supported by the legacyAssemblyBuilderimplementation. It will only be implemented onDynamicILGeneratorandILGeneratorImpl.