use crate::entity::{EntityRef, PrimaryMap};
use crate::{
CustomSectionIndex, DataIndex, ElemIndex, ExportIndex, ExportType, ExternType, FunctionIndex,
FunctionType, GlobalIndex, GlobalInit, GlobalType, ImportIndex, ImportType, LocalFunctionIndex,
LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, MemoryType, ModuleHash,
SignatureIndex, TableIndex, TableInitializer, TableType,
};
use indexmap::IndexMap;
use rkyv::rancor::{Fallible, Source, Trace};
use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::fmt;
use std::iter::ExactSizeIterator;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)]
#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
#[rkyv(derive(Debug))]
pub struct ModuleId {
id: usize,
}
impl ModuleId {
pub fn id(&self) -> String {
format!("{}", &self.id)
}
}
impl Default for ModuleId {
fn default() -> Self {
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
Self {
id: NEXT_ID.fetch_add(1, SeqCst),
}
}
}
#[derive(Debug, Hash, Eq, PartialEq, Clone, Default, RkyvSerialize, RkyvDeserialize, Archive)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[rkyv(derive(PartialOrd, Ord, PartialEq, Eq, Hash, Debug))]
pub struct ImportKey {
pub module: String,
pub field: String,
pub import_idx: u32,
}
impl From<(String, String, u32)> for ImportKey {
fn from((module, field, import_idx): (String, String, u32)) -> Self {
Self {
module,
field,
import_idx,
}
}
}
#[cfg(feature = "enable-serde")]
mod serde_imports {
use crate::ImportIndex;
use crate::ImportKey;
use indexmap::IndexMap;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
type InitialType = IndexMap<ImportKey, ImportIndex>;
type SerializedType = Vec<(ImportKey, ImportIndex)>;
pub fn serialize<S: Serializer>(s: &InitialType, serializer: S) -> Result<S::Ok, S::Error> {
let vec: SerializedType = s
.iter()
.map(|(a, b)| (a.clone(), b.clone()))
.collect::<Vec<_>>();
vec.serialize(serializer)
}
pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<InitialType, D::Error> {
let serialized = <SerializedType as Deserialize>::deserialize(deserializer)?;
Ok(serialized.into_iter().collect())
}
}
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
pub struct ModuleInfo {
#[cfg_attr(feature = "enable-serde", serde(skip_serializing, skip_deserializing))]
pub id: ModuleId,
pub hash: Option<ModuleHash>,
pub name: Option<String>,
#[cfg_attr(feature = "enable-serde", serde(with = "serde_imports"))]
pub imports: IndexMap<ImportKey, ImportIndex>,
pub exports: IndexMap<String, ExportIndex>,
pub start_function: Option<FunctionIndex>,
pub table_initializers: Vec<TableInitializer>,
pub passive_elements: HashMap<ElemIndex, Box<[FunctionIndex]>>,
pub passive_data: HashMap<DataIndex, Box<[u8]>>,
pub global_initializers: PrimaryMap<LocalGlobalIndex, GlobalInit>,
pub function_names: HashMap<FunctionIndex, String>,
pub signatures: PrimaryMap<SignatureIndex, FunctionType>,
pub functions: PrimaryMap<FunctionIndex, SignatureIndex>,
pub tables: PrimaryMap<TableIndex, TableType>,
pub memories: PrimaryMap<MemoryIndex, MemoryType>,
pub globals: PrimaryMap<GlobalIndex, GlobalType>,
pub custom_sections: IndexMap<String, CustomSectionIndex>,
pub custom_sections_data: PrimaryMap<CustomSectionIndex, Box<[u8]>>,
pub num_imported_functions: usize,
pub num_imported_tables: usize,
pub num_imported_memories: usize,
pub num_imported_globals: usize,
}
#[derive(Debug, RkyvSerialize, RkyvDeserialize, Archive)]
#[rkyv(derive(Debug))]
pub struct ArchivableModuleInfo {
name: Option<String>,
hash: Option<ModuleHash>,
imports: IndexMap<ImportKey, ImportIndex>,
exports: IndexMap<String, ExportIndex>,
start_function: Option<FunctionIndex>,
table_initializers: Vec<TableInitializer>,
passive_elements: BTreeMap<ElemIndex, Box<[FunctionIndex]>>,
passive_data: BTreeMap<DataIndex, Box<[u8]>>,
global_initializers: PrimaryMap<LocalGlobalIndex, GlobalInit>,
function_names: BTreeMap<FunctionIndex, String>,
signatures: PrimaryMap<SignatureIndex, FunctionType>,
functions: PrimaryMap<FunctionIndex, SignatureIndex>,
tables: PrimaryMap<TableIndex, TableType>,
memories: PrimaryMap<MemoryIndex, MemoryType>,
globals: PrimaryMap<GlobalIndex, GlobalType>,
custom_sections: IndexMap<String, CustomSectionIndex>,
custom_sections_data: PrimaryMap<CustomSectionIndex, Box<[u8]>>,
num_imported_functions: usize,
num_imported_tables: usize,
num_imported_memories: usize,
num_imported_globals: usize,
}
impl From<ModuleInfo> for ArchivableModuleInfo {
fn from(it: ModuleInfo) -> Self {
Self {
name: it.name,
hash: it.hash,
imports: it.imports,
exports: it.exports,
start_function: it.start_function,
table_initializers: it.table_initializers,
passive_elements: it.passive_elements.into_iter().collect(),
passive_data: it.passive_data.into_iter().collect(),
global_initializers: it.global_initializers,
function_names: it.function_names.into_iter().collect(),
signatures: it.signatures,
functions: it.functions,
tables: it.tables,
memories: it.memories,
globals: it.globals,
custom_sections: it.custom_sections,
custom_sections_data: it.custom_sections_data,
num_imported_functions: it.num_imported_functions,
num_imported_tables: it.num_imported_tables,
num_imported_memories: it.num_imported_memories,
num_imported_globals: it.num_imported_globals,
}
}
}
impl From<ArchivableModuleInfo> for ModuleInfo {
fn from(it: ArchivableModuleInfo) -> Self {
Self {
id: Default::default(),
name: it.name,
hash: it.hash,
imports: it.imports,
exports: it.exports,
start_function: it.start_function,
table_initializers: it.table_initializers,
passive_elements: it.passive_elements.into_iter().collect(),
passive_data: it.passive_data.into_iter().collect(),
global_initializers: it.global_initializers,
function_names: it.function_names.into_iter().collect(),
signatures: it.signatures,
functions: it.functions,
tables: it.tables,
memories: it.memories,
globals: it.globals,
custom_sections: it.custom_sections,
custom_sections_data: it.custom_sections_data,
num_imported_functions: it.num_imported_functions,
num_imported_tables: it.num_imported_tables,
num_imported_memories: it.num_imported_memories,
num_imported_globals: it.num_imported_globals,
}
}
}
impl From<&ModuleInfo> for ArchivableModuleInfo {
fn from(it: &ModuleInfo) -> Self {
Self::from(it.clone())
}
}
impl Archive for ModuleInfo {
type Archived = <ArchivableModuleInfo as Archive>::Archived;
type Resolver = <ArchivableModuleInfo as Archive>::Resolver;
fn resolve(&self, resolver: Self::Resolver, out: rkyv::Place<Self::Archived>) {
ArchivableModuleInfo::from(self).resolve(resolver, out)
}
}
impl<S: rkyv::ser::Allocator + rkyv::ser::Writer + Fallible + ?Sized> RkyvSerialize<S>
for ModuleInfo
where
<S as Fallible>::Error: rkyv::rancor::Source + rkyv::rancor::Trace,
{
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
ArchivableModuleInfo::from(self).serialize(serializer)
}
}
impl<D: Fallible + ?Sized> RkyvDeserialize<ModuleInfo, D> for ArchivedArchivableModuleInfo
where
D::Error: Source + Trace,
{
fn deserialize(&self, deserializer: &mut D) -> Result<ModuleInfo, D::Error> {
let archived = RkyvDeserialize::<ArchivableModuleInfo, D>::deserialize(self, deserializer)?;
Ok(ModuleInfo::from(archived))
}
}
impl PartialEq for ModuleInfo {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
&& self.imports == other.imports
&& self.exports == other.exports
&& self.start_function == other.start_function
&& self.table_initializers == other.table_initializers
&& self.passive_elements == other.passive_elements
&& self.passive_data == other.passive_data
&& self.global_initializers == other.global_initializers
&& self.function_names == other.function_names
&& self.signatures == other.signatures
&& self.functions == other.functions
&& self.tables == other.tables
&& self.memories == other.memories
&& self.globals == other.globals
&& self.custom_sections == other.custom_sections
&& self.custom_sections_data == other.custom_sections_data
&& self.num_imported_functions == other.num_imported_functions
&& self.num_imported_tables == other.num_imported_tables
&& self.num_imported_memories == other.num_imported_memories
&& self.num_imported_globals == other.num_imported_globals
}
}
impl Eq for ModuleInfo {}
impl ModuleInfo {
pub fn new() -> Self {
Default::default()
}
pub fn hash(&self) -> Option<ModuleHash> {
self.hash
}
pub fn get_passive_element(&self, index: ElemIndex) -> Option<&[FunctionIndex]> {
self.passive_elements.get(&index).map(|es| &**es)
}
pub fn exported_signatures(&self) -> Vec<FunctionType> {
self.exports
.iter()
.filter_map(|(_name, export_index)| match export_index {
ExportIndex::Function(i) => {
let signature = self.functions.get(*i).unwrap();
let func_type = self.signatures.get(*signature).unwrap();
Some(func_type.clone())
}
_ => None,
})
.collect::<Vec<FunctionType>>()
}
pub fn exports(&'_ self) -> ExportsIterator<impl Iterator<Item = ExportType> + '_> {
let iter = self.exports.iter().map(move |(name, export_index)| {
let extern_type = match export_index {
ExportIndex::Function(i) => {
let signature = self.functions.get(*i).unwrap();
let func_type = self.signatures.get(*signature).unwrap();
ExternType::Function(func_type.clone())
}
ExportIndex::Table(i) => {
let table_type = self.tables.get(*i).unwrap();
ExternType::Table(*table_type)
}
ExportIndex::Memory(i) => {
let memory_type = self.memories.get(*i).unwrap();
ExternType::Memory(*memory_type)
}
ExportIndex::Global(i) => {
let global_type = self.globals.get(*i).unwrap();
ExternType::Global(*global_type)
}
};
ExportType::new(name, extern_type)
});
ExportsIterator::new(iter, self.exports.len())
}
pub fn imports(&'_ self) -> ImportsIterator<impl Iterator<Item = ImportType> + '_> {
let iter =
self.imports
.iter()
.map(move |(ImportKey { module, field, .. }, import_index)| {
let extern_type = match import_index {
ImportIndex::Function(i) => {
let signature = self.functions.get(*i).unwrap();
let func_type = self.signatures.get(*signature).unwrap();
ExternType::Function(func_type.clone())
}
ImportIndex::Table(i) => {
let table_type = self.tables.get(*i).unwrap();
ExternType::Table(*table_type)
}
ImportIndex::Memory(i) => {
let memory_type = self.memories.get(*i).unwrap();
ExternType::Memory(*memory_type)
}
ImportIndex::Global(i) => {
let global_type = self.globals.get(*i).unwrap();
ExternType::Global(*global_type)
}
};
ImportType::new(module, field, extern_type)
});
ImportsIterator::new(iter, self.imports.len())
}
pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator<Item = Box<[u8]>> + 'a {
self.custom_sections
.iter()
.filter_map(move |(section_name, section_index)| {
if name != section_name {
return None;
}
Some(self.custom_sections_data[*section_index].clone())
})
}
pub fn func_index(&self, local_func: LocalFunctionIndex) -> FunctionIndex {
FunctionIndex::new(self.num_imported_functions + local_func.index())
}
pub fn local_func_index(&self, func: FunctionIndex) -> Option<LocalFunctionIndex> {
func.index()
.checked_sub(self.num_imported_functions)
.map(LocalFunctionIndex::new)
}
pub fn is_imported_function(&self, index: FunctionIndex) -> bool {
index.index() < self.num_imported_functions
}
pub fn table_index(&self, local_table: LocalTableIndex) -> TableIndex {
TableIndex::new(self.num_imported_tables + local_table.index())
}
pub fn local_table_index(&self, table: TableIndex) -> Option<LocalTableIndex> {
table
.index()
.checked_sub(self.num_imported_tables)
.map(LocalTableIndex::new)
}
pub fn is_imported_table(&self, index: TableIndex) -> bool {
index.index() < self.num_imported_tables
}
pub fn memory_index(&self, local_memory: LocalMemoryIndex) -> MemoryIndex {
MemoryIndex::new(self.num_imported_memories + local_memory.index())
}
pub fn local_memory_index(&self, memory: MemoryIndex) -> Option<LocalMemoryIndex> {
memory
.index()
.checked_sub(self.num_imported_memories)
.map(LocalMemoryIndex::new)
}
pub fn is_imported_memory(&self, index: MemoryIndex) -> bool {
index.index() < self.num_imported_memories
}
pub fn global_index(&self, local_global: LocalGlobalIndex) -> GlobalIndex {
GlobalIndex::new(self.num_imported_globals + local_global.index())
}
pub fn local_global_index(&self, global: GlobalIndex) -> Option<LocalGlobalIndex> {
global
.index()
.checked_sub(self.num_imported_globals)
.map(LocalGlobalIndex::new)
}
pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
index.index() < self.num_imported_globals
}
pub fn name(&self) -> String {
match self.name {
Some(ref name) => name.to_string(),
None => "<module>".to_string(),
}
}
pub fn imported_function_types(&'_ self) -> impl Iterator<Item = FunctionType> + '_ {
self.functions
.values()
.take(self.num_imported_functions)
.map(move |sig_index| self.signatures[*sig_index].clone())
}
}
impl fmt::Display for ModuleInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
pub struct ExportsIterator<I: Iterator<Item = ExportType> + Sized> {
iter: I,
size: usize,
}
impl<I: Iterator<Item = ExportType> + Sized> ExportsIterator<I> {
pub fn new(iter: I, size: usize) -> Self {
Self { iter, size }
}
}
impl<I: Iterator<Item = ExportType> + Sized> ExactSizeIterator for ExportsIterator<I> {
fn len(&self) -> usize {
self.size
}
}
impl<I: Iterator<Item = ExportType> + Sized> ExportsIterator<I> {
pub fn functions(self) -> impl Iterator<Item = ExportType<FunctionType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Function(ty) => Some(ExportType::new(extern_.name(), ty.clone())),
_ => None,
})
}
pub fn memories(self) -> impl Iterator<Item = ExportType<MemoryType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Memory(ty) => Some(ExportType::new(extern_.name(), *ty)),
_ => None,
})
}
pub fn tables(self) -> impl Iterator<Item = ExportType<TableType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Table(ty) => Some(ExportType::new(extern_.name(), *ty)),
_ => None,
})
}
pub fn globals(self) -> impl Iterator<Item = ExportType<GlobalType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Global(ty) => Some(ExportType::new(extern_.name(), *ty)),
_ => None,
})
}
}
impl<I: Iterator<Item = ExportType> + Sized> Iterator for ExportsIterator<I> {
type Item = ExportType;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
pub struct ImportsIterator<I: Iterator<Item = ImportType> + Sized> {
iter: I,
size: usize,
}
impl<I: Iterator<Item = ImportType> + Sized> ImportsIterator<I> {
pub fn new(iter: I, size: usize) -> Self {
Self { iter, size }
}
}
impl<I: Iterator<Item = ImportType> + Sized> ExactSizeIterator for ImportsIterator<I> {
fn len(&self) -> usize {
self.size
}
}
impl<I: Iterator<Item = ImportType> + Sized> ImportsIterator<I> {
pub fn functions(self) -> impl Iterator<Item = ImportType<FunctionType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Function(ty) => Some(ImportType::new(
extern_.module(),
extern_.name(),
ty.clone(),
)),
_ => None,
})
}
pub fn memories(self) -> impl Iterator<Item = ImportType<MemoryType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Memory(ty) => Some(ImportType::new(extern_.module(), extern_.name(), *ty)),
_ => None,
})
}
pub fn tables(self) -> impl Iterator<Item = ImportType<TableType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Table(ty) => Some(ImportType::new(extern_.module(), extern_.name(), *ty)),
_ => None,
})
}
pub fn globals(self) -> impl Iterator<Item = ImportType<GlobalType>> + Sized {
self.iter.filter_map(|extern_| match extern_.ty() {
ExternType::Global(ty) => Some(ImportType::new(extern_.module(), extern_.name(), *ty)),
_ => None,
})
}
}
impl<I: Iterator<Item = ImportType> + Sized> Iterator for ImportsIterator<I> {
type Item = ImportType;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}