github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/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 allocator, _ := ctx.Value(expctxkeys.MemoryAllocatorKey{}).(experimental.MemoryAllocator) 367 368 m.buildGlobals(module, m.Engine.FunctionInstanceReference) 369 m.buildMemory(module, allocator) 370 m.Exports = module.Exports 371 for _, exp := range m.Exports { 372 if exp.Type == ExternTypeTable { 373 t := m.Tables[exp.Index] 374 t.involvingModuleInstances = append(t.involvingModuleInstances, m) 375 } 376 } 377 378 // As of reference types proposal, data segment validation must happen after instantiation, 379 // and the side effect must persist even if there's out of bounds error after instantiation. 380 // https://github.com/WebAssembly/spec/blob/d39195773112a22b245ffbe864bab6d1182ccb06/test/core/linking.wast#L395-L405 381 if !s.EnabledFeatures.IsEnabled(api.CoreFeatureReferenceTypes) { 382 if err = m.validateData(module.DataSection); err != nil { 383 return nil, err 384 } 385 } 386 387 // After engine creation, we can create the funcref element instances and initialize funcref type globals. 388 m.buildElementInstances(module.ElementSection) 389 390 // Now all the validation passes, we are safe to mutate memory instances (possibly imported ones). 391 if err = m.applyData(module.DataSection); err != nil { 392 return nil, err 393 } 394 395 m.applyElements(module.ElementSection) 396 397 m.Engine.DoneInstantiation() 398 399 // Execute the start function. 400 if module.StartSection != nil { 401 funcIdx := *module.StartSection 402 ce := m.Engine.NewFunction(funcIdx) 403 _, err = ce.Call(ctx) 404 if exitErr, ok := err.(*sys.ExitError); ok { // Don't wrap an exit error! 405 return nil, exitErr 406 } else if err != nil { 407 return nil, fmt.Errorf("start %s failed: %w", module.funcDesc(SectionIDFunction, funcIdx), err) 408 } 409 } 410 return 411 } 412 413 func (m *ModuleInstance) resolveImports(module *Module) (err error) { 414 for moduleName, imports := range module.ImportPerModule { 415 var importedModule *ModuleInstance 416 importedModule, err = m.s.module(moduleName) 417 if err != nil { 418 return err 419 } 420 421 for _, i := range imports { 422 var imported *Export 423 imported, err = importedModule.getExport(i.Name, i.Type) 424 if err != nil { 425 return 426 } 427 428 switch i.Type { 429 case ExternTypeFunc: 430 expectedType := &module.TypeSection[i.DescFunc] 431 src := importedModule.Source 432 actual := src.typeOfFunction(imported.Index) 433 if !actual.EqualsSignature(expectedType.Params, expectedType.Results) { 434 err = errorInvalidImport(i, fmt.Errorf("signature mismatch: %s != %s", expectedType, actual)) 435 return 436 } 437 438 m.Engine.ResolveImportedFunction(i.IndexPerType, imported.Index, importedModule.Engine) 439 case ExternTypeTable: 440 expected := i.DescTable 441 importedTable := importedModule.Tables[imported.Index] 442 if expected.Type != importedTable.Type { 443 err = errorInvalidImport(i, fmt.Errorf("table type mismatch: %s != %s", 444 RefTypeName(expected.Type), RefTypeName(importedTable.Type))) 445 return 446 } 447 448 if expected.Min > importedTable.Min { 449 err = errorMinSizeMismatch(i, expected.Min, importedTable.Min) 450 return 451 } 452 453 if expected.Max != nil { 454 expectedMax := *expected.Max 455 if importedTable.Max == nil { 456 err = errorNoMax(i, expectedMax) 457 return 458 } else if expectedMax < *importedTable.Max { 459 err = errorMaxSizeMismatch(i, expectedMax, *importedTable.Max) 460 return 461 } 462 } 463 m.Tables[i.IndexPerType] = importedTable 464 importedTable.involvingModuleInstancesMutex.Lock() 465 if len(importedTable.involvingModuleInstances) == 0 { 466 panic("BUG: involvingModuleInstances must not be nil when it's imported") 467 } 468 importedTable.involvingModuleInstances = append(importedTable.involvingModuleInstances, m) 469 importedTable.involvingModuleInstancesMutex.Unlock() 470 case ExternTypeMemory: 471 expected := i.DescMem 472 importedMemory := importedModule.MemoryInstance 473 474 if expected.Min > memoryBytesNumToPages(uint64(len(importedMemory.Buffer))) { 475 err = errorMinSizeMismatch(i, expected.Min, importedMemory.Min) 476 return 477 } 478 479 if expected.Max < importedMemory.Max { 480 err = errorMaxSizeMismatch(i, expected.Max, importedMemory.Max) 481 return 482 } 483 m.MemoryInstance = importedMemory 484 m.Engine.ResolveImportedMemory(importedModule.Engine) 485 case ExternTypeGlobal: 486 expected := i.DescGlobal 487 importedGlobal := importedModule.Globals[imported.Index] 488 489 if expected.Mutable != importedGlobal.Type.Mutable { 490 err = errorInvalidImport(i, fmt.Errorf("mutability mismatch: %t != %t", 491 expected.Mutable, importedGlobal.Type.Mutable)) 492 return 493 } 494 495 if expected.ValType != importedGlobal.Type.ValType { 496 err = errorInvalidImport(i, fmt.Errorf("value type mismatch: %s != %s", 497 ValueTypeName(expected.ValType), ValueTypeName(importedGlobal.Type.ValType))) 498 return 499 } 500 m.Globals[i.IndexPerType] = importedGlobal 501 } 502 } 503 } 504 return 505 } 506 507 func errorMinSizeMismatch(i *Import, expected, actual uint32) error { 508 return errorInvalidImport(i, fmt.Errorf("minimum size mismatch: %d > %d", expected, actual)) 509 } 510 511 func errorNoMax(i *Import, expected uint32) error { 512 return errorInvalidImport(i, fmt.Errorf("maximum size mismatch: %d, but actual has no max", expected)) 513 } 514 515 func errorMaxSizeMismatch(i *Import, expected, actual uint32) error { 516 return errorInvalidImport(i, fmt.Errorf("maximum size mismatch: %d < %d", expected, actual)) 517 } 518 519 func errorInvalidImport(i *Import, err error) error { 520 return fmt.Errorf("import %s[%s.%s]: %w", ExternTypeName(i.Type), i.Module, i.Name, err) 521 } 522 523 // executeConstExpressionI32 executes the ConstantExpression which returns ValueTypeI32. 524 // The validity of the expression is ensured when calling this function as this is only called 525 // during instantiation phrase, and the validation happens in compilation (validateConstExpression). 526 func executeConstExpressionI32(importedGlobals []*GlobalInstance, expr *ConstantExpression) (ret int32) { 527 switch expr.Opcode { 528 case OpcodeI32Const: 529 ret, _, _ = leb128.LoadInt32(expr.Data) 530 case OpcodeGlobalGet: 531 id, _, _ := leb128.LoadUint32(expr.Data) 532 g := importedGlobals[id] 533 ret = int32(g.Val) 534 } 535 return 536 } 537 538 // initialize initializes the value of this global instance given the const expr and imported globals. 539 // funcRefResolver is called to get the actual funcref (engine specific) from the OpcodeRefFunc const expr. 540 // 541 // Global initialization constant expression can only reference the imported globals. 542 // See the note on https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#constant-expressions%E2%91%A0 543 func (g *GlobalInstance) initialize(importedGlobals []*GlobalInstance, expr *ConstantExpression, funcRefResolver func(funcIndex Index) Reference) { 544 switch expr.Opcode { 545 case OpcodeI32Const: 546 // Treat constants as signed as their interpretation is not yet known per /RATIONALE.md 547 v, _, _ := leb128.LoadInt32(expr.Data) 548 g.Val = uint64(uint32(v)) 549 case OpcodeI64Const: 550 // Treat constants as signed as their interpretation is not yet known per /RATIONALE.md 551 v, _, _ := leb128.LoadInt64(expr.Data) 552 g.Val = uint64(v) 553 case OpcodeF32Const: 554 g.Val = uint64(binary.LittleEndian.Uint32(expr.Data)) 555 case OpcodeF64Const: 556 g.Val = binary.LittleEndian.Uint64(expr.Data) 557 case OpcodeGlobalGet: 558 id, _, _ := leb128.LoadUint32(expr.Data) 559 importedG := importedGlobals[id] 560 switch importedG.Type.ValType { 561 case ValueTypeI32: 562 g.Val = uint64(uint32(importedG.Val)) 563 case ValueTypeI64: 564 g.Val = importedG.Val 565 case ValueTypeF32: 566 g.Val = importedG.Val 567 case ValueTypeF64: 568 g.Val = importedG.Val 569 case ValueTypeV128: 570 g.Val, g.ValHi = importedG.Val, importedG.ValHi 571 case ValueTypeFuncref, ValueTypeExternref: 572 g.Val = importedG.Val 573 } 574 case OpcodeRefNull: 575 switch expr.Data[0] { 576 case ValueTypeExternref, ValueTypeFuncref: 577 g.Val = 0 // Reference types are opaque 64bit pointer at runtime. 578 } 579 case OpcodeRefFunc: 580 v, _, _ := leb128.LoadUint32(expr.Data) 581 g.Val = uint64(funcRefResolver(v)) 582 case OpcodeVecV128Const: 583 g.Val, g.ValHi = binary.LittleEndian.Uint64(expr.Data[0:8]), binary.LittleEndian.Uint64(expr.Data[8:16]) 584 } 585 } 586 587 // String implements api.Global. 588 func (g *GlobalInstance) String() string { 589 switch g.Type.ValType { 590 case ValueTypeI32, ValueTypeI64: 591 return fmt.Sprintf("global(%d)", g.Val) 592 case ValueTypeF32: 593 return fmt.Sprintf("global(%f)", api.DecodeF32(g.Val)) 594 case ValueTypeF64: 595 return fmt.Sprintf("global(%f)", api.DecodeF64(g.Val)) 596 default: 597 panic(fmt.Errorf("BUG: unknown value type %X", g.Type.ValType)) 598 } 599 } 600 601 func (g *GlobalInstance) Value() (uint64, uint64) { 602 if g.Me != nil { 603 return g.Me.GetGlobalValue(g.Index) 604 } 605 return g.Val, g.ValHi 606 } 607 608 func (g *GlobalInstance) SetValue(lo, hi uint64) { 609 if g.Me != nil { 610 g.Me.SetGlobalValue(g.Index, lo, hi) 611 } else { 612 g.Val, g.ValHi = lo, hi 613 } 614 } 615 616 func (s *Store) GetFunctionTypeIDs(ts []FunctionType) ([]FunctionTypeID, error) { 617 ret := make([]FunctionTypeID, len(ts)) 618 for i := range ts { 619 t := &ts[i] 620 inst, err := s.GetFunctionTypeID(t) 621 if err != nil { 622 return nil, err 623 } 624 ret[i] = inst 625 } 626 return ret, nil 627 } 628 629 func (s *Store) GetFunctionTypeID(t *FunctionType) (FunctionTypeID, error) { 630 s.mux.RLock() 631 key := t.key() 632 id, ok := s.typeIDs[key] 633 s.mux.RUnlock() 634 if !ok { 635 s.mux.Lock() 636 defer s.mux.Unlock() 637 // Check again in case another goroutine has already added the type. 638 if id, ok = s.typeIDs[key]; ok { 639 return id, nil 640 } 641 l := len(s.typeIDs) 642 if uint32(l) >= s.functionMaxTypes { 643 return 0, fmt.Errorf("too many function types in a store") 644 } 645 id = FunctionTypeID(l) 646 s.typeIDs[key] = id 647 } 648 return id, nil 649 } 650 651 // CloseWithExitCode implements the same method as documented on wazero.Runtime. 652 func (s *Store) CloseWithExitCode(ctx context.Context, exitCode uint32) (err error) { 653 s.mux.Lock() 654 defer s.mux.Unlock() 655 // Close modules in reverse initialization order. 656 for m := s.moduleList; m != nil; m = m.next { 657 // If closing this module errs, proceed anyway to close the others. 658 if e := m.closeWithExitCode(ctx, exitCode); e != nil && err == nil { 659 // TODO: use multiple errors handling in Go 1.20. 660 err = e // first error 661 } 662 } 663 s.moduleList = nil 664 s.nameToModule = nil 665 s.nameToModuleCap = 0 666 s.typeIDs = nil 667 return 668 }