Skip to content

The "PInvokeTableGenerator" task failed unexpectedly when using delegate* in [DllImport] #60802

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

Closed
mattleibow opened this issue Oct 23, 2021 · 3 comments
Assignees
Labels
arch-wasm WebAssembly architecture area-Interop-mono
Milestone

Comments

@mattleibow
Copy link
Member

mattleibow commented Oct 23, 2021

Description

This code results in an error:

extern static void my_class_set_callback(nint obj, delegate* unmanaged<bool, nint, void> callback, nint state);
System.BadImageFormatException: Expected signature header for 'Property' or 'Method', but found '9' (0x09).
   at System.Reflection.Metadata.Ecma335.SignatureDecoder`2.CheckMethodOrPropertyHeader(SignatureHeader header)
   at System.Reflection.Metadata.Ecma335.SignatureDecoder`2.DecodeMethodSignature(BlobReader& blobReader)
   at System.Reflection.Metadata.Ecma335.SignatureDecoder`2.DecodeType(BlobReader& blobReader, Boolean allowTypeSpecifications, Int32 typeCode)
   at System.Reflection.Metadata.Ecma335.SignatureDecoder`2.DecodeMethodSignature(BlobReader& blobReader)
   at System.Reflection.Metadata.MethodDefinition.DecodeSignature[TType,TGenericContext](ISignatureTypeProvider`2 provider, TGenericContext genericContext)
   at System.Reflection.TypeLoading.Ecma.EcmaMethodDecoder.SpecializeMethodSig(IRoMethodBase roMethodBase)
   at System.Reflection.TypeLoading.RoDefinitionMethod`1.ComputeMethodSig()
   at System.Reflection.TypeLoading.RoMethod.get_ReturnType()
   at PInvokeTableGenerator.GenPInvokeDecl(PInvoke pinvoke)
   at PInvokeTableGenerator.EmitPInvokeTable(StreamWriter w, Dictionary`2 modules, List`1 pinvokes)
   at PInvokeTableGenerator.GenPInvokeTable(String[] pinvokeModules, String[] assemblies)
   at PInvokeTableGenerator.Execute()
   at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
   at Microsoft.Build.BackEnd.TaskBuilder.<ExecuteInstantiatedTask>d__26.MoveNext()

I tried void* in there just in case it was something to do with the delegate*, but I now get a runtime error:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.InvalidProgramException: Invalid IL due to: method BlazorApp6.Pages.FetchData/MyClass:OnCallbackUnmanagedOnly (bool,intptr) with UnmanagedCallersOnlyAttribute has non-blittable parameters or return type assembly:<unknown assembly> type:<unknown type> member:(null)
   at BlazorApp6.Pages.FetchData.MyClass..ctor() in C:\Projects\Testing\BlazorApp6\BlazorApp6\Pages\FetchData.razor:line 21
   at BlazorApp6.Pages.FetchData..ctor() in C:\Projects\Testing\BlazorApp6\BlazorApp6\Pages\FetchData.razor:line 11
   at System.Reflection.RuntimeConstructorInfo.InternalInvoke(Object obj, Object[] parameters, Boolean wrapExceptions)
   --- End of inner exception stack trace ---
   at System.Reflection.RuntimeConstructorInfo.InternalInvoke(Object obj, Object[] parameters, Boolean wrapExceptions)
   at System.RuntimeType.CreateInstanceMono(Boolean nonPublic, Boolean wrapExceptions)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at System.Activator.CreateInstance(Type type)
   at Microsoft.AspNetCore.Components.DefaultComponentActivator.CreateInstance(Type componentType)
   at Microsoft.AspNetCore.Components.ComponentFactory.InstantiateComponent(IServiceProvider serviceProvider, Type componentType)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.InstantiateComponent(Type componentType)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.InstantiateChildComponentOnFrame(RenderTreeFrame& frame, Int32 parentComponentId)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewComponentFrame(DiffContext& diffContext, Int32 frameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewSubtree(DiffContext& diffContext, Int32 frameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InsertNewFrame(DiffContext& diffContext, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, Int32 componentId, ArrayRange`1 oldTree, ArrayRange`1 newTree)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()

Reproduction Steps

  1. Create a Blazor WASM app
  2. Add this line:
    extern static void my_class_set_callback(
        nint obj, delegate* unmanaged<bool, nint, void> callback, nint state);
  3. Observe error

Expected behavior

No error. I am trying to replicate this code without the MonoPInvokeCallbackAttribute:

MyClass myInstance = new MyClass();

unsafe class MyClass
{
    private nint handle;

    public MyClass()
    {
        handle = my_class_new();
        var callbackState = (IntPtr)1234;
        my_class_set_callback(handle, OnCallbackProxy, callbackState);
        my_class_set_value(handle, 10);
    }

    [DllImport("library")] extern static nint my_class_new();
    [DllImport("library")] extern static void my_class_set_value(nint obj, int value);
    [DllImport("library")] extern static void my_class_set_callback(nint obj, MyCallback callback, nint state);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate void MyCallback([MarshalAs(UnmanagedType.I1)] bool is10x, nint state);

    [MonoPInvokeCallback(typeof(MyCallback))]
    static void OnCallback([MarshalAs(UnmanagedType.I1)] bool is10x, nint state)
    {
        Console.WriteLine($"Here! {is10x} and {state}");
    }

    static readonly MyCallback OnCallbackProxy = OnCallback;
}

[AttributeUsage(AttributeTargets.Method)]
internal sealed class MonoPInvokeCallbackAttribute : Attribute
{
    public MonoPInvokeCallbackAttribute(Type type)
    {
        Type = type;
    }

    public Type Type { get; private set; }
}

Actual behavior

Error.

Using this:

MyClass myInstance = new MyClass();

unsafe class MyClass
{
    private nint handle;

    public MyClass()
    {
        handle = my_class_new();
        var callbackState = (IntPtr)1234;
        delegate* unmanaged<bool, nint, void> proxy = &OnCallbackUnmanagedOnly;
        my_class_set_callback(handle, (void*)proxy, callbackState);
        my_class_set_value(handle, 10);
    }

    [DllImport("library")] extern static nint my_class_new();
    [DllImport("library")] extern static void my_class_set_value(nint obj, int value);
    [DllImport("library")] extern static void my_class_set_callback(nint obj, void* callback, nint state);
    //[DllImport("library")] extern static void my_class_set_callback(nint obj, delegate* unmanaged<bool, nint, void> callback, nint state);

    [UnmanagedCallersOnly]
    static void OnCallbackUnmanagedOnly(bool is10x, nint state)
    {
        Console.WriteLine($"Here! {is10x} and {state}");
    }
}

Regression?

No response

Known Workarounds

Use the old [MonoPInvokeCallback] format

Configuration

  • .NET 6.0.100-rtm.21521.3
  • WASM Tools 6.0.0-rtm.21514.1

Other information

BlazorApp6.zip

@ghost ghost added area-Interop-coreclr untriaged New issue has not been triaged by the area owner labels Oct 23, 2021
@lambdageek lambdageek added arch-wasm WebAssembly architecture area-Interop-mono and removed area-Interop-coreclr labels Oct 23, 2021
@ghost
Copy link

ghost commented Oct 23, 2021

Tagging subscribers to 'arch-wasm': @lewing
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

This code results in an error:

extern static void my_class_set_callback(nint obj, delegate* unmanaged<bool, nint, void> callback, nint state);
System.BadImageFormatException: Expected signature header for 'Property' or 'Method', but found '9' (0x09).
   at System.Reflection.Metadata.Ecma335.SignatureDecoder`2.CheckMethodOrPropertyHeader(SignatureHeader header)
   at System.Reflection.Metadata.Ecma335.SignatureDecoder`2.DecodeMethodSignature(BlobReader& blobReader)
   at System.Reflection.Metadata.Ecma335.SignatureDecoder`2.DecodeType(BlobReader& blobReader, Boolean allowTypeSpecifications, Int32 typeCode)
   at System.Reflection.Metadata.Ecma335.SignatureDecoder`2.DecodeMethodSignature(BlobReader& blobReader)
   at System.Reflection.Metadata.MethodDefinition.DecodeSignature[TType,TGenericContext](ISignatureTypeProvider`2 provider, TGenericContext genericContext)
   at System.Reflection.TypeLoading.Ecma.EcmaMethodDecoder.SpecializeMethodSig(IRoMethodBase roMethodBase)
   at System.Reflection.TypeLoading.RoDefinitionMethod`1.ComputeMethodSig()
   at System.Reflection.TypeLoading.RoMethod.get_ReturnType()
   at PInvokeTableGenerator.GenPInvokeDecl(PInvoke pinvoke)
   at PInvokeTableGenerator.EmitPInvokeTable(StreamWriter w, Dictionary`2 modules, List`1 pinvokes)
   at PInvokeTableGenerator.GenPInvokeTable(String[] pinvokeModules, String[] assemblies)
   at PInvokeTableGenerator.Execute()
   at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
   at Microsoft.Build.BackEnd.TaskBuilder.<ExecuteInstantiatedTask>d__26.MoveNext()

I tried void* in there just in case it was something to do with the delegate*, but I now get a runtime error:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.InvalidProgramException: Invalid IL due to: method BlazorApp6.Pages.FetchData/MyClass:OnCallbackUnmanagedOnly (bool,intptr) with UnmanagedCallersOnlyAttribute has non-blittable parameters or return type assembly:<unknown assembly> type:<unknown type> member:(null)
   at BlazorApp6.Pages.FetchData.MyClass..ctor() in C:\Projects\Testing\BlazorApp6\BlazorApp6\Pages\FetchData.razor:line 21
   at BlazorApp6.Pages.FetchData..ctor() in C:\Projects\Testing\BlazorApp6\BlazorApp6\Pages\FetchData.razor:line 11
   at System.Reflection.RuntimeConstructorInfo.InternalInvoke(Object obj, Object[] parameters, Boolean wrapExceptions)
   --- End of inner exception stack trace ---
   at System.Reflection.RuntimeConstructorInfo.InternalInvoke(Object obj, Object[] parameters, Boolean wrapExceptions)
   at System.RuntimeType.CreateInstanceMono(Boolean nonPublic, Boolean wrapExceptions)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at System.Activator.CreateInstance(Type type)
   at Microsoft.AspNetCore.Components.DefaultComponentActivator.CreateInstance(Type componentType)
   at Microsoft.AspNetCore.Components.ComponentFactory.InstantiateComponent(IServiceProvider serviceProvider, Type componentType)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.InstantiateComponent(Type componentType)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.InstantiateChildComponentOnFrame(RenderTreeFrame& frame, Int32 parentComponentId)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewComponentFrame(DiffContext& diffContext, Int32 frameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewSubtree(DiffContext& diffContext, Int32 frameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InsertNewFrame(DiffContext& diffContext, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, Int32 componentId, ArrayRange`1 oldTree, ArrayRange`1 newTree)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()

Reproduction Steps

  1. Create a Blazor WASM app
  2. Add this line:
    extern static void my_class_set_callback(
        nint obj, delegate* unmanaged<bool, nint, void> callback, nint state);
  3. Observe error

Expected behavior

No error. I am trying to replicate this code without the MonoPInvokeCallbackAttribute:

MyClass myInstance = new MyClass();

unsafe class MyClass
{
    private nint handle;

    public MyClass()
    {
        handle = my_class_new();
        var callbackState = (IntPtr)1234;
        my_class_set_callback(handle, OnCallbackProxy, callbackState);
        my_class_set_value(handle, 10);
    }

    [DllImport("library")] extern static nint my_class_new();
    [DllImport("library")] extern static void my_class_set_value(nint obj, int value);
    [DllImport("library")] extern static void my_class_set_callback(nint obj, MyCallback callback, nint state);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate void MyCallback([MarshalAs(UnmanagedType.I1)] bool is10x, nint state);

    [MonoPInvokeCallback(typeof(MyCallback))]
    static void OnCallback([MarshalAs(UnmanagedType.I1)] bool is10x, nint state)
    {
        Console.WriteLine($"Here! {is10x} and {state}");
    }

    static readonly MyCallback OnCallbackProxy = OnCallback;
}

[AttributeUsage(AttributeTargets.Method)]
internal sealed class MonoPInvokeCallbackAttribute : Attribute
{
    public MonoPInvokeCallbackAttribute(Type type)
    {
        Type = type;
    }

    public Type Type { get; private set; }
}

Actual behavior

Error.

Using this:

MyClass myInstance = new MyClass();

unsafe class MyClass
{
    private nint handle;

    public MyClass()
    {
        handle = my_class_new();
        var callbackState = (IntPtr)1234;
        delegate* unmanaged<bool, nint, void> proxy = &OnCallbackUnmanagedOnly;
        my_class_set_callback(handle, (void*)proxy, callbackState);
        my_class_set_value(handle, 10);
    }

    [DllImport("library")] extern static nint my_class_new();
    [DllImport("library")] extern static void my_class_set_value(nint obj, int value);
    [DllImport("library")] extern static void my_class_set_callback(nint obj, void* callback, nint state);
    //[DllImport("library")] extern static void my_class_set_callback(nint obj, delegate* unmanaged<bool, nint, void> callback, nint state);

    [UnmanagedCallersOnly]
    static void OnCallbackUnmanagedOnly(bool is10x, nint state)
    {
        Console.WriteLine($"Here! {is10x} and {state}");
    }
}

Regression?

No response

Known Workarounds

Use the old [MonoPInvokeCallback] format

Configuration

  • .NET 6.0.100-rtm.21521.3
  • WASM Tools 6.0.0-rtm.21514.1

Other information

BlazorApp6.zip

Author: mattleibow
Assignees: -
Labels:

arch-wasm, untriaged, area-Interop-mono

Milestone: -

@lewing lewing removed the untriaged New issue has not been triaged by the area owner label Oct 23, 2021
@lewing lewing added this to the 6.0.1 milestone Oct 23, 2021
@lambdageek
Copy link
Member

The pinvoke table generator can't handle unmanaged function pointers, because MetadataLoadContext does not support function pointers.

@radical
Copy link
Member

radical commented Oct 24, 2021

Thank you for sharing the code, which I can use in a test case. This is a dupe of #56145 .

@radical radical closed this as completed Oct 24, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Nov 24, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
arch-wasm WebAssembly architecture area-Interop-mono
Projects
None yet
Development

No branches or pull requests

4 participants