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