Skip to content

Commit

Permalink
Adds wasm_exec for Go generated Wasm
Browse files Browse the repository at this point in the history
Signed-off-by: Adrian Cole <adrian@tetrate.io>
  • Loading branch information
Adrian Cole committed Jul 22, 2022
1 parent 866fac2 commit e758e20
Show file tree
Hide file tree
Showing 22 changed files with 2,206 additions and 1 deletion.
2 changes: 1 addition & 1 deletion RATIONALE.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ value (possibly `PWD`). Those unable to control the compiled code should only
use absolute paths in configuration.

See
* https://github.com/golang/go/blob/go1.19beta1/src/syscall/fs_js.go#L324
* https://github.com/golang/go/blob/go1.19rc2/src/syscall/fs_js.go#L324
* https://github.com/WebAssembly/wasi-libc/pull/214#issue-673090117

### FdPrestatDirName
Expand Down
43 changes: 43 additions & 0 deletions examples/wasi/cat_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package main

import (
"context"
"io/fs"
"testing"

"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/internal/testing/maintester"
"github.com/tetratelabs/wazero/internal/testing/require"
"github.com/tetratelabs/wazero/wasi_snapshot_preview1"
)

// Test_main ensures the following will work:
Expand All @@ -21,3 +25,42 @@ func Test_main(t *testing.T) {
})
}
}

func Benchmark_main(b *testing.B) {
// Choose the context to use for function calls.
ctx := context.Background()

// Create a new WebAssembly Runtime.
r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfig().
// Enable WebAssembly 2.0 support, which is required for TinyGo 0.24+.
WithWasmCore2())
defer r.Close(ctx) // This closes everything this Runtime created.

// Instantiate WASI, which implements system I/O such as console output.
if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil {
b.Fatal(err)
}

// Compile the WebAssembly module using the default configuration.
code, err := r.CompileModule(ctx, catWasmTinyGo, wazero.NewCompileConfig())
if err != nil {
b.Fatal(err)
}

rooted, err := fs.Sub(catFS, "testdata")
if err != nil {
b.Fatal(err)
}
config := wazero.NewModuleConfig().WithFS(rooted).WithArgs("cat", "/test.txt")

b.Run("tinygo cat", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
if mod, err := r.InstantiateModule(ctx, code, config); err != nil {
b.Fatal(err)
} else {
mod.Close(ctx)
}
}
})
}
1 change: 1 addition & 0 deletions examples/wasm_exec/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
wasi
4 changes: 4 additions & 0 deletions examples/wasm_exec/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## WASI example

This example shows how to use I/O in your WebAssembly modules using WASI
(WebAssembly System Interface).
66 changes: 66 additions & 0 deletions examples/wasm_exec/cat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package main

import (
"context"
"embed"
_ "embed"
"io/fs"
"log"
"os"

"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/sys"
"github.com/tetratelabs/wazero/wasm_exec"
)

// catFS is an embedded filesystem limited to test.txt
//go:embed testdata/test.txt
var catFS embed.FS

// catWasm was compiled the TinyGo source testdata/cat.go
//go:embed testdata/cat.wasm
var catWasm []byte

// main writes an input file to stdout, just like `cat`.
//
// This is a basic introduction to the WebAssembly System Interface (WASI).
// See https://github.com/WebAssembly/WASI
func main() {
// Choose the context to use for function calls.
ctx := context.Background()

// Create a new WebAssembly Runtime.
r := wazero.NewRuntime()
defer r.Close(ctx) // This closes everything this Runtime created.

// Compile the WebAssembly module using the default configuration.
compiled, err := r.CompileModule(ctx, catWasm, wazero.NewCompileConfig())
if err != nil {
log.Panicln(err)
}

we, err := wasm_exec.NewBuilder().Build(ctx, r)
if err != nil {
log.Panicln(err)
}

// Since wazero uses fs.FS, we can use standard libraries to do things like trim the leading path.
rooted, err := fs.Sub(catFS, "testdata")
if err != nil {
log.Panicln(err)
}

// Create a configuration for running main, overriding defaults (which discard stdout and has no file system).
config := wazero.NewModuleConfig().
WithFS(rooted).
WithStdout(os.Stdout).
WithStderr(os.Stderr).
WithArgs("cat", os.Args[1])

err = we.Run(ctx, compiled, config)
if exitErr, ok := err.(*sys.ExitError); ok && exitErr.ExitCode() != 0 {
log.Panicln(err)
} else if !ok {
log.Panicln(err)
}
}
57 changes: 57 additions & 0 deletions examples/wasm_exec/cat_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package main

import (
"context"
"io/fs"
"testing"

"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/internal/testing/maintester"
"github.com/tetratelabs/wazero/internal/testing/require"
"github.com/tetratelabs/wazero/sys"
"github.com/tetratelabs/wazero/wasm_exec"
)

// Test_main ensures the following will work:
//
// go run cat.go /test.txt
func Test_main(t *testing.T) {
stdout, stderr := maintester.TestMain(t, main, "cat", "/test.txt")
require.Equal(t, "", stderr)
require.Equal(t, "greet filesystem\n", stdout)
}

func Benchmark_main(b *testing.B) {
// Choose the context to use for function calls.
ctx := context.Background()

// Create a new WebAssembly Runtime.
r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfigCompiler())
defer r.Close(ctx) // This closes everything this Runtime created.

// Compile the WebAssembly module using the default configuration.
compiled, err := r.CompileModule(ctx, catWasm, wazero.NewCompileConfig())
if err != nil {
b.Fatal(err)
}

we, err := wasm_exec.NewBuilder().Build(ctx, r)

rooted, err := fs.Sub(catFS, "testdata")
if err != nil {
b.Fatal(err)
}
config := wazero.NewModuleConfig().WithFS(rooted).WithArgs("cat", "/test.txt")

b.Run("go cat", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
err = we.Run(ctx, compiled, config)
if exitErr, ok := err.(*sys.ExitError); ok && exitErr.ExitCode() != 0 {
b.Fatal(err)
} else if !ok {
b.Fatal(err)
}
}
})
}
21 changes: 21 additions & 0 deletions examples/wasm_exec/testdata/cat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"io/ioutil"
"os"
)

// main is the same as wasi: "concatenate and print files."
func main() {
// Start at arg[1] because args[0] is the program name.
for i := 1; i < len(os.Args); i++ {
// Intentionally use ioutil.ReadFile instead of os.ReadFile for TinyGo.
bytes, err := ioutil.ReadFile(os.Args[i])
if err != nil {
os.Exit(1)
}

// Use write to avoid needing to worry about Windows newlines.
os.Stdout.Write(bytes)
}
}
Binary file added examples/wasm_exec/testdata/cat.wasm
Binary file not shown.
1 change: 1 addition & 0 deletions examples/wasm_exec/testdata/sub/test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
greet sub dir
1 change: 1 addition & 0 deletions examples/wasm_exec/testdata/test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
greet filesystem
5 changes: 5 additions & 0 deletions internal/wasm/call_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ func (f *FunctionInstance) Call(ctx context.Context, params ...uint64) (ret []ui
return
}

// GlobalVal is an internal hack to get the lower 64 bits of a global.
func (m *CallContext) GlobalVal(idx Index) uint64 {
return m.module.Globals[idx].Val
}

// ExportedGlobal implements the same method as documented on api.Module.
func (m *CallContext) ExportedGlobal(name string) api.Global {
exp, err := m.module.getExport(name, ExternTypeGlobal)
Expand Down
Loading

0 comments on commit e758e20

Please sign in to comment.