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  }