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

Draft: Add support for by-ref-like (ref struct) parameter types such as Span<T> and ReadOnlySpan<T> #664

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

stakx
Copy link
Member

@stakx stakx commented Sep 2, 2023

This is a partially working proof of concept for #663, but it still needs quite a bit of work:

  • verify that converter types implement the implicit protocol correctly before emitting code that invokes them
  • perform value conversion for inbound by-ref-like values (in by-value and in method parameters)
  • perform value conversion for outbound by-ref-like values (in method return values, ref and out parameters)
  • add unit tests
  • add documentation on how to use ProxyGenerationOptions.ByRefLikeConverterSelector
  • ideally, support generic converter types

Usage example 1: defining conversions for by-ref-like argument values

This shows how one would set up a converter for by-ref-like argument values. In this case, one gets set up such that Span<byte> arguments will get converted to a byte[] copy during invocation:

// this will get invoked during interception:
public class CopyByteSpanConverter
{
    // a converter for by-ref-like argument values must be accessible to the dynamically generated assembly,
    // and it must have...

    // (1) for inbound conversions, a public `Box` method
    //     with a single `in` parameter of a by-ref-like type, and return type `object`:
    public object Box(in Span<byte> span)
    {
        return span.ToArray();
    }

    // (2) for outbound conversions, a public `Unbox` method
    //     with an `object` parameter and a `ref` parameter of a by-ref-like type:
    public void Unbox(object argument, ref Span<byte> span)
    {
        var array = (byte[])argument;
        array.CopyTo(span);
    }
}

// this will get invoked during proxy type generation:
class ConverterSelector : IByRefLikeConverterSelector
{
    public Type SelectConverterType(MethodInfo method, int parameterPosition, Type parameterType)
    {
        return parameterType == typeof(Span<byte>) ? typeof(CopyByteSpanConverter) : null;
    }
}

// this is what we want to proxy:
public interface IFoo
{
    void Method(in Span<byte> bytes);
}

var generator = new ProxyGenerator();
var options = new ProxyGenerationOptions { ByRefLikeConverterSelector = new ConverterSelector() };
var proxy = generator.CreateInterfaceProxyWithoutTarget<IFoo>(options, ...);
var span = new byte[] { ... }.AsSpan();
proxy.Method(in span);  // this argument will end up in the invocation's `Arguments` as a `byte[]` array!

Usage example 2: new default / fallback behavior

If no converter is set up, by-ref-like argument values no longer cause an exception during invocation, but now get converted to null references in the IInvocation.Arguments array.

// this is what we want to proxy:
public interface IFoo
{
    void Method(in Span<byte> bytes);
}

var generator = new ProxyGenerator();
var span = new byte[] { ... }.AsSpan();
proxy.Method(in span);  // this argument will end up in the interceptor's `Arguments` as a `null` reference

@stakx stakx self-assigned this Sep 2, 2023
@stakx stakx marked this pull request as draft September 2, 2023 20:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant