-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Background and motivation
The UnsafeAccessorAttribute
mechanism was designed to provide access to a non-visible static or instance member (that is, method or field). There are however limitations with this design when involving static
types or accessing members on non-visible types. This attribute helps bridge that gap.
Consider the following two scenarios involving methods. Note that fields suffer from the same issue.
Scenario 1 - Private type
// Assembly A
private class C
{
private static int Method(int a) { ... }
}
// Assembly B
[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name="Method")]
static extern int CallMethod(??? c, int a); // One cannot write type C due to visibility.
Scenario 2 - Static type
// Assembly A
public static class C
{
private static int Method(int a) { ... }
}
// Assembly B
[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name="Method")]
static extern int CallMethod(??? c, int a); // A static class cannot be used as a parameter in a signature.
Scenario 3 - Private class parameters
// Assembly A
public class C
{
private class D { }
private static int Method(D d) { ... }
}
// Assembly B
[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name="Method")]
static extern int CallMethod(??? d); // Unable express D type as a parameter.
API Proposal
The following attribute would accept a fully qualified or partially qualified type name to use for member look-up. The string supplied via the typeName
would be in a format that is accepted by Type.GetType(string typeName)
. This API will be specifcally limited by what that API is capable of. There may be additional restrictions for the initial implementation of this. For example, it only works on reference types for now.
namespace System.Runtime.CompilerServices;
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = false, Inherited = false)]
public sealed class UnsafeAccessorTypeAttribute : Attribute
{
/// <summary>
/// Instantiates an <see cref="UnsafeAccessorTypeAttribute"/> providing access to a type supplied by <paramref name="typeName"/>.
/// </summary>
/// <param name="typeName">A fully qualified or partially qualified type name.</param>
public UnsafeAccessorTypeAttribute(string typeName)
{
TypeName = typeName;
}
/// <summary>
/// Fully qualified or partially qualified type name to target.
/// </summary>
public string TypeName { get; }
}
API Usage
Scenario 1 - Private type
// Assembly A
private class C
{
private static int Method(int a) { ... }
}
// Assembly B
[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name="Method")]
static extern int CallMethod([UnsafeAccessorType("C, A, Version=1.0.0.0, Culture=neutral")] object c, int a);
Scenario 2 - Static type
// Assembly A
public static class C
{
private static int Method(int a) { ... }
}
// Assembly B
[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name="Method")]
static extern int CallMethod([UnsafeAccessorType("C, A")] object? c, int a);
Scenario 3 - Private class parameters
// Assembly A
public class C
{
private class D { }
private static int Method(D d) { ... }
}
// Assembly B
[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name="Method")]
static extern int CallMethod([UnsafeAccessorType("C, A")] object? c, [UnsafeAccessorType("C+D, A")] object d); // Use attribute to look up type
Alternative Designs
Expand the UnsafeAccessorAttribute
attribute to have an optional type target field/property. Note This API option wouldn't address scenario (3).
namespace System.Runtime.CompilerServices;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public sealed class UnsafeAccessorAttribute : Attribute
{
+ /// <summary>
+ /// Fully qualified or partially qualified type name to target.
+ /// </summary>
+ public string TargetTypeName { get; set; }
}
Risks
No response