//! Data structures for representing decoded wasm modules. use cranelift_codegen::ir; use cranelift_entity::{EntityRef, PrimaryMap}; use cranelift_wasm::{ DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex, }; use std::cmp; use std::collections::HashMap; use std::string::String; use std::vec::Vec; use tunables::Tunables; /// A WebAssembly table initializer. #[derive(Clone, Debug)] pub struct TableElements { /// The index of a table to initialize. pub table_index: TableIndex, /// Optionally, a global variable giving a base index. pub base: Option, /// The offset to add to the base. pub offset: usize, /// The values to write into the table elements. pub elements: Vec, } /// An entity to export. #[derive(Clone, Debug)] pub enum Export { /// Function export. Function(FuncIndex), /// Table export. Table(TableIndex), /// Memory export. Memory(MemoryIndex), /// Global export. Global(GlobalIndex), } /// Implemenation styles for WebAssembly linear memory. #[derive(Debug, Clone)] pub enum MemoryStyle { /// The actual memory can be resized and moved. Dynamic, /// Addresss space is allocated up front. Static { /// The number of mapped and unmapped pages. bound: u32, }, } impl MemoryStyle { /// Decide on an implementation style for the given `Memory`. pub fn for_memory(memory: Memory, tunables: &Tunables) -> (Self, u64) { if let Some(maximum) = memory.maximum { // A heap with a declared maximum is prepared to be used with // threads and therefore be immovable, so make it static. ( MemoryStyle::Static { bound: cmp::max(tunables.static_memory_bound, maximum), }, tunables.static_memory_offset_guard_size, ) } else { // A heap without a declared maximum is likely to want to be small // at least some of the time, so make it dynamic. ( MemoryStyle::Dynamic, tunables.dynamic_memory_offset_guard_size, ) } } } /// A WebAssembly linear memory description along with our chosen style for /// implementing it. #[derive(Debug)] pub struct MemoryPlan { /// The WebAssembly linear memory description. pub memory: Memory, /// Our chosen implementation style. pub style: MemoryStyle, /// Our chosen offset-guard size. pub offset_guard_size: u64, } impl MemoryPlan { /// Draw up a plan for implementing a `Memory`. 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, } } } /// A translated WebAssembly module, excluding the function bodies and /// memory initializers. #[derive(Debug)] pub struct Module { /// Unprocessed signatures exactly as provided by `declare_signature()`. pub signatures: PrimaryMap, /// Names of imported functions. pub imported_funcs: PrimaryMap, /// Types of functions, imported and local. pub functions: PrimaryMap, /// WebAssembly tables. pub tables: PrimaryMap, /// WebAssembly linear memory plans. pub memory_plans: PrimaryMap, /// WebAssembly global variables. pub globals: PrimaryMap, /// Exported entities. pub exports: HashMap, /// The module "start" function, if present. pub start_func: Option, /// WebAssembly table initializers. pub table_elements: Vec, } impl Module { /// Allocates the module data structures. pub fn new() -> Self { Self { signatures: PrimaryMap::new(), imported_funcs: PrimaryMap::new(), functions: PrimaryMap::new(), tables: PrimaryMap::new(), memory_plans: PrimaryMap::new(), globals: PrimaryMap::new(), exports: HashMap::new(), start_func: None, table_elements: Vec::new(), } } /// Convert a `DefinedFuncIndex` into a `FuncIndex`. pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex { FuncIndex::new(self.imported_funcs.len() + defined_func.index()) } /// Convert a `FuncIndex` into a `DefinedFuncIndex`. Returns None if the /// index is an imported function. pub fn defined_func_index(&self, func: FuncIndex) -> Option { if func.index() < self.imported_funcs.len() { None } else { Some(DefinedFuncIndex::new( func.index() - self.imported_funcs.len(), )) } } } /// A data initializer for linear memory. pub struct DataInitializer<'data> { /// The index of the memory to initialize. pub memory_index: MemoryIndex, /// Optionally a globalvar base to initialize at. pub base: Option, /// A constant offset to initialize at. pub offset: usize, /// The initialization data. pub data: &'data [u8], } /// References to the input wasm data buffer to be decoded and processed later, /// separately from the main module translation. pub struct LazyContents<'data> { /// References to the function bodies. pub function_body_inputs: PrimaryMap, /// References to the data initializers. pub data_initializers: Vec>, } impl<'data> LazyContents<'data> { pub fn new() -> Self { Self { function_body_inputs: PrimaryMap::new(), data_initializers: Vec::new(), } } }