//! Module instance: per-module address-space view into the [`Store`]. //! //! An instance carries the parsed module's type section verbatim plus a //! handle for every function, table, memory, global, element segment, and //! data segment the module declared, in declaration order. Indices in the //! original module map 1:1 into these vectors; the store-side addresses //! ([`FuncAddr`] etc.) point at the actual runtime objects. //! //! Imports come first in each vector (so module-local index 0 of a function //! is the first import, if any), matching the binary-format rule. The //! `*_imports` counts make it cheap to ask "is this local index an import?" //! without scanning. use std::collections::HashMap; use crate::runtime::store::{DataAddr, ElementAddr, FuncAddr, GlobalAddr, MemoryAddr, TableAddr}; use crate::types::FuncType; /// An externally-visible value an instance can hand to another instance via /// imports or expose via exports. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ExternVal { Func(FuncAddr), Table(TableAddr), Memory(MemoryAddr), Global(GlobalAddr), } /// A live module instance. #[derive(Debug, Default, Clone)] pub struct ModuleInstance { pub types: Vec, pub func_addrs: Vec, pub table_addrs: Vec, pub memory_addrs: Vec, pub global_addrs: Vec, pub element_addrs: Vec, pub data_addrs: Vec, /// Number of imported functions sitting at the front of `func_addrs`. pub imported_funcs: u32, pub imported_tables: u32, pub imported_memories: u32, pub imported_globals: u32, /// Exports keyed by name. pub exports: HashMap, } impl ModuleInstance { /// A fresh, empty instance — used as the starting point during /// instantiation, which the instantiation pipeline fills in. pub fn new() -> Self { Self::default() } /// Resolve a module-local function index to a store address. pub fn resolve_func(&self, index: u32) -> Option { self.func_addrs.get(index as usize).copied() } /// Resolve a module-local table index to a store address. pub fn resolve_table(&self, index: u32) -> Option { self.table_addrs.get(index as usize).copied() } /// Resolve a module-local memory index to a store address. pub fn resolve_memory(&self, index: u32) -> Option { self.memory_addrs.get(index as usize).copied() } /// Resolve a module-local global index to a store address. pub fn resolve_global(&self, index: u32) -> Option { self.global_addrs.get(index as usize).copied() } /// Resolve a module-local element segment index to a store address. pub fn resolve_element(&self, index: u32) -> Option { self.element_addrs.get(index as usize).copied() } /// Resolve a module-local data segment index to a store address. pub fn resolve_data(&self, index: u32) -> Option { self.data_addrs.get(index as usize).copied() } /// True if the given module-local function index refers to an imported /// function (vs one defined in this module). pub fn is_func_imported(&self, index: u32) -> bool { index < self.imported_funcs } /// True if the given module-local table index refers to an imported /// table. pub fn is_table_imported(&self, index: u32) -> bool { index < self.imported_tables } /// True if the given module-local memory index refers to an imported /// memory. pub fn is_memory_imported(&self, index: u32) -> bool { index < self.imported_memories } /// True if the given module-local global index refers to an imported /// global. pub fn is_global_imported(&self, index: u32) -> bool { index < self.imported_globals } /// Look up an export by name. pub fn export(&self, name: &str) -> Option { self.exports.get(name).copied() } } #[cfg(test)] mod tests { use super::*; #[test] fn empty_instance() { let inst = ModuleInstance::new(); assert!(inst.types.is_empty()); assert!(inst.func_addrs.is_empty()); assert_eq!(inst.imported_funcs, 0); assert!(inst.resolve_func(0).is_none()); assert!(inst.export("anything").is_none()); } #[test] fn resolve_indices() { let mut inst = ModuleInstance::new(); inst.func_addrs.push(FuncAddr::from_raw(10)); inst.func_addrs.push(FuncAddr::from_raw(11)); inst.imported_funcs = 1; assert_eq!(inst.resolve_func(0), Some(FuncAddr::from_raw(10))); assert_eq!(inst.resolve_func(1), Some(FuncAddr::from_raw(11))); assert_eq!(inst.resolve_func(2), None); assert!(inst.is_func_imported(0)); assert!(!inst.is_func_imported(1)); } #[test] fn import_vs_own_partition() { let mut inst = ModuleInstance::new(); inst.table_addrs.push(TableAddr::from_raw(7)); inst.table_addrs.push(TableAddr::from_raw(8)); inst.imported_tables = 2; assert!(inst.is_table_imported(0)); assert!(inst.is_table_imported(1)); assert!(!inst.is_table_imported(2)); inst.memory_addrs.push(MemoryAddr::from_raw(0)); inst.imported_memories = 0; assert!(!inst.is_memory_imported(0)); inst.global_addrs.push(GlobalAddr::from_raw(0)); inst.global_addrs.push(GlobalAddr::from_raw(1)); inst.imported_globals = 1; assert!(inst.is_global_imported(0)); assert!(!inst.is_global_imported(1)); } #[test] fn exports_lookup() { let mut inst = ModuleInstance::new(); inst.exports .insert("main".to_string(), ExternVal::Func(FuncAddr::from_raw(0))); inst.exports.insert( "mem".to_string(), ExternVal::Memory(MemoryAddr::from_raw(0)), ); assert_eq!( inst.export("main"), Some(ExternVal::Func(FuncAddr::from_raw(0))) ); assert_eq!( inst.export("mem"), Some(ExternVal::Memory(MemoryAddr::from_raw(0))) ); assert_eq!(inst.export("missing"), None); } #[test] fn resolve_returns_none_for_out_of_range() { let inst = ModuleInstance::new(); assert!(inst.resolve_table(0).is_none()); assert!(inst.resolve_memory(0).is_none()); assert!(inst.resolve_global(0).is_none()); assert!(inst.resolve_element(0).is_none()); assert!(inst.resolve_data(0).is_none()); } }