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 }