github.com/tetratelabs/wazero@v1.2.1/internal/wasm/store.go (about)

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