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  }