Makes all examples and docs use Runtime.Close (#537)

This removes tedium in our examples and docs by using `Runtime.Close`
instead of tracking everything. Internal tests still track too much, but
anyway at least this stops suggesting others should do it.

This also changes our examples to use log.PanicXX so that the line
number goes into the console output.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2022-05-10 12:08:25 +08:00
committed by GitHub
parent 8dd797e108
commit 03bfa31928
17 changed files with 127 additions and 109 deletions

View File

@@ -24,11 +24,20 @@ func main() {
// Read a WebAssembly binary containing an exported "fac" function. // Read a WebAssembly binary containing an exported "fac" function.
// * Ex. (func (export "fac") (param i64) (result i64) ... // * Ex. (func (export "fac") (param i64) (result i64) ...
source, _ := os.ReadFile("./path/to/fac.wasm") source, err := os.ReadFile("./path/to/fac.wasm")
if err != nil {
log.Panicln(err)
}
// Create a new WebAssembly Runtime.
r := wazero.NewRuntime()
defer r.Close(ctx) // This closes everything this Runtime created.
// Instantiate the module and return its exported functions // Instantiate the module and return its exported functions
module, _ := wazero.NewRuntime().InstantiateModuleFromCode(ctx, source) module, err := r.InstantiateModuleFromCode(ctx, source)
defer module.Close(ctx) if err != nil {
log.Panicln(err)
}
// Discover 7! is 5040 // Discover 7! is 5040
fmt.Println(module.ExportedFunction("fac").Call(ctx, 7)) fmt.Println(module.ExportedFunction("fac").Call(ctx, 7))
@@ -57,15 +66,14 @@ For example, you can grant WebAssembly code access to your console by exporting
a function written in Go. The below function can be imported into standard a function written in Go. The below function can be imported into standard
WebAssembly as the module "env" and the function name "log_i32". WebAssembly as the module "env" and the function name "log_i32".
```go ```go
env, err := r.NewModuleBuilder("env"). _, err := r.NewModuleBuilder("env").
ExportFunction("log_i32", func(v uint32) { ExportFunction("log_i32", func(v uint32) {
fmt.Println("log_i32 >>", v) fmt.Println("log_i32 >>", v)
}). }).
Instantiate(ctx) Instantiate(ctx)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer env.Close(ctx)
``` ```
The WebAssembly community has [subgroups][4] which maintain work that may not The WebAssembly community has [subgroups][4] which maintain work that may not
@@ -78,12 +86,13 @@ bundles an implementation. That way, you don't have to write these functions.
For example, here's how you can allow WebAssembly modules to read For example, here's how you can allow WebAssembly modules to read
"/work/home/a.txt" as "/a.txt" or "./a.txt": "/work/home/a.txt" as "/a.txt" or "./a.txt":
```go ```go
wm, err := wasi.InstantiateSnapshotPreview1(ctx, r) _, err := wasi.InstantiateSnapshotPreview1(ctx, r)
defer wm.Close(ctx) if err != nil {
log.Panicln(err)
}
config := wazero.NewModuleConfig().WithFS(os.DirFS("/work/home")) config := wazero.NewModuleConfig().WithFS(os.DirFS("/work/home"))
module, err := r.InstantiateModule(ctx, compiled, config) module, err := r.InstantiateModule(ctx, compiled, config)
defer module.Close(ctx)
... ...
``` ```

View File

@@ -105,6 +105,7 @@ func ValueTypeName(t ValueType) string {
// Module return functions exported in a module, post-instantiation. // Module return functions exported in a module, post-instantiation.
// //
// Note: Closing the wazero.Runtime closes any Module it instantiated.
// Note: This is an interface for decoupling, not third-party implementations. All implementations are in wazero. // Note: This is an interface for decoupling, not third-party implementations. All implementations are in wazero.
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#external-types%E2%91%A0 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#external-types%E2%91%A0
type Module interface { type Module interface {

View File

@@ -15,6 +15,9 @@ import (
// Ex. Below defines and instantiates a module named "env" with one function: // Ex. Below defines and instantiates a module named "env" with one function:
// //
// ctx := context.Background() // ctx := context.Background()
// r := wazero.NewRuntime()
// defer r.Close(ctx) // This closes everything this Runtime created.
//
// hello := func() { // hello := func() {
// fmt.Fprintln(stdout, "hello!") // fmt.Fprintln(stdout, "hello!")
// } // }
@@ -29,10 +32,8 @@ import (
// Compile(ctx, wazero.NewCompileConfig()) // Compile(ctx, wazero.NewCompileConfig())
// //
// env1, _ := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("env.1")) // env1, _ := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("env.1"))
// defer env1.Close(ctx)
// //
// env2, _ := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("env.2")) // env2, _ := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("env.2"))
// defer env2.Close(ctx)
// //
// Notes: // Notes:
// * ModuleBuilder is mutable. WithXXX functions return the same instance for chaining. // * ModuleBuilder is mutable. WithXXX functions return the same instance for chaining.
@@ -157,10 +158,13 @@ type ModuleBuilder interface {
ExportGlobalF64(name string, v float64) ModuleBuilder ExportGlobalF64(name string, v float64) ModuleBuilder
// Compile returns a module to instantiate, or an error if any of the configuration is invalid. // Compile returns a module to instantiate, or an error if any of the configuration is invalid.
//
// Note: Closing the wazero.Runtime closes any CompiledModule it compiled.
Compile(context.Context, CompileConfig) (CompiledModule, error) Compile(context.Context, CompileConfig) (CompiledModule, error)
// Instantiate is a convenience that calls Build, then Runtime.InstantiateModule, using default configuration. // Instantiate is a convenience that calls Build, then Runtime.InstantiateModule, using default configuration.
// //
// Note: Closing the wazero.Runtime closes any api.Module it instantiated.
// Note: Fields in the builder are copied during instantiation: Later changes do not affect the instantiated result. // Note: Fields in the builder are copied during instantiation: Later changes do not affect the instantiated result.
// Note: To avoid using configuration defaults, use Compile instead. // Note: To avoid using configuration defaults, use Compile instead.
Instantiate(context.Context) (api.Module, error) Instantiate(context.Context) (api.Module, error)

View File

@@ -218,6 +218,7 @@ func (c *runtimeConfig) WithWasmCore2() RuntimeConfig {
// CompiledModule is a WebAssembly 1.0 module ready to be instantiated (Runtime.InstantiateModule) as an api.Module. // CompiledModule is a WebAssembly 1.0 module ready to be instantiated (Runtime.InstantiateModule) as an api.Module.
// //
// Note: Closing the wazero.Runtime closes any CompiledModule it compiled.
// Note: In WebAssembly language, this is a decoded, validated, and possibly also compiled module. wazero avoids using // Note: In WebAssembly language, this is a decoded, validated, and possibly also compiled module. wazero avoids using
// the name "Module" for both before and after instantiation as the name conflation has caused confusion. // the name "Module" for both before and after instantiation as the name conflation has caused confusion.
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#semantic-phases%E2%91%A0 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#semantic-phases%E2%91%A0

View File

@@ -27,7 +27,7 @@ func Example() {
(export "add" (func $add)) (export "add" (func $add))
)`)) )`))
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer mod.Close(ctx) defer mod.Close(ctx)
@@ -37,7 +37,7 @@ func Example() {
x, y := uint64(1), uint64(2) x, y := uint64(1), uint64(2)
results, err := add.Call(ctx, x, y) results, err := add.Call(ctx, x, y)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
fmt.Printf("%s: %d + %d = %d\n", mod.Name(), x, y, results[0]) fmt.Printf("%s: %d + %d = %d\n", mod.Name(), x, y, results[0])

View File

@@ -25,24 +25,23 @@ func main() {
// Create a new WebAssembly Runtime. // Create a new WebAssembly Runtime.
r := wazero.NewRuntime() r := wazero.NewRuntime()
defer r.Close(ctx) // This closes everything this Runtime created.
// Instantiate a Go-defined module named "env" that exports a function to // Instantiate a Go-defined module named "env" that exports a function to
// log to the console. // log to the console.
env, err := r.NewModuleBuilder("env"). _, err := r.NewModuleBuilder("env").
ExportFunction("log", logString). ExportFunction("log", logString).
Instantiate(ctx) Instantiate(ctx)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer env.Close(ctx)
// Instantiate a WebAssembly module that imports the "log" function defined // Instantiate a WebAssembly module that imports the "log" function defined
// in "env" and exports "memory" and functions we'll use in this example. // in "env" and exports "memory" and functions we'll use in this example.
mod, err := r.InstantiateModuleFromCode(ctx, greetWasm) mod, err := r.InstantiateModuleFromCode(ctx, greetWasm)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer mod.Close(ctx)
// Get references to WebAssembly functions we'll use in this example. // Get references to WebAssembly functions we'll use in this example.
greet := mod.ExportedFunction("greet") greet := mod.ExportedFunction("greet")
@@ -59,7 +58,7 @@ func main() {
// function could be used to pass binary serialized data to Wasm. // function could be used to pass binary serialized data to Wasm.
results, err := allocate.Call(ctx, nameSize) results, err := allocate.Call(ctx, nameSize)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
namePtr := results[0] namePtr := results[0]
// This pointer was allocated by Rust, but owned by Go, So, we have to // This pointer was allocated by Rust, but owned by Go, So, we have to
@@ -68,21 +67,21 @@ func main() {
// The pointer is a linear memory offset, which is where we write the name. // The pointer is a linear memory offset, which is where we write the name.
if !mod.Memory().Write(ctx, uint32(namePtr), []byte(name)) { if !mod.Memory().Write(ctx, uint32(namePtr), []byte(name)) {
log.Fatalf("Memory.Write(%d, %d) out of range of memory size %d", log.Panicf("Memory.Write(%d, %d) out of range of memory size %d",
namePtr, nameSize, mod.Memory().Size(ctx)) namePtr, nameSize, mod.Memory().Size(ctx))
} }
// Now, we can call "greet", which reads the string we wrote to memory! // Now, we can call "greet", which reads the string we wrote to memory!
_, err = greet.Call(ctx, namePtr, nameSize) _, err = greet.Call(ctx, namePtr, nameSize)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
// Finally, we get the greeting message "greet" printed. This shows how to // Finally, we get the greeting message "greet" printed. This shows how to
// read-back something allocated by Rust. // read-back something allocated by Rust.
ptrSize, err := greeting.Call(ctx, namePtr, nameSize) ptrSize, err := greeting.Call(ctx, namePtr, nameSize)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
greetingPtr := uint32(ptrSize[0] >> 32) greetingPtr := uint32(ptrSize[0] >> 32)
greetingSize := uint32(ptrSize[0]) greetingSize := uint32(ptrSize[0])
@@ -92,7 +91,7 @@ func main() {
// The pointer is a linear memory offset, which is where we write the name. // The pointer is a linear memory offset, which is where we write the name.
if bytes, ok := mod.Memory().Read(ctx, greetingPtr, greetingSize); !ok { if bytes, ok := mod.Memory().Read(ctx, greetingPtr, greetingSize); !ok {
log.Fatalf("Memory.Read(%d, %d) out of range of memory size %d", log.Panicf("Memory.Read(%d, %d) out of range of memory size %d",
greetingPtr, greetingSize, mod.Memory().Size(ctx)) greetingPtr, greetingSize, mod.Memory().Size(ctx))
} else { } else {
fmt.Println("go >>", string(bytes)) fmt.Println("go >>", string(bytes))
@@ -102,7 +101,7 @@ func main() {
func logString(ctx context.Context, m api.Module, offset, byteCount uint32) { func logString(ctx context.Context, m api.Module, offset, byteCount uint32) {
buf, ok := m.Memory().Read(ctx, offset, byteCount) buf, ok := m.Memory().Read(ctx, offset, byteCount)
if !ok { if !ok {
log.Fatalf("Memory.Read(%d, %d) out of range", offset, byteCount) log.Panicf("Memory.Read(%d, %d) out of range", offset, byteCount)
} }
fmt.Println(string(buf)) fmt.Println(string(buf))
} }

View File

@@ -26,32 +26,29 @@ func main() {
// Create a new WebAssembly Runtime. // Create a new WebAssembly Runtime.
r := wazero.NewRuntime() r := wazero.NewRuntime()
defer r.Close(ctx) // This closes everything this Runtime created.
// Instantiate a Go-defined module named "env" that exports a function to // Instantiate a Go-defined module named "env" that exports a function to
// log to the console. // log to the console.
env, err := r.NewModuleBuilder("env"). _, err := r.NewModuleBuilder("env").
ExportFunction("log", logString). ExportFunction("log", logString).
Instantiate(ctx) Instantiate(ctx)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer env.Close(ctx)
// Note: testdata/greet.go doesn't use WASI, but TinyGo needs it to // Note: testdata/greet.go doesn't use WASI, but TinyGo needs it to
// implement functions such as panic. // implement functions such as panic.
wm, err := wasi.InstantiateSnapshotPreview1(ctx, r) if _, err = wasi.InstantiateSnapshotPreview1(ctx, r); err != nil {
if err != nil { log.Panicln(err)
log.Fatal(err)
} }
defer wm.Close(ctx)
// Instantiate a WebAssembly module that imports the "log" function defined // Instantiate a WebAssembly module that imports the "log" function defined
// in "env" and exports "memory" and functions we'll use in this example. // in "env" and exports "memory" and functions we'll use in this example.
mod, err := r.InstantiateModuleFromCode(ctx, greetWasm) mod, err := r.InstantiateModuleFromCode(ctx, greetWasm)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer mod.Close(ctx)
// Get references to WebAssembly functions we'll use in this example. // Get references to WebAssembly functions we'll use in this example.
greet := mod.ExportedFunction("greet") greet := mod.ExportedFunction("greet")
@@ -69,7 +66,7 @@ func main() {
// function could be used to pass binary serialized data to Wasm. // function could be used to pass binary serialized data to Wasm.
results, err := malloc.Call(ctx, nameSize) results, err := malloc.Call(ctx, nameSize)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
namePtr := results[0] namePtr := results[0]
// This pointer is managed by TinyGo, but TinyGo is unaware of external usage. // This pointer is managed by TinyGo, but TinyGo is unaware of external usage.
@@ -78,28 +75,28 @@ func main() {
// The pointer is a linear memory offset, which is where we write the name. // The pointer is a linear memory offset, which is where we write the name.
if !mod.Memory().Write(ctx, uint32(namePtr), []byte(name)) { if !mod.Memory().Write(ctx, uint32(namePtr), []byte(name)) {
log.Fatalf("Memory.Write(%d, %d) out of range of memory size %d", log.Panicf("Memory.Write(%d, %d) out of range of memory size %d",
namePtr, nameSize, mod.Memory().Size(ctx)) namePtr, nameSize, mod.Memory().Size(ctx))
} }
// Now, we can call "greet", which reads the string we wrote to memory! // Now, we can call "greet", which reads the string we wrote to memory!
_, err = greet.Call(ctx, namePtr, nameSize) _, err = greet.Call(ctx, namePtr, nameSize)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
// Finally, we get the greeting message "greet" printed. This shows how to // Finally, we get the greeting message "greet" printed. This shows how to
// read-back something allocated by TinyGo. // read-back something allocated by TinyGo.
ptrSize, err := greeting.Call(ctx, namePtr, nameSize) ptrSize, err := greeting.Call(ctx, namePtr, nameSize)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
// Note: This pointer is still owned by TinyGo, so don't try to free it! // Note: This pointer is still owned by TinyGo, so don't try to free it!
greetingPtr := uint32(ptrSize[0] >> 32) greetingPtr := uint32(ptrSize[0] >> 32)
greetingSize := uint32(ptrSize[0]) greetingSize := uint32(ptrSize[0])
// The pointer is a linear memory offset, which is where we write the name. // The pointer is a linear memory offset, which is where we write the name.
if bytes, ok := mod.Memory().Read(ctx, greetingPtr, greetingSize); !ok { if bytes, ok := mod.Memory().Read(ctx, greetingPtr, greetingSize); !ok {
log.Fatalf("Memory.Read(%d, %d) out of range of memory size %d", log.Panicf("Memory.Read(%d, %d) out of range of memory size %d",
greetingPtr, greetingSize, mod.Memory().Size(ctx)) greetingPtr, greetingSize, mod.Memory().Size(ctx))
} else { } else {
fmt.Println("go >>", string(bytes)) fmt.Println("go >>", string(bytes))
@@ -109,7 +106,7 @@ func main() {
func logString(ctx context.Context, m api.Module, offset, byteCount uint32) { func logString(ctx context.Context, m api.Module, offset, byteCount uint32) {
buf, ok := m.Memory().Read(ctx, offset, byteCount) buf, ok := m.Memory().Read(ctx, offset, byteCount)
if !ok { if !ok {
log.Fatalf("Memory.Read(%d, %d) out of range", offset, byteCount) log.Panicf("Memory.Read(%d, %d) out of range", offset, byteCount)
} }
fmt.Println(string(buf)) fmt.Println(string(buf))
} }

View File

@@ -19,6 +19,7 @@ func main() {
// Create a new WebAssembly Runtime. // Create a new WebAssembly Runtime.
r := wazero.NewRuntime() r := wazero.NewRuntime()
defer r.Close(ctx) // This closes everything this Runtime created.
// Add a module to the runtime named "wasm/math" which exports one function "add", implemented in WebAssembly. // Add a module to the runtime named "wasm/math" which exports one function "add", implemented in WebAssembly.
wasm, err := r.InstantiateModuleFromCode(ctx, []byte(`(module $wasm/math wasm, err := r.InstantiateModuleFromCode(ctx, []byte(`(module $wasm/math
@@ -30,9 +31,8 @@ func main() {
(export "add" (func $add)) (export "add" (func $add))
)`)) )`))
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer wasm.Close(ctx)
// Add a module to the runtime named "host/math" which exports one function "add", implemented in Go. // Add a module to the runtime named "host/math" which exports one function "add", implemented in Go.
host, err := r.NewModuleBuilder("host/math"). host, err := r.NewModuleBuilder("host/math").
@@ -40,9 +40,8 @@ func main() {
return v1 + v2 return v1 + v2
}).Instantiate(ctx) }).Instantiate(ctx)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer host.Close(ctx)
// Read two args to add. // Read two args to add.
x, y := readTwoArgs() x, y := readTwoArgs()
@@ -52,7 +51,7 @@ func main() {
add := mod.ExportedFunction("add") add := mod.ExportedFunction("add")
results, err := add.Call(ctx, x, y) results, err := add.Call(ctx, x, y)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
fmt.Printf("%s: %d + %d = %d\n", mod.Name(), x, y, results[0]) fmt.Printf("%s: %d + %d = %d\n", mod.Name(), x, y, results[0])
@@ -62,12 +61,12 @@ func main() {
func readTwoArgs() (uint64, uint64) { func readTwoArgs() (uint64, uint64) {
x, err := strconv.ParseUint(os.Args[1], 10, 64) x, err := strconv.ParseUint(os.Args[1], 10, 64)
if err != nil { if err != nil {
log.Fatalf("invalid arg %v: %v", os.Args[1], err) log.Panicf("invalid arg %v: %v", os.Args[1], err)
} }
y, err := strconv.ParseUint(os.Args[2], 10, 64) y, err := strconv.ParseUint(os.Args[2], 10, 64)
if err != nil { if err != nil {
log.Fatalf("invalid arg %v: %v", os.Args[2], err) log.Panicf("invalid arg %v: %v", os.Args[2], err)
} }
return x, y return x, y
} }

View File

@@ -22,6 +22,7 @@ func main() {
// Create a new WebAssembly Runtime. // Create a new WebAssembly Runtime.
r := wazero.NewRuntime() r := wazero.NewRuntime()
defer r.Close(ctx) // This closes everything this Runtime created.
// Instantiate a Go-defined module named "env" that exports functions to // Instantiate a Go-defined module named "env" that exports functions to
// get the current year and log to the console. // get the current year and log to the console.
@@ -30,7 +31,7 @@ func main() {
// constrained to a subset of numeric types. // constrained to a subset of numeric types.
// Note: "env" is a module name conventionally used for arbitrary // Note: "env" is a module name conventionally used for arbitrary
// host-defined functions, but any name would do. // host-defined functions, but any name would do.
env, err := r.NewModuleBuilder("env"). _, err := r.NewModuleBuilder("env").
ExportFunction("log_i32", func(v uint32) { ExportFunction("log_i32", func(v uint32) {
fmt.Println("log_i32 >>", v) fmt.Println("log_i32 >>", v)
}). }).
@@ -42,9 +43,8 @@ func main() {
}). }).
Instantiate(ctx) Instantiate(ctx)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer env.Close(ctx)
// Instantiate a WebAssembly module named "age-calculator" that imports // Instantiate a WebAssembly module named "age-calculator" that imports
// functions defined in "env". // functions defined in "env".
@@ -85,26 +85,25 @@ func main() {
)`)) )`))
// ^^ Note: wazero's text compiler is incomplete #59. We are using it anyway to keep this example dependency free. // ^^ Note: wazero's text compiler is incomplete #59. We are using it anyway to keep this example dependency free.
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer ageCalculator.Close(ctx)
// Read the birthYear from the arguments to main // Read the birthYear from the arguments to main
birthYear, err := strconv.ParseUint(os.Args[1], 10, 64) birthYear, err := strconv.ParseUint(os.Args[1], 10, 64)
if err != nil { if err != nil {
log.Fatalf("invalid arg %v: %v", os.Args[1], err) log.Panicf("invalid arg %v: %v", os.Args[1], err)
} }
// First, try calling the "get_age" function and printing to the console externally. // First, try calling the "get_age" function and printing to the console externally.
results, err := ageCalculator.ExportedFunction("get_age").Call(ctx, birthYear) results, err := ageCalculator.ExportedFunction("get_age").Call(ctx, birthYear)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
fmt.Println("println >>", results[0]) fmt.Println("println >>", results[0])
// First, try calling the "log_age" function and printing to the console externally. // First, try calling the "log_age" function and printing to the console externally.
_, err = ageCalculator.ExportedFunction("log_age").Call(ctx, birthYear) _, err = ageCalculator.ExportedFunction("log_age").Call(ctx, birthYear)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
} }

View File

@@ -29,21 +29,20 @@ func main() {
ctx := context.Background() ctx := context.Background()
// Create a new WebAssembly Runtime. // Create a new WebAssembly Runtime.
runtime := wazero.NewRuntime() r := wazero.NewRuntime()
defer r.Close(ctx) // This closes everything this Runtime created.
// Add a module that uses offset parameters for multiple results, with functions defined in WebAssembly. // Add a module that uses offset parameters for multiple results, with functions defined in WebAssembly.
wasm, err := resultOffsetWasmFunctions(ctx, runtime) wasm, err := resultOffsetWasmFunctions(ctx, r)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer wasm.Close(ctx)
// Add a module that uses offset parameters for multiple results, with functions defined in Go. // Add a module that uses offset parameters for multiple results, with functions defined in Go.
host, err := resultOffsetHostFunctions(ctx, runtime) host, err := resultOffsetHostFunctions(ctx, r)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer host.Close(ctx)
// wazero enables only W3C recommended features by default. Opt-in to other features like so: // wazero enables only W3C recommended features by default. Opt-in to other features like so:
runtimeWithMultiValue := wazero.NewRuntimeWithConfig( runtimeWithMultiValue := wazero.NewRuntimeWithConfig(
@@ -54,23 +53,21 @@ func main() {
// Add a module that uses multiple results values, with functions defined in WebAssembly. // Add a module that uses multiple results values, with functions defined in WebAssembly.
wasmWithMultiValue, err := multiValueWasmFunctions(ctx, runtimeWithMultiValue) wasmWithMultiValue, err := multiValueWasmFunctions(ctx, runtimeWithMultiValue)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer wasmWithMultiValue.Close(ctx)
// Add a module that uses multiple results values, with functions defined in Go. // Add a module that uses multiple results values, with functions defined in Go.
hostWithMultiValue, err := multiValueHostFunctions(ctx, runtimeWithMultiValue) hostWithMultiValue, err := multiValueHostFunctions(ctx, runtimeWithMultiValue)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer hostWithMultiValue.Close(ctx)
// Call the same function in all modules and print the results to the console. // Call the same function in all modules and print the results to the console.
for _, mod := range []api.Module{wasm, host, wasmWithMultiValue, hostWithMultiValue} { for _, mod := range []api.Module{wasm, host, wasmWithMultiValue, hostWithMultiValue} {
getAge := mod.ExportedFunction("call_get_age") getAge := mod.ExportedFunction("call_get_age")
results, err := getAge.Call(ctx) results, err := getAge.Call(ctx)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
fmt.Printf("%s: age=%d\n", mod.Name(), results[0]) fmt.Printf("%s: age=%d\n", mod.Name(), results[0])

View File

@@ -18,6 +18,7 @@ func main() {
// Create a new WebAssembly Runtime. // Create a new WebAssembly Runtime.
r := wazero.NewRuntime() r := wazero.NewRuntime()
defer r.Close(ctx) // This closes everything this Runtime created.
// Instantiate a Go-defined module named "assemblyscript" that exports a // Instantiate a Go-defined module named "assemblyscript" that exports a
// function to close the module that calls "abort". // function to close the module that calls "abort".
@@ -26,7 +27,7 @@ func main() {
_ = m.CloseWithExitCode(ctx, 255) _ = m.CloseWithExitCode(ctx, 255)
}).Instantiate(ctx) }).Instantiate(ctx)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer host.Close(ctx) defer host.Close(ctx)
@@ -45,14 +46,14 @@ func main() {
(export "abort" (func 0)) ;; exports the import for testing (export "abort" (func 0)) ;; exports the import for testing
)`), compileConfig) )`), compileConfig)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer code.Close(ctx) defer code.Close(ctx)
// Instantiate the WebAssembly module. // Instantiate the WebAssembly module.
mod, err := r.InstantiateModule(ctx, code, wazero.NewModuleConfig()) mod, err := r.InstantiateModule(ctx, code, wazero.NewModuleConfig())
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer mod.Close(ctx) defer mod.Close(ctx)

View File

@@ -30,36 +30,32 @@ func main() {
// Create a new WebAssembly Runtime. // Create a new WebAssembly Runtime.
r := wazero.NewRuntime() r := wazero.NewRuntime()
defer r.Close(ctx) // This closes everything this Runtime created.
// Since wazero uses fs.FS, we can use standard libraries to do things like trim the leading path. // Since wazero uses fs.FS, we can use standard libraries to do things like trim the leading path.
rooted, err := fs.Sub(catFS, "testdata") rooted, err := fs.Sub(catFS, "testdata")
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
// Combine the above into our baseline config, overriding defaults (which discard stdout and have no file system). // Combine the above into our baseline config, overriding defaults (which discard stdout and have no file system).
config := wazero.NewModuleConfig().WithStdout(os.Stdout).WithFS(rooted) config := wazero.NewModuleConfig().WithStdout(os.Stdout).WithFS(rooted)
// Instantiate WASI, which implements system I/O such as console output. // Instantiate WASI, which implements system I/O such as console output.
wm, err := wasi.InstantiateSnapshotPreview1(ctx, r) if _, err = wasi.InstantiateSnapshotPreview1(ctx, r); err != nil {
if err != nil { log.Panicln(err)
log.Fatal(err)
} }
defer wm.Close(ctx)
// Compile the WebAssembly module using the default configuration. // Compile the WebAssembly module using the default configuration.
code, err := r.CompileModule(ctx, catWasm, wazero.NewCompileConfig()) code, err := r.CompileModule(ctx, catWasm, wazero.NewCompileConfig())
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer code.Close(ctx)
// InstantiateModule runs the "_start" function which is what TinyGo compiles "main" to. // InstantiateModule runs the "_start" function which is what TinyGo compiles "main" to.
// * Set the program name (arg[0]) to "wasi" and add args to write "test.txt" to stdout twice. // * Set the program name (arg[0]) to "wasi" and add args to write "test.txt" to stdout twice.
// * We use "/test.txt" or "./test.txt" because WithFS by default maps the workdir "." to "/". // * We use "/test.txt" or "./test.txt" because WithFS by default maps the workdir "." to "/".
cat, err := r.InstantiateModule(ctx, code, config.WithArgs("wasi", os.Args[1])) if _, err = r.InstantiateModule(ctx, code, config.WithArgs("wasi", os.Args[1])); err != nil {
if err != nil { log.Panicln(err)
log.Fatal(err)
} }
defer cat.Close(ctx)
} }

View File

@@ -60,11 +60,11 @@ func Example_listener() {
ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, &loggerFactory{}) ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, &loggerFactory{})
r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfigInterpreter()) r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfigInterpreter())
wm, err := wasi.InstantiateSnapshotPreview1(ctx, r) defer r.Close(ctx) // This closes everything this Runtime created.
if err != nil {
log.Fatal(err) if _, err := wasi.InstantiateSnapshotPreview1(ctx, r); err != nil {
log.Panicln(err)
} }
defer wm.Close(ctx)
// Compile the WebAssembly module using the default configuration. // Compile the WebAssembly module using the default configuration.
code, err := r.CompileModule(ctx, []byte(`(module $listener code, err := r.CompileModule(ctx, []byte(`(module $listener
@@ -75,15 +75,13 @@ func Example_listener() {
(start 1) ;; call the second function (start 1) ;; call the second function
)`), wazero.NewCompileConfig()) )`), wazero.NewCompileConfig())
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer code.Close(ctx)
mod, err := r.InstantiateModule(ctx, code, wazero.NewModuleConfig().WithStdout(os.Stdout)) _, err = r.InstantiateModule(ctx, code, wazero.NewModuleConfig().WithStdout(os.Stdout))
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer mod.Close(ctx)
// Output: // Output:
// >> listener.[1] // >> listener.[1]

View File

@@ -37,11 +37,11 @@ func Example_sys() {
ctx := context.WithValue(context.Background(), experimental.SysKey{}, &fakeSys{}) ctx := context.WithValue(context.Background(), experimental.SysKey{}, &fakeSys{})
r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfigInterpreter()) r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfigInterpreter())
wm, err := wasi.InstantiateSnapshotPreview1(ctx, r) defer r.Close(ctx) // This closes everything this Runtime created.
if err != nil {
log.Fatal(err) if _, err := wasi.InstantiateSnapshotPreview1(ctx, r); err != nil {
log.Panicln(err)
} }
defer wm.Close(ctx)
code, err := r.CompileModule(ctx, []byte(`(module code, err := r.CompileModule(ctx, []byte(`(module
(import "wasi_snapshot_preview1" "random_get" (import "wasi_snapshot_preview1" "random_get"
@@ -51,19 +51,17 @@ func Example_sys() {
(start 1) ;; call the second function (start 1) ;; call the second function
)`), wazero.NewCompileConfig()) )`), wazero.NewCompileConfig())
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer code.Close(ctx)
mod, err := r.InstantiateModule(ctx, code, wazero.NewModuleConfig().WithStdout(os.Stdout)) mod, err := r.InstantiateModule(ctx, code, wazero.NewModuleConfig().WithStdout(os.Stdout))
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer mod.Close(ctx)
// Try to read 4 bytes of random data. // Try to read 4 bytes of random data.
if bytes, ok := mod.Memory().Read(ctx, 0, 4); !ok { if bytes, ok := mod.Memory().Read(ctx, 0, 4); !ok {
log.Fatalf("Memory.Read(0, 4) out of range of memory size %d", mod.Memory().Size(ctx)) log.Panicf("Memory.Read(0, 4) out of range of memory size %d", mod.Memory().Size(ctx))
} else { } else {
fmt.Println(hex.EncodeToString(bytes)) fmt.Println(hex.EncodeToString(bytes))
} }

View File

@@ -23,7 +23,7 @@ func Example() {
// Instantiate WASI, which implements system I/O such as console output. // Instantiate WASI, which implements system I/O such as console output.
wm, err := InstantiateSnapshotPreview1(ctx, r) wm, err := InstantiateSnapshotPreview1(ctx, r)
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer wm.Close(testCtx) defer wm.Close(testCtx)
@@ -40,7 +40,7 @@ func Example() {
) )
`), wazero.NewCompileConfig()) `), wazero.NewCompileConfig())
if err != nil { if err != nil {
log.Fatal(err) log.Panicln(err)
} }
defer code.Close(ctx) defer code.Close(ctx)

View File

@@ -26,7 +26,17 @@ const ModuleSnapshotPreview1 = "wasi_snapshot_preview1"
// InstantiateSnapshotPreview1 instantiates ModuleSnapshotPreview1, so that other modules can import them. // InstantiateSnapshotPreview1 instantiates ModuleSnapshotPreview1, so that other modules can import them.
// //
// Ex. If your source (%.wasm binary) includes an import "wasi_snapshot_preview1", call InstantiateSnapshotPreview1
// prior to instantiating it. Otherwise, it will error due to missing imports.
// ctx := context.Background()
// r := wazero.NewRuntime()
// defer r.Close(ctx) // This closes everything this Runtime created.
//
// _, _ = wasi.InstantiateSnapshotPreview1(ctx, r)
// mod, _ := r.InstantiateModuleFromCode(ctx, source)
//
// Note: All WASI functions return a single Errno result, ErrnoSuccess on success. // Note: All WASI functions return a single Errno result, ErrnoSuccess on success.
// Note: Closing the wazero.Runtime closes any api.Module it instantiated.
func InstantiateSnapshotPreview1(ctx context.Context, r wazero.Runtime) (api.Module, error) { func InstantiateSnapshotPreview1(ctx context.Context, r wazero.Runtime) (api.Module, error) {
_, fns := snapshotPreview1Functions(ctx) _, fns := snapshotPreview1Functions(ctx)
return r.NewModuleBuilder(ModuleSnapshotPreview1).ExportFunctions(fns).Instantiate(ctx) return r.NewModuleBuilder(ModuleSnapshotPreview1).ExportFunctions(fns).Instantiate(ctx)

27
wasm.go
View File

@@ -16,11 +16,12 @@ import (
// Runtime allows embedding of WebAssembly modules. // Runtime allows embedding of WebAssembly modules.
// //
// Ex. // Ex. The below is the basic initialization of wazero's WebAssembly Runtime.
// ctx := context.Background() // ctx := context.Background()
// r := wazero.NewRuntime() // r := wazero.NewRuntime()
// defer r.Close(ctx) // This closes everything this Runtime created.
//
// module, _ := r.InstantiateModuleFromCode(ctx, source) // module, _ := r.InstantiateModuleFromCode(ctx, source)
// defer module.Close()
type Runtime interface { type Runtime interface {
// NewModuleBuilder lets you create modules out of functions defined in Go. // NewModuleBuilder lets you create modules out of functions defined in Go.
// //
@@ -52,8 +53,10 @@ type Runtime interface {
// //
// Ex. // Ex.
// ctx := context.Background() // ctx := context.Background()
// module, _ := wazero.NewRuntime().InstantiateModuleFromCode(ctx, source) // r := wazero.NewRuntime()
// defer module.Close() // defer r.Close(ctx) // This closes everything this Runtime created.
//
// module, _ := r.InstantiateModuleFromCode(ctx, source)
// //
// Note: When the context is nil, it defaults to context.Background. // Note: When the context is nil, it defaults to context.Background.
// Note: This is a convenience utility that chains CompileModule with InstantiateModule. To instantiate the same // Note: This is a convenience utility that chains CompileModule with InstantiateModule. To instantiate the same
@@ -66,10 +69,10 @@ type Runtime interface {
// Ex. // Ex.
// ctx := context.Background() // ctx := context.Background()
// r := wazero.NewRuntime() // r := wazero.NewRuntime()
// defer r.Close(ctx) // This closes everything this Runtime created.
//
// compiled, _ := r.CompileModule(ctx, source, wazero.NewCompileConfig()) // compiled, _ := r.CompileModule(ctx, source, wazero.NewCompileConfig())
// defer compiled.Close()
// module, _ := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("prod")) // module, _ := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("prod"))
// defer module.Close()
// //
// While CompiledModule is pre-validated, there are a few situations which can cause an error: // While CompiledModule is pre-validated, there are a few situations which can cause an error:
// * The module name is already in use. // * The module name is already in use.
@@ -80,7 +83,9 @@ type Runtime interface {
// //
// ctx := context.Background() // ctx := context.Background()
// r := wazero.NewRuntime() // r := wazero.NewRuntime()
// wasi, _ := wasi.InstantiateSnapshotPreview1(r) // defer r.Close(ctx) // This closes everything this Runtime created.
//
// _, _ := wasi.InstantiateSnapshotPreview1(r)
// compiled, _ := r.CompileModule(ctx, source, wazero.NewCompileConfig()) // compiled, _ := r.CompileModule(ctx, source, wazero.NewCompileConfig())
// //
// // Initialize base configuration: // // Initialize base configuration:
@@ -99,7 +104,9 @@ type Runtime interface {
// Ex. // Ex.
// ctx := context.Background() // ctx := context.Background()
// r := wazero.NewRuntime() // r := wazero.NewRuntime()
// defer r.Close(ctx) // defer r.Close(ctx) // This closes everything this Runtime created.
//
// // Everything below here can be closed, but will anyway due to above.
// _, _ = wasi.InstantiateSnapshotPreview1(ctx, r) // _, _ = wasi.InstantiateSnapshotPreview1(ctx, r)
// mod, _ := r.InstantiateModuleFromCode(ctx, source) // mod, _ := r.InstantiateModuleFromCode(ctx, source)
Close(context.Context) error Close(context.Context) error
@@ -110,7 +117,9 @@ type Runtime interface {
// Ex. // Ex.
// ctx := context.Background() // ctx := context.Background()
// r := wazero.NewRuntime() // r := wazero.NewRuntime()
// defer r.CloseWithExitCode(ctx, 2) // defer r.CloseWithExitCode(ctx, 2) // This closes everything this Runtime created.
//
// // Everything below here can be closed, but will anyway due to above.
// _, _ = wasi.InstantiateSnapshotPreview1(ctx, r) // _, _ = wasi.InstantiateSnapshotPreview1(ctx, r)
// mod, _ := r.InstantiateModuleFromCode(ctx, source) // mod, _ := r.InstantiateModuleFromCode(ctx, source)
CloseWithExitCode(ctx context.Context, exitCode uint32) error CloseWithExitCode(ctx context.Context, exitCode uint32) error