wasmtime-dotnet
wasmtime-dotnet copied to clipboard
`Function.InvokeCallback()` calls `callback.Method.Invoke()`, which can have different parameter count than the `Delegate`
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!