Skip to content

[API Proposal]: Reflection introspection support for FunctionPointer #69273

@steveharter

Description

@steveharter

Background and motivation

This is the proposed API to support introspection of Function Pointers (delegate*) based on the user story, original issue and function pointer design. In summary, when function pointers were added in 5.0 the corresponding support for reflection was not added which resulted in an IntPtr being returned as the type when using typeof or reflection. This feature changes that to now return System.Type which then allows access to function pointer metadata including the calling convention, return type and parameters. This is a breaking change.

UPDATE: the original approved API for .NET 7 was not implemented due to concerns with breaking scenarios with managed C++ and adding unnecessary runtime overhead. This .NET 8 proposal changes the design to return "modified types" that expose custom modifiers on function pointers instead of exposing them on runtime-based types.

Corresponding support will also be added to MetadataLoadContext. The inbox version will have full support but the netstandard version, for simplicity, will throw NotSupportedException for the new members that return new types (since they are not in netstandard). For members that don't return new types, it will be possible to use reflection to invoke them.

This API issue is focused on introspection; the links above have additional features that can be layered on this including support for IL Emit.

API Proposal

namespace System
{
    public abstract class Type
    {
+       public virtual bool IsFunctionPointer { get; }
+       public virtual bool IsUnmanagedFunctionPointer { get; }

        // These throw InvalidOperationException if IsFunctionPointer = false:
+       public virtual Type GetFunctionPointerReturnType();
+       public virtual Type[] GetFunctionPointerParameterTypes();

        // These require a "modified type" to return custom modifier types:
+       public virtual Type[] GetRequiredCustomModifiers();
+       public virtual Type[] GetOptionalCustomModifiers();
+       public virtual Type[] GetFunctionPointerCallingConventions(); // Throws if IsFunctionPointer = false
    }
}

// Return a "modified type" from a field, property or parameter if its type is a:
// - Function pointer type
// - Pointer or array since they may reference a function pointer
// - Parameter or return type from a function pointer
namespace System.Reflection
{
    public abstract class FieldInfo
    {
+       public virtual Type GetModifiedFieldType() => throw new NotSupportedException();
    }

    public abstract class PropertyInfo
    {
+       public virtual Type GetModifiedPropertyType() => throw new NotSupportedException();
    }

    public abstract class ParameterInfo
    {
+       public virtual Type GetModifiedParameterType() => throw new NotSupportedException();
    }
}

A modified type's UnderlyingSystemType property returns the unmodified type and all members on a modified forward to that except:

  • GetRequiredCustomModifiers()
  • GetOptionalCustomModifiers()
  • GetFunctionPointerCallingConventions()
  • GetFunctionPointerParameterTypes()
  • GetFunctionPointerReturnType()

which instead return the information kept on the modified type.

API Usage

See the design referenced above. Here's some short examples:

Type type = typeof(delegate*<int, bool>);
bool b = type.IsFunctionPointer(); // true
string s = type.ToString(); // "System.Boolean(System.Int32)"
Type returnType = type.GetFunctionPointerReturnType(); // System.Boolean
Type param1Type = type.GetFunctionPointerParameterTypes()[0]; // System.Int32

FieldInfo fieldInfo = typeof(Util).GetField("_field")!;
bool isUnmanaged = fieldInfo.FieldType.IsUnmanagedFunctionPointer; // true

// Get the modified type in order to obtain the custom modifiers
Type modifiedType = fieldInfo.GetModifiedFieldType();

// Get the unmanaged calling conventions
type = modifiedType.GetFunctionPointerCallingConventions()[0]; // CallConvCdecl

// Get the first custom modifier from the first parameter
type = modifiedType.GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers()[0]; // OutAttribute

unsafe public class Util
{
    public delegate* unmanaged[CDecl]<out bool, void>; _field;
}

Risks

The breaking change nature of not returning IntPtr any longer.

Metadata

Metadata

Assignees

Labels

api-approvedAPI was approved in API review, it can be implementedarea-System.Reflectionbreaking-changeIssue or PR that represents a breaking API or functional change over a prerelease.

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions