Code Monkey home page Code Monkey logo

wasmtime-dotnet's Introduction

wasmtime-dotnet

.NET embedding of Wasmtime

A Bytecode Alliance project

CI status Latest Version Documentation

Installation

You can add a package reference with the .NET SDK:

$ dotnet add package wasmtime

Introduction

For this introduction, we'll be using a simple WebAssembly module that imports a hello function and exports a run function:

(module
  (func $hello (import "" "hello"))
  (func (export "run") (call $hello))
)

To use this module from .NET, create a new console project:

$ mkdir wasmintro
$ cd wasmintro
$ dotnet new console

Next, add a reference to the Wasmtime package:

$ dotnet add package wasmtime

Replace the contents of Program.cs with the following code:

using System;
using Wasmtime;

using var engine = new Engine();

using var module = Module.FromText(
    engine,
    "hello",
    "(module (func $hello (import \"\" \"hello\")) (func (export \"run\") (call $hello)))"
);

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

linker.Define(
    "",
    "hello",
    Function.FromCallback(store, () => Console.WriteLine("Hello from C#!"))
);

var instance = linker.Instantiate(store, module);
var run = instance.GetAction("run")!;
run();

An Engine is created and then a WebAssembly module is loaded from a string in WebAssembly text format.

A Linker defines a function called hello that simply prints a hello message.

The module is instantiated and the instance's run export is invoked.

To run the application, simply use dotnet:

$ dotnet run

This should print Hello from C#!.

Contributing

Building

Use dotnet to build the repository:

$ dotnet build Wasmtime.sln

This will download the latest development snapshot of Wasmtime for your platform.

Testing

Use dotnet to run the unit tests:

$ dotnet test Wasmtime.sln

Creating the NuGet package

Use dotnet to create a NuGet package:

$ cd src
$ dotnet pack Wasmtime.sln -c Release /p:Packing=true

This will create a .nupkg file in src/bin/Release.

By default, local builds will use a -dev suffix for the package to differentiate between official packages and development packages.

Updating Wasmtime for a release

To update the Wasmtime library used for a new release, change WasmtimeVersion in Directory.Build.props:

<WasmtimeVersion Condition="'$(WasmtimeVersion)'==''">$VERSION</WasmtimeVersion>

Additionally, edit Wasmtime.csproj to change the PackageReleaseNotes to the new version:

<PackageReleaseNotes>Update Wasmtime to $VERSION.</PackageReleaseNotes>

Publishing the Wasmtime .NET NuGet package

GitHub actions is used to automatically publish a package to NuGet when a tag is pushed to the repository.

To publish a new release, create a release in GitHub and add the relevant release notes.

Use a tag of the format v$VERSION where $VERSION matches the Wasmtime version used by the .NET package; ensure the tagged commit matches the last commit to make for the release.

When the release is published on GitHub, an action should automatically start to build and publish the package to NuGet.

wasmtime-dotnet's People

Contributors

alexcrichton avatar alexhiggins732 avatar austinwise avatar havarnov avatar henrikrxn avatar hoangpq avatar is4code avatar joshhua5 avatar justingaffney avatar kpreisser avatar martindevans avatar my0n avatar ok-ul-ch avatar peterhuene avatar redradist avatar redthing1 avatar rob4001 avatar sunfishcode avatar yeseh avatar yvt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wasmtime-dotnet's Issues

Path to wasmtime shared lib in published NuGet packages incorrect for Linux, Windows RIDs

Published NuGet packages for wasmtime-dotnet place wasmtime.dll and libwasmtime.so at win-x64/native/lib/libwasmtime.dylib/wasmtime.dll and linux-x64/native/lib/libwasmtime.dylib/libwasmtime.so respectively. As a result, P/Invoke calls using the published packages fail to find the required wasmtime shared library.

image

By contrast, when building the NuGet package locally, the extra libwasmtime.dylib entry on the path is removed:

image

Config.WithCompilerStrategy() throws System.ArgumentException

When running the code:

class Program
{
    static void Main(string[] args)
    {
        var config = new Wasmtime.Config().WithCompilerStrategy(Wasmtime.CompilerStrategy.Cranelift);
    }
}

Result:

Unhandled exception. System.ArgumentException: Enum underlying type and the object must be same type
or object must be a String. Type passed in was 'System.Byte'; the enum underlying type was 'System.Int32'.
   at System.RuntimeType.IsEnumDefined(Object value)
   at System.Enum.IsDefined(Type enumType, Object value)
   at Wasmtime.Config.WithCompilerStrategy(CompilerStrategy strategy)
   at Program.Main(String[] args) in C:\XXXX\Program.cs:line 5
  • wasmtime-dotnet: 0.29.0-preview1
  • dotnet --version: 5.0.205

Calling function with string arguments returns out of bounds memory error.

Seems like trying to load and call a function that uses pointers to access strings fails to access memory in either an exported or imported memory.

Example code:

            using var engine = new Engine();
            using var module = Module.FromFile(engine, "../../../wasm/bucketing-lib-debug.wasm");
            using var linker = new Linker(engine);
            using var store = new Store(engine);
            linker.Define(
                "env",
                "abort",
                Function.FromCallback(store, (int message, int filename, int linenum, int colnum) =>
                {
                    var inst = linker.Instantiate(store, module);
                    var mem = inst.GetMemory(store, "memory");

                    var messageStr = From64Bitstring(mem.ReadString(store, message, 1000));
                    var filenameStr = From64Bitstring(mem.ReadString(store, filename, 1000));
                    var lineNumStr = From64Bitstring(mem.ReadString(store, linenum, 1000));
                    var colNumStr = From64Bitstring(mem.ReadString(store, colnum, 1000));
                    Console.WriteLine(messageStr);
                    Console.WriteLine(filenameStr);
                    Console.WriteLine(lineNumStr);
                    Console.WriteLine(colNumStr);
                })
            );
            var memory = new Memory(store, 10);
            linker.Define("env", "memory", memory);
            
            var config = "{\"project\":{\"_id\":\"_project\"},\"environment\":{\"_id\":\"environment\"}}\0";
            var user = "{\"user_id\":\"test_id\"}";
            
            var configaddr = 0;
            
            
            var configBytesWritten = memory?.WriteString(store, configaddr, config);
            var useraddr = configaddr + configBytesWritten!;
            var userBytesWritten = memory?.WriteString(store, (int) useraddr, user);
            
            var instance = linker.Instantiate(store, module);
            //var memory = instance.GetMemory(store, "memory");
            
            
            dynamic generateBucketedConfig = instance.GetFunction(store, "generateBucketedConfig")!;


            if (configBytesWritten == null || userBytesWritten == null)
            {
                Console.Out.WriteLine("Failed to write to memory.");
                return;
            }

            var config2 = memory.ReadString(store, configaddr, (int) configBytesWritten);
            var user2 = memory.ReadString(store, (int) useraddr, (int) userBytesWritten);


            Console.Out.WriteLine("Config data: " + config2);
            Console.Out.WriteLine("user data: " + user2);
            var span = memory.GetSpan(store);
            var result = generateBucketedConfig?.Invoke(store, (int)configaddr, (int)useraddr);

            Console.WriteLine("generateBucketedConfig result: " + result);

WAT file of relevant function showing param usage via pointers:

(func $src/index/generateBucketedConfig (param $0 i32) (param $1 i32) (result i32)
  (local $2 i32)
  (local $3 i32)
  (local $4 i32)
  (local $5 i32)
  global.get $~lib/memory/__stack_pointer
  i32.const 12
  i32.sub
  global.set $~lib/memory/__stack_pointer
  call $~stack_check
  global.get $~lib/memory/__stack_pointer
  i64.const 0
  i64.store
  global.get $~lib/memory/__stack_pointer
  i32.const 0
  i32.store offset=8
  global.get $~lib/memory/__stack_pointer
  global.get $~lib/memory/__stack_pointer
  local.get $0
  local.set $2
  local.get $2
  call $~lib/assemblyscript-json/JSON/_JSON.parse<~lib/string/String>
  local.tee $2
  i32.store
  local.get $2
  i32.const 3
  call $~lib/rt/__instanceof
  if (result i32)
   local.get $2
  else
   i32.const 1184
   i32.const 2960
   i32.const 4
   i32.const 41
   call $~lib/builtins/abort
   unreachable
  end
  local.tee $2
  i32.store
  global.get $~lib/memory/__stack_pointer
  global.get $~lib/memory/__stack_pointer
  local.get $1
  local.set $3
  local.get $3
  call $~lib/assemblyscript-json/JSON/_JSON.parse<~lib/string/String>
  local.tee $3
  i32.store offset=4
  local.get $3
  i32.const 3
  call $~lib/rt/__instanceof
  if (result i32)
   local.get $3
  else
   i32.const 1184
   i32.const 2960
   i32.const 5
   i32.const 39
   call $~lib/builtins/abort
   unreachable
  end
  local.tee $3
  i32.store offset=4
  global.get $~lib/memory/__stack_pointer
  i32.const 0
  local.get $2
  call $src/index/BucketedUserConfig#constructor
  local.tee $4
  i32.store offset=8
  local.get $4
  call $src/index/BucketedUserConfig#stringify
  local.set $5
  global.get $~lib/memory/__stack_pointer
  i32.const 12
  i32.add
  global.set $~lib/memory/__stack_pointer
  local.get $5
 )

Error output:

Config data: {"project":{"_id":"_project"},"environment":{"_id":"environment"}}
user data: {"user_id":"test_id"}
Unhandled exception. Wasmtime.TrapException: wasm trap: out of bounds memory access
wasm backtrace:
    0:  0x36a - <unknown>!~lib/string/String.UTF8.byteLength
    1: 0x69f2 - <unknown>!~lib/string/String.UTF8.encode
    2: 0x1638 - <unknown>!~lib/string/String.UTF8.encode@varargs
    3: 0x6c0e - <unknown>!~lib/assemblyscript-json/util/index/Buffer.fromString
    4: 0x5f16 - <unknown>!~lib/assemblyscript-json/JSON/_JSON.parse<~lib/string/String>
    5: 0x7c4b - <unknown>!src/index/generateBucketedConfig
    6: 0x7f2d - <unknown>!export:src/index/generateBucketedConfig

   at Wasmtime.Function.Invoke(StoreContext context, ReadOnlySpan`1 arguments)
   at Wasmtime.Function.Invoke(IStore store, Object[] arguments)
   at System.Dynamic.UpdateDelegates.UpdateAndExecute4[T0,T1,T2,T3,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3)
   at Example.Program.runWASM() in /Users/jamiesinn/git/dotnet-server-sdk/Example/Program.cs:line 75
   at Example.Program.Main(String[] args) in /Users/jamiesinn/git/dotnet-server-sdk/Example/Program.cs:line 12
   at Example.Program.<Main>(String[] args)

What are we doing wrong here? or is this an unsupported method of accessing a function?

No support for tables in Instance.Externs

If Module.Exports.Tables contains any values, the exception NotSupportedException with the message "Unsupported extern type." is thrown in the Externs constructor when Host.Instantiate is called

Debugging WASM module in .NET host

Hello,
I've been using Wasmtime as the runtime of a little project of mine, a fantasy console inspired by early 2000s game consoles and running WASM-based games.
It's been working quite well, but I've hit a bit of a snag. I'm trying to find ways to improve the debugging experience - log message debugging isn't super ideal, I'd like to be able to breakpoint my Rust source code and step through it from VSCode.
So, I installed the CodeLLDB plugin, set up a launch.json which boots my fantasy console passing the WASM module it should load on start, and then set a breakpoint and... nothing happens. It just skips right past the breakpoint, which seems to never resolve and just stays greyed out.
I'm definitely compiling my WASM module in debug mode (even added "-g" to my RUSTFLAGS just to be sure), and in my .NET host I've made sure I'm using a configuration with the .WithDebugInfo(true) option.
I'm not sure if I've missed something here or if this isn't actually a supported case?
Running on Windows 64-bit, .NET 6.0. Any help would be appreciated!

EDIT: Oh. I see, JIT debugging in LLDB seems to still be unsupported on Windows. Does that mean I should be trying to use GDB from mingw-64 instead?

Proposal: Wasmtime 1.0

I wanted to raise awareness in this repository about the proposal for Wasmtime to become 1.0. If others are watching just this repository and are interested to learn what 1.0 would mean for Wasmtime and this embedding, feel free to read over the RFC and leave comments!

Question: Thread-Safety

I have asked that in the past most likely via an at-mention in https://github.com/christophwille/dotnet-opa-wasm/ - but today it came back up for multi-threaded usage of the Wasmtime API. I have cooked up a sample and I am pretty sure I remember our past discussion wrongly... the overall goal is to not incur the compilation hit.

https://github.com/christophwille/dotnet-opa-wasm/blob/master/spikes/HigherLevelApisSpike/IPolicyFactory.cs#L129

I load a wasm file into a byte array, create an engine and use the engine for module load - and for the sake of discussion, throw away that engine. The question now is: is Module safe to use in parallel later for

// _store is created on a new Engine, _linker same
var instance = _linker.Instantiate(_store, module);

Ie can one cached Module be used to create unlimited instances in parallel.

Thanks.

wasmtime cannot be installed in an VSTO office addin

Hi,
I would like to use wasmtime in an VSTO C# office addin project.
However, I have trouble installing wasmtime as the prerequisites do not match.
wasmtime requires .NetStandard 2.1.
But the addin uses .Net framework 4.7.2. and according to https://docs.microsoft.com/en-us/dotnet/standard/net-standard .NetStandard 2.1 is not supported by .Net framework.

Also https://devblogs.microsoft.com/dotnet/announcing-net-standard-2-1/ states

Given many of the API additions in .NET Standard 2.1 require runtime changes in order to be meaningful, .NET Framework 4.8 will remain on .NET Standard 2.0 rather than implement .NET Standard 2.1

The following workaround also did not work: https://stackoverflow.com/questions/42039069/could-not-install-package-you-are-trying-to-install-this-package-into-a-pr

Also, trying to use net5.0 ("net5.0") breaks the addin.

Anyone has some advice?

Allow reporting errors when loading wasm modules into .NET

Feature

When loading wasm modules into .NET with wasmtime, an exception is raised if a module is invalid. This exception doesn't provide any details as to what went wrong, however, making it hard to fix the module.

Implementation

The .NET implementation relies on the wasm_module_new function.
https://github.com/bytecodealliance/wasmtime/blob/121bbd36568c506a39b2dd97a95dd2b963960eb7/crates/c-api/src/lib.rs#L828

This function currently discards the error information with a match expression:

https://github.com/bytecodealliance/wasmtime/blob/121bbd36568c506a39b2dd97a95dd2b963960eb7/crates/c-api/src/lib.rs#L836

Allowing this information to be captured in a callback or in a mutable pointer to a string that could capture the Debug derive for the anyhow::Error would be helpful in diagnosing errors that occur when loading modules.

Overflow Exception In Trap Handling

I'm experimenting with a better way to handle traps and I've come across what looks like a bug in the current implementation.

I modified Trap.wat to cause a stackoverflow instead of using unreachable. I expected that to cause a TrapException of type StackOverflow, instead I'm seeing an OverflowException:

Xunit.Sdk.XunitException
Expected a <Wasmtime.TrapException> to be thrown, but found <System.OverflowException>: "
"System.OverflowException with message "Arithmetic operation resulted in an overflow."
     at System.UIntPtr.op_Explicit(UIntPtr value)
     at Wasmtime.TrapFrame..ctor(IntPtr frame) in C:\Users\Martin\Documents\dotnet\wasmtime-dotnet\src\TrapException.cs:line 50
     at Wasmtime.TrapException.FromOwnedTrap(IntPtr trap) in C:\Users\Martin\Documents\dotnet\wasmtime-dotnet\src\TrapException.cs:line 179
     at Wasmtime.Function.Invoke(ReadOnlySpan`1 arguments, Span`1 resultsOut) in C:\Users\Martin\Documents\dotnet\wasmtime-dotnet\src\Function.cs:line 2218
     at Wasmtime.Function.InvokeWithoutReturn(ReadOnlySpan`1 arguments) in C:\Users\Martin\Documents\dotnet\wasmtime-dotnet\src\Function.cs:line 2063

This is coming from this code in the TrapFrame constructor:

FunctionOffset = (int)Native.wasm_frame_func_offset(frame); // <-- Exception Here!
FunctionName = null;
ModuleOffset = (int)Native.wasm_frame_module_offset(frame);
ModuleName = null;

Native.wasm_frame_func_offset returns a UIntPtr (platform dependent, 64 bits on my PC) but it's being cast into an int (always 32 bits).

Should FunctionOffset be converted into an nuint instead? If so should the same change be made to ModuleName?

Proposal: Replace `byte[]` for V128

wasmtime supports a 128 bit value (V128) which does not map neatly to any integer types in dotnet/C# (since they only extend to 64 bits). Currently wasmtime-dotnet is using a 16 element byte[] to represent V128.

In my ongoing work to make function calls more typesafe this is presenting a problem - in cases where I'm converting to/from wasm types the type system only sees a byte[] and doesn't know anything about the size. I'll have to insert runtime checking to ensure that the array is the correct size. Also of course this allocates a small array for every V128.

It seems like we could replace this byte[16] with a Vector128<byte>, which solves both of these problems:

  • It's type safe - the size is right there in the type name!
  • No allocations.

Does this seems reasonable? If so I'll put together this change and include it in my ValueBox PR (#114)

wasmtime version bump

Is there additional work which needs to be accomplished before wasmtime-dotnet can be upgraded to match the current wasmtime version? Is there a way to see what needs to be accomplished?

What version progress needs to happen before the NuGet package can come out of preview?

How to transfer arguments to WebAssembly binary generated from C?

Hello, I converted below C code to WebAssembly binary using wasi-sdk-14 (I downloaded wasi-sdk-14.0-macos.tar.gz)

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main (int argc, char ** argv) {
    char * buf = (char *) malloc(100);
    memcpy(buf, argv[1], 100);
    printf("buf: %s\n", buf); 
    return 0;
}

And I am using below script to run the WebAssembly binary file which name is prog.wasm.

open System
open Wasmtime

[<EntryPoint>]
let main _ =
  let path = "prog.wasm"
  let engine = new Engine()
  let wm = Module.FromFile(engine, path)
  let linker = new Linker(engine)
  let store = new Store(engine)

  let cfg = new WasiConfiguration()

  linker.DefineWasi()
  store.SetWasiConfiguration(cfg)

  let instance = linker.Instantiate(store, wm)
  let func = instance.GetFunction(store, "_start")
  let result = func.Invoke(store)
  0

What I want is to transfer arguments to the binary. But _start function does not have any arguments.
I guess _start function is entry point of code which executes main function...

So my question is how to transfer arguments to the binary generated from C code?
Additionally, I also wonder that how to pass standard input.

Error loading shared library libwasmtime in alpine docker image

I am experiencing this error when trying to build my docker image that uses wasmtime.

Unhandled exception. System.DllNotFoundException: Unable to load shared library 'wasmtime' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: Error loading shared library libwasmtime: No such file or directory
   at Wasmtime.Engine.Native.wasm_engine_new()
   at Wasmtime.Engine..ctor()

I stopped trying to build my own docker image and instead used the https://github.com/bytecodealliance/wasmtime-dotnet/tree/main/examples/hello sample project. The only thing I changed was to use <PackageReference Include="Wasmtime" Version="0.32.0-preview1" /> instead of the ProjectReference.

I tried 3 different Dockerfiles:

# FROM mcr.microsoft.com/dotnet/sdk:5.0-bullseye-slim
# FROM mcr.microsoft.com/dotnet/sdk:5.0-alpine
# FROM mcr.microsoft.com/dotnet/sdk:5.0-focal
COPY . /build
WORKDIR /build
RUN dotnet build -c Release
ENTRYPOINT ["dotnet", "run"]

All 3 build successfully with docker build -t test .. When run with docker run test, Debian and Ubuntu both print Hello from C#, WebAssembly! as expected, but alpine throws the above exception.

WasmTime.Net: Access to Handle in Memory Class

I originally built https://github.com/christophwille/csharp-opa-wasm/ using WasmerSharp. Today I started my initial run on porting it to WasmTime, however, I ran into a snag with the import for env.memory:

https://github.com/christophwille/csharp-opa-wasm/blob/master/WasmTimeTest/Program.cs#L19

All my imports are satisfied by name, however, the env.memory import bombs, exception details in the source code via comment, but basically the wasm expects two pages of memory minimum.

The WasmerSharp equivalent is here https://github.com/christophwille/csharp-opa-wasm/blob/master/src/Opa.Wasm/OpaPolicy.cs#L14. My guess is that I'd actually need the handle here which is internal for Memory, or: I am using the entirely wrong class because I am translating from a different API by similarity...

Proposal: Reduce Boxing

I'm currently looking into using wasmtime-dotnet in a Unity game. Generally in Unity you want to reduce allocations as much as possible to prevent any GC stuttering down the line. As it stands any time you invoke a wasm function there is some unavoidable boxing of primitive arguments because the arguments are passed as params object[] (the ReadOnlySpan<object> variant of the call avoids the allocation of the params array, but not the allocation caused by boxing all of the individual arguments).

I can see two possible workaround for this.

Generic Invoke

Define a large number of overloads for Invoke with varying numbers of generic arguments:

public object? Invoke<TA>(IStore store, TA arg1);
public object? Invoke<TA, TB>(IStore store, TA arg1, TB arg2);
public object? Invoke<TA, TB, TC>(IStore store, TA arg1, TB arg2, TC arg3);
// etc up to as many arguments as you want to support

For the sake of sanity, writing this would probably require code generation!

Struct Boxing

An alternative option would be to define a new struct which works in basically the same way as the ValueUnion struct (let's call it ArgBox) and to have implicit casts from the various supported types into this new struct. Then there just need to be two new methods defined:

public object? Invoke(IStore store, params ArgBox?[] args);
public object? Invoke(IStore store, ReadOnlySpan<ArgBox?> args);

This is essentially just the same boxing operation that is already going on, but without the need for silent heap allocations.

Interest?

Does this proposal sound reasonable and would you be interested in merging a PR that contained something like this (and if so, which one)? I'm willing to write it if so.

Passing CLR objects to Wasmtime

Currently this runtime can execute Wasmtime one way, but there's no clear documentation how to pass CLR objects or return value types back to Wasmtime since execution engines like Wasmtime should expect return types from the host too.

How to make a store that uses WASI?

So I saw that there is SetWasiConfiguration method, I presume I have to pass something to it in order to enable WASI?

I took a wild guess and just did this:

            WasiConfiguration wasiConfiguration = new WasiConfiguration()
                                                    .WithInheritedArgs()
                                                    .WithInheritedArgs()
                                                    .WithInheritedEnvironment()
                                                    .WithInheritedStandardError()
                                                    .WithInheritedStandardInput()
                                                    .WithInheritedStandardOutput();
            m_Store.SetWasiConfiguration(wasiConfiguration);

But my wasi_snapshot_preview1 functions still are not defined.

Is there any example on how I configure wasmtime to use WASI through dotnet?

Disposing the Store

In 0.39.1-preview2, I had a test in my project which made sure I could still use an Instance after a Linker had been disposed. This was just a sanity check to make sure my chosen approach was valid, because I wasn't interested in holding onto a reference to the Linker when I no longer needed it.

In 0.40.0-preview1, the API surface was altered to drop the reference to store in the various calls off the Instance. This was simpler, and a nice change, but it means I am once again holding onto a reference to an object which I no longer need after creating Instance. However, the lifetime of the store matters to the Instance. This test fails, despite looking like it should pass:

    [Fact]
    public void InstanceRunAfterStoreIsDisposed()
    {
        var expected = 1;
        using var engine = new Engine();
        using var module = Module.FromText(engine, "hello", SampleWat);
        Instance instance;

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

            linker.Define(
                "",
                "hello",
                Function.FromCallback(store, () => expected)
            );

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

        var result = instance.GetFunction("run")?.Invoke();
        result.Should().Be(expected);
    }

    private const string SampleWat = @"(module
  (import """" ""hello"" (func $.hello (result i32)))
  (func (export ""run"") (result i32) call $.hello))";

Perhaps this is simply an artifact of the way I am using the Store, but if 0.40.0-preview1 was trying to prevent us from accidentally cross-invoking with the wrong Store, does that mean Store is only intended to be used by a single Instance, and Instance should be responsible for disposing of the Store?

For the moment, my class is just holding a copy of the Store alongside the Instance which it can dispose, so I am not hurting for a solution. It just feels odd that I need a reference to this object purely as a mechanism to manage its disposal.

Question/Proposal `Function.IsWasi`

For my application I want to know if a given function is part of WASI (this is so I know that a particular WASM file is going to require WASI defined to work and also so that I can hide these functions from the UI). Is it possible to get this information somehow?

If not, I propose adding an IsWasi property to Function. I'm willing to write a PR for this if you think it's a good idea, but if so I'll need some guidance on how best to get the information from the underlying wasmtime system.

Using wasmtime-dotnet in Azure Functions errors with 'image not found'

Hi all!

I have tried to use wasmtime-dotnet in an Azure Function.
For now, I am running this locally on macOS.

It fails when calling the HTTP-triggered function:

Executed 'RunWasm' (Failed, Id=f2ddeb26-1bdc-4f7c-a22c-85315832c10f)
System.Private.CoreLib: Exception while executing function: RunWasm. Wasmtime.Dotnet: Unable to load shared library 'wasmtime' or one of its dependencies. In order to help diagnose loading problems, consider setting the DYLD_PRINT_LIBRARIES environment variable: dlopen(libwasmtime, 1): image not found.

As we can see, the runtimes are present in the bin folder:

image

Any ideas on what could be the issue?

You can see the repro here:
https://github.com/ChristianWeyer/azure-functions-wasmtime-repro

Thanks,
-Christian

Details on "Reimplement the C# API" for 0.15

I was just updating the NuGet from 0.12 and literally everything turned red. To clarify the API changes I'll take this as an example:

https://github.com/christophwille/csharp-opa-wasm/blob/master/src/Opa.Wasm/OpaHost.cs

No more IHost, no more Import - basically everything is now runtime on Host?

https://github.com/christophwille/csharp-opa-wasm/blob/master/src/Opa.Wasm.ConsoleSample/Program.cs

No more Engine, no more Store?

Given that the API changed in such a big way, can you describe a bit about why now this way compared to before? (I'd like to fix my code in a way that matches your API). Thanks.

Cross-Engine Instantation

I finally got around to updating to the latest version, this was fairly smooth. For fun I tried the following:

https://github.com/christophwille/csharp-opa-wasm/blob/master/src/Opa.Wasm/OpaModule.cs#L19

Basically, using one Engine to load the module, a new Engine to run it - and that bombed:

Wasmtime.WasmtimeException
  HResult=0x80131500
  Message=cross-`Engine` instantiation is not currently supported
  Source=Wasmtime.Dotnet
  StackTrace:
   at Wasmtime.Instance..ctor(LinkerHandle linker, Module module)
   at Wasmtime.Host.Instantiate(Module module)
   at Opa.Wasm.OpaPolicy.Initialize(Module module) in D:\GitWorkspace\csharp-opa-wasm\src\Opa.Wasm\OpaPolicy.cs:line 104
   at Opa.Wasm.OpaPolicy..ctor(OpaModule module, Module wasmModule) in D:\GitWorkspace\csharp-opa-wasm\src\Opa.Wasm\OpaPolicy.cs:line 24
   at Opa.Wasm.ConsoleSample.Program.EvaluateHelloWorld() in D:\GitWorkspace\csharp-opa-wasm\src\Opa.Wasm.ConsoleSample\Program.cs:line 40
   at Opa.Wasm.ConsoleSample.Program.Main(String[] args) in D:\GitWorkspace\csharp-opa-wasm\src\Opa.Wasm.ConsoleSample\Program.cs:line 13

As you remember, I do the "precompilation" to save time on run. Is it actually safe to use the Engine concurrently?

Error running libwasmtime on M1 Mac

When trying to consume wasmtime-dotnet, I get an error trying to load a module. This works on my other machines, but not the M1 MacBook Pro.

System.DllNotFoundException : Unable to load shared library 'wasmtime' or one of its dependencies. In order to help diagnose loading problems, consider setting the DYLD_PRINT_LIBRARIES environment variable: dlopen(libwasmtime, 0x0001): tried: 'libwasmtime' (no such file)

Upstream wasmtime publishes an artifact for macos aarch64. Can wasmtime-dotnet publish that?

Update the release process with information regarding the Wasmtime .NET package

Tracking issue for me to update the release process documentation to:

  • Update the Wasmtime .NET package version bump in the script
  • Document where the version lives for the Wasmtime .NET package
  • Instructions on how to build the package
  • Instructions on how to push the package (open questions: API key management and should we have a BA nuget.org account to transfer the package ownership to?).

How to execute a wasm (rust) function with &str input and String output?

The rust signature for the function I want to call from C# using wasmtime.net

pub extern "C" fn makesha256(s: &str) -> String {
...
}

I use wasm-pack to compile and generate bootstrapping JS.

The function signature in wasm ends up being...

func (;27;) (type 4) (param i32 i32 i32)

The JS code generated by wasm-pack includes a few functions (and wrapping code) like these to facilitate simple calling by string, instead of pointers/etc. ...

function passStringToWasm0(arg, malloc, realloc) {}

function getStringFromWasm0(ptr, len) {}

What would be the equivalent for use in C#?

I tried to get a few ideas from the "Allocator" used here https://hacks.mozilla.org/2019/12/using-webassembly-from-dotnet-with-wasmtime/ .

But I couldn't make it match successfully the generated signatures (not sure if there are differences in types, or compiler, or simply versions over time that led to me having 3 arguments instead of 2).
By "successfully" I mean that I was able to call the function with what I thought would be the right locations and length, but after seemingly executing the function, I get null object returned.

I would appreciate any tips & tricks.

(I realize I did not supply huge amounts of detail - but these can, of course, be provided, if necessary)

Using .wasm module built with Emscripten: unknown import: `env::emscripten_resize_heap` has not been defined

Hi there,
I get the following error when trying to run a .wasm module built with Emscripten v 1.40.1.

➜  wasmtime-dotnet-emscripten-repro git:(master) ✗ dotnet run
Unhandled exception. Wasmtime.WasmtimeException: unknown import: `env::emscripten_resize_heap` has not been defined
   at Wasmtime.Instance..ctor(LinkerHandle linker, Module module)
   at Wasmtime.Host.Instantiate(Module module)
   at WasmtimeTest1.Program.Main(String[] args) in /Users/christianweyer/sources/wasmtime-dotnet-emscripten-repro/Program.cs:line 14

A repro can be found here:
https://github.com/ChristianWeyer/wasmtime-dotnet-emscripten-repro

Any idea what could be the issue?

Thanks,
Christian

Manually consume fuel from store

Is it possible to manually consume fuel from the store? Looks like adding fuel and getting the current amount of consumed fuel is supported, but I wanted to put an extra cost to some of the functions that I'm calling.

I'm very new to wasm stuff in general, but it looks like this should be possible using Store::consume_fuel - calling that function seems straightforward so I may be able to take a swing at this if you'd like

How to see stack traces?

Maybe Wasmtime dotnet wrapper includes old wastime without stack traces support?

Stack traces can help for debugging, for example "unreachable" functions.

wasmtime NetStandard 2.0

Hi,

In order to use this inside Unity, I've 'ported' the system to NetStandard2.0. It was a fairly quick port, but didn't modify too much of the code. I would be much more comfortable if there was an official port. Or #if 's in the code to handle it.

I'm happy to put my code somewhere if you want to see it, but I'm sure it's inferior to what the author of the module would do.

Cheers,
Ryan.

Optional String Encoding of Memory

The memory layout of WASM strings compiled with AssemblyScript is in UTF16 format, while the memory.readstring functions are all hard coded to UTF8. AssemblyScript/assemblyscript#1653

Providing an optional override for this format would be helpful, the temporary workaround is to encode strings within wasm, but ideally this could be avoided.

No way to get access to exported memory outside of a Host callback

Inside a host defined callback we can do this:

host.DefineFunction("ns", "someHostFunction", (Caller caller, int data_ptr, int data_len) => {
       var mem = caller.GetMemory("memory"); 
}

However there is no exposed API for getting access to a memory via the instance directly like you can do within rust:

let memory = instance.get_memory("memory").ok_or(anyhow::format_err!("failed to find `memory` export"))?;	

Implement reference types for .NET embeddings.

Now that the reference types proposal is implemented in Wasmtime, let's implement it for .NET embeddings!

This means fully supporting the ref values, ref value types, handling reference types in host functions, handling reference types when invoking Wasm functions, and properly rooting .NET GC heap references when passing in and out of WebAssembly.

Race in SafeHandle finalizers leads to undefined behavior

As store-based objects in the C API need to used from a single thread, the finalizers of the various SafeHandle-derived types that call wasm_<type>_delete introduce a race on the underlying thread-unsafe reference count of the Wasmtime Store.

This may lead to undefined behavior when one thread is modifying the store's reference count and the finalizer thread is decrementing the reference count: sometimes leaking the Store (and the associated objects) or prematurely deallocating the Store while still in use.

A workaround may be needed in the .NET API to account for this limitation of the C API.

Users may also work-around this problem by always calling Dispose on any IDispoable type in the Wasmtime .NET API.

Ideally, Wasmtime would allow calls to wasm_<type>_delete from a different thread, provided the given object is no longer in use by any other thread (guaranteed to be the case for a SafeHandle finalizer in the .NET API).

Allow for detecting WASI exit traps

Users of the .NET API are currently unable to get the WASI exit status for WASI programs that call proc_exit.

Therefore, there's no way to distinguish between a Wasm trap and the Wasmtime representation of a WASI program that has explicitly exited.

To implement this, we should import wasmtime_trap_exit_status to check for the trap being an exit trap.

If it is an exit trap, Function.Invoke should probably throw a different type of exception (i.e. not TrapException) to indicate a program exit.

wasmtime_config_interruptable_set missing

System.EntryPointNotFoundException: Unable to find an entry point named 'wasmtime_config_interruptable_set' in shared library 'wasmtime'.
   at Wasmtime.Config.Native.wasmtime_config_interruptable_set(Handle config, Boolean enable)
   at Wasmtime.Config.WithInterruptability(Boolean enable)

Change default branch name

As a policy, the Bytecode Alliance is changing the default branch names in all repositories. We would like for all projects to change the default to main by June 26. (We mention June 26th because there is some suggestion that GitHub may be adding the ability to make this process more seamless. Feel free to wait for that, but only up to June 26. We'll provide further support and documentation before that date.)

Please consider this a tracking issue. It is not intended for public debate.

  • Change branch name
  • Update CI
  • Update build scripts
  • Update documentation

Improve debuggability by adding original host function exceptions to TrapException

Right now if a host function throws a .NET exception, the exception is caught and translated to a trap representation.

The trap representation only keeps the exception message. When the trap bubbles back up to .NET, the trap is thrown as a TrapException.

Since only the message is kept, debugging the source of the host function exception can be difficult, especially from a log that prints only the TrapException.

When a trap representation is created, the original caught exception should be stored somewhere that can be utilized from where TrapException is thrown and passed in as the inner exception.

Reference types?

Is there reference type support for .NET? According to here, it is supported in wasmtime. I am very new to all this, so bear with me if I am missing some basic understanding.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.