package wasm import ( "fmt" "github.com/tetratelabs/wazero/api" ) // moduleListNode is a node in a doubly linked list of names. type moduleListNode struct { name string module *ModuleInstance next, prev *moduleListNode } // setModule makes the module visible for import. func (s *Store) setModule(m *ModuleInstance) error { s.mux.Lock() defer s.mux.Unlock() node, ok := s.nameToNode[m.ModuleName] if !ok { return fmt.Errorf("module[%s] name has not been required", m.ModuleName) } node.module = m return nil } // deleteModule makes the moduleName available for instantiation again. func (s *Store) deleteModule(node *moduleListNode) error { if node == nil { return nil } s.mux.Lock() defer s.mux.Unlock() // remove this module name if node.prev != nil { node.prev.next = node.next } if node.next != nil { node.next.prev = node.prev } if s.moduleList == node { s.moduleList = node.next } // clear the node state so it does not enter any other branch // on subsequent calls to deleteModule node.prev = nil node.next = nil if node.name != "" { delete(s.nameToNode, node.name) } return nil } // module returns the module of the given name or error if not in this store func (s *Store) module(moduleName string) (*ModuleInstance, error) { s.mux.RLock() defer s.mux.RUnlock() node, ok := s.nameToNode[moduleName] if !ok { return nil, fmt.Errorf("module[%s] not in store", moduleName) } if node.module == nil { return nil, fmt.Errorf("module[%s] not set in store", moduleName) } return node.module, nil } // requireModules returns all instantiated modules whose names equal the keys in the input, or errs if any are missing. func (s *Store) requireModules(moduleNames map[string]struct{}) (map[string]*ModuleInstance, error) { ret := make(map[string]*ModuleInstance, len(moduleNames)) s.mux.RLock() defer s.mux.RUnlock() for n := range moduleNames { node, ok := s.nameToNode[n] if !ok { return nil, fmt.Errorf("module[%s] not instantiated", n) } ret[n] = node.module } return ret, nil } // requireModuleName is a pre-flight check to reserve a module. // This must be reverted on error with deleteModule if initialization fails. func (s *Store) requireModuleName(moduleName string) (*moduleListNode, error) { node := &moduleListNode{name: moduleName} s.mux.Lock() defer s.mux.Unlock() if _, ok := s.nameToNode[moduleName]; ok { return nil, fmt.Errorf("module[%s] has already been instantiated", moduleName) } // add the newest node to the moduleNamesList as the head. node.next = s.moduleList if node.next != nil { node.next.prev = node } s.moduleList = node s.nameToNode[moduleName] = node return node, nil } func (s *Store) registerAnonymous() *moduleListNode { node := &moduleListNode{name: ""} s.mux.Lock() defer s.mux.Unlock() // add the newest node to the moduleNamesList as the head. node.next = s.moduleList if node.next != nil { node.next.prev = node } s.moduleList = node return node } // AliasModule aliases the instantiated module named `src` as `dst`. // // Note: This is only used for spectests. func (s *Store) AliasModule(src, dst string) error { s.mux.Lock() defer s.mux.Unlock() s.nameToNode[dst] = s.nameToNode[src] return nil } // Module implements wazero.Runtime Module func (s *Store) Module(moduleName string) api.Module { m, err := s.module(moduleName) if err != nil { return nil } return m }