Skip to content
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

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

Closed
Tracked by #75358
steveharter opened this issue May 12, 2022 · 41 comments · Fixed by #81006
Closed
Tracked by #75358

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

steveharter opened this issue May 12, 2022 · 41 comments · Fixed by #81006
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-System.Reflection breaking-change Issue or PR that represents a breaking API or functional change over a prerelease.
Milestone

Comments

@steveharter
Copy link
Member

steveharter commented May 12, 2022

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.

@steveharter steveharter added api-suggestion Early API idea and discussion, it is NOT ready for implementation breaking-change Issue or PR that represents a breaking API or functional change over a prerelease. labels May 12, 2022
@steveharter steveharter added this to the 7.0.0 milestone May 12, 2022
@steveharter steveharter self-assigned this May 12, 2022
@ghost
Copy link

ghost commented May 12, 2022

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

Issue Details

Background and motivation

This is the proposed API to support introspection of Function Pointers (delegate*) based on the user story and original issue.

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.

Corresponding support will also be added to MetadataLoadContext. The v7 version will have full support but the netstandard version, for simplicity, will likely throw NotSupportedException like it does today since there are new types and new System.Type members not exposed in netstandard.

This API issue is focused on introspection. Once this is in we can consider:

  • Support invoke such as by adding a DynamicInvoke(), by returning a bound Delegate or by returning a bound MethodInfo.
  • Being able to box for invoke parameter support. This means supporting the existing reflection invoke APIs by allowing a previously created IntPtr (by casting the function pointer) to be passed to a method that has a strongly-typed function pointer type argument, where it is unwound. This would be similar to how value type pointers (e.g. int*) can be passed as a boxed IntPtr (or System.Reflection.Pointer) to such methods.
    • Note that at runtime, outside of reflection, function pointers are still passed as either the strongly-typed pointer itself, a void* pointer or an IntPtr. This API issue does not expose a unified type+value class such as System.Reflection.Pointer although it could be added later.
  • Emit APIs to create a function pointer.

API Proposal

namespace System.Reflection
{
    // A function pointer is a stand-alone signature, meaning it does not have an independent,
    // reusable declaration like a Delegate.
+   public abstract class FunctionPointerInfo
+   {
+       internal FunctionPointerInfo(); // prevent external derivation
+
+       public abstract CallingConvention CallingConvention { get; }
+       public abstract ParameterInfo ReturnParameter { get; }
+       public abstract Type UnderlyingType { get; }
+       public abstract ReadOnlySpan<ParameterInfo> GetParameters();

        // Although each function pointer declaration will be its own instance (pending possible caching strategy)
        // we support Equals to compare against them.
+       public abstract bool Equals(object? obj);
+       public abstract int GetHashCode();
+       public abstract string? ToString();

        // Future invoke and MemberInfo scenarios:
        // public abstract unsafe object? DynamicInvoke(void* pfnTarget, params object?[]? args);
        // public abstract unsafe Delegate CreateDelegate(void* pfnTarget, Type delegateType);
+    }

    public class Type
    {
+       public virtual bool IsFunctionPointer { get; }

        // Throws InvalidOperationException if IsFunctionPointer is false.
+       public virtual FunctionPointerInfo GetFunctionPointerInfo();

        // Note that IsPointer will return 'false' for function pointers.
        public virtual bool IsPointer { get; }
    }

    // We are not re-using this as it exposes "Any". Ideally this could have been called "AnyManaged"
    // in which case we may have extended it.
    // Currently used in limited scenarios on MethodBase primarily to filter via GetMethods() and GetConstructor()
    [Flags]
    public enum CallingConventions
    {
        Standard = 0x1,
        VarArgs = 0x2,
        Any = 0x3,
        HasThis = 0x20,
        ExplicitThis = 0x40, // would be set for function pointers
    }

    // New CallingConvention that matches System.Reflection.Metadata.SignatureCallingConvention except uses Managed\Unmanaged prefixes
+   public enum CallingConvention : byte
+   {
+       Managed = 0,
+       UnmanagedCdecl = 1,
+       UnmanagedStdCall = 2,
+       UnmanagedThisCall = 3,
+       UnmanagedFastCall = 4,
+       ManagedVarArgs = 5, // not supported with function pointers
+       Unmanaged = 9,
+   }
}

API Usage

using System.Reflection;

namespace ConsoleApp
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // typeof:
            Type type = typeof(delegate*<void>);
            bool b = type.IsFunctionPointer(); // true
            FunctionPointerInfo fpi = Type.GetFunctionPointerInfo();
            string s = fpi.ToString(); // "void ()" (actual is TBD, but will list all parameters if there any)
            Type returnType = fpi.ReturnParameter.ParameterType; // System.Void

            // Also works with reflection:
            MethodInfo method = typeof(Util).GetMethod(nameof(Util.GetLog), BindingFlags.Public | BindingFlags.Instance)!;
            type = method.ReturnType;
            b = type.IsFunctionPointer(); // true
            fpi = Type.GetFunctionPointerInfo();
        }
    }

    unsafe public class Util
    {
        public static void Log() { Console.WriteLine("Log"); }

        public delegate*<void> GetLog()
        {
            return &Log;
        }
    }
}

Alternative Designs

  • A new MethodInfo-derived type.
  • A new System.Type-derived type.
  • A new System.Reflection.FunctionPointer type similar to System.Reflection.Pointer. This is still possible if we want to wrap the type and value together.

Risks

The breaking change.

Author: steveharter
Assignees: steveharter
Labels:

api-suggestion, area-System.Reflection, breaking-change

Milestone: 7.0.0

@AaronRobinsonMSFT
Copy link
Member

AaronRobinsonMSFT commented May 12, 2022

public abstract CallingConvention CallingConvention { get; }

Instead of reusing that enum, which has aged poorly, I would follow the pattern established by UnmanagedCallersOnlyAttribute.CallConvs and UnmanagedCallConvsAttribute.

@teo-tsirpanis
Copy link
Contributor

  • Perhaps we need a way to create FunctionPointerInfo members at runtime, like a FunctionPointerInfo.Create static method.
  • Type.GetFunctionPointerInfo() could return null if the FPI does not exist, requiring one call and zero potential exceptions, removing the need for Type.IsFunctionPointer, and matching MethodBase.GetMethodBody()'s shape.

@jkotas
Copy link
Member

jkotas commented May 12, 2022

Type.GetFunctionPointerInfo() could return null if the FPI does not exist,

If it does exist, it would end up allocating FunctionPointerInfo instance that the caller may not be interested at all. I think there is a value in having IsFunctionPointer method that just returns bool.

@MichalStrehovsky
Copy link
Member

MichalStrehovsky commented May 13, 2022

What's the motivation behind FunctionPointerInfo? There's no GenericParameterInfo, ArrayInfo, PointerInfo, ByReferenceInfo, or GenericInstanceInfo - the properties/methods that are only relevant for pareterized types/generic instances are dumped directly onto System.Type. It's up to the caller to decide whether the properties/methods make sense to call for a given System.Type instance.

Function pointers are constructed types like any other constructed type - why do we shape the reflection API differently?

@svick
Copy link
Contributor

svick commented May 14, 2022

@MichalStrehovsky

the properties/methods that are only relevant for pareterized types/generic instances are dumped directly onto System.Type

Personally, I dislike that design: it makes it harder to discover which members you need or to ensure you're using them correctly. To me, this proposal looks good, since it clearly separates the concept of function pointers from the rest of the API surface, while not being too out of place regarding the existing API (unlike e.g. a new System.Type-derived type).

So I see it as a question of choosing something that's better in isolation, but less consistent versus choosing something worse on its own, but more consistent.

@MichalStrehovsky
Copy link
Member

I'm not a fan of that design either but it has advantages in some cases. E.g. if I want a Type that tweaks one of the properties of an existing type, I would use a System.Refection.TypeDelegator and override that one property. It's unclear how TypeDelegator fits with FunctionPointerInfo. E.g. if I want the exact same function pointer type but with a different calling convention.

The reflection stack is built around dumping everything onto System.Type.

@steveharter
Copy link
Member Author

steveharter commented May 16, 2022

What's the motivation behind FunctionPointerInfo? There's no GenericParameterInfo, ArrayInfo, PointerInfo, ByReferenceInfo, or GenericInstanceInfo - the properties/methods that are only relevant for pareterized types/generic instances are dumped directly onto System.Type. It's up to the caller to decide whether the properties/methods make sense to call for a given System.Type instance.
Function pointers are constructed types like any other constructed type - why do we shape the reflection API differently?

Function pointers are more like methods and have more metadata than generics, arrays, existing pointer, etc so instead of adding directly to Type some options considered:

  1. Provide a new stand-alone FunctionPointerInfo type
  2. Extend MethodInfo - e.g. FunctionPointerInfo : MethodInfo.
  3. Extend MemberInfo - e.g. FunctionPointerInfo : MemberInfo.
  4. Extend Type with inheritance FunctionPointerType : Type

The stand-alone option has the least friction and doesn't pollute Type.

We may also want to add DynamicInvoke() capabilities, so having a separate type would also be used for that.

@steveharter
Copy link
Member Author

It's unclear how TypeDelegator fits with FunctionPointerInfo. E.g. if I want the exact same function pointer type but with a different calling convention.

Yes thanks to support that we would need to add a Type.GetFunctionPointerInfoImpl(...) similar to Type.GetMethodInfoImpl().

@MichalStrehovsky
Copy link
Member

MichalStrehovsky commented May 16, 2022

Function pointers are more like methods and have more metadata than generics, arrays, existing pointer, etc.

I think there might be some confusion about the amount of metadata function pointer carries. They really don't carry much.

Also the choice of ParameterInfo is odd - most of the properties on ParameterInfo don't make sense for function pointers - ParameterInfo.MetadataToken can only throw because function pointer parameters don't have tokens (they're not parameters in metadata sense). There's also no ParameterInfo.Member, or Name, or any of the other properties besides ParameterType and modifier-related APIs. The ParameterInfo.MemberImpl field is also not-nullable so it's unclear what value we would put there since there's no associated member.

Here's a list of APIs on Type that are only relevant if System.Type represents a generic parameter as an example of par for the course and why the argument of "polluting" doesn't hold.

  • DeclaringMethod
  • DeclaringType
  • GenericParameterAttributes
  • GenericParameterPosition
  • GetGenericParameterConstraints

I'm discussing pretty much just adding three properties to System.Type - one to get the information about return type, one to get information about parameters, and one for the calling convention:

class Type
{
       public abstract CallingConvention CallingConvention { get; }

       // Not actually proposing using ParameterInfo per above
       public abstract ParameterInfo ReturnParameter { get; }

       // Not ReadOnlySpan for consistency
       public abstract ParameterInfo[] GetParameters();
}

I understand we would also like to add "execution" APIs for this. Those should go on a separate class. There's prior art:

  • There's no Type.CreateInstance or Type.CreateArray. To "execute" a named type, one uses Activator. To "execute" an array, one uses Array.CreateInstance.
  • API proposal: Activator.CreateFactory and ConstructorInfo.CreateDelegate #36194 assumes the API to create a delegate to a constructor would go on Activator (a specialized type), not on Type, even though it could be placed on type.
  • There's no object Type.ReadPointer(IntPtr address) API to dereference a pointer if the System.Type is a pointer. One can e.g. use Marshal APIs.

The "execution" APIs never go on Type and we would keep it that way for function pointers.

Yes thanks to support that we would need to add a Type.GetFunctionPointerInfoImpl(...) similar to Type.GetMethodInfoImpl().

That would not address the problem in a consistent way. For the scenario I wrote above (just change calling convention), do I really need to reimplement all of FunctionPointerInfo? Why there's no convenient FuctionPointerInfoDelegator that works as TypeDelegator? As a reflection user, it's what I expect based on how these APIs work for everything else, when I just need to tweak a single aspect of the type.

@steveharter
Copy link
Member Author

Thanks for the detailed feedback @MichalStrehovsky. Per offline discussion, the original intent of FunctionPointerInfo was to hide new members on Type for a better experience, however for the following reasons the API proposal was updated to remove FunctionPointerInfo:

  • FunctionPointerInfo didn't fit in with previous changes such as to support generics, so, in effect it was overkill.
  • Type delegators would need their own pattern.
  • Future invocation APIs are probably best elsewhere, not in FunctionPointerInfo. Having separate APIs for introspection (e.g. Type class) vs. invocation is a positive pattern IMO.

@MichalStrehovsky
Copy link
Member

Thank you for the good offline discussion! I have a couple of observations for the new design:

  • Function pointer parameters cannot be named in the metadata. We can't return anything but null in FunctionPointerParameter.Name. It might be better not to introduce the member at this point.
  • Similarly, the In/Out bit cannot be expressed in the function pointer metadata either. They would always be false.
  • Should we add a property to FunctionPointerParameter to get to the owning type? ParameterInfo has the Member property that serves the purpose.
  • Is there a specific scenario for introduction of Equals/GetHashCode overrides? ParameterInfo doesn't have those.

For the FunctionPointerCallingConvention member, how will this look for delegate* unmanaged[Stdcall, SuppressGCTransition]<void>. Will the returned object be an array of two System.Type instances (one for CallConvSuppressGCTransition, the other for CallConvStdcall). Instead of typing FunctionPointerCallingConvention as object, could we type it as Type[] for parity with UnmanagedCallersOnlyAttribute.CallConvs?

@steveharter steveharter added blocking Marks issues that we want to fast track in order to unblock other important work api-ready-for-review API is ready for review, it is NOT ready for implementation and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation labels May 24, 2022
@steveharter
Copy link
Member Author

steveharter commented May 24, 2022

Thank you for the good offline discussion! I have a couple of observations for the new design:

Thanks. Removed Name, Equals, GetHashCode, IsIn, IsOut. Did not add "OwningType";' we could always add that later and I'm concerned with future asks for aliasing that this could either be null or prevent effective caching.

For the FunctionPointerCallingConvention member, ...

The FunctionPointerCallingConvention should have been Type[], not object. I also added some more comments around that. Basically FunctionPointerCallingConvention and FunctionPointerReturnParameter.GetOptionalCustomModifiers() will return the exact same CallConv* types however FunctionPointerReturnParameter.GetOptionalCustomModifiers() may return additional non-CallConv* mod opts. The idea is that FunctionPointerCallingConvention is a filter on top of FunctionPointerReturnParameter.GetOptionalCustomModifiers() and returns just the CallConv* types, and some of those types may be synthesized based on the non-exposed CallKind -- all to hide CallKind and prevent bugs related to misuse of that.

@jkotas
Copy link
Member

jkotas commented May 24, 2022

FunctionPointerReturnParameter.GetOptionalCustomModifiers() may return additional mod opts like CallConvSuppressGCTransition

CallConvSuppressGCTransition is also a CallConv* type. Did you mean to use a different example?

@steveharter
Copy link
Member Author

CallConvSuppressGCTransition is also a CallConv* type. Did you mean to use a different example?

Yep thanks. I just updated to say "non-CallConv" mod opts.

@steveharter steveharter changed the title [API Proposal]: System.Reflection.FunctionPointerInfo [API Proposal]: Reflection introspection support for FunctionPointer May 26, 2022
@bartonjs bartonjs added api-approved API was approved in API review, it can be implemented and removed blocking Marks issues that we want to fast track in order to unblock other important work api-ready-for-review API is ready for review, it is NOT ready for implementation labels May 26, 2022
@carlreinke
Copy link
Contributor

@bartonjs You forgot to hit "Comment".

@MichalStrehovsky
Copy link
Member

Watched the recording. If we're proposing IsPointer return true for function pointers, how are we going to reconcile it with places like this:

Will we always have to do IsPointer && !IsFunctionPointer? Or be very careful about the order in which we check the properties (treat function pointers first and the rest is what everyone else considers pointer types)?

Function pointers are not a subset of unmanaged pointers in the ECMA-335 spec either (page 44 of the PDF has a helpful hierarchy - function pointers are pointers same way as byrefs are pointers).

Do we also return true from HasElementType or do we only implement GetElementType to return void?

I'm not sure I agree with the argument that we need IsPointer to be true so that NetStandard serializers can use it to ignore function pointers. Desktop CLR is going to treat them as UIntPtr forever and IsPointer is false for those. We'll get inconsistent behavior no matter what.

@steveharter steveharter added the Cost:M Work that requires one engineer up to 2 weeks label Dec 5, 2022
@steveharter
Copy link
Member Author

For those interested in the design, please review dotnet/designs#282.

@steveharter steveharter added api-ready-for-review API is ready for review, it is NOT ready for implementation blocking Marks issues that we want to fast track in order to unblock other important work and removed Cost:M Work that requires one engineer up to 2 weeks api-needs-work API needs work before it is approved, it is NOT ready for implementation blocking Marks issues that we want to fast track in order to unblock other important work labels Jan 9, 2023
@steveharter
Copy link
Member Author

steveharter commented Jan 10, 2023

cc @MichalStrehovsky @GrabYourPitchforks @jkotas

We had a review today and @tannergooding was asking if Type.IsPointer returns true or not for function pointers. The short answer is the current design returns false. Long answer is that returning true was originally proposed early in the 7.0 design but then changed to false based on discussions:

  • Function pointers *() and pointers (*) are different types in the runtime and CLI and IsPointer was added for detecting the one and only pointer type, and not necessarily "any kind of pointer".
  • Existing code (which is all function-pointer agnostic today) such as if (type.IsPointer) may break if we return true and thus may need to changed to if (type.IsPointer && !type.IsFunctionPointer). However, since a function pointer value can be cast to a void* pointer, that is somewhat mitigated.

Tanner was suggesting that we do this (as was originally in the 7.0 proposal as well)

  • Have IsPointer return true for function pointers.
  • Have ElementType return typeof(void) for function pointers.

-or-

  • Add a IsPointerOrFunctionPointer to make it a bit more obvious what the semantics are if we keep IsPointer == false.

@jkotas
Copy link
Member

jkotas commented Jan 10, 2023

Have ElementType return typeof(void*) for function pointers.

Should that be typeof(void) instead? What would Type.HasElementType return for function pointers with this plan?

@MichalStrehovsky
Copy link
Member

I summed up my arguments about IsPointer here: #69273 (comment)

@steveharter
Copy link
Member Author

Should that be typeof(void) instead? What would Type.HasElementType return for function pointers with this plan?

Yes it should be void and not void*. Type.HasElementType would be true.

@jkotas
Copy link
Member

jkotas commented Jan 11, 2023

My vote is to keep POR proposal (IsPointer is false for function pointers), for similar reasons that Michal shared.

Add a IsPointerOrFunctionPointer

I do not see enough added clarity over type.IsPointer || type.IsFunctionPointer. It is very common in code that operates over reflection object model to have a bool || conditions like this.

@tannergooding
Copy link
Member

I do not see enough added clarity over type.IsPointer || type.IsFunctionPointer. It is very common in code that operates over reflection object model to have a bool || conditions like this.

The clarity is that when you look at IntelliSense, you see both IsPointer and IsPointerOrFunctionPointer side by side. This allows you to determine that IsPointer doesn't imply IsFunctionPointer, which is the natural presumption for such APIs (particularly if coming from other compilers that do model them this way).

@tannergooding
Copy link
Member

The same applies to docs and any other context where you might be looking at the available Is* APIs for Type

@jkotas
Copy link
Member

jkotas commented Jan 11, 2023

The same problem exists for IsByRef. Byrefs are pointers in the more general sense as well - they are called managed pointers in number of contexts. Reflection object model has many intricacies that are not discoverable using IntelliSense. One has to read the docs.

@tannergooding
Copy link
Member

The difference is they don't have pointer in the name for the API side of things, and so while one might expect IsPointer to return true, there isn't any implication.

However, IsPointer vs IsFunctionPointer being distinct and non-overlapping queries violates several existing conventions in our API surface/design guidelines and directly violates several intuitive assumptions people may immediately form when seeing them.

@MichalStrehovsky
Copy link
Member

People who don't read docs are going to interpret this in all sorts of way. There's prior art in Cecil (or CCI, or others). We can just look for how people use this.

E.g. here is someone puzzled why IsFunctionPointer doesn't return true for delegates:

https://github.com/partypooperarchive/DataDumper/blob/98e25bbdb62e45fa61b60f2d026bb765a527cea5/DataDumper/AssemblyParser.cs#L376

They're right, delegates are also conceptually function pointers. One needs to read docs...

@bartonjs
Copy link
Member

bartonjs commented Jan 17, 2023

Video

Looks good as proposed.

namespace System
{
    public partial 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 partial class FieldInfo
    {
       public virtual Type GetModifiedFieldType() => throw new NotSupportedException();
    }

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

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

@bartonjs bartonjs added api-approved API was approved in API review, it can be implemented and removed blocking Marks issues that we want to fast track in order to unblock other important work api-ready-for-review API is ready for review, it is NOT ready for implementation labels Jan 17, 2023
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Jan 24, 2023
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Feb 16, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Apr 14, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-System.Reflection breaking-change Issue or PR that represents a breaking API or functional change over a prerelease.
Projects
None yet