1 package wazero
2
3 import (
4 "context"
5 "fmt"
6 "sync/atomic"
7
8 "github.com/tetratelabs/wazero/api"
9 experimentalapi "github.com/tetratelabs/wazero/experimental"
10 internalclose "github.com/tetratelabs/wazero/internal/close"
11 internalsock "github.com/tetratelabs/wazero/internal/sock"
12 internalsys "github.com/tetratelabs/wazero/internal/sys"
13 "github.com/tetratelabs/wazero/internal/wasm"
14 binaryformat "github.com/tetratelabs/wazero/internal/wasm/binary"
15 "github.com/tetratelabs/wazero/sys"
16 )
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 type Runtime interface {
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 Instantiate(ctx context.Context, source []byte) (api.Module, error)
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 InstantiateWithConfig(ctx context.Context, source []byte, config ModuleConfig) (api.Module, error)
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 NewHostModuleBuilder(moduleName string) HostModuleBuilder
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 CompileModule(ctx context.Context, binary []byte) (CompiledModule, error)
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 InstantiateModule(ctx context.Context, compiled CompiledModule, config ModuleConfig) (api.Module, error)
122
123
124
125
126
127
128
129
130
131
132
133
134 CloseWithExitCode(ctx context.Context, exitCode uint32) error
135
136
137 Module(moduleName string) api.Module
138
139
140 api.Closer
141 }
142
143
144 func NewRuntime(ctx context.Context) Runtime {
145 return NewRuntimeWithConfig(ctx, NewRuntimeConfig())
146 }
147
148
149 func NewRuntimeWithConfig(ctx context.Context, rConfig RuntimeConfig) Runtime {
150 config := rConfig.(*runtimeConfig)
151 var engine wasm.Engine
152 var cacheImpl *cache
153 if c := config.cache; c != nil {
154
155 cacheImpl = c.(*cache)
156 engine = cacheImpl.initEngine(config.engineKind, config.newEngine, ctx, config.enabledFeatures)
157 } else {
158
159 engine = config.newEngine(ctx, config.enabledFeatures, nil)
160 }
161 store := wasm.NewStore(config.enabledFeatures, engine)
162 return &runtime{
163 cache: cacheImpl,
164 store: store,
165 enabledFeatures: config.enabledFeatures,
166 memoryLimitPages: config.memoryLimitPages,
167 memoryCapacityFromMax: config.memoryCapacityFromMax,
168 dwarfDisabled: config.dwarfDisabled,
169 storeCustomSections: config.storeCustomSections,
170 ensureTermination: config.ensureTermination,
171 }
172 }
173
174
175 type runtime struct {
176 store *wasm.Store
177 cache *cache
178 enabledFeatures api.CoreFeatures
179 memoryLimitPages uint32
180 memoryCapacityFromMax bool
181 dwarfDisabled bool
182 storeCustomSections bool
183
184
185
186
187
188
189
190 closed atomic.Uint64
191
192 ensureTermination bool
193 }
194
195
196 func (r *runtime) Module(moduleName string) api.Module {
197 if len(moduleName) == 0 {
198 return nil
199 }
200 return r.store.Module(moduleName)
201 }
202
203
204 func (r *runtime) CompileModule(ctx context.Context, binary []byte) (CompiledModule, error) {
205 if err := r.failIfClosed(); err != nil {
206 return nil, err
207 }
208
209 internal, err := binaryformat.DecodeModule(binary, r.enabledFeatures,
210 r.memoryLimitPages, r.memoryCapacityFromMax, !r.dwarfDisabled, r.storeCustomSections)
211 if err != nil {
212 return nil, err
213 } else if err = internal.Validate(r.enabledFeatures); err != nil {
214
215
216 return nil, err
217 }
218
219
220
221 internal.BuildMemoryDefinitions()
222
223 c := &compiledModule{module: internal, compiledEngine: r.store.Engine}
224
225
226 typeIDs, err := r.store.GetFunctionTypeIDs(internal.TypeSection)
227 if err != nil {
228 return nil, err
229 }
230 c.typeIDs = typeIDs
231
232 listeners, err := buildFunctionListeners(ctx, internal)
233 if err != nil {
234 return nil, err
235 }
236 internal.AssignModuleID(binary, listeners, r.ensureTermination)
237 if err = r.store.Engine.CompileModule(ctx, internal, listeners, r.ensureTermination); err != nil {
238 return nil, err
239 }
240 return c, nil
241 }
242
243 func buildFunctionListeners(ctx context.Context, internal *wasm.Module) ([]experimentalapi.FunctionListener, error) {
244
245 fnlf := ctx.Value(experimentalapi.FunctionListenerFactoryKey{})
246 if fnlf == nil {
247 return nil, nil
248 }
249 factory := fnlf.(experimentalapi.FunctionListenerFactory)
250 importCount := internal.ImportFunctionCount
251 listeners := make([]experimentalapi.FunctionListener, len(internal.FunctionSection))
252 for i := 0; i < len(listeners); i++ {
253 listeners[i] = factory.NewFunctionListener(internal.FunctionDefinition(uint32(i) + importCount))
254 }
255 return listeners, nil
256 }
257
258
259 func (r *runtime) failIfClosed() error {
260 if closed := r.closed.Load(); closed != 0 {
261 return fmt.Errorf("runtime closed with exit_code(%d)", uint32(closed>>32))
262 }
263 return nil
264 }
265
266
267 func (r *runtime) Instantiate(ctx context.Context, binary []byte) (api.Module, error) {
268 return r.InstantiateWithConfig(ctx, binary, NewModuleConfig())
269 }
270
271
272 func (r *runtime) InstantiateWithConfig(ctx context.Context, binary []byte, config ModuleConfig) (api.Module, error) {
273 if compiled, err := r.CompileModule(ctx, binary); err != nil {
274 return nil, err
275 } else {
276 compiled.(*compiledModule).closeWithModule = true
277 return r.InstantiateModule(ctx, compiled, config)
278 }
279 }
280
281
282 func (r *runtime) InstantiateModule(
283 ctx context.Context,
284 compiled CompiledModule,
285 mConfig ModuleConfig,
286 ) (mod api.Module, err error) {
287 if err = r.failIfClosed(); err != nil {
288 return nil, err
289 }
290
291 code := compiled.(*compiledModule)
292 config := mConfig.(*moduleConfig)
293
294
295 if !code.module.IsHostModule {
296 if sockConfig, ok := ctx.Value(internalsock.ConfigKey{}).(*internalsock.Config); ok {
297 config.sockConfig = sockConfig
298 }
299 }
300
301 var sysCtx *internalsys.Context
302 if sysCtx, err = config.toSysContext(); err != nil {
303 return
304 }
305
306 name := config.name
307 if !config.nameSet && code.module.NameSection != nil && code.module.NameSection.ModuleName != "" {
308 name = code.module.NameSection.ModuleName
309 }
310
311
312 mod, err = r.store.Instantiate(ctx, code.module, name, sysCtx, code.typeIDs)
313 if err != nil {
314
315 if code.closeWithModule {
316 _ = code.Close(ctx)
317 }
318 return
319 }
320
321 if closeNotifier, ok := ctx.Value(internalclose.NotifierKey{}).(internalclose.Notifier); ok {
322 mod.(*wasm.ModuleInstance).CloseNotifier = closeNotifier
323 }
324
325
326
327 if code.closeWithModule {
328 mod.(*wasm.ModuleInstance).CodeCloser = code
329 }
330
331
332 for _, fn := range config.startFunctions {
333 start := mod.ExportedFunction(fn)
334 if start == nil {
335 continue
336 }
337 if _, err = start.Call(ctx); err != nil {
338 _ = mod.Close(ctx)
339
340 if se, ok := err.(*sys.ExitError); ok {
341 if se.ExitCode() == 0 {
342 err = nil
343 }
344 return
345 }
346 err = fmt.Errorf("module[%s] function[%s] failed: %w", name, fn, err)
347 return
348 }
349 }
350 return
351 }
352
353
354 func (r *runtime) Close(ctx context.Context) error {
355 return r.CloseWithExitCode(ctx, 0)
356 }
357
358
359
360
361 func (r *runtime) CloseWithExitCode(ctx context.Context, exitCode uint32) error {
362 closed := uint64(1) + uint64(exitCode)<<32
363 if !r.closed.CompareAndSwap(0, closed) {
364 return nil
365 }
366 err := r.store.CloseWithExitCode(ctx, exitCode)
367 if r.cache == nil {
368
369 if errCloseEngine := r.store.Engine.Close(); errCloseEngine != nil {
370 return errCloseEngine
371 }
372 }
373 return err
374 }
375
View as plain text