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