wa-lang.org/wazero@v1.0.2/internal/wasm/store.go (about)

     1  package wasm
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"sync"
     8  
     9  	"wa-lang.org/wazero/api"
    10  	"wa-lang.org/wazero/internal/ieee754"
    11  	"wa-lang.org/wazero/internal/leb128"
    12  	internalsys "wa-lang.org/wazero/internal/sys"
    13  	"wa-lang.org/wazero/sys"
    14  )
    15  
    16  type (
    17  	// Store is the runtime representation of "instantiated" Wasm module and objects.
    18  	// Multiple modules can be instantiated within a single store, and each instance,
    19  	// (e.g. function instance) can be referenced by other module instances in a Store via Module.ImportSection.
    20  	//
    21  	// Every type whose name ends with "Instance" suffix belongs to exactly one store.
    22  	//
    23  	// Note that store is not thread (concurrency) safe, meaning that using single Store
    24  	// via multiple goroutines might result in race conditions. In that case, the invocation
    25  	// and access to any methods and field of Store must be guarded by mutex.
    26  	//
    27  	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#store%E2%91%A0
    28  	Store struct {
    29  		// EnabledFeatures are read-only to allow optimizations.
    30  		EnabledFeatures api.CoreFeatures
    31  
    32  		// Engine is a global context for a Store which is in responsible for compilation and execution of Wasm modules.
    33  		Engine Engine
    34  
    35  		// typeIDs maps each FunctionType.String() to a unique FunctionTypeID. This is used at runtime to
    36  		// do type-checks on indirect function calls.
    37  		typeIDs map[string]FunctionTypeID
    38  
    39  		// functionMaxTypes represents the limit on the number of function types in a store.
    40  		// Note: this is fixed to 2^27 but have this a field for testability.
    41  		functionMaxTypes uint32
    42  
    43  		// namespaces are all Namespace instances for this store including the default one.
    44  		namespaces []*Namespace // guarded by mux
    45  
    46  		// mux is used to guard the fields from concurrent access.
    47  		mux sync.RWMutex
    48  	}
    49  
    50  	// ModuleInstance represents instantiated wasm module.
    51  	// The difference from the spec is that in wazero, a ModuleInstance holds pointers
    52  	// to the instances, rather than "addresses" (i.e. index to Store.Functions, Globals, etc) for convenience.
    53  	//
    54  	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-moduleinst
    55  	ModuleInstance struct {
    56  		Name      string
    57  		Exports   map[string]*ExportInstance
    58  		Functions []*FunctionInstance
    59  		Globals   []*GlobalInstance
    60  		// Memory is set when Module.MemorySection had a memory, regardless of whether it was exported.
    61  		Memory *MemoryInstance
    62  		Tables []*TableInstance
    63  		Types  []*FunctionType
    64  
    65  		// CallCtx holds default function call context from this function instance.
    66  		CallCtx *CallContext
    67  
    68  		// Engine implements function calls for this module.
    69  		Engine ModuleEngine
    70  
    71  		// TypeIDs is index-correlated with types and holds typeIDs which is uniquely assigned to a type by store.
    72  		// This is necessary to achieve fast runtime type checking for indirect function calls at runtime.
    73  		TypeIDs     []FunctionTypeID
    74  		TypeIDIndex map[string]FunctionTypeID
    75  
    76  		// DataInstances holds data segments bytes of the module.
    77  		// This is only used by bulk memory operations.
    78  		//
    79  		// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/runtime.html#data-instances
    80  		DataInstances []DataInstance
    81  
    82  		// ElementInstances holds the element instance, and each holds the references to either functions
    83  		// or external objects (unimplemented).
    84  		ElementInstances []ElementInstance
    85  	}
    86  
    87  	// DataInstance holds bytes corresponding to the data segment in a module.
    88  	//
    89  	// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/runtime.html#data-instances
    90  	DataInstance = []byte
    91  
    92  	// ExportInstance represents an exported instance in a Store.
    93  	// The difference from the spec is that in wazero, a ExportInstance holds pointers
    94  	// to the instances, rather than "addresses" (i.e. index to Store.Functions, Globals, etc) for convenience.
    95  	//
    96  	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-exportinst
    97  	ExportInstance struct {
    98  		Type     ExternType
    99  		Function *FunctionInstance
   100  		Global   *GlobalInstance
   101  		Memory   *MemoryInstance
   102  		Table    *TableInstance
   103  	}
   104  
   105  	// FunctionInstance represents a function instance in a Store.
   106  	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#function-instances%E2%91%A0
   107  	FunctionInstance struct {
   108  		// IsHostFunction is the data returned by the same field documented on
   109  		// wasm.Code.
   110  		IsHostFunction bool
   111  
   112  		// Type is the signature of this function.
   113  		Type *FunctionType
   114  
   115  		// LocalTypes holds types of locals, set when Kind == FunctionKindWasm
   116  		LocalTypes []ValueType
   117  
   118  		// Body is the function body in WebAssembly Binary Format, set when Kind == FunctionKindWasm
   119  		Body []byte
   120  
   121  		// GoFunc is non-nil when IsHostFunction and defined in go, either
   122  		// api.GoFunction or api.GoModuleFunction.
   123  		//
   124  		// Note: This has no serialization format, so is not encodable.
   125  		// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#host-functions%E2%91%A2
   126  		GoFunc interface{}
   127  
   128  		// Fields above here are settable prior to instantiation. Below are set by the Store during instantiation.
   129  
   130  		// ModuleInstance holds the pointer to the module instance to which this function belongs.
   131  		Module *ModuleInstance
   132  
   133  		// TypeID is assigned by a store for FunctionType.
   134  		TypeID FunctionTypeID
   135  
   136  		// Idx holds the index of this function instance in the function index namespace (beginning with imports).
   137  		Idx Index
   138  
   139  		// Definition is known at compile time.
   140  		Definition api.FunctionDefinition
   141  	}
   142  
   143  	// GlobalInstance represents a global instance in a store.
   144  	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#global-instances%E2%91%A0
   145  	GlobalInstance struct {
   146  		Type *GlobalType
   147  		// Val holds a 64-bit representation of the actual value.
   148  		Val uint64
   149  		// ValHi is only used for vector type globals, and holds the higher bits of the vector.
   150  		ValHi uint64
   151  		// ^^ TODO: this should be guarded with atomics when mutable
   152  	}
   153  
   154  	// FunctionTypeID is a uniquely assigned integer for a function type.
   155  	// This is wazero specific runtime object and specific to a store,
   156  	// and used at runtime to do type-checks on indirect function calls.
   157  	FunctionTypeID uint32
   158  )
   159  
   160  // The wazero specific limitations described at RATIONALE.md.
   161  const maximumFunctionTypes = 1 << 27
   162  
   163  // addSections adds section elements to the ModuleInstance
   164  func (m *ModuleInstance) addSections(module *Module, importedFunctions, functions []*FunctionInstance,
   165  	importedGlobals, globals []*GlobalInstance, tables []*TableInstance, memory, importedMemory *MemoryInstance,
   166  	types []*FunctionType,
   167  ) {
   168  	m.Types = types
   169  	m.TypeIDIndex = make(map[string]FunctionTypeID, len(types))
   170  	for i, t := range types {
   171  		m.TypeIDIndex[t.string] = m.TypeIDs[i]
   172  	}
   173  	m.Functions = append(importedFunctions, functions...)
   174  	m.Globals = append(importedGlobals, globals...)
   175  	m.Tables = tables
   176  
   177  	if importedMemory != nil {
   178  		m.Memory = importedMemory
   179  	} else {
   180  		m.Memory = memory
   181  	}
   182  
   183  	m.BuildExports(module.ExportSection)
   184  }
   185  
   186  func (m *ModuleInstance) buildElementInstances(elements []*ElementSegment) {
   187  	m.ElementInstances = make([]ElementInstance, len(elements))
   188  	for i, elm := range elements {
   189  		if elm.Type == RefTypeFuncref && elm.Mode == ElementModePassive {
   190  			// Only passive elements can be access as element instances.
   191  			// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/syntax/modules.html#element-segments
   192  			m.ElementInstances[i] = *m.Engine.CreateFuncElementInstance(elm.Init)
   193  		}
   194  	}
   195  }
   196  
   197  func (m *ModuleInstance) applyTableInits(tables []*TableInstance, tableInits []tableInitEntry) {
   198  	for _, init := range tableInits {
   199  		table := tables[init.tableIndex]
   200  		references := table.References
   201  		if int(init.offset)+len(init.functionIndexes) > len(references) ||
   202  			int(init.offset)+init.nullExternRefCount > len(references) {
   203  			// ErrElementOffsetOutOfBounds is the error raised when the active element offset exceeds the table length.
   204  			// Before CoreFeatureReferenceTypes, this was checked statically before instantiation, after the proposal,
   205  			// this must be raised as runtime error (as in assert_trap in spectest), not even an instantiation error.
   206  			// https://github.com/WebAssembly/spec/blob/d39195773112a22b245ffbe864bab6d1182ccb06/test/core/linking.wast#L264-L274
   207  			//
   208  			// In wazero, we ignore it since in any way, the instantiated module and engines are fine and can be used
   209  			// for function invocations.
   210  			return
   211  		}
   212  
   213  		if table.Type == RefTypeExternref {
   214  			for i := 0; i < init.nullExternRefCount; i++ {
   215  				references[init.offset+uint32(i)] = Reference(0)
   216  			}
   217  		} else {
   218  			for i, fnIndex := range init.functionIndexes {
   219  				if fnIndex != nil {
   220  					references[init.offset+uint32(i)] = m.Engine.FunctionInstanceReference(*fnIndex)
   221  				}
   222  			}
   223  		}
   224  	}
   225  }
   226  
   227  func (m *ModuleInstance) BuildExports(exports []*Export) {
   228  	m.Exports = make(map[string]*ExportInstance, len(exports))
   229  	for _, exp := range exports {
   230  		index := exp.Index
   231  		var ei *ExportInstance
   232  		switch exp.Type {
   233  		case ExternTypeFunc:
   234  			ei = &ExportInstance{Type: exp.Type, Function: m.Functions[index]}
   235  		case ExternTypeGlobal:
   236  			ei = &ExportInstance{Type: exp.Type, Global: m.Globals[index]}
   237  		case ExternTypeMemory:
   238  			ei = &ExportInstance{Type: exp.Type, Memory: m.Memory}
   239  		case ExternTypeTable:
   240  			ei = &ExportInstance{Type: exp.Type, Table: m.Tables[index]}
   241  		}
   242  
   243  		// We already validated the duplicates during module validation phase.
   244  		m.Exports[exp.Name] = ei
   245  	}
   246  }
   247  
   248  // validateData ensures that data segments are valid in terms of memory boundary.
   249  // Note: this is used only when bulk-memory/reference type feature is disabled.
   250  func (m *ModuleInstance) validateData(data []*DataSegment) (err error) {
   251  	for i, d := range data {
   252  		if !d.IsPassive() {
   253  			offset := int(executeConstExpression(m.Globals, d.OffsetExpression).(int32))
   254  			ceil := offset + len(d.Init)
   255  			if offset < 0 || ceil > len(m.Memory.Buffer) {
   256  				return fmt.Errorf("%s[%d]: out of bounds memory access", SectionIDName(SectionIDData), i)
   257  			}
   258  		}
   259  	}
   260  	return
   261  }
   262  
   263  // applyData uses the given data segments and mutate the memory according to the initial contents on it
   264  // and populate the `DataInstances`. This is called after all the validation phase passes and out of
   265  // bounds memory access error here is not a validation error, but rather a runtime error.
   266  func (m *ModuleInstance) applyData(data []*DataSegment) error {
   267  	m.DataInstances = make([][]byte, len(data))
   268  	for i, d := range data {
   269  		m.DataInstances[i] = d.Init
   270  		if !d.IsPassive() {
   271  			offset := executeConstExpression(m.Globals, d.OffsetExpression).(int32)
   272  			if offset < 0 || int(offset)+len(d.Init) > len(m.Memory.Buffer) {
   273  				return fmt.Errorf("%s[%d]: out of bounds memory access", SectionIDName(SectionIDData), i)
   274  			}
   275  			copy(m.Memory.Buffer[offset:], d.Init)
   276  		}
   277  	}
   278  	return nil
   279  }
   280  
   281  // GetExport returns an export of the given name and type or errs if not exported or the wrong type.
   282  func (m *ModuleInstance) getExport(name string, et ExternType) (*ExportInstance, error) {
   283  	exp, ok := m.Exports[name]
   284  	if !ok {
   285  		return nil, fmt.Errorf("%q is not exported in module %q", name, m.Name)
   286  	}
   287  	if exp.Type != et {
   288  		return nil, fmt.Errorf("export %q in module %q is a %s, not a %s", name, m.Name, ExternTypeName(exp.Type), ExternTypeName(et))
   289  	}
   290  	return exp, nil
   291  }
   292  
   293  func NewStore(enabledFeatures api.CoreFeatures, engine Engine) (*Store, *Namespace) {
   294  	ns := newNamespace()
   295  	return &Store{
   296  		EnabledFeatures:  enabledFeatures,
   297  		Engine:           engine,
   298  		namespaces:       []*Namespace{ns},
   299  		typeIDs:          map[string]FunctionTypeID{},
   300  		functionMaxTypes: maximumFunctionTypes,
   301  	}, ns
   302  }
   303  
   304  // NewNamespace implements the same method as documented on wazero.Runtime.
   305  func (s *Store) NewNamespace(context.Context) *Namespace {
   306  	ns := newNamespace()
   307  	s.mux.Lock()
   308  	defer s.mux.Unlock()
   309  	s.namespaces = append(s.namespaces, ns)
   310  	return ns
   311  }
   312  
   313  // Instantiate uses name instead of the Module.NameSection ModuleName as it allows instantiating the same module under
   314  // different names safely and concurrently.
   315  //
   316  // * ctx: the default context used for function calls.
   317  // * name: the name of the module.
   318  // * sys: the system context, which will be closed (SysContext.Close) on CallContext.Close.
   319  //
   320  // Note: Module.Validate must be called prior to instantiation.
   321  func (s *Store) Instantiate(
   322  	ctx context.Context,
   323  	ns *Namespace,
   324  	module *Module,
   325  	name string,
   326  	sys *internalsys.Context,
   327  ) (*CallContext, error) {
   328  	// Collect any imported modules to avoid locking the namespace too long.
   329  	importedModuleNames := map[string]struct{}{}
   330  	for _, i := range module.ImportSection {
   331  		importedModuleNames[i.Module] = struct{}{}
   332  	}
   333  
   334  	// Read-Lock the namespace and ensure imports needed are present.
   335  	importedModules, err := ns.requireModules(importedModuleNames)
   336  	if err != nil {
   337  		return nil, err
   338  	}
   339  
   340  	// Write-Lock the namespace and claim the name of the current module.
   341  	if err = ns.requireModuleName(name); err != nil {
   342  		return nil, err
   343  	}
   344  
   345  	// Instantiate the module and add it to the namespace so that other modules can import it.
   346  	if callCtx, err := s.instantiate(ctx, ns, module, name, sys, importedModules); err != nil {
   347  		ns.deleteModule(name)
   348  		return nil, err
   349  	} else {
   350  		// Now that the instantiation is complete without error, add it.
   351  		// This makes the module visible for import, and ensures it is closed when the namespace is.
   352  		ns.addModule(callCtx.module)
   353  		return callCtx, nil
   354  	}
   355  }
   356  
   357  func (s *Store) instantiate(
   358  	ctx context.Context,
   359  	ns *Namespace,
   360  	module *Module,
   361  	name string,
   362  	sysCtx *internalsys.Context,
   363  	modules map[string]*ModuleInstance,
   364  ) (*CallContext, error) {
   365  	typeIDs, err := s.getFunctionTypeIDs(module.TypeSection)
   366  	if err != nil {
   367  		return nil, err
   368  	}
   369  
   370  	importedFunctions, importedGlobals, importedTables, importedMemory, err := resolveImports(module, modules)
   371  	if err != nil {
   372  		return nil, err
   373  	}
   374  
   375  	tables, tableInit, err := module.buildTables(importedTables, importedGlobals,
   376  		// As of reference-types proposal, boundary check must be done after instantiation.
   377  		s.EnabledFeatures.IsEnabled(api.CoreFeatureReferenceTypes))
   378  	if err != nil {
   379  		return nil, err
   380  	}
   381  	globals, memory := module.buildGlobals(importedGlobals), module.buildMemory()
   382  
   383  	m := &ModuleInstance{Name: name, TypeIDs: typeIDs}
   384  	functions := m.BuildFunctions(module)
   385  
   386  	// Now we have all instances from imports and local ones, so ready to create a new ModuleInstance.
   387  	m.addSections(module, importedFunctions, functions, importedGlobals, globals, tables, importedMemory, memory, module.TypeSection)
   388  
   389  	// As of reference types proposal, data segment validation must happen after instantiation,
   390  	// and the side effect must persist even if there's out of bounds error after instantiation.
   391  	// https://github.com/WebAssembly/spec/blob/d39195773112a22b245ffbe864bab6d1182ccb06/test/core/linking.wast#L395-L405
   392  	if !s.EnabledFeatures.IsEnabled(api.CoreFeatureReferenceTypes) {
   393  		if err = m.validateData(module.DataSection); err != nil {
   394  			return nil, err
   395  		}
   396  	}
   397  
   398  	// Plus, we are ready to compile functions.
   399  	m.Engine, err = s.Engine.NewModuleEngine(name, module, importedFunctions, functions)
   400  	if err != nil {
   401  		return nil, err
   402  	}
   403  
   404  	// After engine creation, we can create the funcref element instances and initialize funcref type globals.
   405  	m.buildElementInstances(module.ElementSection)
   406  	m.Engine.InitializeFuncrefGlobals(globals)
   407  
   408  	// Now all the validation passes, we are safe to mutate memory instances (possibly imported ones).
   409  	if err = m.applyData(module.DataSection); err != nil {
   410  		return nil, err
   411  	}
   412  
   413  	m.applyTableInits(tables, tableInit)
   414  
   415  	// Compile the default context for calls to this module.
   416  	callCtx := NewCallContext(ns, m, sysCtx)
   417  	m.CallCtx = callCtx
   418  
   419  	// Execute the start function.
   420  	if module.StartSection != nil {
   421  		funcIdx := *module.StartSection
   422  		f := m.Functions[funcIdx]
   423  
   424  		ce, err := f.Module.Engine.NewCallEngine(callCtx, f)
   425  		if err != nil {
   426  			return nil, fmt.Errorf("create call engine for start function[%s]: %v",
   427  				module.funcDesc(SectionIDFunction, funcIdx), err)
   428  		}
   429  
   430  		_, err = ce.Call(ctx, callCtx, nil)
   431  		if exitErr, ok := err.(*sys.ExitError); ok { // Don't wrap an exit error!
   432  			return nil, exitErr
   433  		} else if err != nil {
   434  			return nil, fmt.Errorf("start %s failed: %w", module.funcDesc(SectionIDFunction, funcIdx), err)
   435  		}
   436  	}
   437  
   438  	return m.CallCtx, nil
   439  }
   440  
   441  func resolveImports(module *Module, modules map[string]*ModuleInstance) (
   442  	importedFunctions []*FunctionInstance,
   443  	importedGlobals []*GlobalInstance,
   444  	importedTables []*TableInstance,
   445  	importedMemory *MemoryInstance,
   446  	err error,
   447  ) {
   448  	for idx, i := range module.ImportSection {
   449  		m, ok := modules[i.Module]
   450  		if !ok {
   451  			err = fmt.Errorf("module[%s] not instantiated", i.Module)
   452  			return
   453  		}
   454  
   455  		var imported *ExportInstance
   456  		imported, err = m.getExport(i.Name, i.Type)
   457  		if err != nil {
   458  			return
   459  		}
   460  
   461  		switch i.Type {
   462  		case ExternTypeFunc:
   463  			typeIndex := i.DescFunc
   464  			// TODO: this shouldn't be possible as invalid should fail validate
   465  			if int(typeIndex) >= len(module.TypeSection) {
   466  				err = errorInvalidImport(i, idx, fmt.Errorf("function type out of range"))
   467  				return
   468  			}
   469  			expectedType := module.TypeSection[i.DescFunc]
   470  			importedFunction := imported.Function
   471  
   472  			d := importedFunction.Definition
   473  			if !expectedType.EqualsSignature(d.ParamTypes(), d.ResultTypes()) {
   474  				actualType := &FunctionType{Params: d.ParamTypes(), Results: d.ResultTypes()}
   475  				err = errorInvalidImport(i, idx, fmt.Errorf("signature mismatch: %s != %s", expectedType, actualType))
   476  				return
   477  			}
   478  
   479  			importedFunctions = append(importedFunctions, importedFunction)
   480  		case ExternTypeTable:
   481  			expected := i.DescTable
   482  			importedTable := imported.Table
   483  			if expected.Type != importedTable.Type {
   484  				err = errorInvalidImport(i, idx, fmt.Errorf("table type mismatch: %s != %s",
   485  					RefTypeName(expected.Type), RefTypeName(importedTable.Type)))
   486  			}
   487  
   488  			if expected.Min > importedTable.Min {
   489  				err = errorMinSizeMismatch(i, idx, expected.Min, importedTable.Min)
   490  				return
   491  			}
   492  
   493  			if expected.Max != nil {
   494  				expectedMax := *expected.Max
   495  				if importedTable.Max == nil {
   496  					err = errorNoMax(i, idx, expectedMax)
   497  					return
   498  				} else if expectedMax < *importedTable.Max {
   499  					err = errorMaxSizeMismatch(i, idx, expectedMax, *importedTable.Max)
   500  					return
   501  				}
   502  			}
   503  			importedTables = append(importedTables, importedTable)
   504  		case ExternTypeMemory:
   505  			expected := i.DescMem
   506  			importedMemory = imported.Memory
   507  
   508  			if expected.Min > memoryBytesNumToPages(uint64(len(importedMemory.Buffer))) {
   509  				err = errorMinSizeMismatch(i, idx, expected.Min, importedMemory.Min)
   510  				return
   511  			}
   512  
   513  			if expected.Max < importedMemory.Max {
   514  				err = errorMaxSizeMismatch(i, idx, expected.Max, importedMemory.Max)
   515  				return
   516  			}
   517  		case ExternTypeGlobal:
   518  			expected := i.DescGlobal
   519  			importedGlobal := imported.Global
   520  
   521  			if expected.Mutable != importedGlobal.Type.Mutable {
   522  				err = errorInvalidImport(i, idx, fmt.Errorf("mutability mismatch: %t != %t",
   523  					expected.Mutable, importedGlobal.Type.Mutable))
   524  				return
   525  			}
   526  
   527  			if expected.ValType != importedGlobal.Type.ValType {
   528  				err = errorInvalidImport(i, idx, fmt.Errorf("value type mismatch: %s != %s",
   529  					ValueTypeName(expected.ValType), ValueTypeName(importedGlobal.Type.ValType)))
   530  				return
   531  			}
   532  			importedGlobals = append(importedGlobals, importedGlobal)
   533  		}
   534  	}
   535  	return
   536  }
   537  
   538  func errorMinSizeMismatch(i *Import, idx int, expected, actual uint32) error {
   539  	return errorInvalidImport(i, idx, fmt.Errorf("minimum size mismatch: %d > %d", expected, actual))
   540  }
   541  
   542  func errorNoMax(i *Import, idx int, expected uint32) error {
   543  	return errorInvalidImport(i, idx, fmt.Errorf("maximum size mismatch: %d, but actual has no max", expected))
   544  }
   545  
   546  func errorMaxSizeMismatch(i *Import, idx int, expected, actual uint32) error {
   547  	return errorInvalidImport(i, idx, fmt.Errorf("maximum size mismatch: %d < %d", expected, actual))
   548  }
   549  
   550  func errorInvalidImport(i *Import, idx int, err error) error {
   551  	return fmt.Errorf("import[%d] %s[%s.%s]: %w", idx, ExternTypeName(i.Type), i.Module, i.Name, err)
   552  }
   553  
   554  // Global initialization constant expression can only reference the imported globals.
   555  // See the note on https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#constant-expressions%E2%91%A0
   556  func executeConstExpression(importedGlobals []*GlobalInstance, expr *ConstantExpression) (v interface{}) {
   557  	switch expr.Opcode {
   558  	case OpcodeI32Const:
   559  		// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
   560  		v, _, _ = leb128.LoadInt32(expr.Data)
   561  	case OpcodeI64Const:
   562  		// Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
   563  		v, _, _ = leb128.LoadInt64(expr.Data)
   564  	case OpcodeF32Const:
   565  		v, _ = ieee754.DecodeFloat32(expr.Data)
   566  	case OpcodeF64Const:
   567  		v, _ = ieee754.DecodeFloat64(expr.Data)
   568  	case OpcodeGlobalGet:
   569  		id, _, _ := leb128.LoadUint32(expr.Data)
   570  		g := importedGlobals[id]
   571  		switch g.Type.ValType {
   572  		case ValueTypeI32:
   573  			v = int32(g.Val)
   574  		case ValueTypeI64:
   575  			v = int64(g.Val)
   576  		case ValueTypeF32:
   577  			v = api.DecodeF32(g.Val)
   578  		case ValueTypeF64:
   579  			v = api.DecodeF64(g.Val)
   580  		case ValueTypeV128:
   581  			v = [2]uint64{g.Val, g.ValHi}
   582  		}
   583  	case OpcodeRefNull:
   584  		switch expr.Data[0] {
   585  		case ValueTypeExternref:
   586  			v = int64(0) // Extern reference types are opaque 64bit pointer at runtime.
   587  		case ValueTypeFuncref:
   588  			// For funcref types, the pointer value will be set by Engines, so
   589  			// here we set the "invalid function index" (-1) to indicate that this should be null reference.
   590  			v = GlobalInstanceNullFuncRefValue
   591  		}
   592  	case OpcodeRefFunc:
   593  		// For ref.func const expression, we temporarily store the index as value,
   594  		// and if this is the const expr for global, the value will be further downed to
   595  		// opaque pointer of the engine-specific compiled function.
   596  		v, _, _ = leb128.LoadUint32(expr.Data)
   597  	case OpcodeVecV128Const:
   598  		v = [2]uint64{binary.LittleEndian.Uint64(expr.Data[0:8]), binary.LittleEndian.Uint64(expr.Data[8:16])}
   599  	}
   600  	return
   601  }
   602  
   603  // GlobalInstanceNullFuncRefValue is the temporary value for ValueTypeFuncref globals which are initialized via ref.null.
   604  const GlobalInstanceNullFuncRefValue int64 = -1
   605  
   606  func (s *Store) getFunctionTypeIDs(ts []*FunctionType) ([]FunctionTypeID, error) {
   607  	ret := make([]FunctionTypeID, len(ts))
   608  	for i, t := range ts {
   609  		inst, err := s.getFunctionTypeID(t)
   610  		if err != nil {
   611  			return nil, err
   612  		}
   613  		ret[i] = inst
   614  	}
   615  	return ret, nil
   616  }
   617  
   618  func (s *Store) getFunctionTypeID(t *FunctionType) (FunctionTypeID, error) {
   619  	key := t.key()
   620  	s.mux.RLock()
   621  	id, ok := s.typeIDs[key]
   622  	s.mux.RUnlock()
   623  	if !ok {
   624  		s.mux.Lock()
   625  		defer s.mux.Unlock()
   626  		// Check again in case another goroutine has already added the type.
   627  		if id, ok = s.typeIDs[key]; ok {
   628  			return id, nil
   629  		}
   630  		l := len(s.typeIDs)
   631  		if uint32(l) >= s.functionMaxTypes {
   632  			return 0, fmt.Errorf("too many function types in a store")
   633  		}
   634  		id = FunctionTypeID(l)
   635  		s.typeIDs[key] = id
   636  	}
   637  	return id, nil
   638  }
   639  
   640  // CloseWithExitCode implements the same method as documented on wazero.Runtime.
   641  func (s *Store) CloseWithExitCode(ctx context.Context, exitCode uint32) (err error) {
   642  	s.mux.Lock()
   643  	defer s.mux.Unlock()
   644  	// Close modules in reverse initialization order.
   645  	for i := len(s.namespaces) - 1; i >= 0; i-- {
   646  		// If closing this namespace errs, proceed anyway to close the others.
   647  		if e := s.namespaces[i].CloseWithExitCode(ctx, exitCode); e != nil && err == nil {
   648  			err = e // first error
   649  		}
   650  	}
   651  	s.namespaces = nil
   652  	s.typeIDs = nil
   653  	return
   654  }