From a0478f0c5cee19dea42e8ec81c62a37a09cdd7e3 Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Tue, 12 Jul 2022 12:21:56 +0900 Subject: [PATCH] Adds ExportedFunctions API on CompiledModule. (#681) Signed-off-by: Takeshi Yoneda --- api/wasm.go | 23 +++++- config.go | 8 ++ internal/wasm/module_exports.go | 41 ++++++++++ internal/wasm/module_exports_test.go | 113 +++++++++++++++++++++++++++ 4 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 internal/wasm/module_exports.go create mode 100644 internal/wasm/module_exports_test.go diff --git a/api/wasm.go b/api/wasm.go index 118f3c1d..87c12b75 100644 --- a/api/wasm.go +++ b/api/wasm.go @@ -173,7 +173,28 @@ type Closer interface { Close(context.Context) error } -// Function is a WebAssembly 1.0 (20191205) function exported from an instantiated module (wazero.Runtime InstantiateModule). +// ExportedFunction is a WebAssembly function exported in a module (wazero.CompiledModule). +// +// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#exports%E2%91%A0 +type ExportedFunction interface { + // Name returns the name of this exported function which is unique across a module. + // Note: the empty name is allowed in the WebAssembly specification so returned "" is meaningful. + Name() string + + // ParamTypes are the possibly empty sequence of value types accepted by a function with this signature. + // + // See ValueType documentation for encoding rules. + ParamTypes() []ValueType + + // ResultTypes are the possibly empty sequence of value types returned by a function with this signature. + // + // When WebAssembly 1.0 (20191205), there can be at most one result: https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#result-types%E2%91%A0 + // + // See ValueType documentation for decoding rules. + ResultTypes() []ValueType +} + +// Function is a WebAssembly function exported from an instantiated module (wazero.Runtime InstantiateModule). // // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-func type Function interface { diff --git a/config.go b/config.go index 9b9ef31e..fdfa384b 100644 --- a/config.go +++ b/config.go @@ -268,6 +268,9 @@ type CompiledModule interface { // // Note: It is safe to call Close while having outstanding calls from an api.Module instantiated from this. Close(context.Context) error + + // ExportedFunctions returns all the exported functions (api.ExportedFunction) in this module. + ExportedFunctions() []api.ExportedFunction } type compiledModule struct { @@ -288,6 +291,11 @@ func (c *compiledModule) Close(_ context.Context) error { return nil } +// ExportedFunctions implements CompiledModule.ExportedFunctions +func (c *compiledModule) ExportedFunctions() []api.ExportedFunction { + return c.module.ExportedFunctions() +} + // CompileConfig allows you to override what was decoded from wasm, prior to compilation (ModuleBuilder.Compile or // Runtime.CompileModule). // diff --git a/internal/wasm/module_exports.go b/internal/wasm/module_exports.go new file mode 100644 index 00000000..352b057c --- /dev/null +++ b/internal/wasm/module_exports.go @@ -0,0 +1,41 @@ +package wasm + +import "github.com/tetratelabs/wazero/api" + +// ExportedFunctions returns the implementations of api.ExportedFunction. +func (m *Module) ExportedFunctions() (ret []api.ExportedFunction) { + for _, exp := range m.ExportSection { + if exp.Type == ExternTypeFunc { + tp := m.TypeOfFunction(exp.Index) + ret = append(ret, &exportedFunction{ + exportedName: exp.Name, + params: tp.Params, + results: tp.Results, + }) + } + } + return +} + +// exportedFunction implements api.ExportedFunction +type exportedFunction struct { + // exportedName is the name of export entry of this function, + // which might differ from the one in the name custom section etc. + exportedName string + params, results []ValueType +} + +// Name implements api.ExportedFunction Name. +func (e *exportedFunction) Name() string { + return e.exportedName +} + +// ParamTypes implements api.ExportedFunction ParamTypes. +func (e *exportedFunction) ParamTypes() []ValueType { + return e.params +} + +// ResultTypes implements api.ExportedFunction ResultTypes. +func (e *exportedFunction) ResultTypes() []ValueType { + return e.results +} diff --git a/internal/wasm/module_exports_test.go b/internal/wasm/module_exports_test.go new file mode 100644 index 00000000..71f3d52c --- /dev/null +++ b/internal/wasm/module_exports_test.go @@ -0,0 +1,113 @@ +package wasm + +import ( + "testing" + + "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/internal/testing/require" +) + +func TestModule_ExportedFunctions(t *testing.T) { + tests := []struct { + name string + m *Module + exp []api.ExportedFunction + }{ + { + name: "no exports", + m: &Module{}, + }, + { + name: "no functions", + m: &Module{ + ExportSection: []*Export{{Type: ExternTypeGlobal, Index: 0}}, + GlobalSection: []*Global{{}}, + }, + }, + { + name: "without imports", + m: &Module{ + ExportSection: []*Export{ + {Name: "function_index=0", Type: ExternTypeFunc, Index: 0}, + {Name: "function_index=2", Type: ExternTypeFunc, Index: 2}, + {Name: "", Type: ExternTypeGlobal, Index: 0}, + {Name: "function_index=1", Type: ExternTypeFunc, Index: 1}, + }, + GlobalSection: []*Global{{}}, + FunctionSection: []Index{1, 2, 0}, + TypeSection: []*FunctionType{ + {Params: []ValueType{}, Results: []ValueType{}}, + {Params: []ValueType{ValueTypeF64, ValueTypeI32}, Results: []ValueType{ValueTypeV128, ValueTypeI64}}, + {Params: []ValueType{ValueTypeF64, ValueTypeF32}, Results: []ValueType{ValueTypeI64}}, + }, + }, + exp: []api.ExportedFunction{ + &exportedFunction{ + exportedName: "function_index=0", + params: []ValueType{ValueTypeF64, ValueTypeI32}, + results: []ValueType{ValueTypeV128, ValueTypeI64}, + }, + &exportedFunction{ + exportedName: "function_index=2", params: []ValueType{}, results: []ValueType{}, + }, + &exportedFunction{ + exportedName: "function_index=1", + params: []ValueType{ValueTypeF64, ValueTypeF32}, + results: []ValueType{ValueTypeI64}, + }, + }, + }, + { + name: "with imports", + m: &Module{ + ImportSection: []*Import{{ + Type: ExternTypeFunc, + DescFunc: 2, // Index of type. + }}, + ExportSection: []*Export{ + {Name: "imported_function", Type: ExternTypeFunc, Index: 0}, + {Name: "function_index=1", Type: ExternTypeFunc, Index: 1}, + {Name: "function_index=2", Type: ExternTypeFunc, Index: 2}, + }, + FunctionSection: []Index{1, 0}, + TypeSection: []*FunctionType{ + {Params: []ValueType{}, Results: []ValueType{}}, + {Params: []ValueType{ValueTypeF64, ValueTypeI32}, Results: []ValueType{ValueTypeV128, ValueTypeI64}}, + {Params: []ValueType{ValueTypeF64, ValueTypeF32}, Results: []ValueType{ValueTypeI64}}, + }, + }, + exp: []api.ExportedFunction{ + &exportedFunction{ + exportedName: "imported_function", + params: []ValueType{ValueTypeF64, ValueTypeF32}, + results: []ValueType{ValueTypeI64}, + }, + &exportedFunction{ + exportedName: "function_index=1", + params: []ValueType{ValueTypeF64, ValueTypeI32}, + results: []ValueType{ValueTypeV128, ValueTypeI64}, + }, + &exportedFunction{ + exportedName: "function_index=2", + params: []ValueType{}, + results: []ValueType{}, + }, + }, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + actual := tc.m.ExportedFunctions() + require.Equal(t, tc.exp, actual) + }) + } +} + +func TestExportedFunction(t *testing.T) { + f := &exportedFunction{exportedName: "abc", params: []ValueType{ValueTypeI32}, results: []ValueType{ValueTypeV128}} + require.Equal(t, "abc", f.exportedName) + require.Equal(t, []ValueType{ValueTypeI32}, f.ParamTypes()) + require.Equal(t, []ValueType{ValueTypeV128}, f.ResultTypes()) +}