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/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/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.go b/internal/wasm/host.go index 59e51168..7485b9d7 100644 --- a/internal/wasm/host.go +++ b/internal/wasm/host.go @@ -90,6 +90,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 +186,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/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/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 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,