Skip to content

Instantly share code, notes, and snippets.

Last active January 29, 2021 22:17
Show Gist options
  • Save GrabYourPitchforks/68212a7c784132d333c8620271c8ff5a to your computer and use it in GitHub Desktop.
Save GrabYourPitchforks/68212a7c784132d333c8620271c8ff5a to your computer and use it in GitHub Desktop.
Draft function pointers in reflection spec

Draft function pointers in reflection spec

Passing function pointers as parameters to MethodInfo.Invoke

The reflection stack should treat function pointer arguments as standard pointer arguments for the purposes of method invocation. This means that boxed IntPtr arguments should be accepted as parameters to these methods in MethodInfo.Invoke scenarios. For methods which return function pointers, they should be returned as boxed IntPtrs.

The reflection stack, for convenience, should also consider allowing developers to pass System.Reflection.Pointer instances as input arguments to these methods.

Inspecting function pointer types

This assumes the existence of a unique Type object per function pointer. It does not cover the work required to make the C# typeof(delegate*<void>) syntax return an instance of such type.

Given a type that represents a function pointer, a caller is likely to perform 3 operations with it:

  1. Inspect its properties, including arguments, return type, and calling convention.
  2. Create a managed typed MulticastDelegate instance around a function pointer of this type.
  3. Invoke it directly without first assigning a MulticastDelegate instance.

(This latter scenario assumes dynamic invocation. If the exact type was known to the caller, it's expected they'd use a standard calli invocation.)

The Type class has a few existing APIs of interest to us.

  • Type.IsPointer : bool
  • Type.GetElementType() : Type?

The IsPointer property currently represents types like void* and int*. Callers can "unwrap" the pointer and retrieve the underlying type via the GetElementType API.

I propose the addition of new APIs to allow the inspection of function pointer types.

namespace System.Reflection
    public class Type
        public virtual bool IsFunctionPointer { get; }

        // this method throws if IsFunctionPointer returns false
        public virtual FunctionPointerInfo GetFunctionPointerInfo();

    public abstract class FunctionPointerInfo
        public abstract Type UnderlyingType { get; }

        public abstract ParameterInfo ReturnParameter { get; }

        public abstract ParameterInfo[] GetParameters();

        public abstract Type[] GetCallConvs();

        public abstract object? DynamicInvoke(void* pfnTarget, params object?[]? args);

        public abstract Delegate CreateDelegate(void* pfnTarget, Type delegateType);

The intent of moving these APIs onto a separate FunctionPointerInfo type is to prevent normal Type consumers from seeing these concepts and to allow for clustering of any future function pointer-related APIs.

The DynamicInvoke and CreateDelegate methods are expected to perform type safety checks where feasible. For instance, an info object representing a delegate*<int, void> should not accept a string for an argument. But if the info object represents a delegate*<delegate*<int, void>, void>, it might not be possible to validate that the IntPtr passed in represents a valid, compatible function pointer.

The DynamicInvoke and CreateDelegate methods aren't guaranteed to root any particular object. The caller must ensure the underlying void* is valid for the lifetime of the call or the lifetime of the returned delegate. (Similar constraint as the Marshal.GetDelegateForFunctionPointer API.)

The DynamicInvoke and CreateDelegate methods are expected to honor any mandatory calling convention. If the calling convention cannot be honored, the method must fail.

Potential consequences of these changes

Currently, if IsPointer returns true, GetElementType returns non-null. The addition of a function pointer concept will introduce the scenario where if IsPointer is true, GetElementType will only return non-null if IsFunctionPointer is false.

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