wasmtime-dotnet icon indicating copy to clipboard operation
wasmtime-dotnet copied to clipboard

`Function.InvokeCallback()` calls `callback.Method.Invoke()`, which can have different parameter count than the `Delegate`

Open kpreisser opened this issue 2 years ago • 0 comments

Hi, consider the following C# program (.NET 6.0, using Wasmtime 1.0.0):

using Wasmtime;

using var engine = new Engine();
using var module = Module.FromText(
    engine,
    "hello",
    @"
(module 
    (func $getI32 (import """" ""getI32"") (result i32)) 
    (func (export ""run"") (result i32) 
       call $getI32
    )    
)");

using var linker = new Linker(engine);
using var store = new Store(engine);

// Get a Func<int> delegate that should return `3`.
Func<int> myFunc = getFuncInt();
int testValue = myFunc(); // returns 3

linker.Define(
    "",
    "getI32",
    Function.FromCallback(store, myFunc)
);

var instance = linker.Instantiate(store, module);

var run = instance.GetFunction<int>("run")!;
int runResult = run();
Console.WriteLine("Run Result: " + runResult);

Func<int> getFuncInt()
{
    var getLengthDelegate = GetLength;
    var getLengthMethod = getLengthDelegate.Method;

    string str = "abc";
    return (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), str, getLengthMethod);

    int GetLength(string s)
    {
        return s.Length;
    }
}

Expected behavior: Runs successfully and prints "Run Result: 3" on the console, because a Func<int> (that returns 3 when called) is defined in the Linker that should be called by the Run function.

Actual behavior: A TargetParameterCountException occurs:

System.Reflection.TargetParameterCountException
  HResult=0x8002000E
  Message=Parameter count mismatch.
  Source=System.Private.CoreLib
  StackTrace:
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Wasmtime.Function.InvokeCallback(Delegate callback, Caller caller, Boolean passCaller, Value* args, Int32 nargs, Value* results, Int32 nresults, IReadOnlyList`1 resultKinds) in C:\Users\Name\Desktop\WasmTest\wasmtime-dotnet\src\Function.cs:line 2423

  This exception was originally thrown at this call stack:
    Wasmtime.Function.InvokeCallback(System.Delegate, Wasmtime.Caller, bool, Wasmtime.Value*, int, Wasmtime.Value*, int, System.Collections.Generic.IReadOnlyList<Wasmtime.ValueKind>) in Function.cs

This is because Function.InvokeCallback invokes the Delegate's MethodInfo: https://github.com/bytecodealliance/wasmtime-dotnet/blob/f3a383a7b4391b8a653af521499de96f50b975ce/src/Function.cs#L2423

However, a Delegate can be bound with a value that is passed as first argument to the MethodInfo, and therefore not contained in the delegate's Invoke method, whereas the MethodInfo's parameter list includes this parameter.

I think instead of using callback.MethodInfo, the code could get the Delegate.Invoke as MethodInfo and call that one instead, for example:

var callbackInvoke = callback.GetType().GetMethod(nameof(Action.Invoke))!; // TODO: Store the invoke method
var result = callbackInvoke.Invoke(callback, BindingFlags.DoNotWrapExceptions, null, invokeArgs, null);

However, this could also be fixed by replacing the use of reflection with generating code using DynamicMethod to invoke the callback (see #158).

Thank you!

kpreisser avatar Oct 03 '22 13:10 kpreisser