-
Notifications
You must be signed in to change notification settings - Fork 2
/
guest_function_wazero.go
112 lines (93 loc) · 3.79 KB
/
guest_function_wazero.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package wasify
import (
"context"
"errors"
"fmt"
"github.com/tetratelabs/wazero/api"
"github.com/wasify-io/wasify-go/internal/types"
"github.com/wasify-io/wasify-go/internal/utils"
)
type wazeroGuestFunction struct {
ctx context.Context
fn api.Function
name string
memory Memory
moduleConfig *ModuleConfig
}
// call invokes wazero's CallWithStack method, which returns ome uint64 message,
// in most cases it is used to call built in methods such as "malloc", "free"
// See wazero's CallWithStack for more details.
func (gf *wazeroGuestFunction) call(params ...uint64) (uint64, error) {
// size of params len(params) + one size for return uint64 value
stack := make([]uint64, len(params)+1)
copy(stack, params)
err := gf.fn.CallWithStack(gf.ctx, stack[:])
if err != nil {
err = errors.Join(errors.New("error invoking internal call func"), err)
gf.moduleConfig.log.Error(err.Error())
return 0, err
}
return stack[0], nil
}
// Invoke calls a specified guest function with the provided parameters. It ensures proper memory management,
// data conversion, and compatibility with data types. Each parameter is converted to its packedData format,
// which provides a compact representation of its memory offset, size, and type information. This packedData
// is written into the WebAssembly memory, allowing the guest function to correctly interpret and use the data.
//
// While the method takes care of memory allocation for the parameters and writing them to memory, it does
// not handle freeing the allocated memory. If an error occurs at any step, from data conversion to memory
// allocation, or during the guest function invocation, the error is logged, and the function returns with an error.
//
// Example:
//
// res, err := module.GuestFunction(ctx, "guestTest").Invoke([]byte("bytes!"), uint32(32), float32(32.0), "Wasify")
//
// params ...any: A variadic list of parameters of any type that the user wants to pass to the guest function.
//
// Return value: The result of invoking the guest function in the form of a GuestFunctionResult pointer,
// or an error if any step in the process fails.
func (gf *wazeroGuestFunction) Invoke(params ...any) (*GuestFunctionResult, error) {
var err error
log := gf.moduleConfig.log.Info
if gf.moduleConfig.Namespace == "malloc" || gf.moduleConfig.Namespace == "free" {
log = gf.moduleConfig.log.Debug
}
log("calling guest function", "namespace", gf.moduleConfig.Namespace, "function", gf.name, "params", params)
stack := make([]uint64, len(params))
for i, p := range params {
valueType, offsetSize, err := types.GetOffsetSizeAndDataTypeByConversion(p)
if err != nil {
err = errors.Join(fmt.Errorf("Can't convert guest func param %s", gf.name), err)
return nil, err
}
// allocate memory for each value
offsetI32, err := gf.memory.Malloc(offsetSize)
if err != nil {
err = errors.Join(fmt.Errorf("An error occurred while attempting to alloc memory for guest func param in: %s", gf.name), err)
gf.moduleConfig.log.Error(err.Error())
return nil, err
}
err = gf.memory.WriteAny(offsetI32, p)
if err != nil {
err = errors.Join(errors.New("Can't write arg to"), err)
return nil, err
}
stack[i], err = utils.PackUI64(valueType, offsetI32, offsetSize)
if err != nil {
err = errors.Join(fmt.Errorf("An error occurred while attempting to pack data for guest func param in: %s", gf.name), err)
gf.moduleConfig.log.Error(err.Error())
return nil, err
}
}
multiPackedData, err := gf.call(stack...)
if err != nil {
err = errors.Join(fmt.Errorf("An error occurred while attempting to invoke the guest function: %s", gf.name), err)
gf.moduleConfig.log.Error(err.Error())
return nil, err
}
res := &GuestFunctionResult{
multiPackedData: multiPackedData,
memory: gf.memory,
}
return res, err
}