Repository: tetratelabs/wazero
Pull request #1276 (2023-03-23 03:49:17)

Removes unused GoFunc types


Patch:
diff --git a/internal/wasm/binary/decoder.go b/internal/wasm/binary/decoder.go
index 45fe943e..4035ca7a 100644
--- a/internal/wasm/binary/decoder.go
+++ b/internal/wasm/binary/decoder.go
@@ -121,7 +121,7 @@ func DecodeModule(
 				return nil, err // avoid re-wrapping the error.
 			}
 		case wasm.SectionIDExport:
-			m.ExportSection, err = decodeExportSection(r)
+			m.ExportSection, m.Exports, err = decodeExportSection(r)
 		case wasm.SectionIDStart:
 			if m.StartSection != nil {
 				return nil, errors.New("multiple start sections are invalid")
diff --git a/internal/wasm/binary/section.go b/internal/wasm/binary/section.go
index b0fcf1e4..55e7ab3c 100644
--- a/internal/wasm/binary/section.go
+++ b/internal/wasm/binary/section.go
@@ -128,27 +128,27 @@ func decodeGlobalSection(r *bytes.Reader, enabledFeatures api.CoreFeatures) ([]w
 	return result, nil
 }
 
-func decodeExportSection(r *bytes.Reader) ([]wasm.Export, error) {
+func decodeExportSection(r *bytes.Reader) ([]wasm.Export, map[string]*wasm.Export, error) {
 	vs, _, sizeErr := leb128.DecodeUint32(r)
 	if sizeErr != nil {
-		return nil, fmt.Errorf("get size of vector: %v", sizeErr)
+		return nil, nil, fmt.Errorf("get size of vector: %v", sizeErr)
 	}
 
-	usedName := make(map[string]struct{}, vs)
+	exportMap := make(map[string]*wasm.Export, vs)
 	exportSection := make([]wasm.Export, vs)
 	for i := wasm.Index(0); i < vs; i++ {
 		export := &exportSection[i]
 		err := decodeExport(r, export)
 		if err != nil {
-			return nil, fmt.Errorf("read export: %w", err)
+			return nil, nil, fmt.Errorf("read export: %w", err)
 		}
-		if _, ok := usedName[export.Name]; ok {
-			return nil, fmt.Errorf("export[%d] duplicates name %q", i, export.Name)
+		if _, ok := exportMap[export.Name]; ok {
+			return nil, nil, fmt.Errorf("export[%d] duplicates name %q", i, export.Name)
 		} else {
-			usedName[export.Name] = struct{}{}
+			exportMap[export.Name] = export
 		}
 	}
-	return exportSection, nil
+	return exportSection, exportMap, nil
 }
 
 func decodeStartSection(r *bytes.Reader) (*wasm.Index, error) {
diff --git a/internal/wasm/host.go b/internal/wasm/host.go
index 59e51168..d30f218e 100644
--- a/internal/wasm/host.go
+++ b/internal/wasm/host.go
@@ -38,22 +38,6 @@ type HostFunc struct {
 	Code Code
 }
 
-// MustGoReflectFunc calls WithGoReflectFunc or panics on error.
-func (f *HostFunc) MustGoReflectFunc(fn interface{}) *HostFunc {
-	if ret, err := f.WithGoReflectFunc(fn); err != nil {
-		panic(err)
-	} else {
-		return ret
-	}
-}
-
-// WithGoFunc returns a copy of the function, replacing its Code.GoFunc.
-func (f *HostFunc) WithGoFunc(fn api.GoFunc) *HostFunc {
-	ret := *f
-	ret.Code.GoFunc = fn
-	return &ret
-}
-
 // WithGoModuleFunc returns a copy of the function, replacing its Code.GoFunc.
 func (f *HostFunc) WithGoModuleFunc(fn api.GoModuleFunc) *HostFunc {
 	ret := *f
@@ -61,14 +45,6 @@ func (f *HostFunc) WithGoModuleFunc(fn api.GoModuleFunc) *HostFunc {
 	return &ret
 }
 
-// WithGoReflectFunc returns a copy of the function, replacing its Code.GoFunc.
-func (f *HostFunc) WithGoReflectFunc(fn interface{}) (*HostFunc, error) {
-	ret := *f
-	var err error
-	ret.ParamTypes, ret.ResultTypes, ret.Code, err = parseGoReflectFunc(fn)
-	return &ret, err
-}
-
 type HostFuncNames struct {
 	Name        string
 	ParamNames  []string
@@ -90,6 +66,7 @@ func NewHostModule(
 
 	if exportCount := uint32(len(nameToGoFunc)); exportCount > 0 {
 		m.ExportSection = make([]Export, 0, exportCount)
+		m.Exports = make(map[string]*Export, exportCount)
 		if err = addFuncs(m, nameToGoFunc, funcToNames, enabledFeatures); err != nil {
 			return
 		}
@@ -185,6 +162,7 @@ func addFuncs(
 		m.CodeSection = append(m.CodeSection, hf.Code)
 		for _, export := range hf.ExportNames {
 			m.ExportSection = append(m.ExportSection, Export{Type: ExternTypeFunc, Name: export, Index: idx})
+			m.Exports[export] = &m.ExportSection[len(m.ExportSection)-1]
 		}
 		m.NameSection.FunctionNames = append(m.NameSection.FunctionNames, &NameAssoc{Index: idx, Name: hf.Name})
 
diff --git a/internal/wasm/module.go b/internal/wasm/module.go
index e8647f00..f3a265ca 100644
--- a/internal/wasm/module.go
+++ b/internal/wasm/module.go
@@ -108,6 +108,9 @@ type Module struct {
 	//
 	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#exports%E2%91%A0
 	ExportSection []Export
+	// Exports maps a name to Export, and is convenient for fast look up of exported instances at runtime.
+	// Each item of this map points to an element of ExportSection.
+	Exports map[string]*Export
 
 	// StartSection is the index of a function to call before returning from Store.Instantiate.
 	//
diff --git a/internal/wasm/store.go b/internal/wasm/store.go
index 2b49d565..2472af0e 100644
--- a/internal/wasm/store.go
+++ b/internal/wasm/store.go
@@ -57,7 +57,7 @@ type (
 	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-moduleinst
 	ModuleInstance struct {
 		Name      string
-		Exports   map[string]ExportInstance
+		Exports   map[string]*Export
 		Functions []FunctionInstance
 		Globals   []*GlobalInstance
 		// Memory is set when Module.MemorySection had a memory, regardless of whether it was exported.
@@ -90,16 +90,6 @@ type (
 	// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/runtime.html#data-instances
 	DataInstance = []byte
 
-	// ExportInstance represents an exported instance in a Store.
-	// The difference from the spec is that in wazero, a ExportInstance holds pointers
-	// to the instances, rather than "addresses" (i.e. index to Store.Functions, Globals, etc) for convenience.
-	//
-	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-exportinst
-	ExportInstance struct {
-		Type  ExternType
-		Index Index
-	}
-
 	// FunctionInstance represents a function instance in a Store.
 	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#function-instances%E2%91%A0
 	FunctionInstance struct {
@@ -195,14 +185,6 @@ func (m *ModuleInstance) applyElements(elems []validatedActiveElementSegment) {
 	}
 }
 
-func (m *ModuleInstance) BuildExports(exports []Export) {
-	m.Exports = make(map[string]ExportInstance, len(exports))
-	for _, exp := range exports {
-		// We already validated the duplicates during module validation phase.
-		m.Exports[exp.Name] = ExportInstance{Type: exp.Type, Index: exp.Index}
-	}
-}
-
 // validateData ensures that data segments are valid in terms of memory boundary.
 // Note: this is used only when bulk-memory/reference type feature is disabled.
 func (m *ModuleInstance) validateData(data []DataSegment) (err error) {
@@ -239,13 +221,13 @@ func (m *ModuleInstance) applyData(data []DataSegment) error {
 }
 
 // GetExport returns an export of the given name and type or errs if not exported or the wrong type.
-func (m *ModuleInstance) getExport(name string, et ExternType) (ExportInstance, error) {
+func (m *ModuleInstance) getExport(name string, et ExternType) (*Export, error) {
 	exp, ok := m.Exports[name]
 	if !ok {
-		return ExportInstance{}, fmt.Errorf("%q is not exported in module %q", name, m.Name)
+		return nil, fmt.Errorf("%q is not exported in module %q", name, m.Name)
 	}
 	if exp.Type != et {
-		return ExportInstance{}, fmt.Errorf("export %q in module %q is a %s, not a %s", name, m.Name, ExternTypeName(exp.Type), ExternTypeName(et))
+		return nil, fmt.Errorf("export %q in module %q is a %s, not a %s", name, m.Name, ExternTypeName(exp.Type), ExternTypeName(et))
 	}
 	return exp, nil
 }
@@ -347,7 +329,7 @@ func (s *Store) instantiate(
 
 	m.buildGlobals(module, m.Engine.FunctionInstanceReference)
 	m.buildMemory(module)
-	m.BuildExports(module.ExportSection)
+	m.Exports = module.Exports
 
 	// As of reference types proposal, data segment validation must happen after instantiation,
 	// and the side effect must persist even if there's out of bounds error after instantiation.
@@ -404,7 +386,7 @@ func (m *ModuleInstance) resolveImports(module *Module, importedModules map[stri
 			return
 		}
 
-		var imported ExportInstance
+		var imported *Export
 		imported, err = importedModule.getExport(i.Name, i.Type)
 		if err != nil {
 			return


Test patch:
diff --git a/builder_test.go b/builder_test.go
index ebcc3cfa..bd073c99 100644
--- a/builder_test.go
+++ b/builder_test.go
@@ -61,6 +61,9 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
 				ExportSection: []wasm.Export{
 					{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
 				},
+				Exports: map[string]*wasm.Export{
+					"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
+				},
 				NameSection: &wasm.NameSection{
 					FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}},
 					ModuleName:    "host",
@@ -84,6 +87,9 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
 				ExportSection: []wasm.Export{
 					{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
 				},
+				Exports: map[string]*wasm.Export{
+					"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
+				},
 				NameSection: &wasm.NameSection{
 					FunctionNames: wasm.NameMap{{Index: 0, Name: "get"}},
 					LocalNames:    []*wasm.NameMapAssoc{{Index: 0, NameMap: wasm.NameMap{{Index: 0, Name: "x"}}}},
@@ -108,6 +114,9 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
 				ExportSection: []wasm.Export{
 					{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
 				},
+				Exports: map[string]*wasm.Export{
+					"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
+				},
 				NameSection: &wasm.NameSection{
 					FunctionNames: wasm.NameMap{{Index: 0, Name: "get"}},
 					ResultNames:   []*wasm.NameMapAssoc{{Index: 0, NameMap: wasm.NameMap{{Index: 0, Name: "x"}}}},
@@ -131,6 +140,9 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
 				ExportSection: []wasm.Export{
 					{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
 				},
+				Exports: map[string]*wasm.Export{
+					"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
+				},
 				NameSection: &wasm.NameSection{
 					FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}},
 					ModuleName:    "host",
@@ -156,6 +168,10 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
 					{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
 					{Name: "2", Type: wasm.ExternTypeFunc, Index: 1},
 				},
+				Exports: map[string]*wasm.Export{
+					"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
+					"2": {Name: "2", Type: wasm.ExternTypeFunc, Index: 1},
+				},
 				NameSection: &wasm.NameSection{
 					FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}, {Index: 1, Name: "2"}},
 					ModuleName:    "host",
@@ -181,6 +197,9 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
 				ExportSection: []wasm.Export{
 					{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
 				},
+				Exports: map[string]*wasm.Export{
+					"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
+				},
 				NameSection: &wasm.NameSection{
 					FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}},
 					ModuleName:    "host",
@@ -206,6 +225,9 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
 				ExportSection: []wasm.Export{
 					{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
 				},
+				Exports: map[string]*wasm.Export{
+					"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
+				},
 				NameSection: &wasm.NameSection{
 					FunctionNames: wasm.NameMap{{Index: 0, Name: "get"}},
 					LocalNames:    []*wasm.NameMapAssoc{{Index: 0, NameMap: wasm.NameMap{{Index: 0, Name: "x"}}}},
@@ -235,6 +257,9 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
 				ExportSection: []wasm.Export{
 					{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
 				},
+				Exports: map[string]*wasm.Export{
+					"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
+				},
 				NameSection: &wasm.NameSection{
 					FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}},
 					ModuleName:    "host",
@@ -267,6 +292,10 @@ func TestNewHostModuleBuilder_Compile(t *testing.T) {
 					{Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
 					{Name: "2", Type: wasm.ExternTypeFunc, Index: 1},
 				},
+				Exports: map[string]*wasm.Export{
+					"1": {Name: "1", Type: wasm.ExternTypeFunc, Index: 0},
+					"2": {Name: "2", Type: wasm.ExternTypeFunc, Index: 1},
+				},
 				NameSection: &wasm.NameSection{
 					FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}, {Index: 1, Name: "2"}},
 					ModuleName:    "host",
@@ -378,6 +407,7 @@ func requireHostModuleEquals(t *testing.T, expected, actual *wasm.Module) {
 	require.Equal(t, expected.MemorySection, actual.MemorySection)
 	require.Equal(t, expected.GlobalSection, actual.GlobalSection)
 	require.Equal(t, expected.ExportSection, actual.ExportSection)
+	require.Equal(t, expected.Exports, actual.Exports)
 	require.Equal(t, expected.StartSection, actual.StartSection)
 	require.Equal(t, expected.ElementSection, actual.ElementSection)
 	require.Equal(t, expected.DataSection, actual.DataSection)
diff --git a/internal/engine/compiler/engine_test.go b/internal/engine/compiler/engine_test.go
index 3027ebb8..39421f7c 100644
--- a/internal/engine/compiler/engine_test.go
+++ b/internal/engine/compiler/engine_test.go
@@ -317,6 +317,10 @@ func TestCompiler_SliceAllocatedOnHeap(t *testing.T) {
 			{Type: wasm.ExternTypeFunc, Index: 1, Name: stackCorruption},
 			{Type: wasm.ExternTypeFunc, Index: 2, Name: callStackCorruption},
 		},
+		Exports: map[string]*wasm.Export{
+			stackCorruption:     {Type: wasm.ExternTypeFunc, Index: 1, Name: stackCorruption},
+			callStackCorruption: {Type: wasm.ExternTypeFunc, Index: 2, Name: callStackCorruption},
+		},
 		ID: wasm.ModuleID{1},
 	}
 	m.BuildFunctionDefinitions()
diff --git a/internal/integration_test/bench/hostfunc_bench_test.go b/internal/integration_test/bench/hostfunc_bench_test.go
index cb2ca8ef..4c6c76bc 100644
--- a/internal/integration_test/bench/hostfunc_bench_test.go
+++ b/internal/integration_test/bench/hostfunc_bench_test.go
@@ -162,6 +162,10 @@ func setupHostCallBench(requireNoError func(error)) *wasm.ModuleInstance {
 			{Name: "go", Type: wasm.ExternTypeFunc, Index: 0},
 			{Name: "go-reflect", Type: wasm.ExternTypeFunc, Index: 1},
 		},
+		Exports: map[string]*wasm.Export{
+			"go":         {Name: "go", Type: wasm.ExternTypeFunc, Index: 0},
+			"go-reflect": {Name: "go-reflect", Type: wasm.ExternTypeFunc, Index: 1},
+		},
 		ID: wasm.ModuleID{1, 2, 3, 4, 5},
 	}
 	hostModule.BuildFunctionDefinitions()
@@ -171,10 +175,9 @@ func setupHostCallBench(requireNoError func(error)) *wasm.ModuleInstance {
 		Functions: make([]wasm.FunctionInstance, len(hostModule.CodeSection)),
 	}
 	host.BuildFunctions(hostModule)
-	host.BuildExports(hostModule.ExportSection)
+	host.Exports = hostModule.Exports
 	goFn := &host.Functions[host.Exports["go"].Index]
 	goReflectFn := &host.Functions[host.Exports["go-reflect"].Index]
-	wasnFn := &host.Functions[host.Exports["wasm"].Index]
 
 	err := eng.CompileModule(testCtx, hostModule, nil, false)
 	requireNoError(err)
@@ -185,23 +188,25 @@ func setupHostCallBench(requireNoError func(error)) *wasm.ModuleInstance {
 
 	// Build the importing module.
 	importingModule := &wasm.Module{
-		ImportFunctionCount: 3,
+		ImportFunctionCount: 2,
 		TypeSection:         []wasm.FunctionType{ft},
 		ImportSection: []wasm.Import{
 			// Placeholders for imports from hostModule.
 			{Type: wasm.ExternTypeFunc},
 			{Type: wasm.ExternTypeFunc},
-			{Type: wasm.ExternTypeFunc},
 		},
-		FunctionSection: []wasm.Index{0, 0, 0},
+		FunctionSection: []wasm.Index{0, 0},
 		ExportSection: []wasm.Export{
-			{Name: callGoHostName, Type: wasm.ExternTypeFunc, Index: 3},
-			{Name: callGoReflectHostName, Type: wasm.ExternTypeFunc, Index: 4},
+			{Name: callGoHostName, Type: wasm.ExternTypeFunc, Index: 2},
+			{Name: callGoReflectHostName, Type: wasm.ExternTypeFunc, Index: 3},
+		},
+		Exports: map[string]*wasm.Export{
+			callGoHostName:        {Name: callGoHostName, Type: wasm.ExternTypeFunc, Index: 2},
+			callGoReflectHostName: {Name: callGoReflectHostName, Type: wasm.ExternTypeFunc, Index: 3},
 		},
 		CodeSection: []wasm.Code{
 			{Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeCall, 0, wasm.OpcodeEnd}}, // Calling the index 0 = host.go.
 			{Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeCall, 1, wasm.OpcodeEnd}}, // Calling the index 1 = host.go-reflect.
-			{Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeCall, 2, wasm.OpcodeEnd}}, // Calling the index 2 = host.wasm.
 		},
 		// Indicates that this module has a memory so that compilers are able to assemble memory-related initialization.
 		MemorySection: &wasm.Memory{Min: 1},
@@ -214,10 +219,10 @@ func setupHostCallBench(requireNoError func(error)) *wasm.ModuleInstance {
 
 	importing := &wasm.ModuleInstance{
 		TypeIDs:   []wasm.FunctionTypeID{0},
-		Functions: []wasm.FunctionInstance{*goFn, *goReflectFn, *wasnFn, {}, {}, {}},
+		Functions: []wasm.FunctionInstance{*goFn, *goReflectFn, {}, {}},
 	}
 	importing.BuildFunctions(importingModule)
-	importing.BuildExports(importingModule.ExportSection)
+	importing.Exports = importingModule.Exports
 
 	importingMe, err := eng.NewModuleEngine(importing.Name, importingModule, importing.Functions)
 	requireNoError(err)
diff --git a/internal/testing/enginetest/enginetest.go b/internal/testing/enginetest/enginetest.go
index fb4f7243..fe266be8 100644
--- a/internal/testing/enginetest/enginetest.go
+++ b/internal/testing/enginetest/enginetest.go
@@ -542,7 +542,7 @@ func RunTestModuleEngine_Memory(t *testing.T, et EngineTester) {
 
 	// To use functions, we need to instantiate them (associate them with a ModuleInstance).
 	module.BuildFunctions(m)
-	module.BuildExports(m.ExportSection)
+	module.Exports = exportMap(m)
 	grow, init := &module.Functions[0], &module.Functions[1]
 
 	// Compile the module
@@ -669,7 +669,7 @@ func setupCallTests(t *testing.T, e wasm.Engine, divBy *wasm.Code, fnlf experime
 		Functions: make([]wasm.FunctionInstance, len(hostModule.FunctionSection)),
 	}
 	host.BuildFunctions(hostModule)
-	host.BuildExports(hostModule.ExportSection)
+	host.Exports = exportMap(hostModule)
 	hostFn := &host.Functions[host.Exports[divByGoName].Index]
 
 	hostME, err := e.NewModuleEngine(host.Name, hostModule, host.Functions)
@@ -709,7 +709,7 @@ func setupCallTests(t *testing.T, e wasm.Engine, divBy *wasm.Code, fnlf experime
 		Functions: []wasm.FunctionInstance{*hostFn, {}, {}},
 	}
 	imported.BuildFunctions(importedModule)
-	imported.BuildExports(importedModule.ExportSection)
+	imported.Exports = exportMap(importedModule)
 	callHostFn := &imported.Functions[imported.Exports[callDivByGoName].Index]
 
 	// Compile the imported module
@@ -746,7 +746,7 @@ func setupCallTests(t *testing.T, e wasm.Engine, divBy *wasm.Code, fnlf experime
 		Functions: []wasm.FunctionInstance{*callHostFn, {}},
 	}
 	importing.BuildFunctions(importingModule)
-	importing.BuildExports(importingModule.ExportSection)
+	importing.Exports = exportMap(importingModule)
 
 	// Compile the importing module
 	importingMe, err := e.NewModuleEngine(importing.Name, importingModule, importing.Functions)
@@ -784,7 +784,7 @@ func setupCallMemTests(t *testing.T, e wasm.Engine, readMem *wasm.Code, fnlf exp
 		Functions: make([]wasm.FunctionInstance, len(hostModule.FunctionSection)),
 	}
 	host.BuildFunctions(hostModule)
-	host.BuildExports(hostModule.ExportSection)
+	host.Exports = exportMap(hostModule)
 	readMemFn := &host.Functions[host.Exports[readMemName].Index]
 
 	hostME, err := e.NewModuleEngine(host.Name, hostModule, host.Functions)
@@ -826,7 +826,7 @@ func setupCallMemTests(t *testing.T, e wasm.Engine, readMem *wasm.Code, fnlf exp
 	}
 	importing.BuildFunctions(importingModule)
 	// Note: adds imported functions readMemFn and callReadMemFn at index 0 and 1.
-	importing.BuildExports(importingModule.ExportSection)
+	importing.Exports = exportMap(importingModule)
 
 	// Compile the importing module
 	importingMe, err := e.NewModuleEngine(importing.Name, importingModule, importing.Functions)
@@ -863,3 +863,12 @@ func buildListeners(factory experimental.FunctionListenerFactory, m *wasm.Module
 	}
 	return listeners
 }
+
+func exportMap(m *wasm.Module) map[string]*wasm.Export {
+	ret := make(map[string]*wasm.Export, len(m.ExportSection))
+	for i := range m.ExportSection {
+		exp := &m.ExportSection[i]
+		ret[exp.Name] = exp
+	}
+	return ret
+}
diff --git a/internal/wasm/binary/section_test.go b/internal/wasm/binary/section_test.go
index 1c7f6c91..e64af77d 100644
--- a/internal/wasm/binary/section_test.go
+++ b/internal/wasm/binary/section_test.go
@@ -166,9 +166,16 @@ func TestDecodeExportSection(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			exports, err := decodeExportSection(bytes.NewReader(tc.input))
+			actual, actualExpMap, err := decodeExportSection(bytes.NewReader(tc.input))
 			require.NoError(t, err)
-			require.Equal(t, tc.expected, exports)
+			require.Equal(t, tc.expected, actual)
+
+			expMap := make(map[string]*wasm.Export, len(tc.expected))
+			for i := range tc.expected {
+				exp := &tc.expected[i]
+				expMap[exp.Name] = exp
+			}
+			require.Equal(t, expMap, actualExpMap)
 		})
 	}
 }
@@ -207,7 +214,7 @@ func TestDecodeExportSection_Errors(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			_, err := decodeExportSection(bytes.NewReader(tc.input))
+			_, _, err := decodeExportSection(bytes.NewReader(tc.input))
 			require.EqualError(t, err, tc.expectedErr)
 		})
 	}
diff --git a/internal/wasm/global_test.go b/internal/wasm/global_test.go
index 09b7ff67..7e677244 100644
--- a/internal/wasm/global_test.go
+++ b/internal/wasm/global_test.go
@@ -173,7 +173,7 @@ func TestPublicModule_Global(t *testing.T) {
 						Init: ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
 					},
 				},
-				ExportSection: []Export{{Type: ExternTypeGlobal, Name: "global"}},
+				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
 			},
 			expected: globalI32(1),
 		},
@@ -186,7 +186,7 @@ func TestPublicModule_Global(t *testing.T) {
 						Init: ConstantExpression{Opcode: OpcodeI64Const, Data: leb128.EncodeInt64(1)},
 					},
 				},
-				ExportSection: []Export{{Type: ExternTypeGlobal, Name: "global"}},
+				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
 			},
 			expected: globalI64(1),
 		},
@@ -202,7 +202,7 @@ func TestPublicModule_Global(t *testing.T) {
 						},
 					},
 				},
-				ExportSection: []Export{{Type: ExternTypeGlobal, Name: "global"}},
+				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
 			},
 			expected: globalF32(api.EncodeF32(1.0)),
 		},
@@ -218,7 +218,7 @@ func TestPublicModule_Global(t *testing.T) {
 						},
 					},
 				},
-				ExportSection: []Export{{Type: ExternTypeGlobal, Name: "global"}},
+				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
 			},
 			expected: globalF64(api.EncodeF64(1.0)),
 		},
@@ -231,7 +231,7 @@ func TestPublicModule_Global(t *testing.T) {
 						Init: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(1)},
 					},
 				},
-				ExportSection: []Export{{Type: ExternTypeGlobal, Name: "global"}},
+				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
 			},
 			expected: &mutableGlobal{
 				g: &GlobalInstance{Type: GlobalType{ValType: ValueTypeI32, Mutable: true}, Val: 1},
@@ -246,7 +246,7 @@ func TestPublicModule_Global(t *testing.T) {
 						Init: ConstantExpression{Opcode: OpcodeI64Const, Data: leb128.EncodeInt64(1)},
 					},
 				},
-				ExportSection: []Export{{Type: ExternTypeGlobal, Name: "global"}},
+				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
 			},
 			expected: &mutableGlobal{
 				g: &GlobalInstance{Type: GlobalType{ValType: ValueTypeI64, Mutable: true}, Val: 1},
@@ -264,7 +264,7 @@ func TestPublicModule_Global(t *testing.T) {
 						},
 					},
 				},
-				ExportSection: []Export{{Type: ExternTypeGlobal, Name: "global"}},
+				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
 			},
 			expected: &mutableGlobal{
 				g: &GlobalInstance{Type: GlobalType{ValType: ValueTypeF32, Mutable: true}, Val: api.EncodeF32(1.0)},
@@ -282,7 +282,7 @@ func TestPublicModule_Global(t *testing.T) {
 						},
 					},
 				},
-				ExportSection: []Export{{Type: ExternTypeGlobal, Name: "global"}},
+				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
 			},
 			expected: &mutableGlobal{
 				g: &GlobalInstance{Type: GlobalType{ValType: ValueTypeF64, Mutable: true}, Val: api.EncodeF64(1.0)},
diff --git a/internal/wasm/host_test.go b/internal/wasm/host_test.go
index 37b978ba..cde01651 100644
--- a/internal/wasm/host_test.go
+++ b/internal/wasm/host_test.go
@@ -69,6 +69,10 @@ func TestNewHostModule(t *testing.T) {
 					{Name: ArgsSizesGetName, Type: ExternTypeFunc, Index: 0},
 					{Name: FdWriteName, Type: ExternTypeFunc, Index: 1},
 				},
+				Exports: map[string]*Export{
+					ArgsSizesGetName: {Name: ArgsSizesGetName, Type: ExternTypeFunc, Index: 0},
+					FdWriteName:      {Name: FdWriteName, Type: ExternTypeFunc, Index: 1},
+				},
 				NameSection: &NameSection{
 					ModuleName: InternalModuleName,
 					FunctionNames: NameMap{
@@ -106,6 +110,7 @@ func TestNewHostModule(t *testing.T) {
 				FunctionSection: []Index{0},
 				CodeSection:     []Code{MustParseGoReflectFuncCode(swap)},
 				ExportSection:   []Export{{Name: "swap", Type: ExternTypeFunc, Index: 0}},
+				Exports:         map[string]*Export{"swap": {Name: "swap", Type: ExternTypeFunc, Index: 0}},
 				NameSection:     &NameSection{ModuleName: "swapper", FunctionNames: NameMap{{Index: 0, Name: "swap"}}},
 			},
 		},
diff --git a/internal/wasm/store_test.go b/internal/wasm/store_test.go
index d888300b..54957c65 100644
--- a/internal/wasm/store_test.go
+++ b/internal/wasm/store_test.go
@@ -48,7 +48,7 @@ func TestModuleInstance_Memory(t *testing.T) {
 			input: &Module{
 				MemorySection:           &Memory{},
 				MemoryDefinitionSection: []MemoryDefinition{{}},
-				ExportSection:           []Export{{Type: ExternTypeMemory, Name: "memory", Index: 0}},
+				Exports:                 map[string]*Export{"memory": {Type: ExternTypeMemory, Name: "memory"}},
 			},
 			expected: true,
 		},
@@ -57,7 +57,7 @@ func TestModuleInstance_Memory(t *testing.T) {
 			input: &Module{
 				MemorySection:           &Memory{Min: 1, Cap: 1},
 				MemoryDefinitionSection: []MemoryDefinition{{}},
-				ExportSection:           []Export{{Type: ExternTypeMemory, Name: "memory", Index: 0}},
+				Exports:                 map[string]*Export{"memory": {Type: ExternTypeMemory, Name: "memory"}},
 			},
 			expected:    true,
 			expectedLen: 65536,
@@ -67,7 +67,7 @@ func TestModuleInstance_Memory(t *testing.T) {
 			input: &Module{
 				MemorySection:           &Memory{Min: 2, Cap: 2},
 				MemoryDefinitionSection: []MemoryDefinition{{}},
-				ExportSection:           []Export{{Type: ExternTypeMemory, Name: "memory", Index: 0}},
+				Exports:                 map[string]*Export{"memory": {Type: ExternTypeMemory, Name: "memory"}},
 			},
 			expected:    true,
 			expectedLen: 65536 * 2,
@@ -148,7 +148,7 @@ func TestStore_CloseWithExitCode(t *testing.T) {
 				TypeSection:               []FunctionType{v_v},
 				FunctionSection:           []uint32{0},
 				CodeSection:               []Code{{Body: []byte{OpcodeEnd}}},
-				ExportSection:             []Export{{Type: ExternTypeFunc, Index: 0, Name: "fn"}},
+				Exports:                   map[string]*Export{"fn": {Type: ExternTypeFunc, Name: "fn"}},
 				FunctionDefinitionSection: []FunctionDefinition{{funcType: &v_v}},
 			}, importedModuleName, nil, []FunctionTypeID{0})
 			require.NoError(t, err)
@@ -651,7 +651,7 @@ func Test_resolveImports(t *testing.T) {
 	})
 	t.Run("export instance not found", func(t *testing.T) {
 		modules := map[string]*ModuleInstance{
-			moduleName: {Exports: map[string]ExportInstance{}, Name: moduleName},
+			moduleName: {Exports: map[string]*Export{}, Name: moduleName},
 		}
 		m := &ModuleInstance{}
 		err := m.resolveImports(&Module{ImportSection: []Import{{Module: moduleName, Name: "unknown"}}}, modules)
@@ -664,7 +664,7 @@ func Test_resolveImports(t *testing.T) {
 					{Definition: &FunctionDefinition{funcType: &FunctionType{Results: []ValueType{ValueTypeF32}}}},
 					{Definition: &FunctionDefinition{funcType: &FunctionType{Results: []ValueType{ValueTypeI32}}}},
 				},
-				Exports: map[string]ExportInstance{
+				Exports: map[string]*Export{
 					name: {Type: ExternTypeFunc, Index: 0},
 					"":   {Type: ExternTypeFunc, Index: 1},
 				},
@@ -690,7 +690,7 @@ func Test_resolveImports(t *testing.T) {
 		})
 		t.Run("type out of range", func(t *testing.T) {
 			modules := map[string]*ModuleInstance{
-				moduleName: {Exports: map[string]ExportInstance{name: {}}, Name: moduleName},
+				moduleName: {Exports: map[string]*Export{name: {}}, Name: moduleName},
 			}
 			m := &ModuleInstance{Functions: make([]FunctionInstance, 1)}
 			err := m.resolveImports(&Module{ImportSection: []Import{{Module: moduleName, Name: name, Type: ExternTypeFunc, DescFunc: 100}}}, modules)
@@ -699,7 +699,7 @@ func Test_resolveImports(t *testing.T) {
 		t.Run("signature mismatch", func(t *testing.T) {
 			externMod := &ModuleInstance{
 				Functions: []FunctionInstance{{Definition: &FunctionDefinition{funcType: &FunctionType{}}}},
-				Exports: map[string]ExportInstance{
+				Exports: map[string]*Export{
 					name: {Type: ExternTypeFunc, Index: 0},
 				},
 				Name: moduleName,
@@ -723,7 +723,7 @@ func Test_resolveImports(t *testing.T) {
 				map[string]*ModuleInstance{
 					moduleName: {
 						Globals: []*GlobalInstance{g},
-						Exports: map[string]ExportInstance{name: {Type: ExternTypeGlobal, Index: 0}}, Name: moduleName,
+						Exports: map[string]*Export{name: {Type: ExternTypeGlobal, Index: 0}}, Name: moduleName,
 					},
 				},
 			)
@@ -734,7 +734,7 @@ func Test_resolveImports(t *testing.T) {
 			importedModules := map[string]*ModuleInstance{
 				moduleName: {
 					Globals: []*GlobalInstance{{Type: GlobalType{Mutable: false}}},
-					Exports: map[string]ExportInstance{name: {
+					Exports: map[string]*Export{name: {
 						Type:  ExternTypeGlobal,
 						Index: 0,
 					}},
@@ -749,7 +749,7 @@ func Test_resolveImports(t *testing.T) {
 			importedModules := map[string]*ModuleInstance{
 				moduleName: {
 					Globals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}}},
-					Exports: map[string]ExportInstance{name: {
+					Exports: map[string]*Export{name: {
 						Type:  ExternTypeGlobal,
 						Index: 0,
 					}},
@@ -768,7 +768,7 @@ func Test_resolveImports(t *testing.T) {
 			importedModules := map[string]*ModuleInstance{
 				moduleName: {
 					Memory: memoryInst,
-					Exports: map[string]ExportInstance{name: {
+					Exports: map[string]*Export{name: {
 						Type: ExternTypeMemory,
 					}},
 					Name: moduleName,
@@ -784,7 +784,7 @@ func Test_resolveImports(t *testing.T) {
 			importedModules := map[string]*ModuleInstance{
 				moduleName: {
 					Memory: &MemoryInstance{Min: importMemoryType.Min - 1, Cap: 2},
-					Exports: map[string]ExportInstance{name: {
+					Exports: map[string]*Export{name: {
 						Type: ExternTypeMemory,
 					}},
 					Name: moduleName,
@@ -800,7 +800,7 @@ func Test_resolveImports(t *testing.T) {
 			modules := map[string]*ModuleInstance{
 				moduleName: {
 					Memory: &MemoryInstance{Max: MemoryLimitPages},
-					Exports: map[string]ExportInstance{name: {
+					Exports: map[string]*Export{name: {
 						Type: ExternTypeMemory,
 					}},
 					Name: moduleName,
diff --git a/internal/wasm/table_test.go b/internal/wasm/table_test.go
index 12c62c3c..68bf26b4 100644
--- a/internal/wasm/table_test.go
+++ b/internal/wasm/table_test.go
@@ -25,7 +25,7 @@ func Test_resolveImports_table(t *testing.T) {
 		importedModules := map[string]*ModuleInstance{
 			moduleName: {
 				Tables:  []*TableInstance{tableInst},
-				Exports: map[string]ExportInstance{name: {Type: ExternTypeTable, Index: 0}},
+				Exports: map[string]*Export{name: {Type: ExternTypeTable, Index: 0}},
 				Name:    moduleName,
 			},
 		}
@@ -39,7 +39,7 @@ func Test_resolveImports_table(t *testing.T) {
 		importedModules := map[string]*ModuleInstance{
 			moduleName: {
 				Tables:  []*TableInstance{{Min: importTableType.Min - 1}},
-				Exports: map[string]ExportInstance{name: {Type: ExternTypeTable}},
+				Exports: map[string]*Export{name: {Type: ExternTypeTable}},
 				Name:    moduleName,
 			},
 		}
@@ -53,7 +53,7 @@ func Test_resolveImports_table(t *testing.T) {
 		importedModules := map[string]*ModuleInstance{
 			moduleName: {
 				Tables:  []*TableInstance{{Min: importTableType.Min - 1}},
-				Exports: map[string]ExportInstance{name: {Type: ExternTypeTable}},
+				Exports: map[string]*Export{name: {Type: ExternTypeTable}},
 				Name:    moduleName,
 			},
 		}
diff --git a/runtime_test.go b/runtime_test.go
index 858d0baf..420cfce9 100644
--- a/runtime_test.go
+++ b/runtime_test.go
@@ -268,8 +268,8 @@ func TestModule_Global(t *testing.T) {
 						Init: wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeInt64(globalVal)},
 					},
 				},
-				ExportSection: []wasm.Export{
-					{Type: wasm.ExternTypeGlobal, Name: "global"},
+				Exports: map[string]*wasm.Export{
+					"global": {Type: wasm.ExternTypeGlobal, Name: "global"},
 				},
 			},
 			expected: true,
@@ -283,8 +283,8 @@ func TestModule_Global(t *testing.T) {
 						Init: wasm.ConstantExpression{Opcode: wasm.OpcodeI64Const, Data: leb128.EncodeInt64(globalVal)},
 					},
 				},
-				ExportSection: []wasm.Export{
-					{Type: wasm.ExternTypeGlobal, Name: "global"},
+				Exports: map[string]*wasm.Export{
+					"global": {Type: wasm.ExternTypeGlobal, Name: "global"},
 				},
 			},
 			expected:        true,