1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
package wasm
import (
"context"
"fmt"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/internal/internalapi"
)
// LookupFunction looks up the table by the given index, and returns the api.Function implementation if found,
// otherwise this panics according to the same semantics as call_indirect instruction.
// Currently, this is only used by emscripten which needs to do call_indirect-like operation in the host function.
func (m *ModuleInstance) LookupFunction(t *TableInstance, typeId FunctionTypeID, tableOffset Index) api.Function {
fm, index := m.Engine.LookupFunction(t, typeId, tableOffset)
if source := fm.Source; source.IsHostModule {
// This case, the found function is a host function stored in the table. Generally, Engine.NewFunction are only
// responsible for calling Wasm-defined functions (not designed for calling Go functions!). Hence we need to wrap
// the host function as a special case.
def := &source.FunctionDefinitionSection[index]
goF := source.CodeSection[index].GoFunc
switch typed := goF.(type) {
case api.GoFunction:
// GoFunction doesn't need looked up module.
return &lookedUpGoFunction{def: def, g: goFunctionAsGoModuleFunction(typed)}
case api.GoModuleFunction:
return &lookedUpGoFunction{def: def, lookedUpModule: m, g: typed}
default:
panic(fmt.Sprintf("unexpected GoFunc type: %T", goF))
}
} else {
return fm.Engine.NewFunction(index)
}
}
// lookedUpGoFunction implements lookedUpGoModuleFunction.
type lookedUpGoFunction struct {
internalapi.WazeroOnly
def *FunctionDefinition
// lookedUpModule is the *ModuleInstance from which this Go function is looked up, i.e. owner of the table.
lookedUpModule *ModuleInstance
g api.GoModuleFunction
}
// goFunctionAsGoModuleFunction converts api.GoFunction to api.GoModuleFunction which ignores the api.Module argument.
func goFunctionAsGoModuleFunction(g api.GoFunction) api.GoModuleFunction {
return api.GoModuleFunc(func(ctx context.Context, _ api.Module, stack []uint64) {
g.Call(ctx, stack)
})
}
// Definition implements api.Function.
func (l *lookedUpGoFunction) Definition() api.FunctionDefinition { return l.def }
// Call implements api.Function.
func (l *lookedUpGoFunction) Call(ctx context.Context, params ...uint64) ([]uint64, error) {
typ := l.def.Functype
stackSize := typ.ParamNumInUint64
rn := typ.ResultNumInUint64
if rn > stackSize {
stackSize = rn
}
stack := make([]uint64, stackSize)
copy(stack, params)
return stack[:rn], l.CallWithStack(ctx, stack)
}
// CallWithStack implements api.Function.
func (l *lookedUpGoFunction) CallWithStack(ctx context.Context, stack []uint64) error {
// The Go host function always needs to access caller's module, in this case the one holding the table.
l.g.Call(ctx, l.lookedUpModule, stack)
return nil
}
|