github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/wasm/module_instance_lookup.go (about)

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