use crate::tunables::Tunables;
use crate::WASM_MAX_PAGES;
use cranelift_codegen::ir;
use cranelift_entity::{EntityRef, PrimaryMap};
use cranelift_wasm::{
DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table,
TableIndex, WasmFuncType,
};
use indexmap::IndexMap;
use more_asserts::assert_ge;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{
atomic::{AtomicUsize, Ordering::SeqCst},
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(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum EntityIndex {
Function(FuncIndex),
Table(TableIndex),
Memory(MemoryIndex),
Global(GlobalIndex),
}
#[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, Clone, Serialize, Deserialize)]
pub struct Module {
#[serde(skip_serializing, skip_deserializing, default = "Module::next_id")]
pub id: usize,
pub name: Option<String>,
pub imports: Vec<(String, String, EntityIndex)>,
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 signatures: PrimaryMap<SignatureIndex, (WasmFuncType, ir::Signature)>,
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>,
}
impl Module {
pub fn new() -> Self {
Self {
id: Self::next_id(),
name: None,
imports: Vec::new(),
exports: IndexMap::new(),
start_func: None,
table_elements: Vec::new(),
passive_elements: HashMap::new(),
passive_data: HashMap::new(),
func_names: HashMap::new(),
num_imported_funcs: 0,
num_imported_tables: 0,
num_imported_memories: 0,
num_imported_globals: 0,
signatures: PrimaryMap::new(),
functions: PrimaryMap::new(),
table_plans: PrimaryMap::new(),
memory_plans: PrimaryMap::new(),
globals: PrimaryMap::new(),
}
}
pub fn get_passive_element(&self, index: ElemIndex) -> Option<&[FuncIndex]> {
self.passive_elements.get(&index).map(|es| &**es)
}
fn next_id() -> usize {
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
NEXT_ID.fetch_add(1, SeqCst)
}
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 native_func_signature(&self, func_index: FuncIndex) -> &ir::Signature {
&self.signatures[self.functions[func_index]].1
}
pub fn wasm_func_type(&self, func_index: FuncIndex) -> &WasmFuncType {
&self.signatures[self.functions[func_index]].0
}
}
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)
}
}