Renames ModuleBuilder to HostModuleBuilder and drops memory and globals (#812)
We at one point considered making `ModuleBuilder` create complete WebAssembly binaries. However, we recently spun out [wabin](https://github.com/tetratelabs/wabin), which allows this. Meanwhile, the features in `ModuleBuilder` were confusing and misused. For example, the only two cases memory was exported on GitHub were done by accident. This is because host functions act on the guest's memory, not their own. Hence, this removes memory and globals from host side definitions, and renames the type to HostModuleBuilder to clarify this is not ever going to be used to construct normal Wasm binaries. Most importantly, this simplifies the API and reduces a lot of code. It is important to make changes like this, particularly deleting any experimental things that didn't end up useful. Signed-off-by: Adrian Cole <adrian@tetrate.io> Co-authored-by: Anuraag Agrawal <anuraaga@gmail.com>
This commit is contained in:
@@ -411,7 +411,7 @@ ways acts similar to a process with a `main` function.
|
|||||||
|
|
||||||
To capture "hello world" written to the console (stdout a.k.a. file descriptor 1) in `exec.Cmd`, you would set the
|
To capture "hello world" written to the console (stdout a.k.a. file descriptor 1) in `exec.Cmd`, you would set the
|
||||||
`Stdout` field accordingly, perhaps to a buffer. In WebAssembly 1.0 (20191205), the only way to perform something like
|
`Stdout` field accordingly, perhaps to a buffer. In WebAssembly 1.0 (20191205), the only way to perform something like
|
||||||
this is via a host function (ex `ModuleBuilder.ExportFunction`) and internally copy memory corresponding to that string
|
this is via a host function (ex `HostModuleBuilder.ExportFunction`) and internally copy memory corresponding to that string
|
||||||
to a buffer.
|
to a buffer.
|
||||||
|
|
||||||
WASI implements system interfaces with host functions. Concretely, to write to console, a WASI command `Module` imports
|
WASI implements system interfaces with host functions. Concretely, to write to console, a WASI command `Module` imports
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ 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
|
||||||
_, err := r.NewModuleBuilder("env").
|
_, err := r.NewHostModuleBuilder("env").
|
||||||
ExportFunction("log_i32", func(v uint32) {
|
ExportFunction("log_i32", func(v uint32) {
|
||||||
fmt.Println("log_i32 >>", v)
|
fmt.Println("log_i32 >>", v)
|
||||||
}).
|
}).
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ const (
|
|||||||
// (func (import "env" "f") (param externref) (result externref))
|
// (func (import "env" "f") (param externref) (result externref))
|
||||||
//
|
//
|
||||||
// This can be defined in Go as:
|
// This can be defined in Go as:
|
||||||
// r.NewModuleBuilder("env").ExportFunctions(map[string]interface{}{
|
// r.NewHostModuleBuilder("env").ExportFunctions(map[string]interface{}{
|
||||||
// "f": func(externref uintptr) (resultExternRef uintptr) { return },
|
// "f": func(externref uintptr) (resultExternRef uintptr) { return },
|
||||||
// })
|
// })
|
||||||
//
|
//
|
||||||
@@ -229,7 +229,7 @@ type FunctionDefinition interface {
|
|||||||
ExportNames() []string
|
ExportNames() []string
|
||||||
|
|
||||||
// GoFunc is present when the function was implemented by the embedder
|
// GoFunc is present when the function was implemented by the embedder
|
||||||
// (ex via wazero.ModuleBuilder) instead of a wasm binary.
|
// (ex via wazero.HostModuleBuilder) instead of a wasm binary.
|
||||||
//
|
//
|
||||||
// This function can be non-deterministic or cause side effects. It also
|
// This function can be non-deterministic or cause side effects. It also
|
||||||
// has special properties not defined in the WebAssembly Core
|
// has special properties not defined in the WebAssembly Core
|
||||||
|
|||||||
219
builder.go
219
builder.go
@@ -2,15 +2,16 @@ package wazero
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
"github.com/tetratelabs/wazero/internal/leb128"
|
|
||||||
"github.com/tetratelabs/wazero/internal/u64"
|
|
||||||
"github.com/tetratelabs/wazero/internal/wasm"
|
"github.com/tetratelabs/wazero/internal/wasm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ModuleBuilder is a way to define a WebAssembly Module in Go.
|
// HostModuleBuilder is a way to define host functions (in Go), so that a
|
||||||
|
// WebAssembly binary (ex. %.wasm file) can import and use them.
|
||||||
|
//
|
||||||
|
// Specifically, this implements the host side of an Application Binary
|
||||||
|
// Interface (ABI) like WASI or AssemblyScript.
|
||||||
//
|
//
|
||||||
// Ex. Below defines and instantiates a module named "env" with one function:
|
// Ex. Below defines and instantiates a module named "env" with one function:
|
||||||
//
|
//
|
||||||
@@ -21,30 +22,47 @@ import (
|
|||||||
// hello := func() {
|
// hello := func() {
|
||||||
// fmt.Fprintln(stdout, "hello!")
|
// fmt.Fprintln(stdout, "hello!")
|
||||||
// }
|
// }
|
||||||
// env, _ := r.NewModuleBuilder("env").
|
// env, _ := r.NewHostModuleBuilder("env").
|
||||||
// ExportFunction("hello", hello).
|
// ExportFunction("hello", hello).
|
||||||
// Instantiate(ctx, r)
|
// Instantiate(ctx, r)
|
||||||
//
|
//
|
||||||
// If the same module may be instantiated multiple times, it is more efficient
|
// If the same module may be instantiated multiple times, it is more efficient
|
||||||
// to separate steps. Ex.
|
// to separate steps. Ex.
|
||||||
//
|
//
|
||||||
// compiled, _ := r.NewModuleBuilder("env").
|
// compiled, _ := r.NewHostModuleBuilder("env").
|
||||||
// ExportFunction("get_random_string", getRandomString).
|
// ExportFunction("get_random_string", getRandomString).
|
||||||
// Compile(ctx, wazero.NewCompileConfig())
|
// Compile(ctx)
|
||||||
//
|
//
|
||||||
// env1, _ := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("env.1"))
|
// env1, _ := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("env.1"))
|
||||||
//
|
//
|
||||||
// env2, _ := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("env.2"))
|
// env2, _ := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("env.2"))
|
||||||
//
|
//
|
||||||
|
// # Memory
|
||||||
|
//
|
||||||
|
// All host functions act on the importing api.Module, including any memory
|
||||||
|
// exported in its binary (%.wasm file). If you are reading or writing memory,
|
||||||
|
// it is sand-boxed Wasm memory defined by the guest.
|
||||||
|
//
|
||||||
|
// Below, `m` is the importing module, defined in Wasm. `fn` is a host function
|
||||||
|
// added via ExportFunction. This means that `x` was read from memory defined
|
||||||
|
// in Wasm, not arbitrary memory in the process.
|
||||||
|
//
|
||||||
|
// fn := func(ctx context.Context, m api.Module, offset uint32) uint32 {
|
||||||
|
// x, _ := m.Memory().ReadUint32Le(ctx, offset)
|
||||||
|
// return x
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// See ExportFunction for valid host function signatures and other details.
|
||||||
|
//
|
||||||
// # Notes
|
// # Notes
|
||||||
//
|
//
|
||||||
// - ModuleBuilder is mutable: each method returns the same instance for
|
// - HostModuleBuilder is mutable: each method returns the same instance for
|
||||||
// chaining.
|
// chaining.
|
||||||
// - methods do not return errors, to allow chaining. Any validation errors
|
// - methods do not return errors, to allow chaining. Any validation errors
|
||||||
// are deferred until Compile.
|
// are deferred until Compile.
|
||||||
// - Insertion order is not retained. Anything defined by this builder is
|
// - Insertion order is not retained. Anything defined by this builder is
|
||||||
// sorted lexicographically on Compile.
|
// sorted lexicographically on Compile.
|
||||||
type ModuleBuilder interface {
|
type HostModuleBuilder interface {
|
||||||
// Note: until golang/go#5860, we can't use example tests to embed code in interface godocs.
|
// Note: until golang/go#5860, we can't use example tests to embed code in interface godocs.
|
||||||
|
|
||||||
// ExportFunction adds a function written in Go, which a WebAssembly module can import.
|
// ExportFunction adds a function written in Go, which a WebAssembly module can import.
|
||||||
@@ -66,7 +84,7 @@ type ModuleBuilder interface {
|
|||||||
// builder.ExportFunction("abort", env.abort, "~lib/builtins/abort",
|
// builder.ExportFunction("abort", env.abort, "~lib/builtins/abort",
|
||||||
// "message", "fileName", "lineNumber", "columnNumber")
|
// "message", "fileName", "lineNumber", "columnNumber")
|
||||||
//
|
//
|
||||||
// Valid Signature
|
// # Valid Signature
|
||||||
//
|
//
|
||||||
// Noting a context exception described later, all parameters or result
|
// Noting a context exception described later, all parameters or result
|
||||||
// types must match WebAssembly 1.0 (20191205) value types. This means
|
// types must match WebAssembly 1.0 (20191205) value types. This means
|
||||||
@@ -108,90 +126,15 @@ type ModuleBuilder interface {
|
|||||||
// --snip--
|
// --snip--
|
||||||
//
|
//
|
||||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#host-functions%E2%91%A2
|
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#host-functions%E2%91%A2
|
||||||
ExportFunction(exportName string, goFunc interface{}, names ...string) ModuleBuilder
|
ExportFunction(exportName string, goFunc interface{}, names ...string) HostModuleBuilder
|
||||||
|
|
||||||
// ExportFunctions is a convenience that calls ExportFunction for each key/value in the provided map.
|
// ExportFunctions is a convenience that calls ExportFunction for each key/value in the provided map.
|
||||||
ExportFunctions(nameToGoFunc map[string]interface{}) ModuleBuilder
|
ExportFunctions(nameToGoFunc map[string]interface{}) HostModuleBuilder
|
||||||
|
|
||||||
// ExportMemory adds linear memory, which a WebAssembly module can import and become available via api.Memory.
|
|
||||||
// If a memory is already exported with the same name, this overwrites it.
|
|
||||||
//
|
|
||||||
// # Parameters
|
|
||||||
//
|
|
||||||
// - name - the name to export. Ex "memory" for wasi_snapshot_preview1.ModuleSnapshotPreview1
|
|
||||||
// - minPages - the possibly zero initial size in pages (65536 bytes per page).
|
|
||||||
//
|
|
||||||
// For example, the WebAssembly 1.0 Text Format below is the equivalent of this builder method:
|
|
||||||
// // (memory (export "memory") 1)
|
|
||||||
// builder.ExportMemory(1)
|
|
||||||
//
|
|
||||||
// # Notes
|
|
||||||
//
|
|
||||||
// - This is allowed to grow to (4GiB) limited by api.MemorySizer. To bound it, use ExportMemoryWithMax.
|
|
||||||
// - Version 1.0 (20191205) of the WebAssembly spec allows at most one memory per module.
|
|
||||||
//
|
|
||||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#memory-section%E2%91%A0
|
|
||||||
ExportMemory(name string, minPages uint32) ModuleBuilder
|
|
||||||
|
|
||||||
// ExportMemoryWithMax is like ExportMemory, but can prevent overuse of memory.
|
|
||||||
//
|
|
||||||
// For example, the WebAssembly 1.0 Text Format below is the equivalent of this builder method:
|
|
||||||
// // (memory (export "memory") 1 1)
|
|
||||||
// builder.ExportMemoryWithMax(1, 1)
|
|
||||||
//
|
|
||||||
// Note: api.MemorySizer determines the capacity.
|
|
||||||
ExportMemoryWithMax(name string, minPages, maxPages uint32) ModuleBuilder
|
|
||||||
|
|
||||||
// ExportGlobalI32 exports a global constant of type api.ValueTypeI32.
|
|
||||||
// If a global is already exported with the same name, this overwrites it.
|
|
||||||
//
|
|
||||||
// For example, the WebAssembly 1.0 Text Format below is the equivalent of this builder method:
|
|
||||||
// // (global (export "canvas_width") i32 (i32.const 1024))
|
|
||||||
// builder.ExportGlobalI32("canvas_width", 1024)
|
|
||||||
//
|
|
||||||
// Note: The maximum value of v is math.MaxInt32 to match constraints of initialization in binary format.
|
|
||||||
//
|
|
||||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#value-types%E2%91%A0 and
|
|
||||||
// https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-globaltype
|
|
||||||
ExportGlobalI32(name string, v int32) ModuleBuilder
|
|
||||||
|
|
||||||
// ExportGlobalI64 exports a global constant of type api.ValueTypeI64.
|
|
||||||
// If a global is already exported with the same name, this overwrites it.
|
|
||||||
//
|
|
||||||
// For example, the WebAssembly 1.0 Text Format below is the equivalent of this builder method:
|
|
||||||
// // (global (export "start_epoch") i64 (i64.const 1620216263544))
|
|
||||||
// builder.ExportGlobalI64("start_epoch", 1620216263544)
|
|
||||||
//
|
|
||||||
// Note: The maximum value of v is math.MaxInt64 to match constraints of initialization in binary format.
|
|
||||||
//
|
|
||||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#value-types%E2%91%A0 and
|
|
||||||
// https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-globaltype
|
|
||||||
ExportGlobalI64(name string, v int64) ModuleBuilder
|
|
||||||
|
|
||||||
// ExportGlobalF32 exports a global constant of type api.ValueTypeF32.
|
|
||||||
// If a global is already exported with the same name, this overwrites it.
|
|
||||||
//
|
|
||||||
// For example, the WebAssembly 1.0 Text Format below is the equivalent of this builder method:
|
|
||||||
// // (global (export "math/pi") f32 (f32.const 3.1415926536))
|
|
||||||
// builder.ExportGlobalF32("math/pi", 3.1415926536)
|
|
||||||
//
|
|
||||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-globaltype
|
|
||||||
ExportGlobalF32(name string, v float32) ModuleBuilder
|
|
||||||
|
|
||||||
// ExportGlobalF64 exports a global constant of type api.ValueTypeF64.
|
|
||||||
// If a global is already exported with the same name, this overwrites it.
|
|
||||||
//
|
|
||||||
// For example, the WebAssembly 1.0 Text Format below is the equivalent of this builder method:
|
|
||||||
// // (global (export "math/pi") f64 (f64.const 3.14159265358979323846264338327950288419716939937510582097494459))
|
|
||||||
// builder.ExportGlobalF64("math/pi", 3.14159265358979323846264338327950288419716939937510582097494459)
|
|
||||||
//
|
|
||||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-globaltype
|
|
||||||
ExportGlobalF64(name string, v float64) ModuleBuilder
|
|
||||||
|
|
||||||
// Compile returns a CompiledModule that can instantiated in any namespace (Namespace).
|
// Compile returns a CompiledModule that can instantiated in any namespace (Namespace).
|
||||||
//
|
//
|
||||||
// Note: Closing the Namespace has the same effect as closing the result.
|
// Note: Closing the Namespace has the same effect as closing the result.
|
||||||
Compile(context.Context, CompileConfig) (CompiledModule, error)
|
Compile(context.Context) (CompiledModule, error)
|
||||||
|
|
||||||
// Instantiate is a convenience that calls Compile, then Namespace.InstantiateModule.
|
// Instantiate is a convenience that calls Compile, then Namespace.InstantiateModule.
|
||||||
//
|
//
|
||||||
@@ -204,7 +147,7 @@ type ModuleBuilder interface {
|
|||||||
// hello := func() {
|
// hello := func() {
|
||||||
// fmt.Fprintln(stdout, "hello!")
|
// fmt.Fprintln(stdout, "hello!")
|
||||||
// }
|
// }
|
||||||
// env, _ := r.NewModuleBuilder("env").
|
// env, _ := r.NewHostModuleBuilder("env").
|
||||||
// ExportFunction("hello", hello).
|
// ExportFunction("hello", hello).
|
||||||
// Instantiate(ctx, r)
|
// Instantiate(ctx, r)
|
||||||
//
|
//
|
||||||
@@ -216,30 +159,26 @@ type ModuleBuilder interface {
|
|||||||
Instantiate(context.Context, Namespace) (api.Module, error)
|
Instantiate(context.Context, Namespace) (api.Module, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// moduleBuilder implements ModuleBuilder
|
// hostModuleBuilder implements HostModuleBuilder
|
||||||
type moduleBuilder struct {
|
type hostModuleBuilder struct {
|
||||||
r *runtime
|
r *runtime
|
||||||
moduleName string
|
moduleName string
|
||||||
nameToGoFunc map[string]interface{}
|
nameToGoFunc map[string]interface{}
|
||||||
funcToNames map[string][]string
|
funcToNames map[string][]string
|
||||||
nameToMemory map[string]*wasm.Memory
|
|
||||||
nameToGlobal map[string]*wasm.Global
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewModuleBuilder implements Runtime.NewModuleBuilder
|
// NewHostModuleBuilder implements Runtime.NewHostModuleBuilder
|
||||||
func (r *runtime) NewModuleBuilder(moduleName string) ModuleBuilder {
|
func (r *runtime) NewHostModuleBuilder(moduleName string) HostModuleBuilder {
|
||||||
return &moduleBuilder{
|
return &hostModuleBuilder{
|
||||||
r: r,
|
r: r,
|
||||||
moduleName: moduleName,
|
moduleName: moduleName,
|
||||||
nameToGoFunc: map[string]interface{}{},
|
nameToGoFunc: map[string]interface{}{},
|
||||||
funcToNames: map[string][]string{},
|
funcToNames: map[string][]string{},
|
||||||
nameToMemory: map[string]*wasm.Memory{},
|
|
||||||
nameToGlobal: map[string]*wasm.Global{},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportFunction implements ModuleBuilder.ExportFunction
|
// ExportFunction implements HostModuleBuilder.ExportFunction
|
||||||
func (b *moduleBuilder) ExportFunction(exportName string, goFunc interface{}, names ...string) ModuleBuilder {
|
func (b *hostModuleBuilder) ExportFunction(exportName string, goFunc interface{}, names ...string) HostModuleBuilder {
|
||||||
b.nameToGoFunc[exportName] = goFunc
|
b.nameToGoFunc[exportName] = goFunc
|
||||||
if len(names) > 0 {
|
if len(names) > 0 {
|
||||||
b.funcToNames[exportName] = names
|
b.funcToNames[exportName] = names
|
||||||
@@ -247,81 +186,17 @@ func (b *moduleBuilder) ExportFunction(exportName string, goFunc interface{}, na
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportFunctions implements ModuleBuilder.ExportFunctions
|
// ExportFunctions implements HostModuleBuilder.ExportFunctions
|
||||||
func (b *moduleBuilder) ExportFunctions(nameToGoFunc map[string]interface{}) ModuleBuilder {
|
func (b *hostModuleBuilder) ExportFunctions(nameToGoFunc map[string]interface{}) HostModuleBuilder {
|
||||||
for k, v := range nameToGoFunc {
|
for k, v := range nameToGoFunc {
|
||||||
b.ExportFunction(k, v)
|
b.ExportFunction(k, v)
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportMemory implements ModuleBuilder.ExportMemory
|
// Compile implements HostModuleBuilder.Compile
|
||||||
func (b *moduleBuilder) ExportMemory(name string, minPages uint32) ModuleBuilder {
|
func (b *hostModuleBuilder) Compile(ctx context.Context) (CompiledModule, error) {
|
||||||
b.nameToMemory[name] = &wasm.Memory{Min: minPages}
|
module, err := wasm.NewHostModule(b.moduleName, b.nameToGoFunc, b.funcToNames, b.r.enabledFeatures)
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportMemoryWithMax implements ModuleBuilder.ExportMemoryWithMax
|
|
||||||
func (b *moduleBuilder) ExportMemoryWithMax(name string, minPages, maxPages uint32) ModuleBuilder {
|
|
||||||
b.nameToMemory[name] = &wasm.Memory{Min: minPages, Max: maxPages, IsMaxEncoded: true}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportGlobalI32 implements ModuleBuilder.ExportGlobalI32
|
|
||||||
func (b *moduleBuilder) ExportGlobalI32(name string, v int32) ModuleBuilder {
|
|
||||||
b.nameToGlobal[name] = &wasm.Global{
|
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32},
|
|
||||||
// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
|
|
||||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(v)},
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportGlobalI64 implements ModuleBuilder.ExportGlobalI64
|
|
||||||
func (b *moduleBuilder) ExportGlobalI64(name string, v int64) ModuleBuilder {
|
|
||||||
b.nameToGlobal[name] = &wasm.Global{
|
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI64},
|
|
||||||
// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
|
|
||||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeInt64(v)},
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportGlobalF32 implements ModuleBuilder.ExportGlobalF32
|
|
||||||
func (b *moduleBuilder) ExportGlobalF32(name string, v float32) ModuleBuilder {
|
|
||||||
b.nameToGlobal[name] = &wasm.Global{
|
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF32},
|
|
||||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF32Const, Data: u64.LeBytes(api.EncodeF32(v))},
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportGlobalF64 implements ModuleBuilder.ExportGlobalF64
|
|
||||||
func (b *moduleBuilder) ExportGlobalF64(name string, v float64) ModuleBuilder {
|
|
||||||
b.nameToGlobal[name] = &wasm.Global{
|
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF64},
|
|
||||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF64Const, Data: u64.LeBytes(api.EncodeF64(v))},
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile implements ModuleBuilder.Compile
|
|
||||||
func (b *moduleBuilder) Compile(ctx context.Context, cConfig CompileConfig) (CompiledModule, error) {
|
|
||||||
config := cConfig.(*compileConfig)
|
|
||||||
|
|
||||||
// Verify the maximum limit here, so we don't have to pass it to wasm.NewHostModule
|
|
||||||
for name, mem := range b.nameToMemory {
|
|
||||||
var maxP *uint32
|
|
||||||
if mem.IsMaxEncoded {
|
|
||||||
maxP = &mem.Max
|
|
||||||
}
|
|
||||||
mem.Min, mem.Cap, mem.Max = config.memorySizer(mem.Min, maxP)
|
|
||||||
if err := mem.Validate(); err != nil {
|
|
||||||
return nil, fmt.Errorf("memory[%s] %v", name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module, err := wasm.NewHostModule(b.moduleName, b.nameToGoFunc, b.funcToNames, b.nameToMemory, b.nameToGlobal, b.r.enabledFeatures)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if err = module.Validate(b.r.enabledFeatures); err != nil {
|
} else if err = module.Validate(b.r.enabledFeatures); err != nil {
|
||||||
@@ -340,9 +215,9 @@ func (b *moduleBuilder) Compile(ctx context.Context, cConfig CompileConfig) (Com
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instantiate implements ModuleBuilder.Instantiate
|
// Instantiate implements HostModuleBuilder.Instantiate
|
||||||
func (b *moduleBuilder) Instantiate(ctx context.Context, ns Namespace) (api.Module, error) {
|
func (b *hostModuleBuilder) Instantiate(ctx context.Context, ns Namespace) (api.Module, error) {
|
||||||
if compiled, err := b.Compile(ctx, NewCompileConfig()); err != nil {
|
if compiled, err := b.Compile(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
compiled.(*compiledModule).closeWithModule = true
|
compiled.(*compiledModule).closeWithModule = true
|
||||||
|
|||||||
279
builder_test.go
279
builder_test.go
@@ -1,18 +1,15 @@
|
|||||||
package wazero
|
package wazero
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
"github.com/tetratelabs/wazero/internal/leb128"
|
|
||||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||||
"github.com/tetratelabs/wazero/internal/u64"
|
|
||||||
"github.com/tetratelabs/wazero/internal/wasm"
|
"github.com/tetratelabs/wazero/internal/wasm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestNewModuleBuilder_Compile only covers a few scenarios to avoid duplicating tests in internal/wasm/host_test.go
|
// TestNewHostModuleBuilder_Compile only covers a few scenarios to avoid duplicating tests in internal/wasm/host_test.go
|
||||||
func TestNewModuleBuilder_Compile(t *testing.T) {
|
func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||||
i32, i64 := api.ValueTypeI32, api.ValueTypeI64
|
i32, i64 := api.ValueTypeI32, api.ValueTypeI64
|
||||||
|
|
||||||
uint32_uint32 := func(uint32) uint32 {
|
uint32_uint32 := func(uint32) uint32 {
|
||||||
@@ -24,27 +21,27 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
|||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
input func(Runtime) ModuleBuilder
|
input func(Runtime) HostModuleBuilder
|
||||||
expected *wasm.Module
|
expected *wasm.Module
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty",
|
name: "empty",
|
||||||
input: func(r Runtime) ModuleBuilder {
|
input: func(r Runtime) HostModuleBuilder {
|
||||||
return r.NewModuleBuilder("")
|
return r.NewHostModuleBuilder("")
|
||||||
},
|
},
|
||||||
expected: &wasm.Module{},
|
expected: &wasm.Module{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "only name",
|
name: "only name",
|
||||||
input: func(r Runtime) ModuleBuilder {
|
input: func(r Runtime) HostModuleBuilder {
|
||||||
return r.NewModuleBuilder("env")
|
return r.NewHostModuleBuilder("env")
|
||||||
},
|
},
|
||||||
expected: &wasm.Module{NameSection: &wasm.NameSection{ModuleName: "env"}},
|
expected: &wasm.Module{NameSection: &wasm.NameSection{ModuleName: "env"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ExportFunction",
|
name: "ExportFunction",
|
||||||
input: func(r Runtime) ModuleBuilder {
|
input: func(r Runtime) HostModuleBuilder {
|
||||||
return r.NewModuleBuilder("").ExportFunction("1", uint32_uint32)
|
return r.NewHostModuleBuilder("").ExportFunction("1", uint32_uint32)
|
||||||
},
|
},
|
||||||
expected: &wasm.Module{
|
expected: &wasm.Module{
|
||||||
TypeSection: []*wasm.FunctionType{
|
TypeSection: []*wasm.FunctionType{
|
||||||
@@ -62,8 +59,8 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ExportFunction with names",
|
name: "ExportFunction with names",
|
||||||
input: func(r Runtime) ModuleBuilder {
|
input: func(r Runtime) HostModuleBuilder {
|
||||||
return r.NewModuleBuilder("").ExportFunction("1", uint32_uint32, "get", "x")
|
return r.NewHostModuleBuilder("").ExportFunction("1", uint32_uint32, "get", "x")
|
||||||
},
|
},
|
||||||
expected: &wasm.Module{
|
expected: &wasm.Module{
|
||||||
TypeSection: []*wasm.FunctionType{
|
TypeSection: []*wasm.FunctionType{
|
||||||
@@ -82,8 +79,8 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ExportFunction overwrites existing",
|
name: "ExportFunction overwrites existing",
|
||||||
input: func(r Runtime) ModuleBuilder {
|
input: func(r Runtime) HostModuleBuilder {
|
||||||
return r.NewModuleBuilder("").ExportFunction("1", uint32_uint32).ExportFunction("1", uint64_uint32)
|
return r.NewHostModuleBuilder("").ExportFunction("1", uint32_uint32).ExportFunction("1", uint64_uint32)
|
||||||
},
|
},
|
||||||
expected: &wasm.Module{
|
expected: &wasm.Module{
|
||||||
TypeSection: []*wasm.FunctionType{
|
TypeSection: []*wasm.FunctionType{
|
||||||
@@ -101,9 +98,9 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ExportFunction twice",
|
name: "ExportFunction twice",
|
||||||
input: func(r Runtime) ModuleBuilder {
|
input: func(r Runtime) HostModuleBuilder {
|
||||||
// Intentionally out of order
|
// Intentionally out of order
|
||||||
return r.NewModuleBuilder("").ExportFunction("2", uint64_uint32).ExportFunction("1", uint32_uint32)
|
return r.NewHostModuleBuilder("").ExportFunction("2", uint64_uint32).ExportFunction("1", uint32_uint32)
|
||||||
},
|
},
|
||||||
expected: &wasm.Module{
|
expected: &wasm.Module{
|
||||||
TypeSection: []*wasm.FunctionType{
|
TypeSection: []*wasm.FunctionType{
|
||||||
@@ -123,8 +120,8 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ExportFunctions",
|
name: "ExportFunctions",
|
||||||
input: func(r Runtime) ModuleBuilder {
|
input: func(r Runtime) HostModuleBuilder {
|
||||||
return r.NewModuleBuilder("").ExportFunctions(map[string]interface{}{
|
return r.NewHostModuleBuilder("").ExportFunctions(map[string]interface{}{
|
||||||
"1": uint32_uint32,
|
"1": uint32_uint32,
|
||||||
"2": uint64_uint32,
|
"2": uint64_uint32,
|
||||||
})
|
})
|
||||||
@@ -147,8 +144,8 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ExportFunctions overwrites",
|
name: "ExportFunctions overwrites",
|
||||||
input: func(r Runtime) ModuleBuilder {
|
input: func(r Runtime) HostModuleBuilder {
|
||||||
b := r.NewModuleBuilder("").ExportFunction("1", uint64_uint32)
|
b := r.NewHostModuleBuilder("").ExportFunction("1", uint64_uint32)
|
||||||
return b.ExportFunctions(map[string]interface{}{
|
return b.ExportFunctions(map[string]interface{}{
|
||||||
"1": uint32_uint32,
|
"1": uint32_uint32,
|
||||||
"2": uint64_uint32,
|
"2": uint64_uint32,
|
||||||
@@ -170,198 +167,14 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "ExportMemory",
|
|
||||||
input: func(r Runtime) ModuleBuilder {
|
|
||||||
return r.NewModuleBuilder("").ExportMemory("memory", 1)
|
|
||||||
},
|
|
||||||
expected: &wasm.Module{
|
|
||||||
MemorySection: &wasm.Memory{Min: 1, Cap: 1, Max: wasm.MemoryLimitPages},
|
|
||||||
ExportSection: []*wasm.Export{
|
|
||||||
{Name: "memory", Type: wasm.ExternTypeMemory, Index: 0},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ExportMemory overwrites",
|
|
||||||
input: func(r Runtime) ModuleBuilder {
|
|
||||||
return r.NewModuleBuilder("").ExportMemory("memory", 1).ExportMemory("memory", 2)
|
|
||||||
},
|
|
||||||
expected: &wasm.Module{
|
|
||||||
MemorySection: &wasm.Memory{Min: 2, Cap: 2, Max: wasm.MemoryLimitPages},
|
|
||||||
ExportSection: []*wasm.Export{
|
|
||||||
{Name: "memory", Type: wasm.ExternTypeMemory, Index: 0},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ExportMemoryWithMax",
|
|
||||||
input: func(r Runtime) ModuleBuilder {
|
|
||||||
return r.NewModuleBuilder("").ExportMemoryWithMax("memory", 1, 1)
|
|
||||||
},
|
|
||||||
expected: &wasm.Module{
|
|
||||||
MemorySection: &wasm.Memory{Min: 1, Cap: 1, Max: 1, IsMaxEncoded: true},
|
|
||||||
ExportSection: []*wasm.Export{
|
|
||||||
{Name: "memory", Type: wasm.ExternTypeMemory, Index: 0},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ExportMemoryWithMax overwrites",
|
|
||||||
input: func(r Runtime) ModuleBuilder {
|
|
||||||
return r.NewModuleBuilder("").ExportMemoryWithMax("memory", 1, 1).ExportMemoryWithMax("memory", 1, 2)
|
|
||||||
},
|
|
||||||
expected: &wasm.Module{
|
|
||||||
MemorySection: &wasm.Memory{Min: 1, Cap: 1, Max: 2, IsMaxEncoded: true},
|
|
||||||
ExportSection: []*wasm.Export{
|
|
||||||
{Name: "memory", Type: wasm.ExternTypeMemory, Index: 0},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ExportGlobalI32",
|
|
||||||
input: func(r Runtime) ModuleBuilder {
|
|
||||||
return r.NewModuleBuilder("").ExportGlobalI32("canvas_width", 1024)
|
|
||||||
},
|
|
||||||
expected: &wasm.Module{
|
|
||||||
GlobalSection: []*wasm.Global{
|
|
||||||
{
|
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32},
|
|
||||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(1024)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExportSection: []*wasm.Export{
|
|
||||||
{Name: "canvas_width", Type: wasm.ExternTypeGlobal, Index: 0},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ExportGlobalI32 overwrites",
|
|
||||||
input: func(r Runtime) ModuleBuilder {
|
|
||||||
return r.NewModuleBuilder("").ExportGlobalI32("canvas_width", 1024).ExportGlobalI32("canvas_width", math.MaxInt32)
|
|
||||||
},
|
|
||||||
expected: &wasm.Module{
|
|
||||||
GlobalSection: []*wasm.Global{
|
|
||||||
{
|
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI32},
|
|
||||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeUint32(math.MaxInt32)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExportSection: []*wasm.Export{
|
|
||||||
{Name: "canvas_width", Type: wasm.ExternTypeGlobal, Index: 0},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ExportGlobalI64",
|
|
||||||
input: func(r Runtime) ModuleBuilder {
|
|
||||||
return r.NewModuleBuilder("").ExportGlobalI64("start_epoch", 1620216263544)
|
|
||||||
},
|
|
||||||
expected: &wasm.Module{
|
|
||||||
GlobalSection: []*wasm.Global{
|
|
||||||
{
|
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI64},
|
|
||||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeUint64(1620216263544)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExportSection: []*wasm.Export{
|
|
||||||
{Name: "start_epoch", Type: wasm.ExternTypeGlobal, Index: 0},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ExportGlobalI64 overwrites",
|
|
||||||
input: func(r Runtime) ModuleBuilder {
|
|
||||||
return r.NewModuleBuilder("").ExportGlobalI64("start_epoch", 1620216263544).ExportGlobalI64("start_epoch", math.MaxInt64)
|
|
||||||
},
|
|
||||||
expected: &wasm.Module{
|
|
||||||
GlobalSection: []*wasm.Global{
|
|
||||||
{
|
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI64},
|
|
||||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeInt64(math.MaxInt64)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExportSection: []*wasm.Export{
|
|
||||||
{Name: "start_epoch", Type: wasm.ExternTypeGlobal, Index: 0},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ExportGlobalF32",
|
|
||||||
input: func(r Runtime) ModuleBuilder {
|
|
||||||
return r.NewModuleBuilder("").ExportGlobalF32("math/pi", 3.1415926536)
|
|
||||||
},
|
|
||||||
expected: &wasm.Module{
|
|
||||||
GlobalSection: []*wasm.Global{
|
|
||||||
{
|
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF32},
|
|
||||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF32Const, Data: u64.LeBytes(api.EncodeF32(3.1415926536))},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExportSection: []*wasm.Export{
|
|
||||||
{Name: "math/pi", Type: wasm.ExternTypeGlobal, Index: 0},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ExportGlobalF32 overwrites",
|
|
||||||
input: func(r Runtime) ModuleBuilder {
|
|
||||||
return r.NewModuleBuilder("").ExportGlobalF32("math/pi", 3.1415926536).ExportGlobalF32("math/pi", math.MaxFloat32)
|
|
||||||
},
|
|
||||||
expected: &wasm.Module{
|
|
||||||
GlobalSection: []*wasm.Global{
|
|
||||||
{
|
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF32},
|
|
||||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF32Const, Data: u64.LeBytes(api.EncodeF32(math.MaxFloat32))},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExportSection: []*wasm.Export{
|
|
||||||
{Name: "math/pi", Type: wasm.ExternTypeGlobal, Index: 0},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ExportGlobalF64",
|
|
||||||
input: func(r Runtime) ModuleBuilder {
|
|
||||||
return r.NewModuleBuilder("").ExportGlobalF64("math/pi", math.Pi)
|
|
||||||
},
|
|
||||||
expected: &wasm.Module{
|
|
||||||
GlobalSection: []*wasm.Global{
|
|
||||||
{
|
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF64},
|
|
||||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF64Const, Data: u64.LeBytes(api.EncodeF64(math.Pi))},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExportSection: []*wasm.Export{
|
|
||||||
{Name: "math/pi", Type: wasm.ExternTypeGlobal, Index: 0},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ExportGlobalF64 overwrites",
|
|
||||||
input: func(r Runtime) ModuleBuilder {
|
|
||||||
return r.NewModuleBuilder("").ExportGlobalF64("math/pi", math.Pi).ExportGlobalF64("math/pi", math.MaxFloat64)
|
|
||||||
},
|
|
||||||
expected: &wasm.Module{
|
|
||||||
GlobalSection: []*wasm.Global{
|
|
||||||
{
|
|
||||||
Type: &wasm.GlobalType{ValType: wasm.ValueTypeF64},
|
|
||||||
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeF64Const, Data: u64.LeBytes(api.EncodeF64(math.MaxFloat64))},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExportSection: []*wasm.Export{
|
|
||||||
{Name: "math/pi", Type: wasm.ExternTypeGlobal, Index: 0},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
tc := tt
|
tc := tt
|
||||||
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
b := tc.input(NewRuntime(testCtx)).(*moduleBuilder)
|
b := tc.input(NewRuntime(testCtx)).(*hostModuleBuilder)
|
||||||
compiled, err := b.Compile(testCtx, NewCompileConfig())
|
compiled, err := b.Compile(testCtx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
m := compiled.(*compiledModule)
|
m := compiled.(*compiledModule)
|
||||||
|
|
||||||
@@ -380,31 +193,27 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestNewModuleBuilder_Compile_Errors only covers a few scenarios to avoid duplicating tests in internal/wasm/host_test.go
|
// TestNewHostModuleBuilder_Compile_Errors only covers a few scenarios to avoid
|
||||||
func TestNewModuleBuilder_Compile_Errors(t *testing.T) {
|
// duplicating tests in internal/wasm/host_test.go
|
||||||
|
func TestNewHostModuleBuilder_Compile_Errors(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
input func(Runtime) ModuleBuilder
|
input func(Runtime) HostModuleBuilder
|
||||||
config CompileConfig
|
|
||||||
expectedErr string
|
expectedErr string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "memory min > limit", // only one test to avoid duplicating tests in module_test.go
|
name: "error compiling", // should fail due to missing result.
|
||||||
input: func(rt Runtime) ModuleBuilder {
|
input: func(rt Runtime) HostModuleBuilder {
|
||||||
return rt.NewModuleBuilder("").ExportMemory("memory", math.MaxUint32)
|
return rt.NewHostModuleBuilder("").
|
||||||
|
ExportFunction("fn", &wasm.HostFunc{
|
||||||
|
ExportNames: []string{"fn"},
|
||||||
|
ResultTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||||
|
Code: &wasm.Code{IsHostFunction: true, Body: []byte{wasm.OpcodeEnd}},
|
||||||
|
})
|
||||||
},
|
},
|
||||||
config: NewCompileConfig(),
|
expectedErr: `invalid function[0] export["fn"]: not enough results
|
||||||
expectedErr: "memory[memory] min 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)",
|
have ()
|
||||||
},
|
want (i32)`,
|
||||||
{
|
|
||||||
name: "memory cap < min", // only one test to avoid duplicating tests in module_test.go
|
|
||||||
input: func(rt Runtime) ModuleBuilder {
|
|
||||||
return rt.NewModuleBuilder("").ExportMemory("memory", 2)
|
|
||||||
},
|
|
||||||
config: NewCompileConfig().WithMemorySizer(func(minPages uint32, maxPages *uint32) (min, capacity, max uint32) {
|
|
||||||
return 2, 1, 2
|
|
||||||
}),
|
|
||||||
expectedErr: "memory[memory] capacity 1 pages (64 Ki) less than minimum 2 pages (128 Ki)",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,16 +221,16 @@ func TestNewModuleBuilder_Compile_Errors(t *testing.T) {
|
|||||||
tc := tt
|
tc := tt
|
||||||
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
_, e := tc.input(NewRuntime(testCtx)).Compile(testCtx, tc.config)
|
_, e := tc.input(NewRuntime(testCtx)).Compile(testCtx)
|
||||||
require.EqualError(t, e, tc.expectedErr)
|
require.EqualError(t, e, tc.expectedErr)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestNewModuleBuilder_Instantiate ensures Runtime.InstantiateModule is called on success.
|
// TestNewHostModuleBuilder_Instantiate ensures Runtime.InstantiateModule is called on success.
|
||||||
func TestNewModuleBuilder_Instantiate(t *testing.T) {
|
func TestNewHostModuleBuilder_Instantiate(t *testing.T) {
|
||||||
r := NewRuntime(testCtx)
|
r := NewRuntime(testCtx)
|
||||||
m, err := r.NewModuleBuilder("env").Instantiate(testCtx, r)
|
m, err := r.NewHostModuleBuilder("env").Instantiate(testCtx, r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// If this was instantiated, it would be added to the store under the same name
|
// If this was instantiated, it would be added to the store under the same name
|
||||||
@@ -432,13 +241,13 @@ func TestNewModuleBuilder_Instantiate(t *testing.T) {
|
|||||||
require.Zero(t, r.(*runtime).store.Engine.CompiledModuleCount())
|
require.Zero(t, r.(*runtime).store.Engine.CompiledModuleCount())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestNewModuleBuilder_Instantiate_Errors ensures errors propagate from Runtime.InstantiateModule
|
// TestNewHostModuleBuilder_Instantiate_Errors ensures errors propagate from Runtime.InstantiateModule
|
||||||
func TestNewModuleBuilder_Instantiate_Errors(t *testing.T) {
|
func TestNewHostModuleBuilder_Instantiate_Errors(t *testing.T) {
|
||||||
r := NewRuntime(testCtx)
|
r := NewRuntime(testCtx)
|
||||||
_, err := r.NewModuleBuilder("env").Instantiate(testCtx, r)
|
_, err := r.NewHostModuleBuilder("env").Instantiate(testCtx, r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = r.NewModuleBuilder("env").Instantiate(testCtx, r)
|
_, err = r.NewHostModuleBuilder("env").Instantiate(testCtx, r)
|
||||||
require.EqualError(t, err, "module[env] has already been instantiated")
|
require.EqualError(t, err, "module[env] has already been instantiated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ func (c *compiledModule) ExportedFunctions() map[string]api.FunctionDefinition {
|
|||||||
return c.module.ExportedFunctions()
|
return c.module.ExportedFunctions()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompileConfig allows you to override what was decoded from wasm, prior to compilation (ModuleBuilder.Compile or
|
// CompileConfig allows you to override what was decoded from wasm, prior to compilation (HostModuleBuilder.Compile or
|
||||||
// Runtime.CompileModule).
|
// Runtime.CompileModule).
|
||||||
//
|
//
|
||||||
// For example, WithMemorySizer allows you to override memory size that doesn't match your requirements.
|
// For example, WithMemorySizer allows you to override memory size that doesn't match your requirements.
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func main() {
|
|||||||
|
|
||||||
// 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.
|
||||||
_, err := r.NewModuleBuilder("env").
|
_, err := r.NewHostModuleBuilder("env").
|
||||||
ExportFunction("log", logString).
|
ExportFunction("log", logString).
|
||||||
Instantiate(ctx, r)
|
Instantiate(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func main() {
|
|||||||
|
|
||||||
// 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.
|
||||||
_, err := r.NewModuleBuilder("env").
|
_, err := r.NewHostModuleBuilder("env").
|
||||||
ExportFunction("log", logString).
|
ExportFunction("log", logString).
|
||||||
Instantiate(ctx, r)
|
Instantiate(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func run() error {
|
|||||||
|
|
||||||
// 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.
|
||||||
_, err := r.NewModuleBuilder("env").
|
_, err := r.NewHostModuleBuilder("env").
|
||||||
ExportFunction("log", logString).
|
ExportFunction("log", logString).
|
||||||
Instantiate(ctx, r)
|
Instantiate(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ log_i32 >> 21
|
|||||||
WebAssembly has neither a mechanism to get the current year, nor one to print to the console, so we define these in Go.
|
WebAssembly has neither a mechanism to get the current year, nor one to print to the console, so we define these in Go.
|
||||||
Similar to Go, WebAssembly functions are namespaced, into modules instead of packages. Just like Go, only exported
|
Similar to Go, WebAssembly functions are namespaced, into modules instead of packages. Just like Go, only exported
|
||||||
functions can be imported into another module. What you'll learn in [age-calculator.go](age-calculator.go), is how to
|
functions can be imported into another module. What you'll learn in [age-calculator.go](age-calculator.go), is how to
|
||||||
export functions using [ModuleBuilder](https://pkg.go.dev/github.com/tetratelabs/wazero#ModuleBuilder) and how a
|
export functions using [HostModuleBuilder](https://pkg.go.dev/github.com/tetratelabs/wazero#HostModuleBuilder) and how a
|
||||||
WebAssembly module defined in its [text format](https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#text-format%E2%91%A0)
|
WebAssembly module defined in its [text format](https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#text-format%E2%91%A0)
|
||||||
imports it. This only uses the text format for demonstration purposes, to show you what's going on. It is likely, you
|
imports it. This only uses the text format for demonstration purposes, to show you what's going on. It is likely, you
|
||||||
will use another language to compile a Wasm (WebAssembly Module) binary, such as TinyGo. Regardless of how wasm is
|
will use another language to compile a Wasm (WebAssembly Module) binary, such as TinyGo. Regardless of how wasm is
|
||||||
|
|||||||
@@ -38,7 +38,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.
|
||||||
_, err := r.NewModuleBuilder("env").
|
_, err := r.NewHostModuleBuilder("env").
|
||||||
ExportFunction("log_i32", func(v uint32) {
|
ExportFunction("log_i32", func(v uint32) {
|
||||||
fmt.Println("log_i32 >>", v)
|
fmt.Println("log_i32 >>", v)
|
||||||
}).
|
}).
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ var multiValueFromImportedHostWasm []byte
|
|||||||
// The imported "get_age" function returns multiple results. The source is in testdata/multi_value_imported.wat
|
// The imported "get_age" function returns multiple results. The source is in testdata/multi_value_imported.wat
|
||||||
func multiValueFromImportedHostWasmFunctions(ctx context.Context, r wazero.Runtime) (api.Module, error) {
|
func multiValueFromImportedHostWasmFunctions(ctx context.Context, r wazero.Runtime) (api.Module, error) {
|
||||||
// Instantiate the host module with the exported `get_age` function which returns multiple results.
|
// Instantiate the host module with the exported `get_age` function which returns multiple results.
|
||||||
if _, err := r.NewModuleBuilder("multi-value/host").
|
if _, err := r.NewHostModuleBuilder("multi-value/host").
|
||||||
// Define a function that returns two results
|
// Define a function that returns two results
|
||||||
ExportFunction("get_age", func() (age uint64, errno uint32) {
|
ExportFunction("get_age", func() (age uint64, errno uint32) {
|
||||||
age = 37
|
age = 37
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ func instantiateWithEnv(ctx context.Context, r wazero.Runtime, module wazero.Com
|
|||||||
|
|
||||||
// Instantiate a new "env" module which exports a stateful function.
|
// Instantiate a new "env" module which exports a stateful function.
|
||||||
c := &counter{}
|
c := &counter{}
|
||||||
_, err := r.NewModuleBuilder("env").
|
_, err := r.NewHostModuleBuilder("env").
|
||||||
ExportFunction("next_i32", c.getAndIncrement).
|
ExportFunction("next_i32", c.getAndIncrement).
|
||||||
Instantiate(ctx, ns)
|
Instantiate(ctx, ns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const (
|
|||||||
// - To add more functions to the "env" module, use FunctionExporter.
|
// - To add more functions to the "env" module, use FunctionExporter.
|
||||||
// - To instantiate into another wazero.Namespace, use FunctionExporter.
|
// - To instantiate into another wazero.Namespace, use FunctionExporter.
|
||||||
func Instantiate(ctx context.Context, r wazero.Runtime) (api.Closer, error) {
|
func Instantiate(ctx context.Context, r wazero.Runtime) (api.Closer, error) {
|
||||||
builder := r.NewModuleBuilder("env")
|
builder := r.NewHostModuleBuilder("env")
|
||||||
NewFunctionExporter().ExportFunctions(builder)
|
NewFunctionExporter().ExportFunctions(builder)
|
||||||
return builder.Instantiate(ctx, r)
|
return builder.Instantiate(ctx, r)
|
||||||
}
|
}
|
||||||
@@ -77,9 +77,9 @@ type FunctionExporter interface {
|
|||||||
// appropriate to use WithTraceToStdout instead.
|
// appropriate to use WithTraceToStdout instead.
|
||||||
WithTraceToStderr() FunctionExporter
|
WithTraceToStderr() FunctionExporter
|
||||||
|
|
||||||
// ExportFunctions builds functions to export with a wazero.ModuleBuilder
|
// ExportFunctions builds functions to export with a wazero.HostModuleBuilder
|
||||||
// named "env".
|
// named "env".
|
||||||
ExportFunctions(builder wazero.ModuleBuilder)
|
ExportFunctions(builder wazero.HostModuleBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFunctionExporter returns a FunctionExporter object with trace disabled.
|
// NewFunctionExporter returns a FunctionExporter object with trace disabled.
|
||||||
@@ -107,7 +107,7 @@ func (e *functionExporter) WithTraceToStderr() FunctionExporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ExportFunctions implements FunctionExporter.ExportFunctions
|
// ExportFunctions implements FunctionExporter.ExportFunctions
|
||||||
func (e *functionExporter) ExportFunctions(builder wazero.ModuleBuilder) {
|
func (e *functionExporter) ExportFunctions(builder wazero.HostModuleBuilder) {
|
||||||
builder.ExportFunction(functionAbort, e.abortFn)
|
builder.ExportFunction(functionAbort, e.abortFn)
|
||||||
builder.ExportFunction(functionTrace, e.traceFn)
|
builder.ExportFunction(functionTrace, e.traceFn)
|
||||||
builder.ExportFunction(functionSeed, seed)
|
builder.ExportFunction(functionSeed, seed)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func Example_functionExporter() {
|
|||||||
defer r.Close(ctx) // This closes everything this Runtime created.
|
defer r.Close(ctx) // This closes everything this Runtime created.
|
||||||
|
|
||||||
// First construct your own module builder for "env"
|
// First construct your own module builder for "env"
|
||||||
envBuilder := r.NewModuleBuilder("env").
|
envBuilder := r.NewHostModuleBuilder("env").
|
||||||
ExportFunction("get_int", func() uint32 { return 1 })
|
ExportFunction("get_int", func() uint32 { return 1 })
|
||||||
|
|
||||||
// Now, add AssemblyScript special function imports into it.
|
// Now, add AssemblyScript special function imports into it.
|
||||||
|
|||||||
@@ -424,10 +424,10 @@ func requireProxyModule(t *testing.T, fns FunctionExporter, config wazero.Module
|
|||||||
|
|
||||||
r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfigInterpreter())
|
r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfigInterpreter())
|
||||||
|
|
||||||
builder := r.NewModuleBuilder("env")
|
builder := r.NewHostModuleBuilder("env")
|
||||||
fns.ExportFunctions(builder)
|
fns.ExportFunctions(builder)
|
||||||
|
|
||||||
envCompiled, err := builder.Compile(ctx, wazero.NewCompileConfig())
|
envCompiled, err := builder.Compile(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = r.InstantiateModule(ctx, envCompiled, config)
|
_, err = r.InstantiateModule(ctx, envCompiled, config)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import (
|
|||||||
// - To add more functions to the "env" module, use FunctionExporter.
|
// - To add more functions to the "env" module, use FunctionExporter.
|
||||||
// - To instantiate into another wazero.Namespace, use FunctionExporter.
|
// - To instantiate into another wazero.Namespace, use FunctionExporter.
|
||||||
func Instantiate(ctx context.Context, r wazero.Runtime) (api.Closer, error) {
|
func Instantiate(ctx context.Context, r wazero.Runtime) (api.Closer, error) {
|
||||||
builder := r.NewModuleBuilder("env")
|
builder := r.NewHostModuleBuilder("env")
|
||||||
NewFunctionExporter().ExportFunctions(builder)
|
NewFunctionExporter().ExportFunctions(builder)
|
||||||
return builder.Instantiate(ctx, r)
|
return builder.Instantiate(ctx, r)
|
||||||
}
|
}
|
||||||
@@ -37,9 +37,9 @@ func Instantiate(ctx context.Context, r wazero.Runtime) (api.Closer, error) {
|
|||||||
// FunctionExporter configures the functions in the "env" module used by
|
// FunctionExporter configures the functions in the "env" module used by
|
||||||
// Emscripten.
|
// Emscripten.
|
||||||
type FunctionExporter interface {
|
type FunctionExporter interface {
|
||||||
// ExportFunctions builds functions to export with a wazero.ModuleBuilder
|
// ExportFunctions builds functions to export with a wazero.HostModuleBuilder
|
||||||
// named "env".
|
// named "env".
|
||||||
ExportFunctions(builder wazero.ModuleBuilder)
|
ExportFunctions(builder wazero.HostModuleBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFunctionExporter returns a FunctionExporter object with trace disabled.
|
// NewFunctionExporter returns a FunctionExporter object with trace disabled.
|
||||||
@@ -50,7 +50,7 @@ func NewFunctionExporter() FunctionExporter {
|
|||||||
type functionExporter struct{}
|
type functionExporter struct{}
|
||||||
|
|
||||||
// ExportFunctions implements FunctionExporter.ExportFunctions
|
// ExportFunctions implements FunctionExporter.ExportFunctions
|
||||||
func (e *functionExporter) ExportFunctions(builder wazero.ModuleBuilder) {
|
func (e *functionExporter) ExportFunctions(builder wazero.HostModuleBuilder) {
|
||||||
builder.ExportFunction(notifyMemoryGrowth.Name, notifyMemoryGrowth)
|
builder.ExportFunction(notifyMemoryGrowth.Name, notifyMemoryGrowth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func Example_functionExporter() {
|
|||||||
|
|
||||||
// Next, construct your own module builder for "env" with any functions
|
// Next, construct your own module builder for "env" with any functions
|
||||||
// you need.
|
// you need.
|
||||||
envBuilder := r.NewModuleBuilder("env").
|
envBuilder := r.NewHostModuleBuilder("env").
|
||||||
ExportFunction("get_int", func() uint32 { return 1 })
|
ExportFunction("get_int", func() uint32 { return 1 })
|
||||||
|
|
||||||
// Now, add Emscripten special function imports into it.
|
// Now, add Emscripten special function imports into it.
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func WithRoundTripper(ctx context.Context, rt http.RoundTripper) context.Context
|
|||||||
// - Both the host and guest module are closed after being run.
|
// - Both the host and guest module are closed after being run.
|
||||||
func Run(ctx context.Context, r wazero.Runtime, compiled wazero.CompiledModule, config wazero.ModuleConfig) error {
|
func Run(ctx context.Context, r wazero.Runtime, compiled wazero.CompiledModule, config wazero.ModuleConfig) error {
|
||||||
// Instantiate the imports needed by go-compiled wasm.
|
// Instantiate the imports needed by go-compiled wasm.
|
||||||
js, err := moduleBuilder(r).Instantiate(ctx, r)
|
js, err := hostModuleBuilder(r).Instantiate(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -86,9 +86,9 @@ func Run(ctx context.Context, r wazero.Runtime, compiled wazero.CompiledModule,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// moduleBuilder returns a new wazero.ModuleBuilder
|
// hostModuleBuilder returns a new wazero.HostModuleBuilder
|
||||||
func moduleBuilder(r wazero.Runtime) wazero.ModuleBuilder {
|
func hostModuleBuilder(r wazero.Runtime) wazero.HostModuleBuilder {
|
||||||
return r.NewModuleBuilder("go").
|
return r.NewHostModuleBuilder("go").
|
||||||
ExportFunction(GetRandomData.Name(), GetRandomData).
|
ExportFunction(GetRandomData.Name(), GetRandomData).
|
||||||
ExportFunction(Nanotime1.Name(), Nanotime1).
|
ExportFunction(Nanotime1.Name(), Nanotime1).
|
||||||
ExportFunction(WasmExit.Name(), WasmExit).
|
ExportFunction(WasmExit.Name(), WasmExit).
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ type Builder interface {
|
|||||||
// Compile compiles the ModuleName module that can instantiated in any
|
// Compile compiles the ModuleName module that can instantiated in any
|
||||||
// namespace (wazero.Namespace).
|
// namespace (wazero.Namespace).
|
||||||
//
|
//
|
||||||
// Note: This has the same effect as the same function on wazero.ModuleBuilder.
|
// Note: This has the same effect as the same function on wazero.HostModuleBuilder.
|
||||||
Compile(context.Context, wazero.CompileConfig) (wazero.CompiledModule, error)
|
Compile(context.Context) (wazero.CompiledModule, error)
|
||||||
|
|
||||||
// Instantiate instantiates the ModuleName module into the given namespace.
|
// Instantiate instantiates the ModuleName module into the given namespace.
|
||||||
//
|
//
|
||||||
// Note: This has the same effect as the same function on wazero.ModuleBuilder.
|
// Note: This has the same effect as the same function on wazero.HostModuleBuilder.
|
||||||
Instantiate(context.Context, wazero.Namespace) (api.Closer, error)
|
Instantiate(context.Context, wazero.Namespace) (api.Closer, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,21 +63,21 @@ func NewBuilder(r wazero.Runtime) Builder {
|
|||||||
|
|
||||||
type builder struct{ r wazero.Runtime }
|
type builder struct{ r wazero.Runtime }
|
||||||
|
|
||||||
// moduleBuilder returns a new wazero.ModuleBuilder for ModuleName
|
// hostModuleBuilder returns a new wazero.HostModuleBuilder for ModuleName
|
||||||
func (b *builder) moduleBuilder() wazero.ModuleBuilder {
|
func (b *builder) hostModuleBuilder() wazero.HostModuleBuilder {
|
||||||
ret := b.r.NewModuleBuilder(ModuleName)
|
ret := b.r.NewHostModuleBuilder(ModuleName)
|
||||||
exportFunctions(ret)
|
exportFunctions(ret)
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile implements Builder.Compile
|
// Compile implements Builder.Compile
|
||||||
func (b *builder) Compile(ctx context.Context, config wazero.CompileConfig) (wazero.CompiledModule, error) {
|
func (b *builder) Compile(ctx context.Context) (wazero.CompiledModule, error) {
|
||||||
return b.moduleBuilder().Compile(ctx, config)
|
return b.hostModuleBuilder().Compile(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instantiate implements Builder.Instantiate
|
// Instantiate implements Builder.Instantiate
|
||||||
func (b *builder) Instantiate(ctx context.Context, ns wazero.Namespace) (api.Closer, error) {
|
func (b *builder) Instantiate(ctx context.Context, ns wazero.Namespace) (api.Closer, error) {
|
||||||
return b.moduleBuilder().Instantiate(ctx, ns)
|
return b.hostModuleBuilder().Instantiate(ctx, ns)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ## Translation notes
|
// ## Translation notes
|
||||||
@@ -109,7 +109,7 @@ func (b *builder) Instantiate(ctx context.Context, ns wazero.Namespace) (api.Clo
|
|||||||
|
|
||||||
// exportFunctions adds all go functions that implement wasi.
|
// exportFunctions adds all go functions that implement wasi.
|
||||||
// These should be exported in the module named ModuleName.
|
// These should be exported in the module named ModuleName.
|
||||||
func exportFunctions(builder wazero.ModuleBuilder) {
|
func exportFunctions(builder wazero.HostModuleBuilder) {
|
||||||
// Note: these are ordered per spec for consistency even if the resulting
|
// Note: these are ordered per spec for consistency even if the resulting
|
||||||
// map can't guarantee that.
|
// map can't guarantee that.
|
||||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#functions
|
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#functions
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero"
|
"github.com/tetratelabs/wazero"
|
||||||
|
"github.com/tetratelabs/wazero/api"
|
||||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||||
|
"github.com/tetratelabs/wazero/internal/wasm"
|
||||||
|
binaryformat "github.com/tetratelabs/wazero/internal/wasm/binary"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testMem = []byte{
|
var testMem = []byte{
|
||||||
@@ -37,11 +40,13 @@ func Test_Benchmark_EnvironGet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_EnvironGet(b *testing.B) {
|
func Benchmark_EnvironGet(b *testing.B) {
|
||||||
r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfigInterpreter())
|
r := wazero.NewRuntime(testCtx)
|
||||||
|
defer r.Close(testCtx)
|
||||||
|
|
||||||
compiled, err := r.NewModuleBuilder(b.Name()).
|
compiled, err := r.CompileModule(testCtx, binaryformat.EncodeModule(&wasm.Module{
|
||||||
ExportMemoryWithMax("memory", 1, 1).
|
MemorySection: &wasm.Memory{Min: 1, Max: 1},
|
||||||
Compile(testCtx, wazero.NewCompileConfig())
|
ExportSection: []*wasm.Export{{Name: "memory", Type: api.ExternTypeMemory}},
|
||||||
|
}), wazero.NewCompileConfig())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,8 +34,7 @@ func requireProxyModule(t *testing.T, config wazero.ModuleConfig) (api.Module, a
|
|||||||
|
|
||||||
r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfigInterpreter())
|
r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfigInterpreter())
|
||||||
|
|
||||||
wasiModuleCompiled, err := (&builder{r}).moduleBuilder().
|
wasiModuleCompiled, err := (&builder{r}).hostModuleBuilder().Compile(ctx)
|
||||||
Compile(ctx, wazero.NewCompileConfig())
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = r.InstantiateModule(ctx, wasiModuleCompiled, config)
|
_, err = r.InstantiateModule(ctx, wasiModuleCompiled, config)
|
||||||
@@ -65,8 +64,7 @@ func requireErrnoNosys(t *testing.T, funcName string, params ...uint64) string {
|
|||||||
defer r.Close(ctx)
|
defer r.Close(ctx)
|
||||||
|
|
||||||
// Instantiate the wasi module.
|
// Instantiate the wasi module.
|
||||||
wasiModuleCompiled, err := (&builder{r}).moduleBuilder().
|
wasiModuleCompiled, err := (&builder{r}).hostModuleBuilder().Compile(ctx)
|
||||||
Compile(ctx, wazero.NewCompileConfig())
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = r.InstantiateModule(ctx, wasiModuleCompiled, wazero.NewModuleConfig())
|
_, err = r.InstantiateModule(ctx, wasiModuleCompiled, wazero.NewModuleConfig())
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ func TestCompiler_SliceAllocatedOnHeap(t *testing.T) {
|
|||||||
// Trigger relocation of goroutine stack because at this point we have the majority of
|
// Trigger relocation of goroutine stack because at this point we have the majority of
|
||||||
// goroutine stack unused after recursive call.
|
// goroutine stack unused after recursive call.
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
}}, nil, map[string]*wasm.Memory{}, map[string]*wasm.Global{}, enabledFeatures)
|
}}, nil, enabledFeatures)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = s.Engine.CompileModule(testCtx, hm)
|
err = s.Engine.CompileModule(testCtx, hm)
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ func TestMustCallFromSP(t *testing.T) {
|
|||||||
defer r.Close(testCtx)
|
defer r.Close(testCtx)
|
||||||
|
|
||||||
funcName := "i64i32i32i32i32_i64i32_withSP"
|
funcName := "i64i32i32i32i32_i64i32_withSP"
|
||||||
im, err := r.NewModuleBuilder("go").
|
im, err := r.NewHostModuleBuilder("go").
|
||||||
ExportFunction(funcName, MustCallFromSP(true, wasm.NewGoFunc(
|
ExportFunction(funcName, MustCallFromSP(true, wasm.NewGoFunc(
|
||||||
funcName, funcName,
|
funcName, funcName,
|
||||||
[]string{"v", "mAddr", "mLen", "argsArray", "argsLen"},
|
[]string{"v", "mAddr", "mLen", "argsArray", "argsLen"},
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ func createRuntime(b *testing.B, config wazero.RuntimeConfig) wazero.Runtime {
|
|||||||
|
|
||||||
r := wazero.NewRuntimeWithConfig(testCtx, config)
|
r := wazero.NewRuntimeWithConfig(testCtx, config)
|
||||||
|
|
||||||
_, err := r.NewModuleBuilder("env").
|
_, err := r.NewHostModuleBuilder("env").
|
||||||
ExportFunction("get_random_string", getRandomString).
|
ExportFunction("get_random_string", getRandomString).
|
||||||
Instantiate(testCtx, r)
|
Instantiate(testCtx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ func testReftypeImports(t *testing.T, r wazero.Runtime) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hostObj := &dog{name: "hello"}
|
hostObj := &dog{name: "hello"}
|
||||||
host, err := r.NewModuleBuilder("host").
|
host, err := r.NewHostModuleBuilder("host").
|
||||||
ExportFunctions(map[string]interface{}{
|
ExportFunctions(map[string]interface{}{
|
||||||
"externref": func(externrefFromRefNull uintptr) uintptr {
|
"externref": func(externrefFromRefNull uintptr) uintptr {
|
||||||
require.Zero(t, externrefFromRefNull)
|
require.Zero(t, externrefFromRefNull)
|
||||||
@@ -176,7 +176,7 @@ func testUnreachable(t *testing.T, r wazero.Runtime) {
|
|||||||
panic("panic in host function")
|
panic("panic in host function")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := r.NewModuleBuilder("host").ExportFunction("cause_unreachable", callUnreachable).Instantiate(testCtx, r)
|
_, err := r.NewHostModuleBuilder("host").ExportFunction("cause_unreachable", callUnreachable).Instantiate(testCtx, r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
module, err := r.InstantiateModuleFromBinary(testCtx, unreachableWasm)
|
module, err := r.InstantiateModuleFromBinary(testCtx, unreachableWasm)
|
||||||
@@ -199,7 +199,7 @@ func testRecursiveEntry(t *testing.T, r wazero.Runtime) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := r.NewModuleBuilder("env").ExportFunction("host_func", hostfunc).Instantiate(testCtx, r)
|
_, err := r.NewHostModuleBuilder("env").ExportFunction("host_func", hostfunc).Instantiate(testCtx, r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
module, err := r.InstantiateModuleFromBinary(testCtx, recursiveWasm)
|
module, err := r.InstantiateModuleFromBinary(testCtx, recursiveWasm)
|
||||||
@@ -222,7 +222,7 @@ func testHostFuncMemory(t *testing.T, r wazero.Runtime) {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
host, err := r.NewModuleBuilder("").ExportFunction("store_int", storeInt).Instantiate(testCtx, r)
|
host, err := r.NewHostModuleBuilder("").ExportFunction("store_int", storeInt).Instantiate(testCtx, r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer host.Close(testCtx)
|
defer host.Close(testCtx)
|
||||||
|
|
||||||
@@ -262,7 +262,7 @@ func testNestedGoContext(t *testing.T, r wazero.Runtime) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
imported, err := r.NewModuleBuilder(importedName).ExportFunctions(fns).Instantiate(testCtx, r)
|
imported, err := r.NewHostModuleBuilder(importedName).ExportFunctions(fns).Instantiate(testCtx, r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer imported.Close(testCtx)
|
defer imported.Close(testCtx)
|
||||||
|
|
||||||
@@ -299,7 +299,7 @@ func testHostFunctionContextParameter(t *testing.T, r wazero.Runtime) {
|
|||||||
|
|
||||||
for test := range fns {
|
for test := range fns {
|
||||||
t.Run(test, func(t *testing.T) {
|
t.Run(test, func(t *testing.T) {
|
||||||
imported, err := r.NewModuleBuilder(importedName).
|
imported, err := r.NewHostModuleBuilder(importedName).
|
||||||
ExportFunction("return_input", fns[test]).
|
ExportFunction("return_input", fns[test]).
|
||||||
Instantiate(testCtx, r)
|
Instantiate(testCtx, r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -369,7 +369,7 @@ func testHostFunctionNumericParameter(t *testing.T, r wazero.Runtime) {
|
|||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
imported, err := r.NewModuleBuilder(importedName).
|
imported, err := r.NewHostModuleBuilder(importedName).
|
||||||
ExportFunction("return_input", fns[test.name]).
|
ExportFunction("return_input", fns[test.name]).
|
||||||
Instantiate(testCtx, r)
|
Instantiate(testCtx, r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -519,8 +519,8 @@ func testCloseInFlight(t *testing.T, r wazero.Runtime) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the host module, which exports the function that closes the importing module.
|
// Create the host module, which exports the function that closes the importing module.
|
||||||
importedCode, err = r.NewModuleBuilder(t.Name()+"-imported").
|
importedCode, err = r.NewHostModuleBuilder(t.Name()+"-imported").
|
||||||
ExportFunction("return_input", closeAndReturn).Compile(testCtx, compileConfig)
|
ExportFunction("return_input", closeAndReturn).Compile(testCtx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
imported, err = r.InstantiateModule(testCtx, importedCode, moduleConfig)
|
imported, err = r.InstantiateModule(testCtx, importedCode, moduleConfig)
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func closeImportedModuleWhileInUse(t *testing.T, r wazero.Runtime) {
|
|||||||
require.NoError(t, imported.Close(testCtx))
|
require.NoError(t, imported.Close(testCtx))
|
||||||
|
|
||||||
// Redefine the imported module, with a function that no longer blocks.
|
// Redefine the imported module, with a function that no longer blocks.
|
||||||
imported, err := r.NewModuleBuilder(imported.Name()).ExportFunction("return_input", func(x uint32) uint32 {
|
imported, err := r.NewHostModuleBuilder(imported.Name()).ExportFunction("return_input", func(x uint32) uint32 {
|
||||||
return x
|
return x
|
||||||
}).Instantiate(testCtx, r)
|
}).Instantiate(testCtx, r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -78,7 +78,7 @@ func closeModuleWhileInUse(t *testing.T, r wazero.Runtime, closeFn func(imported
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the host module, which exports the blocking function.
|
// Create the host module, which exports the blocking function.
|
||||||
imported, err := r.NewModuleBuilder(t.Name()+"-imported").
|
imported, err := r.NewHostModuleBuilder(t.Name()+"-imported").
|
||||||
ExportFunction("return_input", blockAndReturn).Instantiate(testCtx, r)
|
ExportFunction("return_input", blockAndReturn).Instantiate(testCtx, r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer imported.Close(testCtx)
|
defer imported.Close(testCtx)
|
||||||
|
|||||||
@@ -93,18 +93,18 @@ func (r *wazeroRuntime) Compile(ctx context.Context, cfg *RuntimeConfig) (err er
|
|||||||
r.runtime = wazero.NewRuntimeWithConfig(ctx, r.config)
|
r.runtime = wazero.NewRuntimeWithConfig(ctx, r.config)
|
||||||
if cfg.LogFn != nil {
|
if cfg.LogFn != nil {
|
||||||
r.logFn = cfg.LogFn
|
r.logFn = cfg.LogFn
|
||||||
if r.env, err = r.runtime.NewModuleBuilder("env").
|
if r.env, err = r.runtime.NewHostModuleBuilder("env").
|
||||||
ExportFunction("log", r.log).Compile(ctx, wazero.NewCompileConfig()); err != nil {
|
ExportFunction("log", r.log).Compile(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if cfg.EnvFReturnValue != 0 {
|
} else if cfg.EnvFReturnValue != 0 {
|
||||||
if r.env, err = r.runtime.NewModuleBuilder("env").
|
if r.env, err = r.runtime.NewHostModuleBuilder("env").
|
||||||
ExportFunction("f",
|
ExportFunction("f",
|
||||||
// Note: accepting (context.Context, api.Module) is the slowest type of host function with wazero.
|
// Note: accepting (context.Context, api.Module) is the slowest type of host function with wazero.
|
||||||
func(context.Context, api.Module, uint64) uint64 {
|
func(context.Context, api.Module, uint64) uint64 {
|
||||||
return cfg.EnvFReturnValue
|
return cfg.EnvFReturnValue
|
||||||
},
|
},
|
||||||
).Compile(ctx, wazero.NewCompileConfig()); err != nil {
|
).Compile(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ func fail(t TestingT, m1, m2 string, formatWithArgs ...interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// failStack returns the stack leading to the require fail, without test infrastructure.
|
// failStack returns the stack leading to the failure, without test infrastructure.
|
||||||
//
|
//
|
||||||
// Note: This is similar to assert.CallerInfo in testify
|
// Note: This is similar to assert.CallerInfo in testify
|
||||||
// Note: This is untested because it is a lot of work to do that. The rationale to punt is this is a test-only internal
|
// Note: This is untested because it is a lot of work to do that. The rationale to punt is this is a test-only internal
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ func (m *CallContext) close(ctx context.Context, exitCode uint32) (c bool, err e
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
c = true
|
c = true
|
||||||
if sysCtx := m.Sys; sysCtx != nil { // nil if from ModuleBuilder
|
if sysCtx := m.Sys; sysCtx != nil { // nil if from HostModuleBuilder
|
||||||
err = sysCtx.FS(ctx).Close(ctx)
|
err = sysCtx.FS(ctx).Close(ctx)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
"github.com/tetratelabs/wazero/internal/wasmdebug"
|
"github.com/tetratelabs/wazero/internal/wasmdebug"
|
||||||
@@ -91,8 +90,6 @@ func NewHostModule(
|
|||||||
moduleName string,
|
moduleName string,
|
||||||
nameToGoFunc map[string]interface{},
|
nameToGoFunc map[string]interface{},
|
||||||
funcToNames map[string][]string,
|
funcToNames map[string][]string,
|
||||||
nameToMemory map[string]*Memory,
|
|
||||||
nameToGlobal map[string]*Global,
|
|
||||||
enabledFeatures api.CoreFeatures,
|
enabledFeatures api.CoreFeatures,
|
||||||
) (m *Module, err error) {
|
) (m *Module, err error) {
|
||||||
if moduleName != "" {
|
if moduleName != "" {
|
||||||
@@ -101,52 +98,15 @@ func NewHostModule(
|
|||||||
m = &Module{}
|
m = &Module{}
|
||||||
}
|
}
|
||||||
|
|
||||||
funcCount := uint32(len(nameToGoFunc))
|
if exportCount := uint32(len(nameToGoFunc)); exportCount > 0 {
|
||||||
memoryCount := uint32(len(nameToMemory))
|
|
||||||
globalCount := uint32(len(nameToGlobal))
|
|
||||||
exportCount := funcCount + memoryCount + globalCount
|
|
||||||
if exportCount > 0 {
|
|
||||||
m.ExportSection = make([]*Export, 0, exportCount)
|
m.ExportSection = make([]*Export, 0, exportCount)
|
||||||
}
|
|
||||||
|
|
||||||
// Check name collision as exports cannot collide on names, regardless of type.
|
|
||||||
for name := range nameToGoFunc {
|
|
||||||
// manually generate the error message as we don't have debug names yet.
|
|
||||||
if _, ok := nameToMemory[name]; ok {
|
|
||||||
return nil, fmt.Errorf("func[%s.%s] exports the same name as a memory", moduleName, name)
|
|
||||||
}
|
|
||||||
if _, ok := nameToGlobal[name]; ok {
|
|
||||||
return nil, fmt.Errorf("func[%s.%s] exports the same name as a global", moduleName, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for name := range nameToMemory {
|
|
||||||
if _, ok := nameToGlobal[name]; ok {
|
|
||||||
return nil, fmt.Errorf("memory[%s] exports the same name as a global", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if funcCount > 0 {
|
|
||||||
if err = addFuncs(m, nameToGoFunc, funcToNames, enabledFeatures); err != nil {
|
if err = addFuncs(m, nameToGoFunc, funcToNames, enabledFeatures); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if memoryCount > 0 {
|
|
||||||
if err = addMemory(m, nameToMemory); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: we can use enabledFeatures to fail early on things like mutable globals (once supported)
|
|
||||||
if globalCount > 0 {
|
|
||||||
if err = addGlobals(m, nameToGlobal); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assigns the ModuleID by calculating sha256 on inputs as host modules do not have `wasm` to hash.
|
// Assigns the ModuleID by calculating sha256 on inputs as host modules do not have `wasm` to hash.
|
||||||
m.AssignModuleID([]byte(fmt.Sprintf("%s:%v:%v:%v:%v",
|
m.AssignModuleID([]byte(fmt.Sprintf("%s:%v:%v", moduleName, nameToGoFunc, enabledFeatures)))
|
||||||
moduleName, nameToGoFunc, nameToMemory, nameToGlobal, enabledFeatures)))
|
|
||||||
m.BuildFunctionDefinitions()
|
m.BuildFunctionDefinitions()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -264,50 +224,6 @@ func addFuncs(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addMemory(m *Module, nameToMemory map[string]*Memory) error {
|
|
||||||
memoryCount := uint32(len(nameToMemory))
|
|
||||||
|
|
||||||
// Only one memory can be defined or imported
|
|
||||||
if memoryCount > 1 {
|
|
||||||
memoryNames := make([]string, 0, memoryCount)
|
|
||||||
for k := range nameToMemory {
|
|
||||||
memoryNames = append(memoryNames, k)
|
|
||||||
}
|
|
||||||
sort.Strings(memoryNames) // For consistent error messages
|
|
||||||
return fmt.Errorf("only one memory is allowed, but configured: %s", strings.Join(memoryNames, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the memory name to export.
|
|
||||||
var name string
|
|
||||||
for k, v := range nameToMemory {
|
|
||||||
name = k
|
|
||||||
if v.Min > v.Max {
|
|
||||||
return fmt.Errorf("memory[%s] min %d pages (%s) > max %d pages (%s)", name, v.Min, PagesToUnitOfBytes(v.Min), v.Max, PagesToUnitOfBytes(v.Max))
|
|
||||||
}
|
|
||||||
m.MemorySection = v
|
|
||||||
}
|
|
||||||
|
|
||||||
m.ExportSection = append(m.ExportSection, &Export{Type: ExternTypeMemory, Name: name, Index: 0})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func addGlobals(m *Module, globals map[string]*Global) error {
|
|
||||||
globalCount := len(globals)
|
|
||||||
m.GlobalSection = make([]*Global, 0, globalCount)
|
|
||||||
|
|
||||||
globalNames := make([]string, 0, globalCount)
|
|
||||||
for name := range globals {
|
|
||||||
globalNames = append(globalNames, name)
|
|
||||||
}
|
|
||||||
sort.Strings(globalNames) // For consistent iteration order
|
|
||||||
|
|
||||||
for i, name := range globalNames {
|
|
||||||
m.GlobalSection = append(m.GlobalSection, globals[name])
|
|
||||||
m.ExportSection = append(m.ExportSection, &Export{Type: ExternTypeGlobal, Name: name, Index: Index(i)})
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Module) maybeAddType(params, results []ValueType, enabledFeatures api.CoreFeatures) (Index, error) {
|
func (m *Module) maybeAddType(params, results []ValueType, enabledFeatures api.CoreFeatures) (Index, error) {
|
||||||
if len(results) > 1 {
|
if len(results) > 1 {
|
||||||
// Guard >1.0 feature multi-value
|
// Guard >1.0 feature multi-value
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
"github.com/tetratelabs/wazero/internal/leb128"
|
|
||||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,10 +11,6 @@ import (
|
|||||||
type wasiAPI struct {
|
type wasiAPI struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ArgsSizesGet(ctx api.Module, resultArgc, resultArgvBufSize uint32) uint32 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *wasiAPI) ArgsSizesGet(ctx api.Module, resultArgc, resultArgvBufSize uint32) uint32 {
|
func (a *wasiAPI) ArgsSizesGet(ctx api.Module, resultArgc, resultArgvBufSize uint32) uint32 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -37,8 +32,6 @@ func TestNewHostModule(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name, moduleName string
|
name, moduleName string
|
||||||
nameToGoFunc map[string]interface{}
|
nameToGoFunc map[string]interface{}
|
||||||
nameToMemory map[string]*Memory
|
|
||||||
nameToGlobal map[string]*Global
|
|
||||||
expected *Module
|
expected *Module
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@@ -91,91 +84,13 @@ func TestNewHostModule(t *testing.T) {
|
|||||||
NameSection: &NameSection{ModuleName: "swapper", FunctionNames: NameMap{{Index: 0, Name: "swap"}}},
|
NameSection: &NameSection{ModuleName: "swapper", FunctionNames: NameMap{{Index: 0, Name: "swap"}}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "memory",
|
|
||||||
nameToMemory: map[string]*Memory{"memory": {Min: 1, Max: 2}},
|
|
||||||
expected: &Module{
|
|
||||||
MemorySection: &Memory{Min: 1, Max: 2},
|
|
||||||
ExportSection: []*Export{{Name: "memory", Type: ExternTypeMemory, Index: 0}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "globals",
|
|
||||||
nameToGlobal: map[string]*Global{
|
|
||||||
"g2": {
|
|
||||||
Type: &GlobalType{ValType: i32},
|
|
||||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)},
|
|
||||||
},
|
|
||||||
"g1": {
|
|
||||||
Type: &GlobalType{ValType: i32},
|
|
||||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: &Module{
|
|
||||||
GlobalSection: []*Global{
|
|
||||||
{
|
|
||||||
Type: &GlobalType{ValType: i32},
|
|
||||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: &GlobalType{ValType: i32},
|
|
||||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExportSection: []*Export{
|
|
||||||
{Name: "g1", Type: ExternTypeGlobal, Index: 0},
|
|
||||||
{Name: "g2", Type: ExternTypeGlobal, Index: 1},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "one of each",
|
|
||||||
moduleName: "env",
|
|
||||||
nameToGoFunc: map[string]interface{}{
|
|
||||||
functionArgsSizesGet: a.ArgsSizesGet,
|
|
||||||
},
|
|
||||||
nameToMemory: map[string]*Memory{
|
|
||||||
"memory": {Min: 1, Max: 1},
|
|
||||||
},
|
|
||||||
nameToGlobal: map[string]*Global{
|
|
||||||
"g": {
|
|
||||||
Type: &GlobalType{ValType: i32},
|
|
||||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: &Module{
|
|
||||||
TypeSection: []*FunctionType{
|
|
||||||
{Params: []ValueType{i32, i32}, Results: []ValueType{i32}},
|
|
||||||
},
|
|
||||||
FunctionSection: []Index{0},
|
|
||||||
CodeSection: []*Code{MustParseGoFuncCode(a.ArgsSizesGet)},
|
|
||||||
GlobalSection: []*Global{
|
|
||||||
{
|
|
||||||
Type: &GlobalType{ValType: i32},
|
|
||||||
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MemorySection: &Memory{Min: 1, Max: 1},
|
|
||||||
ExportSection: []*Export{
|
|
||||||
{Name: "args_sizes_get", Type: ExternTypeFunc, Index: 0},
|
|
||||||
{Name: "memory", Type: ExternTypeMemory, Index: 0},
|
|
||||||
{Name: "g", Type: ExternTypeGlobal, Index: 0},
|
|
||||||
},
|
|
||||||
NameSection: &NameSection{
|
|
||||||
ModuleName: "env",
|
|
||||||
FunctionNames: NameMap{
|
|
||||||
{Index: 0, Name: "args_sizes_get"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
tc := tt
|
tc := tt
|
||||||
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
m, e := NewHostModule(tc.moduleName, tc.nameToGoFunc, nil, tc.nameToMemory, tc.nameToGlobal,
|
m, e := NewHostModule(tc.moduleName, tc.nameToGoFunc, nil,
|
||||||
api.CoreFeaturesV1|api.CoreFeatureMultiValue)
|
api.CoreFeaturesV1|api.CoreFeatureMultiValue)
|
||||||
require.NoError(t, e)
|
require.NoError(t, e)
|
||||||
requireHostModuleEquals(t, tc.expected, m)
|
requireHostModuleEquals(t, tc.expected, m)
|
||||||
@@ -216,8 +131,6 @@ func TestNewHostModule_Errors(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name, moduleName string
|
name, moduleName string
|
||||||
nameToGoFunc map[string]interface{}
|
nameToGoFunc map[string]interface{}
|
||||||
nameToMemory map[string]*Memory
|
|
||||||
nameToGlobal map[string]*Global
|
|
||||||
expectedErr string
|
expectedErr string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@@ -228,38 +141,15 @@ func TestNewHostModule_Errors(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "function has multiple results",
|
name: "function has multiple results",
|
||||||
nameToGoFunc: map[string]interface{}{"fn": func() (uint32, uint32) { return 0, 0 }},
|
nameToGoFunc: map[string]interface{}{"fn": func() (uint32, uint32) { return 0, 0 }},
|
||||||
nameToMemory: map[string]*Memory{"mem": {Min: 1, Max: 1}},
|
|
||||||
expectedErr: "func[.fn] multiple result types invalid as feature \"multi-value\" is disabled",
|
expectedErr: "func[.fn] multiple result types invalid as feature \"multi-value\" is disabled",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "func collides on memory name",
|
|
||||||
nameToGoFunc: map[string]interface{}{"fn": ArgsSizesGet},
|
|
||||||
nameToMemory: map[string]*Memory{"fn": {Min: 1, Max: 1}},
|
|
||||||
expectedErr: "func[.fn] exports the same name as a memory",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple memories",
|
|
||||||
nameToMemory: map[string]*Memory{"memory": {Min: 1, Max: 1}, "mem": {Min: 2, Max: 2}},
|
|
||||||
expectedErr: "only one memory is allowed, but configured: mem, memory",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "memory max < min",
|
|
||||||
nameToMemory: map[string]*Memory{"memory": {Min: 1, Max: 0}},
|
|
||||||
expectedErr: "memory[memory] min 1 pages (64 Ki) > max 0 pages (0 Ki)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "func collides on global name",
|
|
||||||
nameToGoFunc: map[string]interface{}{"fn": ArgsSizesGet},
|
|
||||||
nameToGlobal: map[string]*Global{"fn": {}},
|
|
||||||
expectedErr: "func[.fn] exports the same name as a global",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
tc := tt
|
tc := tt
|
||||||
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
_, e := NewHostModule(tc.moduleName, tc.nameToGoFunc, nil, tc.nameToMemory, tc.nameToGlobal, api.CoreFeaturesV1)
|
_, e := NewHostModule(tc.moduleName, tc.nameToGoFunc, nil, api.CoreFeaturesV1)
|
||||||
require.EqualError(t, e, tc.expectedErr)
|
require.EqualError(t, e, tc.expectedErr)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -805,7 +805,7 @@ type Export struct {
|
|||||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-code
|
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-code
|
||||||
type Code struct {
|
type Code struct {
|
||||||
// IsHostFunction returns true if the function was implemented by the
|
// IsHostFunction returns true if the function was implemented by the
|
||||||
// embedder (ex via wazero.ModuleBuilder) instead of a wasm binary.
|
// embedder (ex via wazero.HostModuleBuilder) instead of a wasm binary.
|
||||||
//
|
//
|
||||||
// Notably, host functions can use the caller's memory, which might be
|
// Notably, host functions can use the caller's memory, which might be
|
||||||
// different from its defining module.
|
// different from its defining module.
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ func TestModuleInstance_Memory(t *testing.T) {
|
|||||||
|
|
||||||
func TestStore_Instantiate(t *testing.T) {
|
func TestStore_Instantiate(t *testing.T) {
|
||||||
s, ns := newStore()
|
s, ns := newStore()
|
||||||
m, err := NewHostModule("", map[string]interface{}{"fn": func(api.Module) {}}, nil, map[string]*Memory{}, map[string]*Global{}, api.CoreFeaturesV1)
|
m, err := NewHostModule("", map[string]interface{}{"fn": func(api.Module) {}}, nil, api.CoreFeaturesV1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
sysCtx := sys.DefaultContext(nil)
|
sysCtx := sys.DefaultContext(nil)
|
||||||
@@ -169,7 +169,7 @@ func TestStore_CloseWithExitCode(t *testing.T) {
|
|||||||
func TestStore_hammer(t *testing.T) {
|
func TestStore_hammer(t *testing.T) {
|
||||||
const importedModuleName = "imported"
|
const importedModuleName = "imported"
|
||||||
|
|
||||||
m, err := NewHostModule(importedModuleName, map[string]interface{}{"fn": func(api.Module) {}}, nil, map[string]*Memory{}, map[string]*Global{}, api.CoreFeaturesV1)
|
m, err := NewHostModule(importedModuleName, map[string]interface{}{"fn": func(api.Module) {}}, nil, api.CoreFeaturesV1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s, ns := newStore()
|
s, ns := newStore()
|
||||||
@@ -223,7 +223,7 @@ func TestStore_Instantiate_Errors(t *testing.T) {
|
|||||||
const importedModuleName = "imported"
|
const importedModuleName = "imported"
|
||||||
const importingModuleName = "test"
|
const importingModuleName = "test"
|
||||||
|
|
||||||
m, err := NewHostModule(importedModuleName, map[string]interface{}{"fn": func(api.Module) {}}, nil, map[string]*Memory{}, map[string]*Global{}, api.CoreFeaturesV1)
|
m, err := NewHostModule(importedModuleName, map[string]interface{}{"fn": func(api.Module) {}}, nil, api.CoreFeaturesV1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("Fails if module name already in use", func(t *testing.T) {
|
t.Run("Fails if module name already in use", func(t *testing.T) {
|
||||||
@@ -314,7 +314,7 @@ func TestStore_Instantiate_Errors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCallContext_ExportedFunction(t *testing.T) {
|
func TestCallContext_ExportedFunction(t *testing.T) {
|
||||||
host, err := NewHostModule("host", map[string]interface{}{"host_fn": func(api.Module) {}}, nil, map[string]*Memory{}, map[string]*Global{}, api.CoreFeaturesV1)
|
host, err := NewHostModule("host", map[string]interface{}{"host_fn": func(api.Module) {}}, nil, api.CoreFeaturesV1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s, ns := newStore()
|
s, ns := newStore()
|
||||||
|
|||||||
@@ -128,13 +128,13 @@ func (s *stackTrace) FromRecovered(recovered interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we have a runtime.Error, something severe happened which should include the stack trace. This could be
|
// If we have a runtime.Error, something severe happened which should include the stack trace. This could be
|
||||||
// a nil pointer from wazero or a user-defined function from ModuleBuilder.
|
// a nil pointer from wazero or a user-defined function from HostModuleBuilder.
|
||||||
if runtimeErr, ok := recovered.(runtime.Error); ok {
|
if runtimeErr, ok := recovered.(runtime.Error); ok {
|
||||||
// TODO: consider adding debug.Stack(), but last time we attempted, some tests became unstable.
|
// TODO: consider adding debug.Stack(), but last time we attempted, some tests became unstable.
|
||||||
return fmt.Errorf("%w (recovered by wazero)\nwasm stack trace:\n\t%s", runtimeErr, stack)
|
return fmt.Errorf("%w (recovered by wazero)\nwasm stack trace:\n\t%s", runtimeErr, stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point we expect the error was from a function defined by ModuleBuilder that intentionally called panic.
|
// At this point we expect the error was from a function defined by HostModuleBuilder that intentionally called panic.
|
||||||
if runtimeErr, ok := recovered.(error); ok { // Ex. panic(errors.New("whoops"))
|
if runtimeErr, ok := recovered.(error); ok { // Ex. panic(errors.New("whoops"))
|
||||||
return fmt.Errorf("%w (recovered by wazero)\nwasm stack trace:\n\t%s", runtimeErr, stack)
|
return fmt.Errorf("%w (recovered by wazero)\nwasm stack trace:\n\t%s", runtimeErr, stack)
|
||||||
} else { // Ex. panic("whoops")
|
} else { // Ex. panic("whoops")
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ func TestRuntime_Namespace(t *testing.T) {
|
|||||||
defer r.Close(testCtx)
|
defer r.Close(testCtx)
|
||||||
|
|
||||||
// Compile a module to add to the runtime
|
// Compile a module to add to the runtime
|
||||||
compiled, err := r.NewModuleBuilder("env").Compile(testCtx, NewCompileConfig())
|
compiled, err := r.NewHostModuleBuilder("env").Compile(testCtx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Instantiate "env" into the runtime default namespace (base case)
|
// Instantiate "env" into the runtime default namespace (base case)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
//
|
//
|
||||||
// module, _ := r.InstantiateModuleFromBinary(ctx, wasm)
|
// module, _ := r.InstantiateModuleFromBinary(ctx, wasm)
|
||||||
type Runtime interface {
|
type Runtime interface {
|
||||||
// NewModuleBuilder lets you create modules out of functions defined in Go.
|
// NewHostModuleBuilder lets you create modules out of functions defined in Go.
|
||||||
//
|
//
|
||||||
// Ex. Below defines and instantiates a module named "env" with one function:
|
// Ex. Below defines and instantiates a module named "env" with one function:
|
||||||
//
|
//
|
||||||
@@ -30,8 +30,8 @@ type Runtime interface {
|
|||||||
// hello := func() {
|
// hello := func() {
|
||||||
// fmt.Fprintln(stdout, "hello!")
|
// fmt.Fprintln(stdout, "hello!")
|
||||||
// }
|
// }
|
||||||
// _, err := r.NewModuleBuilder("env").ExportFunction("hello", hello).Instantiate(ctx, r)
|
// _, err := r.NewHostModuleBuilder("env").ExportFunction("hello", hello).Instantiate(ctx, r)
|
||||||
NewModuleBuilder(moduleName string) ModuleBuilder
|
NewHostModuleBuilder(moduleName string) HostModuleBuilder
|
||||||
|
|
||||||
// CompileModule decodes the WebAssembly binary (%.wasm) or errs if invalid.
|
// CompileModule decodes the WebAssembly binary (%.wasm) or errs if invalid.
|
||||||
// Any pre-compilation done after decoding wasm is dependent on RuntimeConfig or CompileConfig.
|
// Any pre-compilation done after decoding wasm is dependent on RuntimeConfig or CompileConfig.
|
||||||
|
|||||||
@@ -158,21 +158,20 @@ func TestRuntime_CompileModule_Errors(t *testing.T) {
|
|||||||
func TestModule_Memory(t *testing.T) {
|
func TestModule_Memory(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
builder func(Runtime) ModuleBuilder
|
wasm []byte
|
||||||
expected bool
|
expected bool
|
||||||
expectedLen uint32
|
expectedLen uint32
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no memory",
|
name: "no memory",
|
||||||
builder: func(r Runtime) ModuleBuilder {
|
wasm: binaryformat.EncodeModule(&wasm.Module{}),
|
||||||
return r.NewModuleBuilder(t.Name())
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "memory exported, one page",
|
name: "memory exported, one page",
|
||||||
builder: func(r Runtime) ModuleBuilder {
|
wasm: binaryformat.EncodeModule(&wasm.Module{
|
||||||
return r.NewModuleBuilder(t.Name()).ExportMemory("memory", 1)
|
MemorySection: &wasm.Memory{Min: 1},
|
||||||
},
|
ExportSection: []*wasm.Export{{Name: "memory", Type: api.ExternTypeMemory}},
|
||||||
|
}),
|
||||||
expected: true,
|
expected: true,
|
||||||
expectedLen: 65536,
|
expectedLen: 65536,
|
||||||
},
|
},
|
||||||
@@ -186,7 +185,7 @@ func TestModule_Memory(t *testing.T) {
|
|||||||
defer r.Close(testCtx)
|
defer r.Close(testCtx)
|
||||||
|
|
||||||
// Instantiate the module and get the export of the above memory
|
// Instantiate the module and get the export of the above memory
|
||||||
module, err := tc.builder(r).Instantiate(testCtx, r)
|
module, err := r.InstantiateModuleFromBinary(testCtx, tc.wasm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
mem := module.ExportedMemory("memory")
|
mem := module.ExportedMemory("memory")
|
||||||
@@ -206,7 +205,6 @@ func TestModule_Global(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
module *wasm.Module // module as wat doesn't yet support globals
|
module *wasm.Module // module as wat doesn't yet support globals
|
||||||
builder func(Runtime) ModuleBuilder
|
|
||||||
expected, expectedMutable bool
|
expected, expectedMutable bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@@ -226,8 +224,16 @@ func TestModule_Global(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "global exported",
|
name: "global exported",
|
||||||
builder: func(r Runtime) ModuleBuilder {
|
module: &wasm.Module{
|
||||||
return r.NewModuleBuilder(t.Name()).ExportGlobalI64("global", globalVal)
|
GlobalSection: []*wasm.Global{
|
||||||
|
{
|
||||||
|
Type: &wasm.GlobalType{ValType: wasm.ValueTypeI64},
|
||||||
|
Init: &wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeInt64(globalVal)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExportSection: []*wasm.Export{
|
||||||
|
{Type: wasm.ExternTypeGlobal, Name: "global"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
@@ -256,13 +262,7 @@ func TestModule_Global(t *testing.T) {
|
|||||||
r := NewRuntime(testCtx).(*runtime)
|
r := NewRuntime(testCtx).(*runtime)
|
||||||
defer r.Close(testCtx)
|
defer r.Close(testCtx)
|
||||||
|
|
||||||
var m CompiledModule
|
code := &compiledModule{module: tc.module}
|
||||||
if tc.module != nil {
|
|
||||||
m = &compiledModule{module: tc.module}
|
|
||||||
} else {
|
|
||||||
m, _ = tc.builder(r).Compile(testCtx, NewCompileConfig())
|
|
||||||
}
|
|
||||||
code := m.(*compiledModule)
|
|
||||||
|
|
||||||
err := r.store.Engine.CompileModule(testCtx, code.module)
|
err := r.store.Engine.CompileModule(testCtx, code.module)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -299,7 +299,7 @@ func TestRuntime_InstantiateModule_UsesContext(t *testing.T) {
|
|||||||
require.Equal(t, testCtx, ctx)
|
require.Equal(t, testCtx, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := r.NewModuleBuilder("env").
|
_, err := r.NewHostModuleBuilder("env").
|
||||||
ExportFunction("start", start).
|
ExportFunction("start", start).
|
||||||
Instantiate(testCtx, r)
|
Instantiate(testCtx, r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -376,7 +376,7 @@ func TestRuntime_InstantiateModuleFromBinary_ErrorOnStart(t *testing.T) {
|
|||||||
panic(errors.New("ice cream"))
|
panic(errors.New("ice cream"))
|
||||||
}
|
}
|
||||||
|
|
||||||
host, err := r.NewModuleBuilder("").
|
host, err := r.NewHostModuleBuilder("").
|
||||||
ExportFunction("start", start).
|
ExportFunction("start", start).
|
||||||
Instantiate(testCtx, r)
|
Instantiate(testCtx, r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -428,7 +428,7 @@ func TestRuntime_InstantiateModule_ExitError(t *testing.T) {
|
|||||||
require.NoError(t, m.CloseWithExitCode(ctx, 2))
|
require.NoError(t, m.CloseWithExitCode(ctx, 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := r.NewModuleBuilder("env").ExportFunction("exit", start).Instantiate(testCtx, r)
|
_, err := r.NewHostModuleBuilder("env").ExportFunction("exit", start).Instantiate(testCtx, r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
one := uint32(1)
|
one := uint32(1)
|
||||||
|
|||||||
Reference in New Issue
Block a user