Adds ExportedFunctionDefinitions and ExportedMemoryDefinitions (#986)

This adds ExportedFunctionDefinitions and ExportedMemoryDefinitions to
api.Module so that those who can't access CompileModule can see them.

Fixes #839

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2022-12-31 13:11:37 +08:00
committed by GitHub
parent e7018d19ff
commit da3aa7a5ad
7 changed files with 85 additions and 24 deletions

View File

@@ -147,6 +147,10 @@ type Module interface {
// ExportedFunction returns a function exported from this module or nil if it wasn't.
ExportedFunction(name string) Function
// ExportedFunctionDefinitions returns all the exported function
// definitions in this module, keyed on export name.
ExportedFunctionDefinitions() map[string]FunctionDefinition
// TODO: Table
// ExportedMemory returns a memory exported from this module or nil if it wasn't.
@@ -157,6 +161,13 @@ type Module interface {
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/design/application-abi.md#current-unstable-abi
ExportedMemory(name string) Memory
// ExportedMemoryDefinitions returns all the exported memory definitions
// in this module, keyed on export name.
//
// Note: As of WebAssembly Core Specification 2.0, there can be at most one
// memory.
ExportedMemoryDefinitions() map[string]MemoryDefinition
// ExportedGlobal a global exported from this module or nil if it wasn't.
ExportedGlobal(name string) Global

View File

@@ -362,6 +362,7 @@ func addSpectestModule(t *testing.T, ctx context.Context, s *wasm.Store, ns *was
mod.ExportSection = append(mod.ExportSection, &wasm.Export{Name: "table", Index: 0, Type: wasm.ExternTypeTable})
maybeSetMemoryCap(mod)
mod.BuildMemoryDefinitions()
mod.BuildFunctionDefinitions()
err = mod.Validate(enabledFeatures)
@@ -432,6 +433,7 @@ func Run(t *testing.T, testDataFS embed.FS, ctx context.Context, newEngine func(
}
maybeSetMemoryCap(mod)
mod.BuildMemoryDefinitions()
mod.BuildFunctionDefinitions()
err = s.Engine.CompileModule(ctx, mod, nil)
require.NoError(t, err, msg)
@@ -603,6 +605,7 @@ func requireInstantiationError(t *testing.T, ctx context.Context, s *wasm.Store,
mod.AssignModuleID(buf)
maybeSetMemoryCap(mod)
mod.BuildMemoryDefinitions()
mod.BuildFunctionDefinitions()
err = s.Engine.CompileModule(ctx, mod, nil)
if err != nil {

View File

@@ -135,6 +135,21 @@ func (m *CallContext) ExportedMemory(name string) api.Memory {
return m.memory
}
// ExportedMemoryDefinitions implements the same method as documented on
// api.Module.
func (m *CallContext) ExportedMemoryDefinitions() map[string]api.MemoryDefinition {
// Special case as we currently only support one memory.
if mem := m.module.Memory; mem != nil {
// Now, find out if it is exported
for name, exp := range m.module.Exports {
if exp.Type == ExternTypeMemory {
return map[string]api.MemoryDefinition{name: mem.definition}
}
}
}
return map[string]api.MemoryDefinition{}
}
// ExportedFunction implements the same method as documented on api.Module.
func (m *CallContext) ExportedFunction(name string) api.Function {
exp, err := m.module.getExport(name, ExternTypeFunc)
@@ -145,6 +160,18 @@ func (m *CallContext) ExportedFunction(name string) api.Function {
return m.function(&m.module.Functions[exp.Index])
}
// ExportedFunctionDefinitions implements the same method as documented on
// api.Module.
func (m *CallContext) ExportedFunctionDefinitions() map[string]api.FunctionDefinition {
result := map[string]api.FunctionDefinition{}
for name, exp := range m.module.Exports {
if exp.Type == ExternTypeFunc {
result[name] = m.module.Functions[exp.Index].Definition
}
}
return result
}
// Module is exposed for emscripten.
func (m *CallContext) Module() *ModuleInstance {
return m.module

View File

@@ -657,6 +657,7 @@ func (m *Module) buildMemory() (mem *MemoryInstance) {
memSec := m.MemorySection
if memSec != nil {
mem = NewMemoryInstance(memSec)
mem.definition = m.MemoryDefinitionSection[0]
}
return
}

View File

@@ -842,10 +842,15 @@ func TestModule_buildMemoryInstance(t *testing.T) {
t.Run("non-nil", func(t *testing.T) {
min := uint32(1)
max := uint32(10)
m := Module{MemorySection: &Memory{Min: min, Cap: min, Max: max}}
mDef := &MemoryDefinition{moduleName: "foo"}
m := Module{
MemorySection: &Memory{Min: min, Cap: min, Max: max},
MemoryDefinitionSection: []*MemoryDefinition{mDef},
}
mem := m.buildMemory()
require.Equal(t, min, mem.Min)
require.Equal(t, max, mem.Max)
require.Equal(t, mDef, mem.definition)
})
}

View File

@@ -29,33 +29,35 @@ func TestModuleInstance_Memory(t *testing.T) {
input: &Module{},
},
{
name: "memory not exported",
input: &Module{MemorySection: &Memory{Min: 1, Cap: 1}},
},
{
name: "memory not exported, one page",
input: &Module{MemorySection: &Memory{Min: 1, Cap: 1}},
name: "memory not exported, one page",
input: &Module{
MemorySection: &Memory{Min: 1, Cap: 1},
MemoryDefinitionSection: []*MemoryDefinition{{}},
},
},
{
name: "memory exported, different name",
input: &Module{
MemorySection: &Memory{Min: 1, Cap: 1},
ExportSection: []*Export{{Type: ExternTypeMemory, Name: "momory", Index: 0}},
MemorySection: &Memory{Min: 1, Cap: 1},
MemoryDefinitionSection: []*MemoryDefinition{{}},
ExportSection: []*Export{{Type: ExternTypeMemory, Name: "momory", Index: 0}},
},
},
{
name: "memory exported, but zero length",
input: &Module{
MemorySection: &Memory{},
ExportSection: []*Export{{Type: ExternTypeMemory, Name: "memory", Index: 0}},
MemorySection: &Memory{},
MemoryDefinitionSection: []*MemoryDefinition{{}},
ExportSection: []*Export{{Type: ExternTypeMemory, Name: "memory", Index: 0}},
},
expected: true,
},
{
name: "memory exported, one page",
input: &Module{
MemorySection: &Memory{Min: 1, Cap: 1},
ExportSection: []*Export{{Type: ExternTypeMemory, Name: "memory", Index: 0}},
MemorySection: &Memory{Min: 1, Cap: 1},
MemoryDefinitionSection: []*MemoryDefinition{{}},
ExportSection: []*Export{{Type: ExternTypeMemory, Name: "memory", Index: 0}},
},
expected: true,
expectedLen: 65536,
@@ -63,8 +65,9 @@ func TestModuleInstance_Memory(t *testing.T) {
{
name: "memory exported, two pages",
input: &Module{
MemorySection: &Memory{Min: 2, Cap: 2},
ExportSection: []*Export{{Type: ExternTypeMemory, Name: "memory", Index: 0}},
MemorySection: &Memory{Min: 2, Cap: 2},
MemoryDefinitionSection: []*MemoryDefinition{{}},
ExportSection: []*Export{{Type: ExternTypeMemory, Name: "memory", Index: 0}},
},
expected: true,
expectedLen: 65536 * 2,
@@ -151,11 +154,12 @@ func TestStore_CloseWithExitCode(t *testing.T) {
require.NoError(t, err)
m2, err := s.Instantiate(testCtx, ns, &Module{
TypeSection: []*FunctionType{v_v},
ImportSection: []*Import{{Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0}},
MemorySection: &Memory{Min: 1, Cap: 1},
GlobalSection: []*Global{{Type: &GlobalType{}, Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1}}},
TableSection: []*Table{{Min: 10}},
TypeSection: []*FunctionType{v_v},
ImportSection: []*Import{{Type: ExternTypeFunc, Module: importedModuleName, Name: "fn", DescFunc: 0}},
MemorySection: &Memory{Min: 1, Cap: 1},
MemoryDefinitionSection: []*MemoryDefinition{{}},
GlobalSection: []*Global{{Type: &GlobalType{}, Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: const1}}},
TableSection: []*Table{{Min: 10}},
}, importingModuleName, nil)
require.NoError(t, err)
@@ -191,10 +195,11 @@ func TestStore_hammer(t *testing.T) {
require.True(t, ok)
importingModule := &Module{
TypeSection: []*FunctionType{v_v},
FunctionSection: []uint32{0},
CodeSection: []*Code{{Body: []byte{OpcodeEnd}}},
MemorySection: &Memory{Min: 1, Cap: 1},
TypeSection: []*FunctionType{v_v},
FunctionSection: []uint32{0},
CodeSection: []*Code{{Body: []byte{OpcodeEnd}}},
MemorySection: &Memory{Min: 1, Cap: 1},
MemoryDefinitionSection: []*MemoryDefinition{{}},
GlobalSection: []*Global{{
Type: &GlobalType{ValType: ValueTypeI32},
Init: &ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(1)},

View File

@@ -225,8 +225,13 @@ func TestModule_Memory(t *testing.T) {
mem := module.ExportedMemory("memory")
if tc.expected {
require.Equal(t, tc.expectedLen, mem.Size())
defs := module.ExportedMemoryDefinitions()
require.Equal(t, 1, len(defs))
def := defs["memory"]
require.Equal(t, tc.expectedLen>>16, def.Min())
} else {
require.Nil(t, mem)
require.Zero(t, len(module.ExportedMemoryDefinitions()))
}
})
}
@@ -525,7 +530,11 @@ func TestRuntime_CloseWithExitCode(t *testing.T) {
require.NoError(t, err)
func1 := m1.ExportedFunction("func")
require.Equal(t, map[string]api.FunctionDefinition{"func": func1.Definition()},
m1.ExportedFunctionDefinitions())
func2 := m2.ExportedFunction("func")
require.Equal(t, map[string]api.FunctionDefinition{"func": func2.Definition()},
m2.ExportedFunctionDefinitions())
// Modules not closed so calls succeed