Skip to content

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

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

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
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant