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:
279
builder_test.go
279
builder_test.go
@@ -1,18 +1,15 @@
|
||||
package wazero
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/leb128"
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
"github.com/tetratelabs/wazero/internal/u64"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// TestNewModuleBuilder_Compile only covers a few scenarios to avoid duplicating tests in internal/wasm/host_test.go
|
||||
func TestNewModuleBuilder_Compile(t *testing.T) {
|
||||
// TestNewHostModuleBuilder_Compile only covers a few scenarios to avoid duplicating tests in internal/wasm/host_test.go
|
||||
func TestNewHostModuleBuilder_Compile(t *testing.T) {
|
||||
i32, i64 := api.ValueTypeI32, api.ValueTypeI64
|
||||
|
||||
uint32_uint32 := func(uint32) uint32 {
|
||||
@@ -24,27 +21,27 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input func(Runtime) ModuleBuilder
|
||||
input func(Runtime) HostModuleBuilder
|
||||
expected *wasm.Module
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
input: func(r Runtime) ModuleBuilder {
|
||||
return r.NewModuleBuilder("")
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
return r.NewHostModuleBuilder("")
|
||||
},
|
||||
expected: &wasm.Module{},
|
||||
},
|
||||
{
|
||||
name: "only name",
|
||||
input: func(r Runtime) ModuleBuilder {
|
||||
return r.NewModuleBuilder("env")
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
return r.NewHostModuleBuilder("env")
|
||||
},
|
||||
expected: &wasm.Module{NameSection: &wasm.NameSection{ModuleName: "env"}},
|
||||
},
|
||||
{
|
||||
name: "ExportFunction",
|
||||
input: func(r Runtime) ModuleBuilder {
|
||||
return r.NewModuleBuilder("").ExportFunction("1", uint32_uint32)
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
return r.NewHostModuleBuilder("").ExportFunction("1", uint32_uint32)
|
||||
},
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
@@ -62,8 +59,8 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "ExportFunction with names",
|
||||
input: func(r Runtime) ModuleBuilder {
|
||||
return r.NewModuleBuilder("").ExportFunction("1", uint32_uint32, "get", "x")
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
return r.NewHostModuleBuilder("").ExportFunction("1", uint32_uint32, "get", "x")
|
||||
},
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
@@ -82,8 +79,8 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "ExportFunction overwrites existing",
|
||||
input: func(r Runtime) ModuleBuilder {
|
||||
return r.NewModuleBuilder("").ExportFunction("1", uint32_uint32).ExportFunction("1", uint64_uint32)
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
return r.NewHostModuleBuilder("").ExportFunction("1", uint32_uint32).ExportFunction("1", uint64_uint32)
|
||||
},
|
||||
expected: &wasm.Module{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
@@ -101,9 +98,9 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "ExportFunction twice",
|
||||
input: func(r Runtime) ModuleBuilder {
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
// 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{
|
||||
TypeSection: []*wasm.FunctionType{
|
||||
@@ -123,8 +120,8 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "ExportFunctions",
|
||||
input: func(r Runtime) ModuleBuilder {
|
||||
return r.NewModuleBuilder("").ExportFunctions(map[string]interface{}{
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
return r.NewHostModuleBuilder("").ExportFunctions(map[string]interface{}{
|
||||
"1": uint32_uint32,
|
||||
"2": uint64_uint32,
|
||||
})
|
||||
@@ -147,8 +144,8 @@ func TestNewModuleBuilder_Compile(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "ExportFunctions overwrites",
|
||||
input: func(r Runtime) ModuleBuilder {
|
||||
b := r.NewModuleBuilder("").ExportFunction("1", uint64_uint32)
|
||||
input: func(r Runtime) HostModuleBuilder {
|
||||
b := r.NewHostModuleBuilder("").ExportFunction("1", uint64_uint32)
|
||||
return b.ExportFunctions(map[string]interface{}{
|
||||
"1": uint32_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 {
|
||||
tc := tt
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
b := tc.input(NewRuntime(testCtx)).(*moduleBuilder)
|
||||
compiled, err := b.Compile(testCtx, NewCompileConfig())
|
||||
b := tc.input(NewRuntime(testCtx)).(*hostModuleBuilder)
|
||||
compiled, err := b.Compile(testCtx)
|
||||
require.NoError(t, err)
|
||||
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
|
||||
func TestNewModuleBuilder_Compile_Errors(t *testing.T) {
|
||||
// TestNewHostModuleBuilder_Compile_Errors only covers a few scenarios to avoid
|
||||
// duplicating tests in internal/wasm/host_test.go
|
||||
func TestNewHostModuleBuilder_Compile_Errors(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input func(Runtime) ModuleBuilder
|
||||
config CompileConfig
|
||||
input func(Runtime) HostModuleBuilder
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "memory min > limit", // only one test to avoid duplicating tests in module_test.go
|
||||
input: func(rt Runtime) ModuleBuilder {
|
||||
return rt.NewModuleBuilder("").ExportMemory("memory", math.MaxUint32)
|
||||
name: "error compiling", // should fail due to missing result.
|
||||
input: func(rt Runtime) HostModuleBuilder {
|
||||
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: "memory[memory] min 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)",
|
||||
},
|
||||
{
|
||||
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)",
|
||||
expectedErr: `invalid function[0] export["fn"]: not enough results
|
||||
have ()
|
||||
want (i32)`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -412,16 +221,16 @@ func TestNewModuleBuilder_Compile_Errors(t *testing.T) {
|
||||
tc := tt
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewModuleBuilder_Instantiate ensures Runtime.InstantiateModule is called on success.
|
||||
func TestNewModuleBuilder_Instantiate(t *testing.T) {
|
||||
// TestNewHostModuleBuilder_Instantiate ensures Runtime.InstantiateModule is called on success.
|
||||
func TestNewHostModuleBuilder_Instantiate(t *testing.T) {
|
||||
r := NewRuntime(testCtx)
|
||||
m, err := r.NewModuleBuilder("env").Instantiate(testCtx, r)
|
||||
m, err := r.NewHostModuleBuilder("env").Instantiate(testCtx, r)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 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())
|
||||
}
|
||||
|
||||
// TestNewModuleBuilder_Instantiate_Errors ensures errors propagate from Runtime.InstantiateModule
|
||||
func TestNewModuleBuilder_Instantiate_Errors(t *testing.T) {
|
||||
// TestNewHostModuleBuilder_Instantiate_Errors ensures errors propagate from Runtime.InstantiateModule
|
||||
func TestNewHostModuleBuilder_Instantiate_Errors(t *testing.T) {
|
||||
r := NewRuntime(testCtx)
|
||||
_, err := r.NewModuleBuilder("env").Instantiate(testCtx, r)
|
||||
_, err := r.NewHostModuleBuilder("env").Instantiate(testCtx, r)
|
||||
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")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user