Skip to content

antJack/wasmer-go

 
 

Repository files navigation


A complete and mature WebAssembly runtime for Go based on Wasmer.

You are seeing the readme for the latest Wasmer Go version, if you are using an older version, please go to:

Features

  • Easy to use: The wasmer API mimics the standard WebAssembly API,
  • Fast: wasmer executes the WebAssembly modules as fast as possible, close to native speed,
  • Safe: All calls to WebAssembly will be fast, but more importantly, completely safe and sandboxed.

Install

To install the library, follow the classical:

# Enable cgo
$ export CGO_ENABLED=1; export CC=gcc;

$ go get github.com/wasmerio/wasmer-go/wasmer

Note: Wasmer doesn't work on Windows yet.

If the pre-compiled shared libraries are not compatible with your system, you can try installing it manuallly.

Examples

Basic example: Exported function

There is a toy program in wasmer/test/testdata/examples/simple.rs, written in Rust (or any other language that compiles to WebAssembly):

#[no_mangle]
pub extern fn sum(x: i32, y: i32) -> i32 {
    x + y
}

After compilation to WebAssembly, the wasmer/test/testdata/examples/simple.wasm binary file is generated. (Download it).

Then, we can execute it in Go:

package main

import (
	"fmt"
	wasm "github.com/wasmerio/wasmer-go/wasmer"
)

func main() {
    wasmBytes, _ := wasm.ReadBytes("simple.wasm")

    engine := wasmer.NewEngine()
    store := wasmer.NewStore(engine)

    // Compiles the module
    module, _ := wasmer.NewModule(store, wasmBytes)

    // Instantiates the module
    importObject := wasmer.NewImportObject()
    instance, _ := wasmer.NewInstance(module, importObject)

    // Gets the `sum` exported function from the WebAssembly instance.
    sum, _ := instance.Exports.GetFunction("sum")


    // Calls that exported function with Go standard values. The WebAssembly
    // types are inferred and values are casted automatically.
    result, _ := sum(5, 37)

    fmt.Println(result) // 42!
}

Imported function

A WebAssembly module can export functions, this is how to run a WebAssembly function, like we did in the previous example with instance.Exports["sum"](1, 2). Nonetheless, a WebAssembly module can depend on “extern functions”, then called imported functions. For instance, let's consider the basic following Rust program:

extern {
    fn sum(x: i32, y: i32) -> i32;
}

#[no_mangle]
pub extern fn add1(x: i32, y: i32) -> i32 {
    unsafe { sum(x, y) + 1 }
}

In this case, the add1 function is a WebAssembly exported function, whilst the sum function is a WebAssembly imported function (the WebAssembly instance needs to import it to complete the program). Good news: We can write the implementation of the sum function directly in Go!

First, we need to declare the sum function signature in C inside a Go comment (with the help of cgo):

package main

// #include <stdlib.h>
//
// extern int32_t sum(void *context, int32_t x, int32_t y);
import "C"

Second, we declare the sum function implementation in Go. Notice the //export which is the way cgo uses to map Go code to C code.

//export sum
func sum(x int32, y int32) int32 {
	return x + y
}

Third, we use NewImports to create the WebAssembly imports. In this code:

  • "sum" is the imported function name,
  • sum is the Go function pointer, and
  • C.sum is the cgo function pointer.
importObject := wasmer.NewImportObject()

Finally, we use NewInstanceWithImports to inject the imports:

wasmBytes, _ := wasm.ReadBytes("imported_function.wasm")

engine := wasmer.NewEngine()
store := wasmer.NewStore(engine)

// Compiles the module
module, _ := wasmer.NewModule(store, wasmBytes)

// Instantiates the Module
instance, _ := wasmer.NewInstance(module, importObject)

// Gets the `sum` exported function from the WebAssembly instance.
add1, _ := instance.Exports.GetFunction("add1")

// Gets and calls the `add1` exported function from the WebAssembly instance.
results, _ := add1(1, 2)

fmt.Println(result)
//   add1(1, 2)
// = sum(1 + 2) + 1
// = 1 + 2 + 1
// = 4
// QED

Read the memory

A WebAssembly instance has a linear memory. Let's see how to read it. Consider the following Rust program:

#[no_mangle]
pub extern fn return_hello() -> *const u8 {
    b"Hello, World!\0".as_ptr()
}

The return_hello function returns a pointer to a string. This string is stored in the WebAssembly memory. Let's read it.

wasmBytes, _ := wasm.ReadBytes("simple.wasm")

engine := wasmer.NewEngine()
store := wasmer.NewStore(engine)

// Compiles the module
module, _ := wasmer.NewModule(store, wasmBytes)

// Instantiates the module
importObject := wasmer.NewImportObject()
instance, _ := wasmer.NewInstance(module, importObject)

// Calls the `return_hello` exported function.
// This function returns a pointer to a string.
result, _ := instance.GetFunction("return_hello")

// Gets the pointer value as an integer.
pointer := result.ToI32()

// Reads the memory.
memory := instance.exports.GetMemory("memory").Data()

fmt.Println(string(memory[pointer : pointer+13])) // Hello, World!

In this example, we already know the string length, and we use a slice to read a portion of the memory directly. Notice that the string terminates by a null byte, which means we could iterate over the memory starting from pointer until a null byte is met; that's a similar approach.

For a more complete example, see the Greet Example.

Documentation

The documentation can be read online on pkg.go.dev. It contains function descriptions, short examples, long examples etc. Everything one need to start using Wasmer with Go!

Also, there is this article written for the announcement that introduces the project: Announcing the fastest WebAssembly runtime for Go: wasmer.

Development

The Go library is written in Go and Rust.

To build both parts, run the following commands:

$ just build-runtime
$ just build

To build the Go part, run:

$ just build

(Yes, you need just).

Manual Install

You can install Wasmer manually with the following command:

$ just build-runtime
$ just build
$ go install github.com/wasmerio/wasmer-go/wasmer

If you are a bazel user, add following to your WORKSPACE file

git_repository(
    name = "com_github_wasmerio_wasmer_go",
    remote = "https://github.com/wasmerio/wasmer-go",
    commit = "",
)

Testing

Once the library is build, run the following command:

$ just test

Benchmarks

We compared Wasmer to Wagon and Life. The benchmarks are in benchmarks/. The computer that ran these benchmarks is a MacBook Pro 15" from 2016, 2.9Ghz Core i7 with 16Gb of memory. Here are the results in a table (the lower the ratio is, the better):

Benchmark Runtime Time (ms) Ratio
N-Body Wasmer 42.078
Wagon 1841.950 44×
Life 1976.215 47×
Fibonacci (recursive) Wasmer 28.559
Wagon 3238.050 113×
Life 3029.209 106×
Pollard rho 128 Wasmer 37.478
Wagon 2165.563 58×
Life 2407.752 64×

While both Life and Wagon provide on average the same speed, Wasmer is on average 72 times faster.

Put on a graph, it looks like this (reminder: the lower, the better):

Benchmark results

What is WebAssembly?

Quoting the WebAssembly site:

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust, enabling deployment on the web for client and server applications.

About speed:

WebAssembly aims to execute at native speed by taking advantage of common hardware capabilities available on a wide range of platforms.

About safety:

WebAssembly describes a memory-safe, sandboxed execution environment […].

License

The entire project is under the MIT License. Please read the LICENSE file.

Packages

No packages published

Languages

  • Go 75.5%
  • C 23.1%
  • Starlark 1.4%