Repository: tetratelabs/wazero
Pull request #812 (2022-09-28 05:44:59)

Renames ModuleBuilder to HostModuleBuilder and drops memory and globals
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.

See #440 and #441 which added global and memory exports for a project that hasn't been maintained since.


Patch:
diff --git a/RATIONALE.md b/RATIONALE.md
index 6549b73c..0aed0f00 100644
--- a/RATIONALE.md
+++ b/RATIONALE.md
@@ -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
 `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.
 
 WASI implements system interfaces with host functions. Concretely, to write to console, a WASI command `Module` imports
diff --git a/README.md b/README.md
index d8d366ca..a79e0874 100644
--- a/README.md
+++ b/README.md
@@ -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
 WebAssembly as the module "env" and the function name "log_i32".
 ```go
-_, err := r.NewModuleBuilder("env").
+_, err := r.NewHostModuleBuilder("env").
 	ExportFunction("log_i32", func(v uint32) {
 		fmt.Println("log_i32 >>", v)
 	}).
diff --git a/api/wasm.go b/api/wasm.go
index f4cac34a..92181015 100644
--- a/api/wasm.go
+++ b/api/wasm.go
@@ -95,7 +95,7 @@ const (
 	//	(func (import "env" "f") (param externref) (result externref))
 	//
 	// 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 },
 	//  })
 	//
@@ -229,7 +229,7 @@ type FunctionDefinition interface {
 	ExportNames() []string
 
 	// 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
 	// has special properties not defined in the WebAssembly Core
diff --git a/builder.go b/builder.go
index d95b1cb4..45df2493 100644
--- a/builder.go
+++ b/builder.go
@@ -2,15 +2,16 @@ package wazero
 
 import (
 	"context"
-	"fmt"
 
 	"github.com/tetratelabs/wazero/api"
-	"github.com/tetratelabs/wazero/internal/leb128"
-	"github.com/tetratelabs/wazero/internal/u64"
 	"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:
 //
@@ -21,30 +22,47 @@ import (
 //	hello := func() {
 //		fmt.Fprintln(stdout, "hello!")
 //	}
-//	env, _ := r.NewModuleBuilder("env").
+//	env, _ := r.NewHostModuleBuilder("env").
 //		ExportFunction("hello", hello).
 //		Instantiate(ctx, r)
 //
 // If the same module may be instantiated multiple times, it is more efficient
 // to separate steps. Ex.
 //
-//	compiled, _ := r.NewModuleBuilder("env").
+//	compiled, _ := r.NewHostModuleBuilder("env").
 //		ExportFunction("get_random_string", getRandomString).
-//		Compile(ctx, wazero.NewCompileConfig())
+//		Compile(ctx)
 //
 //	env1, _ := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("env.1"))
 //
 //	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
 //
-//   - ModuleBuilder is mutable: each method returns the same instance for
+//   - HostModuleBuilder is mutable: each method returns the same instance for
 //     chaining.
 //   - methods do not return errors, to allow chaining. Any validation errors
 //     are deferred until Compile.
 //   - Insertion order is not retained. Anything defined by this builder is
 //     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.
 
 	// 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",
 	//		"message", "fileName", "lineNumber", "columnNumber")
 	//
-	// Valid Signature
+	// # Valid Signature
 	//
 	// Noting a context exception described later, all parameters or result
 	// types must match WebAssembly 1.0 (20191205) value types. This means
@@ -108,90 +126,15 @@ type ModuleBuilder interface {
 	//	--snip--
 	//
 	// 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(nameToGoFunc map[string]interface{}) ModuleBuilder
-
-	// 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
+	ExportFunctions(nameToGoFunc map[string]interface{}) HostModuleBuilder
 
 	// Compile returns a CompiledModule that can instantiated in any namespace (Namespace).
 	//
 	// 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.
 	//
@@ -204,7 +147,7 @@ type ModuleBuilder interface {
 	//	hello := func() {
 	//		fmt.Fprintln(stdout, "hello!")
 	//	}
-	//	env, _ := r.NewModuleBuilder("env").
+	//	env, _ := r.NewHostModuleBuilder("env").
 	//		ExportFunction("hello", hello).
 	//		Instantiate(ctx, r)
 	//
@@ -216,30 +159,26 @@ type ModuleBuilder interface {
 	Instantiate(context.Context, Namespace) (api.Module, error)
 }
 
-// moduleBuilder implements ModuleBuilder
-type moduleBuilder struct {
+// hostModuleBuilder implements HostModuleBuilder
+type hostModuleBuilder struct {
 	r            *runtime
 	moduleName   string
 	nameToGoFunc map[string]interface{}
 	funcToNames  map[string][]string
-	nameToMemory map[string]*wasm.Memory
-	nameToGlobal map[string]*wasm.Global
 }
 
-// NewModuleBuilder implements Runtime.NewModuleBuilder
-func (r *runtime) NewModuleBuilder(moduleName string) ModuleBuilder {
-	return &moduleBuilder{
+// NewHostModuleBuilder implements Runtime.NewHostModuleBuilder
+func (r *runtime) NewHostModuleBuilder(moduleName string) HostModuleBuilder {
+	return &hostModuleBuilder{
 		r:            r,
 		moduleName:   moduleName,
 		nameToGoFunc: map[string]interface{}{},
 		funcToNames:  map[string][]string{},
-		nameToMemory: map[string]*wasm.Memory{},
-		nameToGlobal: map[string]*wasm.Global{},
 	}
 }
 
-// ExportFunction implements ModuleBuilder.ExportFunction
-func (b *moduleBuilder) ExportFunction(exportName string, goFunc interface{}, names ...string) ModuleBuilder {
+// ExportFunction implements HostModuleBuilder.ExportFunction
+func (b *hostModuleBuilder) ExportFunction(exportName string, goFunc interface{}, names ...string) HostModuleBuilder {
 	b.nameToGoFunc[exportName] = goFunc
 	if len(names) > 0 {
 		b.funcToNames[exportName] = names
@@ -247,81 +186,17 @@ func (b *moduleBuilder) ExportFunction(exportName string, goFunc interface{}, na
 	return b
 }
 
-// ExportFunctions implements ModuleBuilder.ExportFunctions
-func (b *moduleBuilder) ExportFunctions(nameToGoFunc map[string]interface{}) ModuleBuilder {
+// ExportFunctions implements HostModuleBuilder.ExportFunctions
+func (b *hostModuleBuilder) ExportFunctions(nameToGoFunc map[string]interface{}) HostModuleBuilder {
 	for k, v := range nameToGoFunc {
 		b.ExportFunction(k, v)
 	}
 	return b
 }
 
-// ExportMemory implements ModuleBuilder.ExportMemory
-func (b *moduleBuilder) ExportMemory(name string, minPages uint32) ModuleBuilder {
-	b.nameToMemory[name] = &wasm.Memory{Min: minPages}
-	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)
+// Compile implements HostModuleBuilder.Compile
+func (b *hostModuleBuilder) Compile(ctx context.Context) (CompiledModule, error) {
+	module, err := wasm.NewHostModule(b.moduleName, b.nameToGoFunc, b.funcToNames, b.r.enabledFeatures)
 	if err != nil {
 		return nil, err
 	} 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
 }
 
-// Instantiate implements ModuleBuilder.Instantiate
-func (b *moduleBuilder) Instantiate(ctx context.Context, ns Namespace) (api.Module, error) {
-	if compiled, err := b.Compile(ctx, NewCompileConfig()); err != nil {
+// Instantiate implements HostModuleBuilder.Instantiate
+func (b *hostModuleBuilder) Instantiate(ctx context.Context, ns Namespace) (api.Module, error) {
+	if compiled, err := b.Compile(ctx); err != nil {
 		return nil, err
 	} else {
 		compiled.(*compiledModule).closeWithModule = true
diff --git a/config.go b/config.go
index 8cd47f6a..9977236d 100644
--- a/config.go
+++ b/config.go
@@ -165,7 +165,7 @@ func (c *compiledModule) ExportedFunctions() map[string]api.FunctionDefinition {
 	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).
 //
 // For example, WithMemorySizer allows you to override memory size that doesn't match your requirements.
diff --git a/examples/allocation/rust/greet.go b/examples/allocation/rust/greet.go
index 0becb23d..1001a7ef 100644
--- a/examples/allocation/rust/greet.go
+++ b/examples/allocation/rust/greet.go
@@ -30,7 +30,7 @@ func main() {
 
 	// Instantiate a Go-defined module named "env" that exports a function to
 	// log to the console.
-	_, err := r.NewModuleBuilder("env").
+	_, err := r.NewHostModuleBuilder("env").
 		ExportFunction("log", logString).
 		Instantiate(ctx, r)
 	if err != nil {
diff --git a/examples/allocation/tinygo/greet.go b/examples/allocation/tinygo/greet.go
index cc7215e9..d0c222e5 100644
--- a/examples/allocation/tinygo/greet.go
+++ b/examples/allocation/tinygo/greet.go
@@ -31,7 +31,7 @@ func main() {
 
 	// Instantiate a Go-defined module named "env" that exports a function to
 	// log to the console.
-	_, err := r.NewModuleBuilder("env").
+	_, err := r.NewHostModuleBuilder("env").
 		ExportFunction("log", logString).
 		Instantiate(ctx, r)
 	if err != nil {
diff --git a/examples/allocation/zig/greet.go b/examples/allocation/zig/greet.go
index 179b9622..01fea07a 100644
--- a/examples/allocation/zig/greet.go
+++ b/examples/allocation/zig/greet.go
@@ -36,7 +36,7 @@ func run() error {
 
 	// Instantiate a Go-defined module named "env" that exports a function to
 	// log to the console.
-	_, err := r.NewModuleBuilder("env").
+	_, err := r.NewHostModuleBuilder("env").
 		ExportFunction("log", logString).
 		Instantiate(ctx, r)
 	if err != nil {
diff --git a/examples/import-go/README.md b/examples/import-go/README.md
index b1ed9d56..3503548f 100644
--- a/examples/import-go/README.md
+++ b/examples/import-go/README.md
@@ -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.
 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
-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)
 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
diff --git a/examples/import-go/age-calculator.go b/examples/import-go/age-calculator.go
index 08ae25a2..f93a672c 100644
--- a/examples/import-go/age-calculator.go
+++ b/examples/import-go/age-calculator.go
@@ -38,7 +38,7 @@ func main() {
 	// constrained to a subset of numeric types.
 	// Note: "env" is a module name conventionally used for arbitrary
 	// host-defined functions, but any name would do.
-	_, err := r.NewModuleBuilder("env").
+	_, err := r.NewHostModuleBuilder("env").
 		ExportFunction("log_i32", func(v uint32) {
 			fmt.Println("log_i32 >>", v)
 		}).
diff --git a/examples/multiple-results/multiple-results.go b/examples/multiple-results/multiple-results.go
index 2c63f503..253fbeef 100644
--- a/examples/multiple-results/multiple-results.go
+++ b/examples/multiple-results/multiple-results.go
@@ -109,7 +109,7 @@ var multiValueFromImportedHostWasm []byte
 // 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) {
 	// 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
 		ExportFunction("get_age", func() (age uint64, errno uint32) {
 			age = 37
diff --git a/examples/namespace/counter.go b/examples/namespace/counter.go
index c5feb749..9b5a02dc 100644
--- a/examples/namespace/counter.go
+++ b/examples/namespace/counter.go
@@ -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.
 	c := &counter{}
-	_, err := r.NewModuleBuilder("env").
+	_, err := r.NewHostModuleBuilder("env").
 		ExportFunction("next_i32", c.getAndIncrement).
 		Instantiate(ctx, ns)
 	if err != nil {
diff --git a/imports/assemblyscript/assemblyscript.go b/imports/assemblyscript/assemblyscript.go
index c8b69754..668114b5 100644
--- a/imports/assemblyscript/assemblyscript.go
+++ b/imports/assemblyscript/assemblyscript.go
@@ -54,7 +54,7 @@ const (
 //   - To add more functions to the "env" module, use FunctionExporter.
 //   - To instantiate into another wazero.Namespace, use FunctionExporter.
 func Instantiate(ctx context.Context, r wazero.Runtime) (api.Closer, error) {
-	builder := r.NewModuleBuilder("env")
+	builder := r.NewHostModuleBuilder("env")
 	NewFunctionExporter().ExportFunctions(builder)
 	return builder.Instantiate(ctx, r)
 }
@@ -77,9 +77,9 @@ type FunctionExporter interface {
 	// appropriate to use WithTraceToStdout instead.
 	WithTraceToStderr() FunctionExporter
 
-	// ExportFunctions builds functions to export with a wazero.ModuleBuilder
+	// ExportFunctions builds functions to export with a wazero.HostModuleBuilder
 	// named "env".
-	ExportFunctions(builder wazero.ModuleBuilder)
+	ExportFunctions(builder wazero.HostModuleBuilder)
 }
 
 // NewFunctionExporter returns a FunctionExporter object with trace disabled.
@@ -107,7 +107,7 @@ func (e *functionExporter) WithTraceToStderr() FunctionExporter {
 }
 
 // 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(functionTrace, e.traceFn)
 	builder.ExportFunction(functionSeed, seed)
diff --git a/imports/emscripten/emscripten.go b/imports/emscripten/emscripten.go
index 19f08e25..bd7b0676 100644
--- a/imports/emscripten/emscripten.go
+++ b/imports/emscripten/emscripten.go
@@ -29,7 +29,7 @@ import (
 //   - To add more functions to the "env" module, use FunctionExporter.
 //   - To instantiate into another wazero.Namespace, use FunctionExporter.
 func Instantiate(ctx context.Context, r wazero.Runtime) (api.Closer, error) {
-	builder := r.NewModuleBuilder("env")
+	builder := r.NewHostModuleBuilder("env")
 	NewFunctionExporter().ExportFunctions(builder)
 	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
 // Emscripten.
 type FunctionExporter interface {
-	// ExportFunctions builds functions to export with a wazero.ModuleBuilder
+	// ExportFunctions builds functions to export with a wazero.HostModuleBuilder
 	// named "env".
-	ExportFunctions(builder wazero.ModuleBuilder)
+	ExportFunctions(builder wazero.HostModuleBuilder)
 }
 
 // NewFunctionExporter returns a FunctionExporter object with trace disabled.
@@ -50,7 +50,7 @@ func NewFunctionExporter() FunctionExporter {
 type functionExporter struct{}
 
 // ExportFunctions implements FunctionExporter.ExportFunctions
-func (e *functionExporter) ExportFunctions(builder wazero.ModuleBuilder) {
+func (e *functionExporter) ExportFunctions(builder wazero.HostModuleBuilder) {
 	builder.ExportFunction(notifyMemoryGrowth.Name, notifyMemoryGrowth)
 }
 
diff --git a/imports/go/gojs.go b/imports/go/gojs.go
index 6b0e74f4..1b99b969 100644
--- a/imports/go/gojs.go
+++ b/imports/go/gojs.go
@@ -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.
 func Run(ctx context.Context, r wazero.Runtime, compiled wazero.CompiledModule, config wazero.ModuleConfig) error {
 	// 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 {
 		return err
 	}
@@ -86,9 +86,9 @@ func Run(ctx context.Context, r wazero.Runtime, compiled wazero.CompiledModule,
 	return err
 }
 
-// moduleBuilder returns a new wazero.ModuleBuilder
-func moduleBuilder(r wazero.Runtime) wazero.ModuleBuilder {
-	return r.NewModuleBuilder("go").
+// hostModuleBuilder returns a new wazero.HostModuleBuilder
+func hostModuleBuilder(r wazero.Runtime) wazero.HostModuleBuilder {
+	return r.NewHostModuleBuilder("go").
 		ExportFunction(GetRandomData.Name(), GetRandomData).
 		ExportFunction(Nanotime1.Name(), Nanotime1).
 		ExportFunction(WasmExit.Name(), WasmExit).
diff --git a/imports/wasi_snapshot_preview1/wasi.go b/imports/wasi_snapshot_preview1/wasi.go
index 0e46c1e8..fc1d6fd3 100644
--- a/imports/wasi_snapshot_preview1/wasi.go
+++ b/imports/wasi_snapshot_preview1/wasi.go
@@ -47,12 +47,12 @@ type Builder interface {
 	// Compile compiles the ModuleName module that can instantiated in any
 	// namespace (wazero.Namespace).
 	//
-	// Note: This has the same effect as the same function on wazero.ModuleBuilder.
-	Compile(context.Context, wazero.CompileConfig) (wazero.CompiledModule, error)
+	// Note: This has the same effect as the same function on wazero.HostModuleBuilder.
+	Compile(context.Context) (wazero.CompiledModule, error)
 
 	// 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)
 }
 
@@ -63,21 +63,21 @@ func NewBuilder(r wazero.Runtime) Builder {
 
 type builder struct{ r wazero.Runtime }
 
-// moduleBuilder returns a new wazero.ModuleBuilder for ModuleName
-func (b *builder) moduleBuilder() wazero.ModuleBuilder {
-	ret := b.r.NewModuleBuilder(ModuleName)
+// hostModuleBuilder returns a new wazero.HostModuleBuilder for ModuleName
+func (b *builder) hostModuleBuilder() wazero.HostModuleBuilder {
+	ret := b.r.NewHostModuleBuilder(ModuleName)
 	exportFunctions(ret)
 	return ret
 }
 
 // Compile implements Builder.Compile
-func (b *builder) Compile(ctx context.Context, config wazero.CompileConfig) (wazero.CompiledModule, error) {
-	return b.moduleBuilder().Compile(ctx, config)
+func (b *builder) Compile(ctx context.Context) (wazero.CompiledModule, error) {
+	return b.hostModuleBuilder().Compile(ctx)
 }
 
 // Instantiate implements Builder.Instantiate
 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
@@ -109,7 +109,7 @@ func (b *builder) Instantiate(ctx context.Context, ns wazero.Namespace) (api.Clo
 
 // exportFunctions adds all go functions that implement wasi.
 // 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
 	// map can't guarantee that.
 	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#functions
diff --git a/internal/wasm/call_context.go b/internal/wasm/call_context.go
index 61d21e9d..a408882d 100644
--- a/internal/wasm/call_context.go
+++ b/internal/wasm/call_context.go
@@ -114,7 +114,7 @@ func (m *CallContext) close(ctx context.Context, exitCode uint32) (c bool, err e
 		return false, nil
 	}
 	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)
 	}
 	return
diff --git a/internal/wasm/host.go b/internal/wasm/host.go
index 00307bfc..1995f781 100644
--- a/internal/wasm/host.go
+++ b/internal/wasm/host.go
@@ -4,7 +4,6 @@ import (
 	"errors"
 	"fmt"
 	"sort"
-	"strings"
 
 	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/wasmdebug"
@@ -91,8 +90,6 @@ func NewHostModule(
 	moduleName string,
 	nameToGoFunc map[string]interface{},
 	funcToNames map[string][]string,
-	nameToMemory map[string]*Memory,
-	nameToGlobal map[string]*Global,
 	enabledFeatures api.CoreFeatures,
 ) (m *Module, err error) {
 	if moduleName != "" {
@@ -101,52 +98,15 @@ func NewHostModule(
 		m = &Module{}
 	}
 
-	funcCount := uint32(len(nameToGoFunc))
-	memoryCount := uint32(len(nameToMemory))
-	globalCount := uint32(len(nameToGlobal))
-	exportCount := funcCount + memoryCount + globalCount
-	if exportCount > 0 {
+	if exportCount := uint32(len(nameToGoFunc)); exportCount > 0 {
 		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 {
 			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.
-	m.AssignModuleID([]byte(fmt.Sprintf("%s:%v:%v:%v:%v",
-		moduleName, nameToGoFunc, nameToMemory, nameToGlobal, enabledFeatures)))
+	m.AssignModuleID([]byte(fmt.Sprintf("%s:%v:%v", moduleName, nameToGoFunc, enabledFeatures)))
 	m.BuildFunctionDefinitions()
 	return
 }
@@ -264,50 +224,6 @@ func addFuncs(
 	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) {
 	if len(results) > 1 {
 		// Guard >1.0 feature multi-value
diff --git a/internal/wasm/module.go b/internal/wasm/module.go
index 54b233a5..36005c7e 100644
--- a/internal/wasm/module.go
+++ b/internal/wasm/module.go
@@ -805,7 +805,7 @@ type Export struct {
 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-code
 type Code struct {
 	// 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
 	// different from its defining module.
diff --git a/internal/wasmdebug/debug.go b/internal/wasmdebug/debug.go
index 268ded39..62f3f746 100644
--- a/internal/wasmdebug/debug.go
+++ b/internal/wasmdebug/debug.go
@@ -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
-	// 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 {
 		// 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)
 	}
 
-	// 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"))
 		return fmt.Errorf("%w (recovered by wazero)\nwasm stack trace:\n\t%s", runtimeErr, stack)
 	} else { // Ex. panic("whoops")
diff --git a/runtime.go b/runtime.go
index 3de949ea..2b7527aa 100644
--- a/runtime.go
+++ b/runtime.go
@@ -22,7 +22,7 @@ import (
 //
 //	module, _ := r.InstantiateModuleFromBinary(ctx, wasm)
 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:
 	//
@@ -30,8 +30,8 @@ type Runtime interface {
 	//	hello := func() {
 	//		fmt.Fprintln(stdout, "hello!")
 	//	}
-	//	_, err := r.NewModuleBuilder("env").ExportFunction("hello", hello).Instantiate(ctx, r)
-	NewModuleBuilder(moduleName string) ModuleBuilder
+	//	_, err := r.NewHostModuleBuilder("env").ExportFunction("hello", hello).Instantiate(ctx, r)
+	NewHostModuleBuilder(moduleName string) HostModuleBuilder
 
 	// CompileModule decodes the WebAssembly binary (%.wasm) or errs if invalid.
 	// Any pre-compilation done after decoding wasm is dependent on RuntimeConfig or CompileConfig.


Test patch:
diff --git a/builder_test.go b/builder_test.go
index 22bae5ac..e90b66d5 100644
--- a/builder_test.go
+++ b/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)
-			},
-			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)",
+			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}},
+					})
+			},
+			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")
 }
 
diff --git a/imports/assemblyscript/assemblyscript_example_test.go b/imports/assemblyscript/assemblyscript_example_test.go
index 0a7c6954..7a107ae4 100644
--- a/imports/assemblyscript/assemblyscript_example_test.go
+++ b/imports/assemblyscript/assemblyscript_example_test.go
@@ -34,7 +34,7 @@ func Example_functionExporter() {
 	defer r.Close(ctx) // This closes everything this Runtime created.
 
 	// First construct your own module builder for "env"
-	envBuilder := r.NewModuleBuilder("env").
+	envBuilder := r.NewHostModuleBuilder("env").
 		ExportFunction("get_int", func() uint32 { return 1 })
 
 	// Now, add AssemblyScript special function imports into it.
diff --git a/imports/assemblyscript/assemblyscript_test.go b/imports/assemblyscript/assemblyscript_test.go
index 8a105bfa..7a578c0b 100644
--- a/imports/assemblyscript/assemblyscript_test.go
+++ b/imports/assemblyscript/assemblyscript_test.go
@@ -424,10 +424,10 @@ func requireProxyModule(t *testing.T, fns FunctionExporter, config wazero.Module
 
 	r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfigInterpreter())
 
-	builder := r.NewModuleBuilder("env")
+	builder := r.NewHostModuleBuilder("env")
 	fns.ExportFunctions(builder)
 
-	envCompiled, err := builder.Compile(ctx, wazero.NewCompileConfig())
+	envCompiled, err := builder.Compile(ctx)
 	require.NoError(t, err)
 
 	_, err = r.InstantiateModule(ctx, envCompiled, config)
diff --git a/imports/emscripten/emscripten_example_test.go b/imports/emscripten/emscripten_example_test.go
index 0c52f2fa..50e35619 100644
--- a/imports/emscripten/emscripten_example_test.go
+++ b/imports/emscripten/emscripten_example_test.go
@@ -45,7 +45,7 @@ func Example_functionExporter() {
 
 	// Next, construct your own module builder for "env" with any functions
 	// you need.
-	envBuilder := r.NewModuleBuilder("env").
+	envBuilder := r.NewHostModuleBuilder("env").
 		ExportFunction("get_int", func() uint32 { return 1 })
 
 	// Now, add Emscripten special function imports into it.
diff --git a/imports/wasi_snapshot_preview1/wasi_bench_test.go b/imports/wasi_snapshot_preview1/wasi_bench_test.go
index 42136503..9ec011d2 100644
--- a/imports/wasi_snapshot_preview1/wasi_bench_test.go
+++ b/imports/wasi_snapshot_preview1/wasi_bench_test.go
@@ -4,7 +4,10 @@ import (
 	"testing"
 
 	"github.com/tetratelabs/wazero"
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/testing/require"
+	"github.com/tetratelabs/wazero/internal/wasm"
+	binaryformat "github.com/tetratelabs/wazero/internal/wasm/binary"
 )
 
 var testMem = []byte{
@@ -37,11 +40,13 @@ func Test_Benchmark_EnvironGet(t *testing.T) {
 }
 
 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()).
-		ExportMemoryWithMax("memory", 1, 1).
-		Compile(testCtx, wazero.NewCompileConfig())
+	compiled, err := r.CompileModule(testCtx, binaryformat.EncodeModule(&wasm.Module{
+		MemorySection: &wasm.Memory{Min: 1, Max: 1},
+		ExportSection: []*wasm.Export{{Name: "memory", Type: api.ExternTypeMemory}},
+	}), wazero.NewCompileConfig())
 	if err != nil {
 		b.Fatal(err)
 	}
diff --git a/imports/wasi_snapshot_preview1/wasi_test.go b/imports/wasi_snapshot_preview1/wasi_test.go
index 550396c2..57b58be9 100644
--- a/imports/wasi_snapshot_preview1/wasi_test.go
+++ b/imports/wasi_snapshot_preview1/wasi_test.go
@@ -34,8 +34,7 @@ func requireProxyModule(t *testing.T, config wazero.ModuleConfig) (api.Module, a
 
 	r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfigInterpreter())
 
-	wasiModuleCompiled, err := (&builder{r}).moduleBuilder().
-		Compile(ctx, wazero.NewCompileConfig())
+	wasiModuleCompiled, err := (&builder{r}).hostModuleBuilder().Compile(ctx)
 	require.NoError(t, err)
 
 	_, err = r.InstantiateModule(ctx, wasiModuleCompiled, config)
@@ -65,8 +64,7 @@ func requireErrnoNosys(t *testing.T, funcName string, params ...uint64) string {
 	defer r.Close(ctx)
 
 	// Instantiate the wasi module.
-	wasiModuleCompiled, err := (&builder{r}).moduleBuilder().
-		Compile(ctx, wazero.NewCompileConfig())
+	wasiModuleCompiled, err := (&builder{r}).hostModuleBuilder().Compile(ctx)
 	require.NoError(t, err)
 
 	_, err = r.InstantiateModule(ctx, wasiModuleCompiled, wazero.NewModuleConfig())
diff --git a/internal/engine/compiler/engine_test.go b/internal/engine/compiler/engine_test.go
index f02c73b9..9b016435 100644
--- a/internal/engine/compiler/engine_test.go
+++ b/internal/engine/compiler/engine_test.go
@@ -210,7 +210,7 @@ func TestCompiler_SliceAllocatedOnHeap(t *testing.T) {
 		// Trigger relocation of goroutine stack because at this point we have the majority of
 		// goroutine stack unused after recursive call.
 		runtime.GC()
-	}}, nil, map[string]*wasm.Memory{}, map[string]*wasm.Global{}, enabledFeatures)
+	}}, nil, enabledFeatures)
 	require.NoError(t, err)
 
 	err = s.Engine.CompileModule(testCtx, hm)
diff --git a/internal/gojs/spfunc/spfunc_test.go b/internal/gojs/spfunc/spfunc_test.go
index e2b76ee3..b2c27619 100644
--- a/internal/gojs/spfunc/spfunc_test.go
+++ b/internal/gojs/spfunc/spfunc_test.go
@@ -197,7 +197,7 @@ func TestMustCallFromSP(t *testing.T) {
 	defer r.Close(testCtx)
 
 	funcName := "i64i32i32i32i32_i64i32_withSP"
-	im, err := r.NewModuleBuilder("go").
+	im, err := r.NewHostModuleBuilder("go").
 		ExportFunction(funcName, MustCallFromSP(true, wasm.NewGoFunc(
 			funcName, funcName,
 			[]string{"v", "mAddr", "mLen", "argsArray", "argsLen"},
diff --git a/internal/integration_test/bench/bench_test.go b/internal/integration_test/bench/bench_test.go
index f08cc8e4..bd7bdebc 100644
--- a/internal/integration_test/bench/bench_test.go
+++ b/internal/integration_test/bench/bench_test.go
@@ -215,7 +215,7 @@ func createRuntime(b *testing.B, config wazero.RuntimeConfig) wazero.Runtime {
 
 	r := wazero.NewRuntimeWithConfig(testCtx, config)
 
-	_, err := r.NewModuleBuilder("env").
+	_, err := r.NewHostModuleBuilder("env").
 		ExportFunction("get_random_string", getRandomString).
 		Instantiate(testCtx, r)
 	if err != nil {
diff --git a/internal/integration_test/engine/adhoc_test.go b/internal/integration_test/engine/adhoc_test.go
index f94f3d6a..a9b6f403 100644
--- a/internal/integration_test/engine/adhoc_test.go
+++ b/internal/integration_test/engine/adhoc_test.go
@@ -99,7 +99,7 @@ func testReftypeImports(t *testing.T, r wazero.Runtime) {
 	}
 
 	hostObj := &dog{name: "hello"}
-	host, err := r.NewModuleBuilder("host").
+	host, err := r.NewHostModuleBuilder("host").
 		ExportFunctions(map[string]interface{}{
 			"externref": func(externrefFromRefNull uintptr) uintptr {
 				require.Zero(t, externrefFromRefNull)
@@ -176,7 +176,7 @@ func testUnreachable(t *testing.T, r wazero.Runtime) {
 		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)
 
 	module, err := r.InstantiateModuleFromBinary(testCtx, unreachableWasm)
@@ -199,7 +199,7 @@ func testRecursiveEntry(t *testing.T, r wazero.Runtime) {
 		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)
 
 	module, err := r.InstantiateModuleFromBinary(testCtx, recursiveWasm)
@@ -222,7 +222,7 @@ func testHostFuncMemory(t *testing.T, r wazero.Runtime) {
 		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)
 	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)
 	defer imported.Close(testCtx)
 
@@ -299,7 +299,7 @@ func testHostFunctionContextParameter(t *testing.T, r wazero.Runtime) {
 
 	for test := range fns {
 		t.Run(test, func(t *testing.T) {
-			imported, err := r.NewModuleBuilder(importedName).
+			imported, err := r.NewHostModuleBuilder(importedName).
 				ExportFunction("return_input", fns[test]).
 				Instantiate(testCtx, r)
 			require.NoError(t, err)
@@ -369,7 +369,7 @@ func testHostFunctionNumericParameter(t *testing.T, r wazero.Runtime) {
 		},
 	} {
 		t.Run(test.name, func(t *testing.T) {
-			imported, err := r.NewModuleBuilder(importedName).
+			imported, err := r.NewHostModuleBuilder(importedName).
 				ExportFunction("return_input", fns[test.name]).
 				Instantiate(testCtx, r)
 			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.
-			importedCode, err = r.NewModuleBuilder(t.Name()+"-imported").
-				ExportFunction("return_input", closeAndReturn).Compile(testCtx, compileConfig)
+			importedCode, err = r.NewHostModuleBuilder(t.Name()+"-imported").
+				ExportFunction("return_input", closeAndReturn).Compile(testCtx)
 			require.NoError(t, err)
 
 			imported, err = r.InstantiateModule(testCtx, importedCode, moduleConfig)
diff --git a/internal/integration_test/engine/hammer_test.go b/internal/integration_test/engine/hammer_test.go
index 91b2f830..9b181bc8 100644
--- a/internal/integration_test/engine/hammer_test.go
+++ b/internal/integration_test/engine/hammer_test.go
@@ -49,7 +49,7 @@ func closeImportedModuleWhileInUse(t *testing.T, r wazero.Runtime) {
 		require.NoError(t, imported.Close(testCtx))
 
 		// 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
 		}).Instantiate(testCtx, r)
 		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.
-	imported, err := r.NewModuleBuilder(t.Name()+"-imported").
+	imported, err := r.NewHostModuleBuilder(t.Name()+"-imported").
 		ExportFunction("return_input", blockAndReturn).Instantiate(testCtx, r)
 	require.NoError(t, err)
 	defer imported.Close(testCtx)
diff --git a/internal/integration_test/vs/runtime.go b/internal/integration_test/vs/runtime.go
index fa00a0b9..31ac2c37 100644
--- a/internal/integration_test/vs/runtime.go
+++ b/internal/integration_test/vs/runtime.go
@@ -93,18 +93,18 @@ func (r *wazeroRuntime) Compile(ctx context.Context, cfg *RuntimeConfig) (err er
 	r.runtime = wazero.NewRuntimeWithConfig(ctx, r.config)
 	if cfg.LogFn != nil {
 		r.logFn = cfg.LogFn
-		if r.env, err = r.runtime.NewModuleBuilder("env").
-			ExportFunction("log", r.log).Compile(ctx, wazero.NewCompileConfig()); err != nil {
+		if r.env, err = r.runtime.NewHostModuleBuilder("env").
+			ExportFunction("log", r.log).Compile(ctx); err != nil {
 			return err
 		}
 	} else if cfg.EnvFReturnValue != 0 {
-		if r.env, err = r.runtime.NewModuleBuilder("env").
+		if r.env, err = r.runtime.NewHostModuleBuilder("env").
 			ExportFunction("f",
 				// Note: accepting (context.Context, api.Module) is the slowest type of host function with wazero.
 				func(context.Context, api.Module, uint64) uint64 {
 					return cfg.EnvFReturnValue
 				},
-			).Compile(ctx, wazero.NewCompileConfig()); err != nil {
+			).Compile(ctx); err != nil {
 			return err
 		}
 	}
diff --git a/internal/testing/require/require.go b/internal/testing/require/require.go
index 06ecce22..d36fb420 100644
--- a/internal/testing/require/require.go
+++ b/internal/testing/require/require.go
@@ -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 untested because it is a lot of work to do that. The rationale to punt is this is a test-only internal
diff --git a/internal/wasm/host_test.go b/internal/wasm/host_test.go
index 3a406ce7..0a892913 100644
--- a/internal/wasm/host_test.go
+++ b/internal/wasm/host_test.go
@@ -4,7 +4,6 @@ import (
 	"testing"
 
 	"github.com/tetratelabs/wazero/api"
-	"github.com/tetratelabs/wazero/internal/leb128"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 )
 
@@ -12,10 +11,6 @@ import (
 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 {
 	return 0
 }
@@ -37,8 +32,6 @@ func TestNewHostModule(t *testing.T) {
 	tests := []struct {
 		name, moduleName string
 		nameToGoFunc     map[string]interface{}
-		nameToMemory     map[string]*Memory
-		nameToGlobal     map[string]*Global
 		expected         *Module
 	}{
 		{
@@ -91,91 +84,13 @@ func TestNewHostModule(t *testing.T) {
 				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 {
 		tc := tt
 
 		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)
 			require.NoError(t, e)
 			requireHostModuleEquals(t, tc.expected, m)
@@ -216,8 +131,6 @@ func TestNewHostModule_Errors(t *testing.T) {
 	tests := []struct {
 		name, moduleName string
 		nameToGoFunc     map[string]interface{}
-		nameToMemory     map[string]*Memory
-		nameToGlobal     map[string]*Global
 		expectedErr      string
 	}{
 		{
@@ -228,38 +141,15 @@ func TestNewHostModule_Errors(t *testing.T) {
 		{
 			name:         "function has multiple results",
 			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",
 		},
-		{
-			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 {
 		tc := tt
 
 		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)
 		})
 	}
diff --git a/internal/wasm/store_test.go b/internal/wasm/store_test.go
index 91575105..35f37c00 100644
--- a/internal/wasm/store_test.go
+++ b/internal/wasm/store_test.go
@@ -91,7 +91,7 @@ func TestModuleInstance_Memory(t *testing.T) {
 
 func TestStore_Instantiate(t *testing.T) {
 	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)
 
 	sysCtx := sys.DefaultContext(nil)
@@ -169,7 +169,7 @@ func TestStore_CloseWithExitCode(t *testing.T) {
 func TestStore_hammer(t *testing.T) {
 	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)
 
 	s, ns := newStore()
@@ -223,7 +223,7 @@ func TestStore_Instantiate_Errors(t *testing.T) {
 	const importedModuleName = "imported"
 	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)
 
 	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) {
-	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)
 
 	s, ns := newStore()
diff --git a/namespace_test.go b/namespace_test.go
index 12012c67..d37ede41 100644
--- a/namespace_test.go
+++ b/namespace_test.go
@@ -13,7 +13,7 @@ func TestRuntime_Namespace(t *testing.T) {
 	defer r.Close(testCtx)
 
 	// 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)
 
 	// Instantiate "env" into the runtime default namespace (base case)
diff --git a/runtime_test.go b/runtime_test.go
index 3b0c9e18..426df376 100644
--- a/runtime_test.go
+++ b/runtime_test.go
@@ -158,21 +158,20 @@ func TestRuntime_CompileModule_Errors(t *testing.T) {
 func TestModule_Memory(t *testing.T) {
 	tests := []struct {
 		name        string
-		builder     func(Runtime) ModuleBuilder
+		wasm        []byte
 		expected    bool
 		expectedLen uint32
 	}{
 		{
 			name: "no memory",
-			builder: func(r Runtime) ModuleBuilder {
-				return r.NewModuleBuilder(t.Name())
-			},
+			wasm: binaryformat.EncodeModule(&wasm.Module{}),
 		},
 		{
 			name: "memory exported, one page",
-			builder: func(r Runtime) ModuleBuilder {
-				return r.NewModuleBuilder(t.Name()).ExportMemory("memory", 1)
-			},
+			wasm: binaryformat.EncodeModule(&wasm.Module{
+				MemorySection: &wasm.Memory{Min: 1},
+				ExportSection: []*wasm.Export{{Name: "memory", Type: api.ExternTypeMemory}},
+			}),
 			expected:    true,
 			expectedLen: 65536,
 		},
@@ -186,7 +185,7 @@ func TestModule_Memory(t *testing.T) {
 			defer r.Close(testCtx)
 
 			// 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)
 
 			mem := module.ExportedMemory("memory")
@@ -206,7 +205,6 @@ func TestModule_Global(t *testing.T) {
 	tests := []struct {
 		name                      string
 		module                    *wasm.Module // module as wat doesn't yet support globals
-		builder                   func(Runtime) ModuleBuilder
 		expected, expectedMutable bool
 	}{
 		{
@@ -226,8 +224,16 @@ func TestModule_Global(t *testing.T) {
 		},
 		{
 			name: "global exported",
-			builder: func(r Runtime) ModuleBuilder {
-				return r.NewModuleBuilder(t.Name()).ExportGlobalI64("global", globalVal)
+			module: &wasm.Module{
+				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,
 		},
@@ -256,13 +262,7 @@ func TestModule_Global(t *testing.T) {
 			r := NewRuntime(testCtx).(*runtime)
 			defer r.Close(testCtx)
 
-			var m CompiledModule
-			if tc.module != nil {
-				m = &compiledModule{module: tc.module}
-			} else {
-				m, _ = tc.builder(r).Compile(testCtx, NewCompileConfig())
-			}
-			code := m.(*compiledModule)
+			code := &compiledModule{module: tc.module}
 
 			err := r.store.Engine.CompileModule(testCtx, code.module)
 			require.NoError(t, err)
@@ -299,7 +299,7 @@ func TestRuntime_InstantiateModule_UsesContext(t *testing.T) {
 		require.Equal(t, testCtx, ctx)
 	}
 
-	_, err := r.NewModuleBuilder("env").
+	_, err := r.NewHostModuleBuilder("env").
 		ExportFunction("start", start).
 		Instantiate(testCtx, r)
 	require.NoError(t, err)
@@ -376,7 +376,7 @@ func TestRuntime_InstantiateModuleFromBinary_ErrorOnStart(t *testing.T) {
 				panic(errors.New("ice cream"))
 			}
 
-			host, err := r.NewModuleBuilder("").
+			host, err := r.NewHostModuleBuilder("").
 				ExportFunction("start", start).
 				Instantiate(testCtx, r)
 			require.NoError(t, err)
@@ -428,7 +428,7 @@ func TestRuntime_InstantiateModule_ExitError(t *testing.T) {
 		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)
 
 	one := uint32(1)