74 lines
2.8 KiB
Go
74 lines
2.8 KiB
Go
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
|
|
}
|