use std::collections::HashMap; use wasmparser::{ BlockType, Data, Element, Export, ExternalKind, FunctionBody, Global, Import, LocalsReader, MemoryType, Name, NameSectionReader, Parser, Payload, Result, Table, Type, TypeRef, ValType, }; #[derive(PartialEq, Eq, Clone, Copy)] pub enum External { Func, Table, Memory, Global, Tag, } impl From for External { fn from(value: TypeRef) -> Self { match value { TypeRef::Func(_) => Self::Func, TypeRef::Table(_) => Self::Table, TypeRef::Memory(_) => Self::Memory, TypeRef::Global(_) => Self::Global, TypeRef::Tag(_) => Self::Tag, } } } impl From for External { fn from(value: ExternalKind) -> Self { match value { ExternalKind::Func => Self::Func, ExternalKind::Table => Self::Table, ExternalKind::Memory => Self::Memory, ExternalKind::Global => Self::Global, ExternalKind::Tag => Self::Tag, } } } pub(crate) fn read_checked(reader: I) -> Result> where I: IntoIterator>, { reader.into_iter().collect() } pub(crate) fn read_checked_locals(reader: LocalsReader) -> Result> { read_checked(reader).map(|locals| { let convert = |(a, b)| std::iter::repeat(b).take(usize::try_from(a).unwrap()); locals.into_iter().flat_map(convert).collect() }) } pub struct Module<'a> { type_section: Vec, import_section: Vec>, func_section: Vec, table_section: Vec>, memory_section: Vec, global_section: Vec>, export_section: Vec>, element_section: Vec>, data_section: Vec>, code_section: Vec>, name_section: HashMap, start_section: Option, } impl<'a> Module<'a> { /// # Errors /// /// Returns a `BinaryReaderError` if any module section is malformed. pub fn try_from_data(data: &'a [u8]) -> Result { let mut temp = Module { type_section: Vec::new(), import_section: Vec::new(), func_section: Vec::new(), table_section: Vec::new(), memory_section: Vec::new(), global_section: Vec::new(), export_section: Vec::new(), element_section: Vec::new(), data_section: Vec::new(), code_section: Vec::new(), name_section: HashMap::new(), start_section: None, }; temp.load_data(data)?; Ok(temp) } fn load_data(&mut self, data: &'a [u8]) -> Result<()> { for payload in Parser::new(0).parse_all(data) { match payload? { Payload::TypeSection(v) => self.type_section = read_checked(v)?, Payload::ImportSection(v) => self.import_section = read_checked(v)?, Payload::FunctionSection(v) => self.func_section = read_checked(v)?, Payload::TableSection(v) => self.table_section = read_checked(v)?, Payload::MemorySection(v) => self.memory_section = read_checked(v)?, Payload::GlobalSection(v) => self.global_section = read_checked(v)?, Payload::ExportSection(v) => self.export_section = read_checked(v)?, Payload::ElementSection(v) => self.element_section = read_checked(v)?, Payload::DataSection(v) => self.data_section = read_checked(v)?, Payload::CodeSectionEntry(v) => { self.code_section.push(v); } Payload::StartSection { func, .. } => { self.start_section = Some(func); } Payload::CustomSection(v) if v.name() == "name" => { for name in NameSectionReader::new(v.data(), v.data_offset()) { if let Name::Function(map) = name? { let mut iter = map.into_iter(); while let Some(Ok(elem)) = iter.next() { self.name_section.insert(elem.index, elem.name); } } } } _ => {} } } Ok(()) } #[must_use] pub fn import_count(&self, ext: External) -> usize { let predicate = |v: &&Import| External::from(v.ty) == ext; self.import_section.iter().filter(predicate).count() } #[must_use] pub fn function_space(&self) -> usize { self.import_count(External::Func) + self.func_section.len() } #[must_use] pub fn table_space(&self) -> usize { self.import_count(External::Table) + self.table_section.len() } #[must_use] pub fn memory_space(&self) -> usize { self.import_count(External::Memory) + self.memory_section.len() } #[must_use] pub fn global_space(&self) -> usize { self.import_count(External::Global) + self.global_section.len() } #[must_use] pub fn type_section(&self) -> &[Type] { &self.type_section } #[must_use] pub fn import_section(&self) -> &[Import] { &self.import_section } #[must_use] pub fn func_section(&self) -> &[u32] { &self.func_section } #[must_use] pub fn table_section(&self) -> &[Table] { &self.table_section } #[must_use] pub fn memory_section(&self) -> &[MemoryType] { &self.memory_section } #[must_use] pub fn global_section(&self) -> &[Global] { &self.global_section } #[must_use] pub fn export_section(&self) -> &[Export] { &self.export_section } #[must_use] pub fn element_section(&self) -> &[Element] { &self.element_section } #[must_use] pub fn data_section(&self) -> &[Data] { &self.data_section } #[must_use] pub fn code_section(&self) -> &[FunctionBody] { &self.code_section } #[must_use] pub const fn name_section(&self) -> &HashMap { &self.name_section } #[must_use] pub const fn start_section(&self) -> Option { self.start_section } } pub struct TypeInfo<'a> { type_list: &'a [Type], func_list: Vec, } impl<'a> TypeInfo<'a> { #[must_use] pub fn from_module(wasm: &'a Module) -> Self { let mut temp = Self { type_list: &wasm.type_section, func_list: Vec::new(), }; temp.load_import_list(&wasm.import_section); temp.load_func_list(&wasm.func_section); temp } fn load_import_list(&mut self, list: &[Import]) { let iter = list .iter() .copied() .filter_map(|v| match v.ty { TypeRef::Func(v) => Some(v), _ => None, }) .map(|v| usize::try_from(v).unwrap()); self.func_list.extend(iter); } fn load_func_list(&mut self, list: &[u32]) { let iter = list.iter().copied().map(|v| usize::try_from(v).unwrap()); self.func_list.extend(iter); } pub(crate) fn by_type_index(&self, index: usize) -> (usize, usize) { let Type::Func(ty) = &self.type_list[index] else { unreachable!("type at func index must be a func type"); }; (ty.params().len(), ty.results().len()) } pub(crate) fn by_func_index(&self, index: usize) -> (usize, usize) { let adjusted = self.func_list[index]; self.by_type_index(adjusted) } pub(crate) fn by_block_type(&self, ty: BlockType) -> (usize, usize) { match ty { BlockType::Empty => (0, 0), BlockType::Type(_) => (0, 1), BlockType::FuncType(i) => { let id = i.try_into().unwrap(); self.by_type_index(id) } } } }