bytecodealliance / wasmtime-dotnet

.NET embedding of Wasmtime https://bytecodealliance.github.io/wasmtime-dotnet/
Apache License 2.0
420 stars 52 forks source link

Unable to create a function with parameter when Type.IsValueType is true #174

Open hiinaspace opened 1 year ago

hiinaspace commented 1 year ago

I have a really simple adaption of the externref sample, running inside unity 2021.3, whose csharp runtime is ostensibly .NET standard 2.1 compatible:

(module
  (import "" "getX" (func $.getX (param externref) (result f32)))
  (func (export "run") (param externref) (result f32)
    local.get 0
    call $.getX
  )
)
public class WasmTest : MonoBehaviour
{
    public TextAsset watSource;

    void Start()
    {
        var engine = new Engine();

        var module = Module.FromText(
            engine,
            watSource.name,
            watSource.text
        );

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

        linker.Define(
            "",
            "getX",
            Function.FromCallback(store, (Vector3 vec) => ((Vector3)vec).x)
        );

        var instance = linker.Instantiate(store, module);
        var run = instance.GetFunction<object, float>("run");
        float res = run(transform.position);
        Debug.Log($"the x is {res}");
    }
}

However, as written, I get

WasmtimeException: Unable to create a function with parameter of type 'UnityEngine.Vector3'.
Wasmtime.Function.GetFunctionType (System.Type type, System.Boolean hasReturn, System.Collections.Generic.List`1[T] parameters, System.Collections.Generic.List`1[T] results, System.Boolean& hasCaller, System.Boolean& returnsTuple) (at C:/Users/s/source/repos/wasmtime-dotnet/src/Function.cs:2411)
Wasmtime.Function..ctor (Wasmtime.IStore store, System.Delegate callback, System.Boolean hasReturn) (at C:/Users/s/source/repos/wasmtime-dotnet/src/Function.cs:2242)
Wasmtime.Function.FromCallback[T,TResult] (Wasmtime.IStore store, System.Func`2[T,TResult] callback) (at C:/Users/s/source/repos/wasmtime-dotnet/src/Function.cs:300)
WasmTest.Start () (at Assets/wasmtest/WasmTest.cs:25)

If I change the function argument type from Vector3 to just object and do the cast manually, it works as expected. Looks like it's because it falls out of Value.TryGetKind at https://github.com/bytecodealliance/wasmtime-dotnet/blob/387edd242f105d5394399acd19c25509ebb42b02/src/Value.cs#L210 , because unity sets Type.IsValueType == true.

Is that test of IsValueType necessary? I don't know if this is just a weird unity environment thing.

peterhuene commented 1 year ago

Hi @hiinaspace:

The intention behind this is that externref is a reference to an external (host) object, so it is intentionally limiting the support for .NET types to those that are reference types. It also prevents unintentional boxing of value types were they implicitly allowed.

As Vector3 is a value type, it gets rejected. Using object and manually boxing a Vector3, as you've discovered, should work.

I think you'll have to stick with using object and doing an explicit boxing/unboxing of the value until I'm convinced that implicitly boxing value types for externref is the right approach.

peterhuene commented 1 year ago

At a bare minimum, we should improve this exception message to note that it's because the type is a value type and .NET types are required to be reference types to work with externref.

hiinaspace commented 1 year ago

I see, thanks for the clarification. My java background assuming that all non-primitives are references doesn't work in c#...

I agree that a better error message would be nice, in lieu of autoboxing.