Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:

jobs:
build:
name: Test .NET bindings for Wasmtime
name: Test .NET embedding of Wasmtime
runs-on: ${{ matrix.os }}
strategy:
matrix:
Expand Down
104 changes: 100 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,109 @@
You can add a package reference with the [.NET Core SDK](https://dotnet.microsoft.com/):

```text
dotnet add package --version 0.12.0-preview1 wasmtime
$ dotnet add package --version 0.15.0-preview1 wasmtime
```

## Usage
_Note that the `--version` option is required because the package is currently prerelease._

Coming soon!
## Introduction

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

```wat
(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](https://www.nuget.org/packages/Wasmtime):

```
$ dotnet add package --version 0.15.0-preview1 wasmtime
```

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

```c#
using System;
using Wasmtime;

namespace Tutorial
{
class Program
{
static void Main(string[] args)
{
using var host = new Host();

host.DefineFunction(
"",
"hello",
() => Console.WriteLine("Hello from C#!")
);

using var module = host.LoadModuleText(
"hello",
"(module (func $hello (import \"\" \"hello\")) (func (export \"run\") (call $hello)))"
);

using dynamic instance = host.Instantiate(module);
instance.run();
}
}
}
```

This host defines a function called `hello` that simply prints a hello message.

It then loads the module into the host in WebAssembly text format and invokes the module's `run` export.

To run the application, simply use `dotnet`:

```
$ dotnet run
```

This should print `Hello from C#!`.

## Contributing

Coming soon!
### Building

Use `dotnet` to build the repository:

```
$ dotnet build
```

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

### Testing

Use `dotnet` to run the unit tests:

```
$ dotnet test
```

### Creating the NuGet package

Use `dotnet` to create a NuGet package:

```
$ cd src
$ dotnet pack -c Release
```

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.
8 changes: 5 additions & 3 deletions docs/api/index.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Wasmtime for NET
# .NET embedding of Wasmtime

This is the .NET API for [Wasmtime](https://github.com/bytecodealliance/wasmtime).
This is the [.NET API](https://github.com/bytecodealliance/wasmtime-dotnet) for [Wasmtime](https://github.com/bytecodealliance/wasmtime).

See the [documentation](Wasmtime.html#classes) for the various .NET classes.
See the [documentation](Wasmtime.html#classes) for the various .NET classes.

See the [introduction](/articles/intro.html) for a simple walkthrough of using the .NET embedding for Wasmtime.
142 changes: 25 additions & 117 deletions docs/articles/intro.md
Original file line number Diff line number Diff line change
@@ -1,96 +1,31 @@
# Introduction to Wasmtime for .NET
# Introduction to .NET embedding of Wasmtime

[Wasmtime](https://github.com/bytecodealliance/wasmtime) is a standalone runtime capable of executing [WebAssembly](https://webassembly.org/) outside of a web browser.

Wasmtime for .NET is a .NET API for Wasmtime. It enables .NET developers to easily instantiate and execute WebAssembly modules.
The [.NET embedding of Wasmtime](https://github.com/bytecodealliance/wasmtime-dotnet) enables .NET developers to easily instantiate and execute WebAssembly modules using Wasmtime.

For this tutorial, we will create a WebAssembly module from a program written in Rust and use that WebAssembly module from a .NET Core 3.0 application.
For this tutorial, we will create a WebAssembly module and use that WebAssembly module from a .NET Core 3.1 application.

# Creating a simple WebAssembly module
# A simple WebAssembly module

One of the reasons why WebAssembly is so exciting is that [many languages are able to target WebAssembly](https://github.com/appcypher/awesome-wasm-langs). This means, for example, a plugin model based on WebAssembly could enable developers to write sandboxed, cross-platform plugins in any number of languages.

Here I've decided to use [Rust](https://www.rust-lang.org/) for the implementation of the WebAssembly module. Rust is a modern systems programming language that can easily target WebAssembly.

If you wish to skip creating the WebAssembly module, download the [prebuilt WebAssembly module](https://raw.githubusercontent.com/bytecodealliance/wasmtime/master/crates/misc/dotnet/docs/wasm/intro/hello.wasm) from this tutorial, copy it to your .NET project directory, and continue from the _[Using the WebAssembly module from .NET](#using-the-webassembly-module-from-net)_ section.

## Installing a Rust toolchain

To get started with Rust, install [rustup](https://rustup.rs/), the manager for Rust toolchains.

This will install both a `rustup` command and a `cargo` command (for the active Rust toolchain) to your PATH.

## Installing the WebAssembly target

To target WebAssembly with the active Rust toolchain, install the WebAssembly [target triple](https://forge.rust-lang.org/release/platform-support.html):

```text
rustup target add wasm32-unknown-unknown
```

## Creating the Rust project

Create a new Rust library project named `hello`:

```text
cargo new --lib hello
cd hello
```

To target WebAssembly, the library needs to be built as a `cdylib` (dynamic library) rather than the default of a static Rust library. Add the following to the `Cargo.toml` file in the project root:

```toml
[lib]
crate-type = ["cdylib"]
```

## Implementing the WebAssembly code

The WebAssembly implementation will import a `print` function from the host environment and pass it a string to print. It will export a `run` function that will invoke the imported `print` function.

Replace the code in `src/lib.rs` with the following Rust code:

```rust
extern "C" {
fn print(address: i32, length: i32);
}

#[no_mangle]
pub unsafe extern fn run() {
let message = "Hello world!";
print(message.as_ptr() as i32, message.len() as i32);
}
```

Note that this example passes the string as a pair of _address and length_. This is because WebAssembly only supports a few core types (such as integers and floats) and a "string" has no native representation in WebAssembly.

In the future, WebAssembly will support [interface types](https://hacks.mozilla.org/2019/08/webassembly-interface-types/) that will enable higher-level abstractions of types like strings so they can be represented in a natural way.

Also note that the _address_ is not actually a physical memory address within the address space of a process but an address within the _[WebAssembly memory](https://hacks.mozilla.org/2017/07/memory-in-webassembly-and-why-its-safer-than-you-think/)_ of the module. Thus the WebAssembly module has no direct access to the memory of the host environment.

## Building the WebAssembly module

Use `cargo build` to build the WebAssembly module:
For this introduction, however, we will use a WebAssembly module in [WebAssembly Text Format](https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format):

```text
cargo build --target wasm32-unknown-unknown --release
(module
(func $hello (import "" "hello"))
(func (export "run") (call $hello))
)
```

This should create a `hello.wasm` file in the `target/wasm32-unknown-unknown/release` directory. We will use `hello.wasm` in the next section of the tutorial.

As this example is very simple and does not require any of the data from the custom sections of the WebAssembly module, you may use `wasm-strip` if you have the [WebAssembly Binary Toolkit](https://github.com/WebAssembly/wabt) installed:

```text
wasm-strip target/wasm32-unknown-unknown/release/hello.wasm
```

The resulting file should be less than 200 bytes.
This module simply imports a `hello` function from the host and exports a `run` function that calls the imported function.

# Using the WebAssembly module from .NET

## Installing a .NET Core 3.0 SDK

Install a [.NET Core 3.0 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.0) for your platform if you haven't already.
Install a [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.1) for your platform if you haven't already.

This will add a `dotnet` command to your PATH.

Expand All @@ -104,17 +39,17 @@ cd tutorial
dotnet new console
```

## Referencing the Wasmtime for .NET package
## Referencing the Wasmtime package

To use Wasmtime for .NET from the project, we need to add a reference to the [Wasmtime NuGet package](https://www.nuget.org/packages/Wasmtime):
To use the .NET embedding of Wasmtime from the project, we need to add a reference to the [Wasmtime NuGet package](https://www.nuget.org/packages/Wasmtime):

```text
dotnet add package --version 0.14.0-preview1 wasmtime
dotnet add package --version 0.15.0-preview1 wasmtime
```

_Note that the `--version` option is required because the package is currently prerelease._

This will add a `PackageReference` to the project file so that Wasmtime for .NET can be used.
This will add a `PackageReference` to the project file so that .NET embedding for Wasmtime can be used.

## Implementing the .NET code

Expand All @@ -133,14 +68,12 @@ namespace Tutorial
using var host = new Host();

host.DefineFunction(
"env",
"print",
(Caller caller, int address, int length) => {
Console.WriteLine(caller.GetMemory("mem").ReadString(address, length));
}
"",
"hello",
() => Console.WriteLine("Hello from C#!")
);

using var module = host.LoadModule("hello.wasm");
using var module = host.LoadModuleText("hello", "(module (func $hello (import \"\" \"hello\")) (func (export \"run\") (call $hello)))");

using dynamic instance = host.Instantiate(module);
instance.run();
Expand All @@ -149,20 +82,11 @@ namespace Tutorial
}
```

The `Host` class is responsible for implementing an environment that WebAssembly modules can execute in.

Here we are creating a host that is implementing a function named `print` in the `env` module, which is the default import module name for WebAssembly modules compiled using the Rust toolchain.
The [`Host`](https://bytecodealliance.github.io/wasmtime-dotnet/api/Wasmtime.Host.html) class is responsible for providing an environment that WebAssembly modules can execute in.

A WebAssembly module _instantiation_ is the stateful representation of a module that can be executed. Here, the code is casting the [`Instance`](https://peterhuene.github.io/wasmtime.net/api/Wasmtime.Instance.html) to [`dynamic`](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/using-type-dynamic) which allows us to easily invoke the `run` function that was exported by the WebAssembly module.
Here we are creating a host that is defining a function named `hello` that simply prints `Hello from C#!` when called from WebAssembly.

Alternatively, the `run` function could be invoked without using the runtime binding of the `dynamic` feature like this:

```c#
...
using var instance = host.Instantiate(module);
instance.Externs.Functions[0].Invoke();
...
```
A WebAssembly module _instantiation_ is the stateful representation of a module that can be executed. Here, the code is casting the [`Instance`](https://bytecodealliance.github.io/wasmtime-dotnet/api/Wasmtime.Instance.html) to [`dynamic`](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/using-type-dynamic) which allows us to easily invoke the `run` function that was exported by the WebAssembly module.

## Building the .NET application

Expand All @@ -172,34 +96,18 @@ Use `dotnet build` to build the .NET application:
dotnet build
```

This will create a `tutorial.dll` in the `bin/Debug/netcoreapp3.0` directory that implements the .NET Core application. An executable `tutorial` (or `tutorial.exe` on Windows) should also be present in the same directory to run the application.
This will create a `tutorial` (or `tutorial.exe` on Windows) executable in the `bin/Debug/netcoreapp3.0` directory that implements the .NET Core application.

## Running the .NET application

Before running the application, we need to copy the `hello.wasm` file to the project directory.

Once the WebAssembly module is present in project directory, we can run the application:
To run the .NET application, simply invoke the executable file or use `dotnet`:

```text
dotnet run
```

Alternatively, we can execute the program directly without building the application again:

```text
bin/Debug/netcoreapp3.0/tutorial
```

This should result in the following output:

```text
Hello world!
Hello from C#!
```

# Wrapping up

We did it! We executed a function written in Rust from .NET and a function implemented in .NET from Rust without much trouble at all. And, thanks to the design of WebAssembly, the Rust code was effectively sandboxed from accessing the memory of the .NET application.

Hopefully this introduction to Wasmtime for .NET has offered a small glipse of the potential of using WebAssembly from .NET.

One last note: _Wasmtime for .NET is currently in a very early stage of development and the API might change dramatically in the future_.
2 changes: 1 addition & 1 deletion docs/articles/toc.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
- name: Introduction to Wasmtime for .NET
- name: Introduction to the .NET embedding of Wasmtime
href: intro.md
2 changes: 1 addition & 1 deletion docs/docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"disableGitFeatures": false,
"globalMetadata": {
"_gitContribute": {
"repo": "https://github.com/bytecodealliance/wasmtime",
"repo": "https://github.com/bytecodealliance/wasmtime-dotnet",
"branch": "master"
}
}
Expand Down
8 changes: 3 additions & 5 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Wasmtime for .NET
# .NET embedding of Wasmtime

A .NET API for [Wasmtime](https://github.com/bytecodealliance/wasmtime).
[Wasmtime](https://github.com/bytecodealliance/wasmtime) is a standalone runtime for [WebAssembly](https://webassembly.org/), using the Cranelift JIT compiler.

Wasmtime is a standalone runtime for [WebAssembly](https://webassembly.org/), using the Cranelift JIT compiler.

Wasmtime for .NET enables .NET code to instantiate WebAssembly modules and to interact with them in-process.
The [.NET embedding of Wasmtime](https://github.com/bytecodealliance/wasmtime-dotnet) enables .NET code to instantiate WebAssembly modules and to interact with them in-process.
2 changes: 1 addition & 1 deletion docs/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
href: api/
homepage: api/index.md
- name: GitHub Repo
href: https://github.com/bytecodealliance/wasmtime
href: https://github.com/bytecodealliance/wasmtime-dotnet
4 changes: 2 additions & 2 deletions src/Host.cs
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12
/// <param name="name">The name of the host variable.</param>
/// <param name="initialValue">The initial value of the host variable.</param>
/// <typeparam name="T">The type of the host variable.</typeparam>
/// <returns>Returns a new <see cref="Global"/> representing the defined global variable.</returns>
/// <returns>Returns a new <see cref="Global{T}"/> representing the defined global variable.</returns>
public Global<T> DefineGlobal<T>(string moduleName, string name, T initialValue)
{
CheckDisposed();
Expand Down Expand Up @@ -498,7 +498,7 @@ public Global<T> DefineGlobal<T>(string moduleName, string name, T initialValue)
/// <param name="name">The name of the host variable.</param>
/// <param name="initialValue">The initial value of the host variable.</param>
/// <typeparam name="T">The type of the host variable.</typeparam>
/// <returns>Returns a new <see cref="MutableGlobal"/> representing the defined mutable global variable.</returns>
/// <returns>Returns a new <see cref="MutableGlobal{T}"/> representing the defined mutable global variable.</returns>
public MutableGlobal<T> DefineMutableGlobal<T>(string moduleName, string name, T initialValue)
{
CheckDisposed();
Expand Down
Loading