use crate::tunables::Tunables;
use crate::WASM_MAX_PAGES;
use cranelift_codegen::ir;
use cranelift_entity::{EntityRef, PrimaryMap};
use cranelift_wasm::*;
use indexmap::IndexMap;
use more_asserts::assert_ge;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
pub struct TableElements {
pub table_index: TableIndex,
pub base: Option<GlobalIndex>,
pub offset: usize,
pub elements: Box<[FuncIndex]>,
}
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub enum MemoryStyle {
Dynamic,
Static {
bound: u32,
},
}
impl MemoryStyle {
pub fn for_memory(memory: Memory, tunables: &Tunables) -> (Self, u64) {
let maximum = memory.maximum.unwrap_or(WASM_MAX_PAGES);
if maximum <= tunables.static_memory_bound {
assert_ge!(tunables.static_memory_bound, memory.minimum);
return (
Self::Static {
bound: tunables.static_memory_bound,
},
tunables.static_memory_offset_guard_size,
);
}
(Self::Dynamic, tunables.dynamic_memory_offset_guard_size)
}
}
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub struct MemoryPlan {
pub memory: Memory,
pub style: MemoryStyle,
pub offset_guard_size: u64,
}
impl MemoryPlan {
pub fn for_memory(memory: Memory, tunables: &Tunables) -> Self {
let (style, offset_guard_size) = MemoryStyle::for_memory(memory, tunables);
Self {
memory,
style,
offset_guard_size,
}
}
}
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub enum TableStyle {
CallerChecksSignature,
}
impl TableStyle {
pub fn for_table(_table: Table, _tunables: &Tunables) -> Self {
Self::CallerChecksSignature
}
}
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub struct TablePlan {
pub table: cranelift_wasm::Table,
pub style: TableStyle,
}
impl TablePlan {
pub fn for_table(table: Table, tunables: &Tunables) -> Self {
let style = TableStyle::for_table(table, tunables);
Self { table, style }
}
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
#[allow(missing_docs)]
pub enum ModuleType {
Function(SignatureIndex),
Module(ModuleTypeIndex),
Instance(InstanceTypeIndex),
}
impl ModuleType {
pub fn unwrap_function(&self) -> SignatureIndex {
match self {
ModuleType::Function(f) => *f,
_ => panic!("not a function type"),
}
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct Module {
pub parent: Option<usize>,
pub name: Option<String>,
pub initializers: Vec<Initializer>,
pub exports: IndexMap<String, EntityIndex>,
pub start_func: Option<FuncIndex>,
pub table_elements: Vec<TableElements>,
pub passive_elements: HashMap<ElemIndex, Box<[FuncIndex]>>,
#[serde(with = "passive_data_serde")]
pub passive_data: HashMap<DataIndex, Arc<[u8]>>,
pub func_names: HashMap<FuncIndex, String>,
pub types: PrimaryMap<TypeIndex, ModuleType>,
pub num_imported_funcs: usize,
pub num_imported_tables: usize,
pub num_imported_memories: usize,
pub num_imported_globals: usize,
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
pub table_plans: PrimaryMap<TableIndex, TablePlan>,
pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
pub globals: PrimaryMap<GlobalIndex, Global>,
pub instances: PrimaryMap<InstanceIndex, InstanceTypeIndex>,
pub modules: PrimaryMap<ModuleIndex, ModuleTypeIndex>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Initializer {
Import {
module: String,
field: Option<String>,
index: EntityIndex,
},
AliasParentModule(ModuleIndex),
#[allow(missing_docs)]
AliasInstanceExport {
instance: InstanceIndex,
export: usize,
},
Instantiate {
module: ModuleIndex,
args: Vec<EntityIndex>,
},
DefineModule(usize),
}
impl Module {
pub fn new() -> Self {
Module::default()
}
pub fn get_passive_element(&self, index: ElemIndex) -> Option<&[FuncIndex]> {
self.passive_elements.get(&index).map(|es| &**es)
}
pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex {
FuncIndex::new(self.num_imported_funcs + defined_func.index())
}
pub fn defined_func_index(&self, func: FuncIndex) -> Option<DefinedFuncIndex> {
if func.index() < self.num_imported_funcs {
None
} else {
Some(DefinedFuncIndex::new(
func.index() - self.num_imported_funcs,
))
}
}
pub fn is_imported_function(&self, index: FuncIndex) -> bool {
index.index() < self.num_imported_funcs
}
pub fn table_index(&self, defined_table: DefinedTableIndex) -> TableIndex {
TableIndex::new(self.num_imported_tables + defined_table.index())
}
pub fn defined_table_index(&self, table: TableIndex) -> Option<DefinedTableIndex> {
if table.index() < self.num_imported_tables {
None
} else {
Some(DefinedTableIndex::new(
table.index() - self.num_imported_tables,
))
}
}
pub fn is_imported_table(&self, index: TableIndex) -> bool {
index.index() < self.num_imported_tables
}
pub fn memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex {
MemoryIndex::new(self.num_imported_memories + defined_memory.index())
}
pub fn defined_memory_index(&self, memory: MemoryIndex) -> Option<DefinedMemoryIndex> {
if memory.index() < self.num_imported_memories {
None
} else {
Some(DefinedMemoryIndex::new(
memory.index() - self.num_imported_memories,
))
}
}
pub fn is_imported_memory(&self, index: MemoryIndex) -> bool {
index.index() < self.num_imported_memories
}
pub fn global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex {
GlobalIndex::new(self.num_imported_globals + defined_global.index())
}
pub fn defined_global_index(&self, global: GlobalIndex) -> Option<DefinedGlobalIndex> {
if global.index() < self.num_imported_globals {
None
} else {
Some(DefinedGlobalIndex::new(
global.index() - self.num_imported_globals,
))
}
}
pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
index.index() < self.num_imported_globals
}
pub fn imports(&self) -> impl Iterator<Item = (&str, Option<&str>, EntityType)> {
self.initializers.iter().filter_map(move |i| match i {
Initializer::Import {
module,
field,
index,
} => Some((module.as_str(), field.as_deref(), self.type_of(*index))),
_ => None,
})
}
pub fn type_of(&self, index: EntityIndex) -> EntityType {
match index {
EntityIndex::Global(i) => EntityType::Global(self.globals[i]),
EntityIndex::Table(i) => EntityType::Table(self.table_plans[i].table),
EntityIndex::Memory(i) => EntityType::Memory(self.memory_plans[i].memory),
EntityIndex::Function(i) => EntityType::Function(self.functions[i]),
EntityIndex::Instance(i) => EntityType::Instance(self.instances[i]),
EntityIndex::Module(i) => EntityType::Module(self.modules[i]),
}
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[allow(missing_docs)]
pub struct TypeTables {
pub wasm_signatures: PrimaryMap<SignatureIndex, WasmFuncType>,
pub native_signatures: PrimaryMap<SignatureIndex, ir::Signature>,
pub module_signatures: PrimaryMap<ModuleTypeIndex, ModuleSignature>,
pub instance_signatures: PrimaryMap<InstanceTypeIndex, InstanceSignature>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleSignature {
pub imports: Vec<(String, Option<String>, EntityType)>,
pub exports: InstanceTypeIndex,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InstanceSignature {
pub exports: IndexMap<String, EntityType>,
}
mod passive_data_serde {
use super::{Arc, DataIndex, HashMap};
use serde::{de::MapAccess, de::Visitor, ser::SerializeMap, Deserializer, Serializer};
use std::fmt;
pub(super) fn serialize<S>(
data: &HashMap<DataIndex, Arc<[u8]>>,
ser: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = ser.serialize_map(Some(data.len()))?;
for (k, v) in data {
map.serialize_entry(k, v.as_ref())?;
}
map.end()
}
struct PassiveDataVisitor;
impl<'de> Visitor<'de> for PassiveDataVisitor {
type Value = HashMap<DataIndex, Arc<[u8]>>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a passive_data map")
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut map = HashMap::with_capacity(access.size_hint().unwrap_or(0));
while let Some((key, value)) = access.next_entry::<_, Vec<u8>>()? {
map.insert(key, value.into());
}
Ok(map)
}
}
pub(super) fn deserialize<'de, D>(de: D) -> Result<HashMap<DataIndex, Arc<[u8]>>, D::Error>
where
D: Deserializer<'de>,
{
de.deserialize_map(PassiveDataVisitor)
}
}