1package wasm
2
3import (
4 "context"
5 "encoding/binary"
6 "errors"
7 "fmt"
8 "sync"
9 "sync/atomic"
10
11 "github.com/tetratelabs/wazero/api"
12 "github.com/tetratelabs/wazero/experimental"
13 "github.com/tetratelabs/wazero/internal/expctxkeys"
14 "github.com/tetratelabs/wazero/internal/internalapi"
15 "github.com/tetratelabs/wazero/internal/leb128"
16 internalsys "github.com/tetratelabs/wazero/internal/sys"
17 "github.com/tetratelabs/wazero/sys"
18)
19
20// nameToModuleShrinkThreshold is the size the nameToModule map can grow to
21// before it starts to be monitored for shrinking.
22// The capacity will never be smaller than this once the threshold is met.
23const nameToModuleShrinkThreshold = 100
24
25type (
26 // Store is the runtime representation of "instantiated" Wasm module and objects.
27 // Multiple modules can be instantiated within a single store, and each instance,
28 // (e.g. function instance) can be referenced by other module instances in a Store via Module.ImportSection.
29 //
30 // Every type whose name ends with "Instance" suffix belongs to exactly one store.
31 //
32 // Note that store is not thread (concurrency) safe, meaning that using single Store
33 // via multiple goroutines might result in race conditions. In that case, the invocation
34 // and access to any methods and field of Store must be guarded by mutex.
35 //
36 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#store%E2%91%A0
37 Store struct {
38 // moduleList ensures modules are closed in reverse initialization order.
39 moduleList *ModuleInstance // guarded by mux
40
41 // nameToModule holds the instantiated Wasm modules by module name from Instantiate.
42 // It ensures no race conditions instantiating two modules of the same name.
43 nameToModule map[string]*ModuleInstance // guarded by mux
44
45 // nameToModuleCap tracks the growth of the nameToModule map in order to
46 // track when to shrink it.
47 nameToModuleCap int // guarded by mux
48
49 // EnabledFeatures are read-only to allow optimizations.
50 EnabledFeatures api.CoreFeatures
51
52 // Engine is a global context for a Store which is in responsible for compilation and execution of Wasm modules.
53 Engine Engine
54
55 // typeIDs maps each FunctionType.String() to a unique FunctionTypeID. This is used at runtime to
56 // do type-checks on indirect function calls.
57 typeIDs map[string]FunctionTypeID
58
59 // functionMaxTypes represents the limit on the number of function types in a store.
60 // Note: this is fixed to 2^27 but have this a field for testability.
61 functionMaxTypes uint32
62
63 // mux is used to guard the fields from concurrent access.
64 mux sync.RWMutex
65 }
66
67 // ModuleInstance represents instantiated wasm module.
68 // The difference from the spec is that in wazero, a ModuleInstance holds pointers
69 // to the instances, rather than "addresses" (i.e. index to Store.Functions, Globals, etc) for convenience.
70 //
71 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-moduleinst
72 //
73 // This implements api.Module.
74 ModuleInstance struct {
75 internalapi.WazeroOnlyType
76
77 ModuleName string
78 Exports map[string]*Export
79 Globals []*GlobalInstance
80 MemoryInstance *MemoryInstance
81 Tables []*TableInstance
82
83 // Engine implements function calls for this module.
84 Engine ModuleEngine
85
86 // TypeIDs is index-correlated with types and holds typeIDs which is uniquely assigned to a type by store.
87 // This is necessary to achieve fast runtime type checking for indirect function calls at runtime.
88 TypeIDs []FunctionTypeID
89
90 // DataInstances holds data segments bytes of the module.
91 // This is only used by bulk memory operations.
92 //
93 // https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/runtime.html#data-instances
94 DataInstances []DataInstance
95
96 // ElementInstances holds the element instance, and each holds the references to either functions
97 // or external objects (unimplemented).
98 ElementInstances []ElementInstance
99
100 // Sys is exposed for use in special imports such as WASI, assemblyscript.
101 //
102 // # Notes
103 //
104 // - This is a part of ModuleInstance so that scope and Close is coherent.
105 // - This is not exposed outside this repository (as a host function
106 // parameter) because we haven't thought through capabilities based
107 // security implications.
108 Sys *internalsys.Context
109
110 // Closed is used both to guard moduleEngine.CloseWithExitCode and to store the exit code.
111 //
112 // The update value is closedType + exitCode << 32. This ensures an exit code of zero isn't mistaken for never closed.
113 //
114 // Note: Exclusively reading and updating this with atomics guarantees cross-goroutine observations.
115 // See /RATIONALE.md
116 Closed atomic.Uint64
117
118 // CodeCloser is non-nil when the code should be closed after this module.
119 CodeCloser api.Closer
120
121 // s is the Store on which this module is instantiated.
122 s *Store
123 // prev and next hold the nodes in the linked list of ModuleInstance held by Store.
124 prev, next *ModuleInstance
125 // Source is a pointer to the Module from which this ModuleInstance derives.
126 Source *Module
127
128 // CloseNotifier is an experimental hook called once on close.
129 CloseNotifier experimental.CloseNotifier
130 }
131
132 // DataInstance holds bytes corresponding to the data segment in a module.
133 //
134 // https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/runtime.html#data-instances
135 DataInstance = []byte
136
137 // GlobalInstance represents a global instance in a store.
138 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#global-instances%E2%91%A0
139 GlobalInstance struct {
140 Type GlobalType
141 // Val holds a 64-bit representation of the actual value.
142 // If me is non-nil, the value will not be updated and the current value is stored in the module engine.
143 Val uint64
144 // ValHi is only used for vector type globals, and holds the higher bits of the vector.
145 // If me is non-nil, the value will not be updated and the current value is stored in the module engine.
146 ValHi uint64
147 // Me is the module engine that owns this global instance.
148 // The .Val and .ValHi fields are only valid when me is nil.
149 // If me is non-nil, the value is stored in the module engine.
150 Me ModuleEngine
151 Index Index
152 }
153
154 // FunctionTypeID is a uniquely assigned integer for a function type.
155 // This is wazero specific runtime object and specific to a store,
156 // and used at runtime to do type-checks on indirect function calls.
157 FunctionTypeID uint32
158)
159
160// The wazero specific limitations described at RATIONALE.md.
161const maximumFunctionTypes = 1 << 27
162
163// GetFunctionTypeID is used by emscripten.
164func (m *ModuleInstance) GetFunctionTypeID(t *FunctionType) FunctionTypeID {
165 id, err := m.s.GetFunctionTypeID(t)
166 if err != nil {
167 // This is not recoverable in practice since the only error GetFunctionTypeID returns is
168 // when there's too many function types in the store.
169 panic(err)
170 }
171 return id
172}
173
174func (m *ModuleInstance) buildElementInstances(elements []ElementSegment) {
175 m.ElementInstances = make([][]Reference, len(elements))
176 for i, elm := range elements {
177 if elm.Type == RefTypeFuncref && elm.Mode == ElementModePassive {
178 // Only passive elements can be access as element instances.
179 // See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/syntax/modules.html#element-segments
180 inits := elm.Init
181 inst := make([]Reference, len(inits))
182 m.ElementInstances[i] = inst
183 for j, idx := range inits {
184 if index, ok := unwrapElementInitGlobalReference(idx); ok {
185 global := m.Globals[index]
186 inst[j] = Reference(global.Val)
187 } else {
188 if idx != ElementInitNullReference {
189 inst[j] = m.Engine.FunctionInstanceReference(idx)
190 }
191 }
192 }
193 }
194 }
195}
196
197func (m *ModuleInstance) applyElements(elems []ElementSegment) {
198 for elemI := range elems {
199 elem := &elems[elemI]
200 if !elem.IsActive() ||
201 // Per https://github.com/WebAssembly/spec/issues/1427 init can be no-op.
202 len(elem.Init) == 0 {
203 continue
204 }
205 var offset uint32
206 if elem.OffsetExpr.Opcode == OpcodeGlobalGet {
207 // Ignore error as it's already validated.
208 globalIdx, _, _ := leb128.LoadUint32(elem.OffsetExpr.Data)
209 global := m.Globals[globalIdx]
210 offset = uint32(global.Val)
211 } else {
212 // Ignore error as it's already validated.
213 o, _, _ := leb128.LoadInt32(elem.OffsetExpr.Data)
214 offset = uint32(o)
215 }
216
217 table := m.Tables[elem.TableIndex]
218 references := table.References
219 if int(offset)+len(elem.Init) > len(references) {
220 // ErrElementOffsetOutOfBounds is the error raised when the active element offset exceeds the table length.
221 // Before CoreFeatureReferenceTypes, this was checked statically before instantiation, after the proposal,
222 // this must be raised as runtime error (as in assert_trap in spectest), not even an instantiation error.
223 // https://github.com/WebAssembly/spec/blob/d39195773112a22b245ffbe864bab6d1182ccb06/test/core/linking.wast#L264-L274
224 //
225 // In wazero, we ignore it since in any way, the instantiated module and engines are fine and can be used
226 // for function invocations.
227 return
228 }
229
230 if table.Type == RefTypeExternref {
231 for i := 0; i < len(elem.Init); i++ {
232 references[offset+uint32(i)] = Reference(0)
233 }
234 } else {
235 for i, init := range elem.Init {
236 if init == ElementInitNullReference {
237 continue
238 }
239
240 var ref Reference
241 if index, ok := unwrapElementInitGlobalReference(init); ok {
242 global := m.Globals[index]
243 ref = Reference(global.Val)
244 } else {
245 ref = m.Engine.FunctionInstanceReference(index)
246 }
247 references[offset+uint32(i)] = ref
248 }
249 }
250 }
251}
252
253// validateData ensures that data segments are valid in terms of memory boundary.
254// Note: this is used only when bulk-memory/reference type feature is disabled.
255func (m *ModuleInstance) validateData(data []DataSegment) (err error) {
256 for i := range data {
257 d := &data[i]
258 if !d.IsPassive() {
259 offset := int(executeConstExpressionI32(m.Globals, &d.OffsetExpression))
260 ceil := offset + len(d.Init)
261 if offset < 0 || ceil > len(m.MemoryInstance.Buffer) {
262 return fmt.Errorf("%s[%d]: out of bounds memory access", SectionIDName(SectionIDData), i)
263 }
264 }
265 }
266 return
267}
268
269// applyData uses the given data segments and mutate the memory according to the initial contents on it
270// and populate the `DataInstances`. This is called after all the validation phase passes and out of
271// bounds memory access error here is not a validation error, but rather a runtime error.
272func (m *ModuleInstance) applyData(data []DataSegment) error {
273 m.DataInstances = make([][]byte, len(data))
274 for i := range data {
275 d := &data[i]
276 m.DataInstances[i] = d.Init
277 if !d.IsPassive() {
278 offset := executeConstExpressionI32(m.Globals, &d.OffsetExpression)
279 if offset < 0 || int(offset)+len(d.Init) > len(m.MemoryInstance.Buffer) {
280 return fmt.Errorf("%s[%d]: out of bounds memory access", SectionIDName(SectionIDData), i)
281 }
282 copy(m.MemoryInstance.Buffer[offset:], d.Init)
283 }
284 }
285 return nil
286}
287
288// GetExport returns an export of the given name and type or errs if not exported or the wrong type.
289func (m *ModuleInstance) getExport(name string, et ExternType) (*Export, error) {
290 exp, ok := m.Exports[name]
291 if !ok {
292 return nil, fmt.Errorf("%q is not exported in module %q", name, m.ModuleName)
293 }
294 if exp.Type != et {
295 return nil, fmt.Errorf("export %q in module %q is a %s, not a %s", name, m.ModuleName, ExternTypeName(exp.Type), ExternTypeName(et))
296 }
297 return exp, nil
298}
299
300func NewStore(enabledFeatures api.CoreFeatures, engine Engine) *Store {
301 return &Store{
302 nameToModule: map[string]*ModuleInstance{},
303 nameToModuleCap: nameToModuleShrinkThreshold,
304 EnabledFeatures: enabledFeatures,
305 Engine: engine,
306 typeIDs: map[string]FunctionTypeID{},
307 functionMaxTypes: maximumFunctionTypes,
308 }
309}
310
311// Instantiate uses name instead of the Module.NameSection ModuleName as it allows instantiating the same module under
312// different names safely and concurrently.
313//
314// * ctx: the default context used for function calls.
315// * name: the name of the module.
316// * sys: the system context, which will be closed (SysContext.Close) on ModuleInstance.Close.
317//
318// Note: Module.Validate must be called prior to instantiation.
319func (s *Store) Instantiate(
320 ctx context.Context,
321 module *Module,
322 name string,
323 sys *internalsys.Context,
324 typeIDs []FunctionTypeID,
325) (*ModuleInstance, error) {
326 // Instantiate the module and add it to the store so that other modules can import it.
327 m, err := s.instantiate(ctx, module, name, sys, typeIDs)
328 if err != nil {
329 return nil, err
330 }
331
332 // Now that the instantiation is complete without error, add it.
333 if err = s.registerModule(m); err != nil {
334 _ = m.Close(ctx)
335 return nil, err
336 }
337 return m, nil
338}
339
340func (s *Store) instantiate(
341 ctx context.Context,
342 module *Module,
343 name string,
344 sysCtx *internalsys.Context,
345 typeIDs []FunctionTypeID,
346) (m *ModuleInstance, err error) {
347 m = &ModuleInstance{ModuleName: name, TypeIDs: typeIDs, Sys: sysCtx, s: s, Source: module}
348
349 m.Tables = make([]*TableInstance, int(module.ImportTableCount)+len(module.TableSection))
350 m.Globals = make([]*GlobalInstance, int(module.ImportGlobalCount)+len(module.GlobalSection))
351 m.Engine, err = s.Engine.NewModuleEngine(module, m)
352 if err != nil {
353 return nil, err
354 }
355
356 if err = m.resolveImports(ctx, module); err != nil {
357 return nil, err
358 }
359
360 err = m.buildTables(module,
361 // As of reference-types proposal, boundary check must be done after instantiation.
362 s.EnabledFeatures.IsEnabled(api.CoreFeatureReferenceTypes))
363 if err != nil {
364 return nil, err
365 }
366
367 allocator, _ := ctx.Value(expctxkeys.MemoryAllocatorKey{}).(experimental.MemoryAllocator)
368
369 m.buildGlobals(module, m.Engine.FunctionInstanceReference)
370 m.buildMemory(module, allocator)
371 m.Exports = module.Exports
372 for _, exp := range m.Exports {
373 if exp.Type == ExternTypeTable {
374 t := m.Tables[exp.Index]
375 t.involvingModuleInstances = append(t.involvingModuleInstances, m)
376 }
377 }
378
379 // As of reference types proposal, data segment validation must happen after instantiation,
380 // and the side effect must persist even if there's out of bounds error after instantiation.
381 // https://github.com/WebAssembly/spec/blob/d39195773112a22b245ffbe864bab6d1182ccb06/test/core/linking.wast#L395-L405
382 if !s.EnabledFeatures.IsEnabled(api.CoreFeatureReferenceTypes) {
383 if err = m.validateData(module.DataSection); err != nil {
384 return nil, err
385 }
386 }
387
388 // After engine creation, we can create the funcref element instances and initialize funcref type globals.
389 m.buildElementInstances(module.ElementSection)
390
391 // Now all the validation passes, we are safe to mutate memory instances (possibly imported ones).
392 if err = m.applyData(module.DataSection); err != nil {
393 return nil, err
394 }
395
396 m.applyElements(module.ElementSection)
397
398 m.Engine.DoneInstantiation()
399
400 // Execute the start function.
401 if module.StartSection != nil {
402 funcIdx := *module.StartSection
403 ce := m.Engine.NewFunction(funcIdx)
404 _, err = ce.Call(ctx)
405 if exitErr, ok := err.(*sys.ExitError); ok { // Don't wrap an exit error!
406 return nil, exitErr
407 } else if err != nil {
408 return nil, fmt.Errorf("start %s failed: %w", module.funcDesc(SectionIDFunction, funcIdx), err)
409 }
410 }
411 return
412}
413
414func (m *ModuleInstance) resolveImports(ctx context.Context, module *Module) (err error) {
415 // Check if ctx contains an ImportResolver.
416 resolveImport, _ := ctx.Value(expctxkeys.ImportResolverKey{}).(experimental.ImportResolver)
417
418 for moduleName, imports := range module.ImportPerModule {
419 var importedModule *ModuleInstance
420 if resolveImport != nil {
421 if v := resolveImport(moduleName); v != nil {
422 importedModule = v.(*ModuleInstance)
423 }
424 }
425 if importedModule == nil {
426 importedModule, err = m.s.module(moduleName)
427 if err != nil {
428 return err
429 }
430 }
431
432 for _, i := range imports {
433 var imported *Export
434 imported, err = importedModule.getExport(i.Name, i.Type)
435 if err != nil {
436 return
437 }
438
439 switch i.Type {
440 case ExternTypeFunc:
441 expectedType := &module.TypeSection[i.DescFunc]
442 src := importedModule.Source
443 actual := src.typeOfFunction(imported.Index)
444 if !actual.EqualsSignature(expectedType.Params, expectedType.Results) {
445 err = errorInvalidImport(i, fmt.Errorf("signature mismatch: %s != %s", expectedType, actual))
446 return
447 }
448
449 m.Engine.ResolveImportedFunction(i.IndexPerType, i.DescFunc, imported.Index, importedModule.Engine)
450 case ExternTypeTable:
451 expected := i.DescTable
452 importedTable := importedModule.Tables[imported.Index]
453 if expected.Type != importedTable.Type {
454 err = errorInvalidImport(i, fmt.Errorf("table type mismatch: %s != %s",
455 RefTypeName(expected.Type), RefTypeName(importedTable.Type)))
456 return
457 }
458
459 if expected.Min > importedTable.Min {
460 err = errorMinSizeMismatch(i, expected.Min, importedTable.Min)
461 return
462 }
463
464 if expected.Max != nil {
465 expectedMax := *expected.Max
466 if importedTable.Max == nil {
467 err = errorNoMax(i, expectedMax)
468 return
469 } else if expectedMax < *importedTable.Max {
470 err = errorMaxSizeMismatch(i, expectedMax, *importedTable.Max)
471 return
472 }
473 }
474 m.Tables[i.IndexPerType] = importedTable
475 importedTable.involvingModuleInstancesMutex.Lock()
476 if len(importedTable.involvingModuleInstances) == 0 {
477 panic("BUG: involvingModuleInstances must not be nil when it's imported")
478 }
479 importedTable.involvingModuleInstances = append(importedTable.involvingModuleInstances, m)
480 importedTable.involvingModuleInstancesMutex.Unlock()
481 case ExternTypeMemory:
482 expected := i.DescMem
483 importedMemory := importedModule.MemoryInstance
484
485 if expected.Min > memoryBytesNumToPages(uint64(len(importedMemory.Buffer))) {
486 err = errorMinSizeMismatch(i, expected.Min, importedMemory.Min)
487 return
488 }
489
490 if expected.Max < importedMemory.Max {
491 err = errorMaxSizeMismatch(i, expected.Max, importedMemory.Max)
492 return
493 }
494 m.MemoryInstance = importedMemory
495 m.Engine.ResolveImportedMemory(importedModule.Engine)
496 case ExternTypeGlobal:
497 expected := i.DescGlobal
498 importedGlobal := importedModule.Globals[imported.Index]
499
500 if expected.Mutable != importedGlobal.Type.Mutable {
501 err = errorInvalidImport(i, fmt.Errorf("mutability mismatch: %t != %t",
502 expected.Mutable, importedGlobal.Type.Mutable))
503 return
504 }
505
506 if expected.ValType != importedGlobal.Type.ValType {
507 err = errorInvalidImport(i, fmt.Errorf("value type mismatch: %s != %s",
508 ValueTypeName(expected.ValType), ValueTypeName(importedGlobal.Type.ValType)))
509 return
510 }
511 m.Globals[i.IndexPerType] = importedGlobal
512 }
513 }
514 }
515 return
516}
517
518func errorMinSizeMismatch(i *Import, expected, actual uint32) error {
519 return errorInvalidImport(i, fmt.Errorf("minimum size mismatch: %d > %d", expected, actual))
520}
521
522func errorNoMax(i *Import, expected uint32) error {
523 return errorInvalidImport(i, fmt.Errorf("maximum size mismatch: %d, but actual has no max", expected))
524}
525
526func errorMaxSizeMismatch(i *Import, expected, actual uint32) error {
527 return errorInvalidImport(i, fmt.Errorf("maximum size mismatch: %d < %d", expected, actual))
528}
529
530func errorInvalidImport(i *Import, err error) error {
531 return fmt.Errorf("import %s[%s.%s]: %w", ExternTypeName(i.Type), i.Module, i.Name, err)
532}
533
534// executeConstExpressionI32 executes the ConstantExpression which returns ValueTypeI32.
535// The validity of the expression is ensured when calling this function as this is only called
536// during instantiation phrase, and the validation happens in compilation (validateConstExpression).
537func executeConstExpressionI32(importedGlobals []*GlobalInstance, expr *ConstantExpression) (ret int32) {
538 switch expr.Opcode {
539 case OpcodeI32Const:
540 ret, _, _ = leb128.LoadInt32(expr.Data)
541 case OpcodeGlobalGet:
542 id, _, _ := leb128.LoadUint32(expr.Data)
543 g := importedGlobals[id]
544 ret = int32(g.Val)
545 }
546 return
547}
548
549// initialize initializes the value of this global instance given the const expr and imported globals.
550// funcRefResolver is called to get the actual funcref (engine specific) from the OpcodeRefFunc const expr.
551//
552// Global initialization constant expression can only reference the imported globals.
553// See the note on https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#constant-expressions%E2%91%A0
554func (g *GlobalInstance) initialize(importedGlobals []*GlobalInstance, expr *ConstantExpression, funcRefResolver func(funcIndex Index) Reference) {
555 switch expr.Opcode {
556 case OpcodeI32Const:
557 // Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
558 v, _, _ := leb128.LoadInt32(expr.Data)
559 g.Val = uint64(uint32(v))
560 case OpcodeI64Const:
561 // Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
562 v, _, _ := leb128.LoadInt64(expr.Data)
563 g.Val = uint64(v)
564 case OpcodeF32Const:
565 g.Val = uint64(binary.LittleEndian.Uint32(expr.Data))
566 case OpcodeF64Const:
567 g.Val = binary.LittleEndian.Uint64(expr.Data)
568 case OpcodeGlobalGet:
569 id, _, _ := leb128.LoadUint32(expr.Data)
570 importedG := importedGlobals[id]
571 switch importedG.Type.ValType {
572 case ValueTypeI32:
573 g.Val = uint64(uint32(importedG.Val))
574 case ValueTypeI64:
575 g.Val = importedG.Val
576 case ValueTypeF32:
577 g.Val = importedG.Val
578 case ValueTypeF64:
579 g.Val = importedG.Val
580 case ValueTypeV128:
581 g.Val, g.ValHi = importedG.Val, importedG.ValHi
582 case ValueTypeFuncref, ValueTypeExternref:
583 g.Val = importedG.Val
584 }
585 case OpcodeRefNull:
586 switch expr.Data[0] {
587 case ValueTypeExternref, ValueTypeFuncref:
588 g.Val = 0 // Reference types are opaque 64bit pointer at runtime.
589 }
590 case OpcodeRefFunc:
591 v, _, _ := leb128.LoadUint32(expr.Data)
592 g.Val = uint64(funcRefResolver(v))
593 case OpcodeVecV128Const:
594 g.Val, g.ValHi = binary.LittleEndian.Uint64(expr.Data[0:8]), binary.LittleEndian.Uint64(expr.Data[8:16])
595 }
596}
597
598// String implements api.Global.
599func (g *GlobalInstance) String() string {
600 switch g.Type.ValType {
601 case ValueTypeI32, ValueTypeI64:
602 return fmt.Sprintf("global(%d)", g.Val)
603 case ValueTypeF32:
604 return fmt.Sprintf("global(%f)", api.DecodeF32(g.Val))
605 case ValueTypeF64:
606 return fmt.Sprintf("global(%f)", api.DecodeF64(g.Val))
607 default:
608 panic(fmt.Errorf("BUG: unknown value type %X", g.Type.ValType))
609 }
610}
611
612func (g *GlobalInstance) Value() (uint64, uint64) {
613 if g.Me != nil {
614 return g.Me.GetGlobalValue(g.Index)
615 }
616 return g.Val, g.ValHi
617}
618
619func (g *GlobalInstance) SetValue(lo, hi uint64) {
620 if g.Me != nil {
621 g.Me.SetGlobalValue(g.Index, lo, hi)
622 } else {
623 g.Val, g.ValHi = lo, hi
624 }
625}
626
627func (s *Store) GetFunctionTypeIDs(ts []FunctionType) ([]FunctionTypeID, error) {
628 ret := make([]FunctionTypeID, len(ts))
629 for i := range ts {
630 t := &ts[i]
631 inst, err := s.GetFunctionTypeID(t)
632 if err != nil {
633 return nil, err
634 }
635 ret[i] = inst
636 }
637 return ret, nil
638}
639
640func (s *Store) GetFunctionTypeID(t *FunctionType) (FunctionTypeID, error) {
641 s.mux.RLock()
642 key := t.key()
643 id, ok := s.typeIDs[key]
644 s.mux.RUnlock()
645 if !ok {
646 s.mux.Lock()
647 defer s.mux.Unlock()
648 // Check again in case another goroutine has already added the type.
649 if id, ok = s.typeIDs[key]; ok {
650 return id, nil
651 }
652 l := len(s.typeIDs)
653 if uint32(l) >= s.functionMaxTypes {
654 return 0, fmt.Errorf("too many function types in a store")
655 }
656 id = FunctionTypeID(l)
657 s.typeIDs[key] = id
658 }
659 return id, nil
660}
661
662// CloseWithExitCode implements the same method as documented on wazero.Runtime.
663func (s *Store) CloseWithExitCode(ctx context.Context, exitCode uint32) error {
664 s.mux.Lock()
665 defer s.mux.Unlock()
666 // Close modules in reverse initialization order.
667 var errs []error
668 for m := s.moduleList; m != nil; m = m.next {
669 // If closing this module errs, proceed anyway to close the others.
670 if err := m.closeWithExitCode(ctx, exitCode); err != nil {
671 errs = append(errs, err)
672 }
673 }
674 s.moduleList = nil
675 s.nameToModule = nil
676 s.nameToModuleCap = 0
677 s.typeIDs = nil
678 return errors.Join(errs...)
679}