github.com/tetratelabs/wazero@v1.2.1/internal/emscripten/emscripten.go (about) 1 package emscripten 2 3 import ( 4 "context" 5 "strconv" 6 7 "github.com/tetratelabs/wazero/api" 8 "github.com/tetratelabs/wazero/internal/wasm" 9 ) 10 11 const FunctionNotifyMemoryGrowth = "emscripten_notify_memory_growth" 12 13 var NotifyMemoryGrowth = &wasm.HostFunc{ 14 ExportName: FunctionNotifyMemoryGrowth, 15 Name: FunctionNotifyMemoryGrowth, 16 ParamTypes: []wasm.ValueType{wasm.ValueTypeI32}, 17 ParamNames: []string{"memory_index"}, 18 Code: wasm.Code{GoFunc: api.GoModuleFunc(func(context.Context, api.Module, []uint64) {})}, 19 } 20 21 // InvokePrefix is the naming convention of Emscripten dynamic functions. 22 // 23 // All `invoke_` functions have an initial "index" parameter of 24 // api.ValueTypeI32. This is the index of the desired funcref in the only table 25 // in the module. The type of the funcref is via naming convention. The first 26 // character after `invoke_` decides the result type: 'v' for no result or 'i' 27 // for api.ValueTypeI32. Any count of 'i' following that are api.ValueTypeI32 28 // parameters. 29 // 30 // For example, the function `invoke_iiiii` signature has five parameters, but 31 // also five i's. The five 'i's mean there are four parameters 32 // 33 // (import "env" "invoke_iiiii" (func $invoke_iiiii 34 // (param i32 i32 i32 i32 i32) (result i32)))) 35 // 36 // So, the above function if invoked `invoke_iiiii(1234, 1, 2, 3, 4)` would 37 // look up the funcref at table index 1234, which has a type i32i32i3232_i32 38 // and invoke it with the remaining parameters. 39 const InvokePrefix = "invoke_" 40 41 func NewInvokeFunc(importName string, params, results []api.ValueType) *wasm.HostFunc { 42 // The type we invoke is the same type as the import except without the 43 // index parameter. 44 fn := &InvokeFunc{&wasm.FunctionType{Results: results}} 45 if len(params) > 1 { 46 fn.FunctionType.Params = params[1:] 47 } 48 49 // Now, make friendly parameter names. 50 paramNames := make([]string, len(params)) 51 paramNames[0] = "index" 52 for i := 1; i < len(paramNames); i++ { 53 paramNames[i] = "a" + strconv.Itoa(i) 54 } 55 return &wasm.HostFunc{ 56 ExportName: importName, 57 ParamTypes: params, 58 ParamNames: paramNames, 59 ResultTypes: results, 60 Code: wasm.Code{GoFunc: fn}, 61 } 62 } 63 64 type InvokeFunc struct { 65 *wasm.FunctionType 66 } 67 68 // Call implements api.GoModuleFunction by special casing dynamic calls needed 69 // for emscripten `invoke_` functions such as `invoke_ii` or `invoke_v`. 70 func (v *InvokeFunc) Call(ctx context.Context, mod api.Module, stack []uint64) { 71 m := mod.(*wasm.ModuleInstance) 72 73 // Lookup the type of the function we are calling indirectly. 74 typeID, err := m.GetFunctionTypeID(v.FunctionType) 75 if err != nil { 76 panic(err) 77 } 78 79 // This needs copy (not reslice) because the stack is reused for results. 80 // Consider invoke_i (zero arguments, one result): index zero (tableOffset) 81 // is needed to store the result. 82 tableOffset := wasm.Index(stack[0]) // position in the module's only table. 83 copy(stack, stack[1:]) // pop the tableOffset. 84 85 // Lookup the table index we will call. 86 t := m.Tables[0] // Note: Emscripten doesn't use multiple tables 87 f, err := m.Engine.LookupFunction(t, typeID, tableOffset) 88 if err != nil { 89 panic(err) 90 } 91 92 err = f.CallWithStack(ctx, stack) 93 if err != nil { 94 panic(err) 95 } 96 }