Skip to content

[API Proposal]: Embrace spans more in System.Reflection.Emit. #63419

Open
@teo-tsirpanis

Description

@teo-tsirpanis

Background and motivation

There are APIs in the System.Reflection.Emit namespace that accept binary data only as byte arrays, forcing callers to allocate an array of exact size for each time they call them. I propose to add overloads to these methods that accept ReadOnlySpan<byte>, reducing needless allocations.

API Proposal

namespace System.Reflection.Emit
{
    public partial class AssemblyBuilder
    {
        public void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
    }

    public partial class ConstructorBuilder
    {
        public void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
    }

    public partial class DynamicILInfo
    {
        public int GetTokenFor(ReadOnlySpan<byte> signature);
        public void SetCode(ReadOnlySpan<byte> code, int maxStackSize);
        public void SetExceptions(ReadOnlySpan<byte> exceptions);
        public void SetLocalSignature(ReadOnlySpan<byte> localSignature);
    }

    public partial class EnumBuilder
    {
        public void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
    }

    public partial class EventBuilder
    {
        public void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
    }

    public partial class FieldBuilder
    {
        public void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
    }

    public partial class GenericTypeParameterBuilder
    {
        public void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
    }

    public partial class MethodBuilder
    {
        public void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
    }

    public partial class ModuleBuilder
    {
        public FieldBuilder DefineInitializedData(string name, ReadOnlySpan<byte> data, FieldAttributes attributes);
        public void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
    }

    public partial class ParameterBuilder
    {
        public void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
    }

    public partial class PropertyBuilder
    {
        public void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
    }

    public partial class TypeBuilder
    {
        public FieldBuilder DefineInitializedData(string name, ReadOnlySpan<byte> data, FieldAttributes attributes);
        public void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
    }
}

Going further and adding span overloads for all methods that accept arrays of any type is out of scope and would require changes to the broader Reflection APIs.

API Usage

ModuleBuilder modBuilder = ...;
var typeBuilder = modBuilder.DefineType("Foo");

var dataField = modBuilder.DefineInitializedData("data", (ReadOnlySpan<byte>) new byte[] {1, 2, 3, 4}, FieldAttributes.Private | FieldAttributes.InitOnly);
AssemblyBuilder asmBuilder = ...;
ReadOnlySpan<byte> attributeData = ...;
asm.Builder.SetCustomAttribute(myAttributeConstructor, attributeData);

Alternative Designs

There is the option not to add the DynamicILInfo.Set*** span overloads without sacrificing performance; there are already overloads accepting pointers, but callers would have to resort to unsafe code for no reason, and pinning spans is not supported in F#.

Risks

One would argue how commonly used these APIs are. Maybe they are not worth the effort but their implementations would be simple. As for popularity, upcoming APIs like #60948 provide a compelling use case in dynamic code generators for the spanified DefineInitializedData overloads.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions