1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package wasm
16
17 import (
18 "context"
19 "fmt"
20 "os"
21 "sync"
22
23 "github.com/tetratelabs/wazero"
24 "github.com/tetratelabs/wazero/api"
25 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
26 )
27
28
29
30 type runtime struct {
31
32
33 ctx context.Context
34
35 wazero.Runtime
36 }
37
38 func newRuntime() runtime {
39 ctx := context.Background()
40 r := wazero.NewRuntime(ctx)
41 wasi_snapshot_preview1.MustInstantiate(ctx, r)
42
43 return runtime{
44 ctx: ctx,
45 Runtime: r,
46 }
47 }
48
49
50
51 func (r *runtime) compile(name string) (*module, error) {
52 buf, err := os.ReadFile(name)
53 if err != nil {
54 return nil, fmt.Errorf("can't compile Wasm module: %w", err)
55 }
56
57 mod, err := r.Runtime.CompileModule(r.ctx, buf)
58 if err != nil {
59 return nil, fmt.Errorf("can't compile Wasm module: %w", err)
60 }
61 return &module{
62 runtime: r,
63 name: name,
64 CompiledModule: mod,
65 }, nil
66 }
67
68
69
70 func (r *runtime) compileAndLoad(name string) (*instance, error) {
71 m, err := r.compile(name)
72 if err != nil {
73 return nil, err
74 }
75 i, err := m.load()
76 if err != nil {
77 return nil, err
78 }
79 return i, nil
80 }
81
82
83 type module struct {
84 *runtime
85 name string
86 wazero.CompiledModule
87 }
88
89
90
91
92 func (m *module) load() (*instance, error) {
93 cfg := wazero.NewModuleConfig().WithName(m.name)
94 wInst, err := m.Runtime.InstantiateModule(m.ctx, m.CompiledModule, cfg)
95 if err != nil {
96 return nil, fmt.Errorf("can't instantiate Wasm module: %w", err)
97 }
98
99 inst := instance{
100 module: m,
101 instance: wInst,
102 alloc: wInst.ExportedFunction("allocate"),
103 free: wInst.ExportedFunction("deallocate"),
104 }
105 return &inst, nil
106 }
107
108
109 type instance struct {
110
111 mu sync.Mutex
112
113 *module
114 instance api.Module
115
116
117
118 alloc api.Function
119
120
121
122 free api.Function
123 }
124
125
126
127 func (i *instance) load(funcName string) (api.Function, error) {
128 i.mu.Lock()
129 defer i.mu.Unlock()
130
131 f := i.instance.ExportedFunction(funcName)
132 if f == nil {
133 return nil, fmt.Errorf("can't find function %q in Wasm module %v", funcName, i.module.Name())
134 }
135 return f, nil
136 }
137
138
139
140 func (i *instance) Alloc(size uint32) (*memory, error) {
141 i.mu.Lock()
142 defer i.mu.Unlock()
143
144 res, err := i.alloc.Call(i.ctx, uint64(size))
145 if err != nil {
146 return nil, fmt.Errorf("can't allocate memory: requested %d bytes", size)
147 }
148 return &memory{
149 i: i,
150 ptr: uint32(res[0]),
151 len: size,
152 }, nil
153 }
154
155
156 func (i *instance) Free(m *memory) {
157 i.mu.Lock()
158 defer i.mu.Unlock()
159
160 i.free.Call(i.ctx, uint64(m.ptr), uint64(m.len))
161 }
162
163
164 func (i *instance) FreeAll(ms []*memory) {
165 i.mu.Lock()
166 defer i.mu.Unlock()
167
168 for _, m := range ms {
169 i.free.Call(i.ctx, uint64(m.ptr), uint64(m.len))
170 }
171 }
172
173
174
175 type memory struct {
176 i *instance
177 ptr uint32
178 len uint32
179 }
180
181
182 func (m *memory) Bytes() []byte {
183 m.i.mu.Lock()
184 defer m.i.mu.Unlock()
185
186 bytes, ok := m.i.instance.Memory().Read(m.ptr, m.len)
187 if !ok {
188 panic(fmt.Sprintf("can't read %d bytes from Wasm address %#x", m.len, m.ptr))
189 }
190 return append([]byte{}, bytes...)
191 }
192
193
194
195 func (m *memory) WriteAt(p []byte, off int64) (int, error) {
196 if (off < 0) || (off >= 1<<32-1) {
197 panic(fmt.Sprintf("can't write %d bytes to Wasm address %#x", len(p), m.ptr))
198 }
199
200 m.i.mu.Lock()
201 defer m.i.mu.Unlock()
202
203 ok := m.i.instance.Memory().Write(m.ptr+uint32(off), p)
204 if !ok {
205 panic(fmt.Sprintf("can't write %d bytes to Wasm address %#x", len(p), m.ptr))
206 }
207 return len(p), nil
208 }
209
210
211
212 func (m *memory) Args() []uint64 {
213 return []uint64{uint64(m.ptr), uint64(m.len)}
214 }
215
View as plain text