Repository: tetratelabs/wazero
Pull request #870 (2022-11-29 08:14:53)

Ensures listeners only bound to compile time objects
This stops binding `experimental.FunctionListener` to wasm.FunctionIntance and removes the need to pass listeners to `Store.Instantiate`. This clarifies more that Function Listeners are compile-time options and prevents the accidental dependence on the instantiation-time objects.

Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>


Patch:
diff --git a/builder.go b/builder.go
index 8e7e4abe..9128777c 100644
--- a/builder.go
+++ b/builder.go
@@ -309,11 +309,12 @@ func (b *hostModuleBuilder) Compile(ctx context.Context) (CompiledModule, error)
 	}
 
 	c := &compiledModule{module: module, compiledEngine: b.r.store.Engine}
-	if c.listeners, err = buildListeners(ctx, module); err != nil {
+	listeners, err := buildListeners(ctx, module)
+	if err != nil {
 		return nil, err
 	}
 
-	if err = b.r.store.Engine.CompileModule(ctx, module, c.listeners); err != nil {
+	if err = b.r.store.Engine.CompileModule(ctx, module, listeners); err != nil {
 		return nil, err
 	}
 
diff --git a/config.go b/config.go
index a872a029..8a19eea5 100644
--- a/config.go
+++ b/config.go
@@ -10,7 +10,6 @@ import (
 	"time"
 
 	"github.com/tetratelabs/wazero/api"
-	"github.com/tetratelabs/wazero/experimental"
 	"github.com/tetratelabs/wazero/internal/engine/compiler"
 	"github.com/tetratelabs/wazero/internal/engine/interpreter"
 	"github.com/tetratelabs/wazero/internal/platform"
@@ -201,8 +200,6 @@ type compiledModule struct {
 	module *wasm.Module
 	// compiledEngine holds an engine on which `module` is compiled.
 	compiledEngine wasm.Engine
-	// listeners are present if the code was compiled with a listener
-	listeners []experimental.FunctionListener
 	// closeWithModule prevents leaking compiled code when a module is compiled implicitly.
 	closeWithModule bool
 }
diff --git a/internal/engine/compiler/engine.go b/internal/engine/compiler/engine.go
index 570bff09..e05f10a7 100644
--- a/internal/engine/compiler/engine.go
+++ b/internal/engine/compiler/engine.go
@@ -261,6 +261,8 @@ type (
 		indexInModule wasm.Index
 		// sourceModule is the module from which this function is compiled. For logging purpose.
 		sourceModule *wasm.Module
+		// listener holds a listener to notify when this function is called.
+		listener experimental.FunctionListener
 	}
 )
 
@@ -472,16 +474,19 @@ func (e *engine) CompileModule(ctx context.Context, module *wasm.Module, listene
 	funcs := make([]*code, len(module.FunctionSection))
 	ln := len(listeners)
 	for i, ir := range irs {
-		withListener := i < ln && listeners[i] != nil
+		var lsn experimental.FunctionListener
+		if i < ln {
+			lsn = listeners[i]
+		}
 
 		funcIndex := wasm.Index(i)
 		var compiled *code
 		if ir.GoFunc != nil {
-			if compiled, err = compileGoDefinedHostFunction(ir, withListener); err != nil {
+			if compiled, err = compileGoDefinedHostFunction(ir, lsn != nil); err != nil {
 				def := module.FunctionDefinitionSection[funcIndex+importedFuncs]
 				return fmt.Errorf("error compiling host go func[%s]: %w", def.DebugName(), err)
 			}
-		} else if compiled, err = compileWasmFunction(e.enabledFeatures, ir, withListener); err != nil {
+		} else if compiled, err = compileWasmFunction(e.enabledFeatures, ir, lsn != nil); err != nil {
 			def := module.FunctionDefinitionSection[funcIndex+importedFuncs]
 			return fmt.Errorf("error compiling wasm func[%s]: %w", def.DebugName(), err)
 		}
@@ -489,6 +494,7 @@ func (e *engine) CompileModule(ctx context.Context, module *wasm.Module, listene
 		// As this uses mmap, we need to munmap on the compiled machine code when it's GCed.
 		e.setFinalizer(compiled, releaseCode)
 
+		compiled.listener = lsn
 		compiled.indexInModule = funcIndex
 		compiled.sourceModule = module
 		funcs[funcIndex] = compiled
@@ -875,9 +881,9 @@ entry:
 			case builtinFunctionIndexTableGrow:
 				ce.builtinFunctionTableGrow(ce.ctx, caller.source.Module.Tables)
 			case builtinFunctionIndexFunctionListenerBefore:
-				ce.builtinFunctionFunctionListenerBefore(ce.ctx, caller.source)
+				ce.builtinFunctionFunctionListenerBefore(ce.ctx, caller)
 			case builtinFunctionIndexFunctionListenerAfter:
-				ce.builtinFunctionFunctionListenerAfter(ce.ctx, caller.source)
+				ce.builtinFunctionFunctionListenerAfter(ce.ctx, caller)
 			}
 			if false {
 				if ce.exitContext.builtinFunctionCallIndex == builtinFunctionIndexBreakPoint {
@@ -942,17 +948,17 @@ func (ce *callEngine) builtinFunctionTableGrow(ctx context.Context, tables []*wa
 	ce.pushValue(uint64(res))
 }
 
-func (ce *callEngine) builtinFunctionFunctionListenerBefore(ctx context.Context, fn *wasm.FunctionInstance) {
+func (ce *callEngine) builtinFunctionFunctionListenerBefore(ctx context.Context, fn *function) {
 	base := int(ce.stackBasePointerInBytes >> 3)
-	listerCtx := fn.Listener.Before(ctx, fn.Definition, ce.stack[base:base+fn.Type.ParamNumInUint64])
+	listerCtx := fn.parent.listener.Before(ctx, fn.source.Definition, ce.stack[base:base+fn.source.Type.ParamNumInUint64])
 	prevStackTop := ce.contextStack
 	ce.contextStack = &contextStack{self: ctx, prev: prevStackTop}
 	ce.ctx = listerCtx
 }
 
-func (ce *callEngine) builtinFunctionFunctionListenerAfter(ctx context.Context, fn *wasm.FunctionInstance) {
+func (ce *callEngine) builtinFunctionFunctionListenerAfter(ctx context.Context, fn *function) {
 	base := int(ce.stackBasePointerInBytes >> 3)
-	fn.Listener.After(ctx, fn.Definition, nil, ce.stack[base:base+fn.Type.ResultNumInUint64])
+	fn.parent.listener.After(ctx, fn.source.Definition, nil, ce.stack[base:base+fn.source.Type.ResultNumInUint64])
 	ce.ctx = ce.contextStack.self
 	ce.contextStack = ce.contextStack.prev
 }
diff --git a/internal/engine/interpreter/interpreter.go b/internal/engine/interpreter/interpreter.go
index a479388b..1a1b9b66 100644
--- a/internal/engine/interpreter/interpreter.go
+++ b/internal/engine/interpreter/interpreter.go
@@ -170,14 +170,16 @@ type callFrame struct {
 }
 
 type code struct {
-	body   []*interpreterOp
-	hostFn interface{}
+	body     []*interpreterOp
+	listener experimental.FunctionListener
+	hostFn   interface{}
 }
 
 type function struct {
 	source *wasm.FunctionInstance
 	body   []*interpreterOp
 	hostFn interface{}
+	parent *code
 }
 
 // functionFromUintptr resurrects the original *function from the given uintptr
@@ -197,6 +199,7 @@ func (c *code) instantiate(f *wasm.FunctionInstance) *function {
 		source: f,
 		body:   c.body,
 		hostFn: c.hostFn,
+		parent: c,
 	}
 }
 
@@ -220,29 +223,38 @@ type interpreterOp struct {
 const callFrameStackSize = 0
 
 // CompileModule implements the same method as documented on wasm.Engine.
-func (e *engine) CompileModule(ctx context.Context, module *wasm.Module, _ []experimental.FunctionListener) error {
+func (e *engine) CompileModule(ctx context.Context, module *wasm.Module, listeners []experimental.FunctionListener) error {
 	if _, ok := e.getCodes(module); ok { // cache hit!
 		return nil
 	}
 
-	funcs := make([]*code, 0, len(module.FunctionSection))
+	funcs := make([]*code, len(module.FunctionSection))
 	irs, err := wazeroir.CompileFunctions(ctx, e.enabledFeatures, callFrameStackSize, module)
 	if err != nil {
 		return err
 	}
 	for i, ir := range irs {
+		var lsn experimental.FunctionListener
+		if i < len(listeners) {
+			lsn = listeners[i]
+		}
+
 		// If this is the host function, there's nothing to do as the runtime representation of
 		// host function in interpreter is its Go function itself as opposed to Wasm functions,
 		// which need to be compiled down to wazeroir.
 		if ir.GoFunc != nil {
-			funcs = append(funcs, &code{hostFn: ir.GoFunc})
+			funcs[i] = &code{hostFn: ir.GoFunc, listener: lsn}
 			continue
-		} else if compiled, err := e.lowerIR(ir); err != nil {
-			def := module.FunctionDefinitionSection[uint32(i)+module.ImportFuncCount()]
-			return fmt.Errorf("failed to lower func[%s] to wazeroir: %w", def.DebugName(), err)
 		} else {
-			funcs = append(funcs, compiled)
+			compiled, err := e.lowerIR(ir)
+			if err != nil {
+				def := module.FunctionDefinitionSection[uint32(i)+module.ImportFuncCount()]
+				return fmt.Errorf("failed to lower func[%s] to wazeroir: %w", def.DebugName(), err)
+			}
+			compiled.listener = lsn
+			funcs[i] = compiled
 		}
+
 	}
 	e.addCodes(module, funcs)
 	return nil
@@ -855,7 +867,7 @@ func (ce *callEngine) recoverOnCall(v interface{}) (err error) {
 func (ce *callEngine) callFunction(ctx context.Context, callCtx *wasm.CallContext, f *function) {
 	if f.hostFn != nil {
 		ce.callGoFuncWithStack(ctx, callCtx, f)
-	} else if lsn := f.source.Listener; lsn != nil {
+	} else if lsn := f.parent.listener; lsn != nil {
 		ce.callNativeFuncWithListener(ctx, callCtx, f, lsn)
 	} else {
 		ce.callNativeFunc(ctx, callCtx, f)
@@ -863,9 +875,10 @@ func (ce *callEngine) callFunction(ctx context.Context, callCtx *wasm.CallContex
 }
 
 func (ce *callEngine) callGoFunc(ctx context.Context, callCtx *wasm.CallContext, f *function, stack []uint64) {
-	if f.source.Listener != nil {
+	lsn := f.parent.listener
+	if lsn != nil {
 		params := stack[:f.source.Type.ParamNumInUint64]
-		ctx = f.source.Listener.Before(ctx, f.source.Definition, params)
+		ctx = lsn.Before(ctx, f.source.Definition, params)
 	}
 	frame := &callFrame{f: f}
 	ce.pushFrame(frame)
@@ -879,10 +892,10 @@ func (ce *callEngine) callGoFunc(ctx context.Context, callCtx *wasm.CallContext,
 	}
 
 	ce.popFrame()
-	if f.source.Listener != nil {
+	if lsn != nil {
 		// TODO: This doesn't get the error due to use of panic to propagate them.
 		results := stack[:f.source.Type.ResultNumInUint64]
-		f.source.Listener.After(ctx, f.source.Definition, nil, results)
+		lsn.After(ctx, f.source.Definition, nil, results)
 	}
 }
 
diff --git a/internal/wasm/module.go b/internal/wasm/module.go
index 967e5a30..9ca7dc92 100644
--- a/internal/wasm/module.go
+++ b/internal/wasm/module.go
@@ -10,7 +10,6 @@ import (
 	"strings"
 
 	"github.com/tetratelabs/wazero/api"
-	"github.com/tetratelabs/wazero/experimental"
 	"github.com/tetratelabs/wazero/internal/ieee754"
 	"github.com/tetratelabs/wazero/internal/leb128"
 )
@@ -590,7 +589,7 @@ func (m *Module) buildGlobals(importedGlobals []*GlobalInstance) (globals []*Glo
 //   - This relies on data generated by Module.BuildFunctionDefinitions.
 //   - This is exported for tests that don't call Instantiate, notably only
 //     enginetest.go.
-func (m *ModuleInstance) BuildFunctions(mod *Module, listeners []experimental.FunctionListener) (fns []*FunctionInstance) {
+func (m *ModuleInstance) BuildFunctions(mod *Module) (fns []*FunctionInstance) {
 	importCount := mod.ImportFuncCount()
 	fns = make([]*FunctionInstance, len(mod.FunctionSection))
 	for i, section := range mod.FunctionSection {
@@ -607,9 +606,6 @@ func (m *ModuleInstance) BuildFunctions(mod *Module, listeners []experimental.Fu
 			Type:           d.funcType,
 			Definition:     d,
 		}
-		if listeners != nil {
-			f.Listener = listeners[i]
-		}
 		fns[i] = f
 	}
 	return
diff --git a/internal/wasm/store.go b/internal/wasm/store.go
index 18965975..2d496bc8 100644
--- a/internal/wasm/store.go
+++ b/internal/wasm/store.go
@@ -8,7 +8,6 @@ import (
 	"sync"
 
 	"github.com/tetratelabs/wazero/api"
-	experimentalapi "github.com/tetratelabs/wazero/experimental"
 	"github.com/tetratelabs/wazero/internal/ieee754"
 	"github.com/tetratelabs/wazero/internal/leb128"
 	internalsys "github.com/tetratelabs/wazero/internal/sys"
@@ -140,9 +139,6 @@ type (
 
 		// Definition is known at compile time.
 		Definition api.FunctionDefinition
-
-		// Listener holds a listener to notify when this function is called.
-		Listener experimentalapi.FunctionListener
 	}
 
 	// GlobalInstance represents a global instance in a store.
@@ -299,7 +295,6 @@ func (s *Store) Instantiate(
 	module *Module,
 	name string,
 	sys *internalsys.Context,
-	listeners []experimentalapi.FunctionListener,
 ) (*CallContext, error) {
 	// Collect any imported modules to avoid locking the namespace too long.
 	importedModuleNames := map[string]struct{}{}
@@ -319,7 +314,7 @@ func (s *Store) Instantiate(
 	}
 
 	// Instantiate the module and add it to the namespace so that other modules can import it.
-	if callCtx, err := s.instantiate(ctx, ns, module, name, sys, listeners, importedModules); err != nil {
+	if callCtx, err := s.instantiate(ctx, ns, module, name, sys, importedModules); err != nil {
 		ns.deleteModule(name)
 		return nil, err
 	} else {
@@ -336,7 +331,6 @@ func (s *Store) instantiate(
 	module *Module,
 	name string,
 	sysCtx *internalsys.Context,
-	listeners []experimentalapi.FunctionListener,
 	modules map[string]*ModuleInstance,
 ) (*CallContext, error) {
 	typeIDs, err := s.getFunctionTypeIDs(module.TypeSection)
@@ -358,7 +352,7 @@ func (s *Store) instantiate(
 	globals, memory := module.buildGlobals(importedGlobals), module.buildMemory()
 
 	m := &ModuleInstance{Name: name, TypeIDs: typeIDs}
-	functions := m.BuildFunctions(module, listeners)
+	functions := m.BuildFunctions(module)
 
 	// Now we have all instances from imports and local ones, so ready to create a new ModuleInstance.
 	m.addSections(module, importedFunctions, functions, importedGlobals, globals, tables, importedMemory, memory, module.TypeSection)
diff --git a/namespace.go b/namespace.go
index 1910cea2..19be3ee6 100644
--- a/namespace.go
+++ b/namespace.go
@@ -79,7 +79,7 @@ func (ns *namespace) InstantiateModule(
 	}
 
 	// Instantiate the module in the appropriate namespace.
-	mod, err = ns.store.Instantiate(ctx, ns.ns, code.module, name, sysCtx, code.listeners)
+	mod, err = ns.store.Instantiate(ctx, ns.ns, code.module, name, sysCtx)
 	if err != nil {
 		// If there was an error, don't leak the compiled module.
 		if code.closeWithModule {
diff --git a/runtime.go b/runtime.go
index 730d419d..c3d21ddf 100644
--- a/runtime.go
+++ b/runtime.go
@@ -189,11 +189,12 @@ func (r *runtime) CompileModule(ctx context.Context, binary []byte) (CompiledMod
 
 	c := &compiledModule{module: internal, compiledEngine: r.store.Engine}
 
-	if c.listeners, err = buildListeners(ctx, internal); err != nil {
+	listeners, err := buildListeners(ctx, internal)
+	if err != nil {
 		return nil, err
 	}
 
-	if err = r.store.Engine.CompileModule(ctx, internal, c.listeners); err != nil {
+	if err = r.store.Engine.CompileModule(ctx, internal, listeners); err != nil {
 		return nil, err
 	}
 


Test patch:
diff --git a/internal/engine/compiler/engine_test.go b/internal/engine/compiler/engine_test.go
index a45b5713..772060b6 100644
--- a/internal/engine/compiler/engine_test.go
+++ b/internal/engine/compiler/engine_test.go
@@ -282,7 +282,7 @@ func TestCompiler_SliceAllocatedOnHeap(t *testing.T) {
 	err = s.Engine.CompileModule(testCtx, hm, nil)
 	require.NoError(t, err)
 
-	_, err = s.Instantiate(testCtx, ns, hm, hostModuleName, nil, nil)
+	_, err = s.Instantiate(testCtx, ns, hm, hostModuleName, nil)
 	require.NoError(t, err)
 
 	const stackCorruption = "value_stack_corruption"
@@ -338,7 +338,7 @@ func TestCompiler_SliceAllocatedOnHeap(t *testing.T) {
 	err = s.Engine.CompileModule(testCtx, m, nil)
 	require.NoError(t, err)
 
-	mi, err := s.Instantiate(testCtx, ns, m, t.Name(), nil, nil)
+	mi, err := s.Instantiate(testCtx, ns, m, t.Name(), nil)
 	require.NoError(t, err)
 
 	for _, fnName := range []string{stackCorruption, callStackCorruption} {
@@ -583,14 +583,19 @@ func Test_callFrameOffset(t *testing.T) {
 
 func TestCallEngine_builtinFunctionFunctionListenerBefore(t *testing.T) {
 	nextContext, currentContext, prevContext := context.Background(), context.Background(), context.Background()
-	f := &wasm.FunctionInstance{
-		Definition: newMockFunctionDefinition("1"),
-		Type:       &wasm.FunctionType{ParamNumInUint64: 3},
-		Listener: mockListener{
-			before: func(ctx context.Context, def api.FunctionDefinition, paramValues []uint64) context.Context {
-				require.Equal(t, currentContext, ctx)
-				require.Equal(t, []uint64{2, 3, 4}, paramValues)
-				return nextContext
+
+	f := &function{
+		source: &wasm.FunctionInstance{
+			Definition: newMockFunctionDefinition("1"),
+			Type:       &wasm.FunctionType{ParamNumInUint64: 3},
+		},
+		parent: &code{
+			listener: mockListener{
+				before: func(ctx context.Context, def api.FunctionDefinition, paramValues []uint64) context.Context {
+					require.Equal(t, currentContext, ctx)
+					require.Equal(t, []uint64{2, 3, 4}, paramValues)
+					return nextContext
+				},
 			},
 		},
 	}
@@ -608,16 +613,21 @@ func TestCallEngine_builtinFunctionFunctionListenerBefore(t *testing.T) {
 
 func TestCallEngine_builtinFunctionFunctionListenerAfter(t *testing.T) {
 	currentContext, prevContext := context.Background(), context.Background()
-	f := &wasm.FunctionInstance{
-		Definition: newMockFunctionDefinition("1"),
-		Type:       &wasm.FunctionType{ResultNumInUint64: 1},
-		Listener: mockListener{
-			after: func(ctx context.Context, def api.FunctionDefinition, err error, resultValues []uint64) {
-				require.Equal(t, currentContext, ctx)
-				require.Equal(t, []uint64{5}, resultValues)
+	f := &function{
+		source: &wasm.FunctionInstance{
+			Definition: newMockFunctionDefinition("1"),
+			Type:       &wasm.FunctionType{ResultNumInUint64: 1},
+		},
+		parent: &code{
+			listener: mockListener{
+				after: func(ctx context.Context, def api.FunctionDefinition, err error, resultValues []uint64) {
+					require.Equal(t, currentContext, ctx)
+					require.Equal(t, []uint64{5}, resultValues)
+				},
 			},
 		},
 	}
+
 	ce := &callEngine{
 		ctx: currentContext, stack: []uint64{0, 1, 2, 3, 4, 5},
 		stackContext: stackContext{stackBasePointerInBytes: 40},
diff --git a/internal/integration_test/bench/hostfunc_bench_test.go b/internal/integration_test/bench/hostfunc_bench_test.go
index cb36f36c..e26405fc 100644
--- a/internal/integration_test/bench/hostfunc_bench_test.go
+++ b/internal/integration_test/bench/hostfunc_bench_test.go
@@ -185,7 +185,7 @@ func setupHostCallBench(requireNoError func(error)) *wasm.ModuleInstance {
 	hostModule.BuildFunctionDefinitions()
 
 	host := &wasm.ModuleInstance{Name: "host", TypeIDs: []wasm.FunctionTypeID{0}}
-	host.Functions = host.BuildFunctions(hostModule, nil)
+	host.Functions = host.BuildFunctions(hostModule)
 	host.BuildExports(hostModule.ExportSection)
 	goFn := host.Exports["go"].Function
 	goReflectFn := host.Exports["go-reflect"].Function
@@ -228,7 +228,7 @@ func setupHostCallBench(requireNoError func(error)) *wasm.ModuleInstance {
 	requireNoError(err)
 
 	importing := &wasm.ModuleInstance{TypeIDs: []wasm.FunctionTypeID{0}}
-	importingFunctions := importing.BuildFunctions(importingModule, nil)
+	importingFunctions := importing.BuildFunctions(importingModule)
 	importing.Functions = append([]*wasm.FunctionInstance{goFn, wasnFn}, importingFunctions...)
 	importing.BuildExports(importingModule.ExportSection)
 
diff --git a/internal/integration_test/spectest/spectest.go b/internal/integration_test/spectest/spectest.go
index d150c203..f8243a6d 100644
--- a/internal/integration_test/spectest/spectest.go
+++ b/internal/integration_test/spectest/spectest.go
@@ -370,7 +370,7 @@ func addSpectestModule(t *testing.T, ctx context.Context, s *wasm.Store, ns *was
 	err = s.Engine.CompileModule(ctx, mod, nil)
 	require.NoError(t, err)
 
-	_, err = s.Instantiate(ctx, ns, mod, mod.NameSection.ModuleName, sys.DefaultContext(nil), nil)
+	_, err = s.Instantiate(ctx, ns, mod, mod.NameSection.ModuleName, sys.DefaultContext(nil))
 	require.NoError(t, err)
 }
 
@@ -436,7 +436,7 @@ func Run(t *testing.T, testDataFS embed.FS, ctx context.Context, newEngine func(
 						err = s.Engine.CompileModule(ctx, mod, nil)
 						require.NoError(t, err, msg)
 
-						_, err = s.Instantiate(ctx, ns, mod, moduleName, nil, nil)
+						_, err = s.Instantiate(ctx, ns, mod, moduleName, nil)
 						lastInstantiatedModuleName = moduleName
 						require.NoError(t, err)
 					case "register":
@@ -574,7 +574,7 @@ func Run(t *testing.T, testDataFS embed.FS, ctx context.Context, newEngine func(
 							err = s.Engine.CompileModule(ctx, mod, nil)
 							require.NoError(t, err, msg)
 
-							_, err = s.Instantiate(ctx, ns, mod, t.Name(), nil, nil)
+							_, err = s.Instantiate(ctx, ns, mod, t.Name(), nil)
 							require.NoError(t, err, msg)
 						} else {
 							requireInstantiationError(t, ctx, s, ns, buf, msg)
@@ -609,7 +609,7 @@ func requireInstantiationError(t *testing.T, ctx context.Context, s *wasm.Store,
 		return
 	}
 
-	_, err = s.Instantiate(ctx, ns, mod, t.Name(), nil, nil)
+	_, err = s.Instantiate(ctx, ns, mod, t.Name(), nil)
 	require.Error(t, err, msg)
 }
 
diff --git a/internal/testing/enginetest/enginetest.go b/internal/testing/enginetest/enginetest.go
index 10c3045d..8b11e3e6 100644
--- a/internal/testing/enginetest/enginetest.go
+++ b/internal/testing/enginetest/enginetest.go
@@ -97,7 +97,7 @@ func RunTestEngine_InitializeFuncrefGlobals(t *testing.T, et EngineTester) {
 
 	// To use the function, we first need to add it to a module.
 	instance := &wasm.ModuleInstance{Name: t.Name(), TypeIDs: []wasm.FunctionTypeID{0}}
-	fns := instance.BuildFunctions(m, buildListeners(et.ListenerFactory(), m))
+	fns := instance.BuildFunctions(m)
 	me, err := e.NewModuleEngine(t.Name(), m, nil, fns, nil, nil)
 	require.NoError(t, err)
 
@@ -148,7 +148,7 @@ func RunTestModuleEngine_Call(t *testing.T, et EngineTester) {
 
 	// To use the function, we first need to add it to a module.
 	module := &wasm.ModuleInstance{Name: t.Name(), TypeIDs: []wasm.FunctionTypeID{0}}
-	module.Functions = module.BuildFunctions(m, listeners)
+	module.Functions = module.BuildFunctions(m)
 
 	// Compile the module
 	me, err := e.NewModuleEngine(module.Name, m, nil, module.Functions, nil, nil)
@@ -216,7 +216,7 @@ func requireNewModuleEngine_emptyTable(t *testing.T, e wasm.Engine, et EngineTes
 	require.NoError(t, err)
 
 	module = &wasm.ModuleInstance{Name: t.Name(), Tables: tables, TypeIDs: []wasm.FunctionTypeID{0}}
-	fns := module.BuildFunctions(m, buildListeners(et.ListenerFactory(), m))
+	fns := module.BuildFunctions(m)
 
 	// Instantiate the module, which has nothing but an empty table.
 	me, err = e.NewModuleEngine(t.Name(), m, nil, fns, tables, nil)
@@ -251,7 +251,7 @@ func requireNewModuleEngine_multiTable(t *testing.T, e wasm.Engine, et EngineTes
 	require.NoError(t, err)
 
 	module = &wasm.ModuleInstance{Name: t.Name(), Tables: tables, TypeIDs: []wasm.FunctionTypeID{0}}
-	fns := module.BuildFunctions(m, buildListeners(et.ListenerFactory(), m))
+	fns := module.BuildFunctions(m)
 
 	tableInits := []wasm.TableInitEntry{
 		{TableIndex: 0, Offset: 0, FunctionIndexes: []*wasm.Index{&func1}},
@@ -290,7 +290,7 @@ func requireNewModuleEngine_tableWithImportedFunction(t *testing.T, e wasm.Engin
 	require.NoError(t, err)
 
 	imported := &wasm.ModuleInstance{Name: t.Name(), Tables: tables, TypeIDs: []wasm.FunctionTypeID{0}}
-	importedFunctions := imported.BuildFunctions(importedModule, buildListeners(et.ListenerFactory(), importedModule))
+	importedFunctions := imported.BuildFunctions(importedModule)
 
 	// Imported functions are compiled before the importing module is instantiated.
 	importedMe, err := e.NewModuleEngine(t.Name(), importedModule, nil, importedFunctions, nil, nil)
@@ -313,7 +313,7 @@ func requireNewModuleEngine_tableWithImportedFunction(t *testing.T, e wasm.Engin
 	}
 
 	importing = &wasm.ModuleInstance{Name: t.Name(), Tables: tables, TypeIDs: []wasm.FunctionTypeID{0}}
-	fns := importing.BuildFunctions(importingModule, buildListeners(et.ListenerFactory(), importingModule))
+	fns := importing.BuildFunctions(importingModule)
 
 	importingMe, err = e.NewModuleEngine(t.Name(), importingModule, importedFunctions, fns, tables, tableInits)
 	require.NoError(t, err)
@@ -341,7 +341,7 @@ func requireNewModuleEngine_tableWithMixedFunctions(t *testing.T, e wasm.Engine,
 	err := e.CompileModule(testCtx, importedModule, nil)
 	require.NoError(t, err)
 	imported := &wasm.ModuleInstance{Name: t.Name(), TypeIDs: []wasm.FunctionTypeID{0}}
-	importedFunctions := imported.BuildFunctions(importedModule, buildListeners(et.ListenerFactory(), importedModule))
+	importedFunctions := imported.BuildFunctions(importedModule)
 
 	// Imported functions are compiled before the importing module is instantiated.
 	importedMe, err := e.NewModuleEngine(t.Name(), importedModule, nil, importedFunctions, nil, nil)
@@ -364,7 +364,7 @@ func requireNewModuleEngine_tableWithMixedFunctions(t *testing.T, e wasm.Engine,
 	require.NoError(t, err)
 
 	importing = &wasm.ModuleInstance{Name: t.Name(), Tables: tables, TypeIDs: []wasm.FunctionTypeID{0}}
-	fns := importing.BuildFunctions(importingModule, buildListeners(et.ListenerFactory(), importingModule))
+	fns := importing.BuildFunctions(importingModule)
 
 	tableInits := []wasm.TableInitEntry{
 		{TableIndex: 0, Offset: 0, FunctionIndexes: []*wasm.Index{&func1, &func2}},
@@ -695,7 +695,7 @@ func RunTestModuleEngine_Memory(t *testing.T, et EngineTester) {
 	var memory api.Memory = module.Memory
 
 	// To use functions, we need to instantiate them (associate them with a ModuleInstance).
-	module.Functions = module.BuildFunctions(m, listeners)
+	module.Functions = module.BuildFunctions(m)
 	module.BuildExports(m.ExportSection)
 	grow, init := module.Functions[0], module.Functions[1]
 
@@ -827,7 +827,7 @@ func setupCallTests(t *testing.T, e wasm.Engine, divBy *wasm.Code, fnlf experime
 	err := e.CompileModule(testCtx, hostModule, lns)
 	require.NoError(t, err)
 	host := &wasm.ModuleInstance{Name: hostModule.NameSection.ModuleName, TypeIDs: []wasm.FunctionTypeID{0}}
-	host.Functions = host.BuildFunctions(hostModule, lns)
+	host.Functions = host.BuildFunctions(hostModule)
 	host.BuildExports(hostModule.ExportSection)
 	hostFn := host.Exports[divByGoName].Function
 
@@ -863,7 +863,7 @@ func setupCallTests(t *testing.T, e wasm.Engine, divBy *wasm.Code, fnlf experime
 	require.NoError(t, err)
 
 	imported := &wasm.ModuleInstance{Name: importedModule.NameSection.ModuleName, TypeIDs: []wasm.FunctionTypeID{0}}
-	importedFunctions := imported.BuildFunctions(importedModule, lns)
+	importedFunctions := imported.BuildFunctions(importedModule)
 	imported.Functions = append([]*wasm.FunctionInstance{hostFn}, importedFunctions...)
 	imported.BuildExports(importedModule.ExportSection)
 	callHostFn := imported.Exports[callDivByGoName].Function
@@ -897,7 +897,7 @@ func setupCallTests(t *testing.T, e wasm.Engine, divBy *wasm.Code, fnlf experime
 
 	// Add the exported function.
 	importing := &wasm.ModuleInstance{Name: importingModule.NameSection.ModuleName, TypeIDs: []wasm.FunctionTypeID{0}}
-	importingFunctions := importing.BuildFunctions(importingModule, lns)
+	importingFunctions := importing.BuildFunctions(importingModule)
 	importing.Functions = append([]*wasm.FunctionInstance{callHostFn}, importingFunctions...)
 	importing.BuildExports(importingModule.ExportSection)
 
@@ -945,7 +945,7 @@ func setupCallMemTests(t *testing.T, e wasm.Engine, readMem *wasm.Code, fnlf exp
 	err := e.CompileModule(testCtx, hostModule, nil)
 	require.NoError(t, err)
 	host := &wasm.ModuleInstance{Name: hostModule.NameSection.ModuleName, TypeIDs: []wasm.FunctionTypeID{0}}
-	host.Functions = host.BuildFunctions(hostModule, buildListeners(fnlf, hostModule))
+	host.Functions = host.BuildFunctions(hostModule)
 	host.BuildExports(hostModule.ExportSection)
 	readMemFn := host.Exports[readMemName].Function
 	callReadMemFn := host.Exports[callReadMemName].Function
@@ -987,7 +987,7 @@ func setupCallMemTests(t *testing.T, e wasm.Engine, readMem *wasm.Code, fnlf exp
 
 	// Add the exported function.
 	importing := &wasm.ModuleInstance{Name: importingModule.NameSection.ModuleName, TypeIDs: []wasm.FunctionTypeID{0}}
-	importingFunctions := importing.BuildFunctions(importingModule, buildListeners(fnlf, importingModule))
+	importingFunctions := importing.BuildFunctions(importingModule)
 	// Note: adds imported functions readMemFn and callReadMemFn at index 0 and 1.
 	importing.Functions = append([]*wasm.FunctionInstance{callReadMemFn, readMemFn}, importingFunctions...)
 	importing.BuildExports(importingModule.ExportSection)
diff --git a/internal/wasm/call_context_test.go b/internal/wasm/call_context_test.go
index 081f3058..fa0311d5 100644
--- a/internal/wasm/call_context_test.go
+++ b/internal/wasm/call_context_test.go
@@ -82,7 +82,7 @@ func TestCallContext_String(t *testing.T) {
 
 		t.Run(tc.name, func(t *testing.T) {
 			// Ensure paths that can create the host module can see the name.
-			m, err := s.Instantiate(context.Background(), ns, &Module{}, tc.moduleName, nil, nil)
+			m, err := s.Instantiate(context.Background(), ns, &Module{}, tc.moduleName, nil)
 			defer m.Close(testCtx) //nolint
 
 			require.NoError(t, err)
@@ -121,7 +121,7 @@ func TestCallContext_Close(t *testing.T) {
 		t.Run(fmt.Sprintf("%s calls ns.CloseWithExitCode(module.name))", tc.name), func(t *testing.T) {
 			for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
 				moduleName := t.Name()
-				m, err := s.Instantiate(ctx, ns, &Module{}, moduleName, nil, nil)
+				m, err := s.Instantiate(ctx, ns, &Module{}, moduleName, nil)
 				require.NoError(t, err)
 
 				// We use side effects to see if Close called ns.CloseWithExitCode (without repeating store_test.go).
@@ -149,7 +149,7 @@ func TestCallContext_Close(t *testing.T) {
 		_, err := fsCtx.OpenFile(testCtx, "/foo")
 		require.NoError(t, err)
 
-		m, err := s.Instantiate(context.Background(), ns, &Module{}, t.Name(), sysCtx, nil)
+		m, err := s.Instantiate(context.Background(), ns, &Module{}, t.Name(), sysCtx)
 		require.NoError(t, err)
 
 		// We use side effects to determine if Close in fact called Context.Close (without repeating sys_test.go).
@@ -177,7 +177,7 @@ func TestCallContext_Close(t *testing.T) {
 		_, err := fsCtx.OpenFile(testCtx, "/foo")
 		require.NoError(t, err)
 
-		m, err := s.Instantiate(context.Background(), ns, &Module{}, t.Name(), sysCtx, nil)
+		m, err := s.Instantiate(context.Background(), ns, &Module{}, t.Name(), sysCtx)
 		require.NoError(t, err)
 
 		require.EqualError(t, m.Close(testCtx), "error closing")
@@ -217,7 +217,7 @@ func TestCallContext_CallDynamic(t *testing.T) {
 		t.Run(fmt.Sprintf("%s calls ns.CloseWithExitCode(module.name))", tc.name), func(t *testing.T) {
 			for _, ctx := range []context.Context{nil, testCtx} { // Ensure it doesn't crash on nil!
 				moduleName := t.Name()
-				m, err := s.Instantiate(ctx, ns, &Module{}, moduleName, nil, nil)
+				m, err := s.Instantiate(ctx, ns, &Module{}, moduleName, nil)
 				require.NoError(t, err)
 
 				// We use side effects to see if Close called ns.CloseWithExitCode (without repeating store_test.go).
@@ -245,7 +245,7 @@ func TestCallContext_CallDynamic(t *testing.T) {
 		_, err := fsCtx.OpenFile(testCtx, "/foo")
 		require.NoError(t, err)
 
-		m, err := s.Instantiate(context.Background(), ns, &Module{}, t.Name(), sysCtx, nil)
+		m, err := s.Instantiate(context.Background(), ns, &Module{}, t.Name(), sysCtx)
 		require.NoError(t, err)
 
 		// We use side effects to determine if Close in fact called Context.Close (without repeating sys_test.go).
@@ -273,7 +273,7 @@ func TestCallContext_CallDynamic(t *testing.T) {
 		_, err := fsCtx.OpenFile(testCtx, "/foo")
 		require.NoError(t, err)
 
-		m, err := s.Instantiate(context.Background(), ns, &Module{}, t.Name(), sysCtx, nil)
+		m, err := s.Instantiate(context.Background(), ns, &Module{}, t.Name(), sysCtx)
 		require.NoError(t, err)
 
 		require.EqualError(t, m.Close(testCtx), "error closing")
diff --git a/internal/wasm/global_test.go b/internal/wasm/global_test.go
index b65f087e..d519616d 100644
--- a/internal/wasm/global_test.go
+++ b/internal/wasm/global_test.go
@@ -298,7 +298,7 @@ func TestPublicModule_Global(t *testing.T) {
 		s, ns := newStore()
 		t.Run(tc.name, func(t *testing.T) {
 			// Instantiate the module and get the export of the above global
-			module, err := s.Instantiate(context.Background(), ns, tc.module, t.Name(), nil, nil)
+			module, err := s.Instantiate(context.Background(), ns, tc.module, t.Name(), nil)
 			require.NoError(t, err)
 
 			if global := module.ExportedGlobal("global"); tc.expected != nil {
diff --git a/internal/wasm/module_test.go b/internal/wasm/module_test.go
index 111d92ee..3cbdf7bf 100644
--- a/internal/wasm/module_test.go
+++ b/internal/wasm/module_test.go
@@ -792,7 +792,7 @@ func TestModule_buildFunctions(t *testing.T) {
 
 	// Note: This only returns module-defined functions, not imported ones. That's why the index starts with 1, not 0.
 	instance := &ModuleInstance{Name: "counter", TypeIDs: []FunctionTypeID{0}}
-	instance.BuildFunctions(m, nil)
+	instance.BuildFunctions(m)
 	for i, f := range instance.Functions {
 		require.Equal(t, i, f.Definition.Index())
 		require.Equal(t, nopCode.Body, f.Body)
diff --git a/internal/wasm/store_test.go b/internal/wasm/store_test.go
index 8d36bc68..09e3c8ad 100644
--- a/internal/wasm/store_test.go
+++ b/internal/wasm/store_test.go
@@ -77,7 +77,7 @@ func TestModuleInstance_Memory(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			s, ns := newStore()
 
-			instance, err := s.Instantiate(testCtx, ns, tc.input, "test", nil, nil)
+			instance, err := s.Instantiate(testCtx, ns, tc.input, "test", nil)
 			require.NoError(t, err)
 
 			mem := instance.ExportedMemory("memory")
@@ -96,7 +96,7 @@ func TestStore_Instantiate(t *testing.T) {
 	require.NoError(t, err)
 
 	sysCtx := sys.DefaultContext(nil)
-	mod, err := s.Instantiate(testCtx, ns, m, "", sysCtx, nil)
+	mod, err := s.Instantiate(testCtx, ns, m, "", sysCtx)
 	require.NoError(t, err)
 	defer mod.Close(testCtx)
 
@@ -137,7 +137,7 @@ func TestStore_CloseWithExitCode(t *testing.T) {
 				CodeSection:               []*Code{{Body: []byte{OpcodeEnd}}},
 				ExportSection:             []*Export{{Type: ExternTypeFunc, Index: 0, Name: "fn"}},
 				FunctionDefinitionSection: []*FunctionDefinition{{funcType: v_v}},
-			}, importedModuleName, nil, nil)
+			}, importedModuleName, nil)
 			require.NoError(t, err)
 
 			m2, err := s.Instantiate(testCtx, ns, &Module{
@@ -146,7 +146,7 @@ func TestStore_CloseWithExitCode(t *testing.T) {
 				MemorySection: &Memory{Min: 1, Cap: 1},
 				GlobalSection: []*Global{{Type: &GlobalType{}, Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1}}},
 				TableSection:  []*Table{{Min: 10}},
-			}, importingModuleName, nil, nil)
+			}, importingModuleName, nil)
 			require.NoError(t, err)
 
 			if tc.testClosed {
@@ -174,7 +174,7 @@ func TestStore_hammer(t *testing.T) {
 	require.NoError(t, err)
 
 	s, ns := newStore()
-	imported, err := s.Instantiate(testCtx, ns, m, importedModuleName, nil, nil)
+	imported, err := s.Instantiate(testCtx, ns, m, importedModuleName, nil)
 	require.NoError(t, err)
 
 	_, ok := ns.modules[imported.Name()]
@@ -205,7 +205,7 @@ func TestStore_hammer(t *testing.T) {
 		N = 100
 	}
 	hammer.NewHammer(t, P, N).Run(func(name string) {
-		mod, instantiateErr := s.Instantiate(testCtx, ns, importingModule, name, sys.DefaultContext(nil), nil)
+		mod, instantiateErr := s.Instantiate(testCtx, ns, importingModule, name, sys.DefaultContext(nil))
 		require.NoError(t, instantiateErr)
 		require.NoError(t, mod.Close(testCtx))
 	}, nil)
@@ -229,17 +229,17 @@ func TestStore_Instantiate_Errors(t *testing.T) {
 
 	t.Run("Fails if module name already in use", func(t *testing.T) {
 		s, ns := newStore()
-		_, err = s.Instantiate(testCtx, ns, m, importedModuleName, nil, nil)
+		_, err = s.Instantiate(testCtx, ns, m, importedModuleName, nil)
 		require.NoError(t, err)
 
 		// Trying to register it again should fail
-		_, err = s.Instantiate(testCtx, ns, m, importedModuleName, nil, nil)
+		_, err = s.Instantiate(testCtx, ns, m, importedModuleName, nil)
 		require.EqualError(t, err, "module[imported] has already been instantiated")
 	})
 
 	t.Run("fail resolve import", func(t *testing.T) {
 		s, ns := newStore()
-		_, err = s.Instantiate(testCtx, ns, m, importedModuleName, nil, nil)
+		_, err = s.Instantiate(testCtx, ns, m, importedModuleName, nil)
 		require.NoError(t, err)
 
 		hm := ns.modules[importedModuleName]
@@ -253,14 +253,14 @@ func TestStore_Instantiate_Errors(t *testing.T) {
 				// But the second one tries to import uninitialized-module ->
 				{Type: ExternTypeFunc, Module: "non-exist", Name: "fn", DescFunc: 0},
 			},
-		}, importingModuleName, nil, nil)
+		}, importingModuleName, nil)
 		require.EqualError(t, err, "module[non-exist] not instantiated")
 	})
 
 	t.Run("compilation failed", func(t *testing.T) {
 		s, ns := newStore()
 
-		_, err = s.Instantiate(testCtx, ns, m, importedModuleName, nil, nil)
+		_, err = s.Instantiate(testCtx, ns, m, importedModuleName, nil)
 		require.NoError(t, err)
 
 		hm := ns.modules[importedModuleName]
@@ -282,7 +282,7 @@ func TestStore_Instantiate_Errors(t *testing.T) {
 		}
 		importingModule.BuildFunctionDefinitions()
 
-		_, err = s.Instantiate(testCtx, ns, importingModule, importingModuleName, nil, nil)
+		_, err = s.Instantiate(testCtx, ns, importingModule, importingModuleName, nil)
 		require.EqualError(t, err, "compilation failed: some compilation error")
 	})
 
@@ -291,7 +291,7 @@ func TestStore_Instantiate_Errors(t *testing.T) {
 		engine := s.Engine.(*mockEngine)
 		engine.callFailIndex = 1
 
-		_, err = s.Instantiate(testCtx, ns, m, importedModuleName, nil, nil)
+		_, err = s.Instantiate(testCtx, ns, m, importedModuleName, nil)
 		require.NoError(t, err)
 
 		hm := ns.modules[importedModuleName]
@@ -309,7 +309,7 @@ func TestStore_Instantiate_Errors(t *testing.T) {
 		}
 		importingModule.BuildFunctionDefinitions()
 
-		_, err = s.Instantiate(testCtx, ns, importingModule, importingModuleName, nil, nil)
+		_, err = s.Instantiate(testCtx, ns, importingModule, importingModuleName, nil)
 		require.EqualError(t, err, "start function[1] failed: call failed")
 	})
 }
@@ -321,7 +321,7 @@ func TestCallContext_ExportedFunction(t *testing.T) {
 	s, ns := newStore()
 
 	// Add the host module
-	imported, err := s.Instantiate(testCtx, ns, host, host.NameSection.ModuleName, nil, nil)
+	imported, err := s.Instantiate(testCtx, ns, host, host.NameSection.ModuleName, nil)
 	require.NoError(t, err)
 	defer imported.Close(testCtx)
 
@@ -331,7 +331,7 @@ func TestCallContext_ExportedFunction(t *testing.T) {
 			ImportSection: []*Import{{Type: ExternTypeFunc, Module: "host", Name: "host_fn", DescFunc: 0}},
 			MemorySection: &Memory{Min: 1, Cap: 1},
 			ExportSection: []*Export{{Type: ExternTypeFunc, Name: "host.fn", Index: 0}},
-		}, "test", nil, nil)
+		}, "test", nil)
 		require.NoError(t, err)
 		defer importing.Close(testCtx)