use super::invoke::{Invoke, Identity}; use super::code::{self, SignaturesBuilder, FunctionBuilder}; use super::memory::{self, MemoryBuilder}; use super::table::{self, TableBuilder}; use super::{import, export, global, data}; use elements; /// Module builder pub struct ModuleBuilder { callback: F, module: ModuleScaffold, } /// Location of the internal module function pub struct CodeLocation { /// Location (index in 'functions' section) of the signature pub signature: u32, /// Location (index in the 'code' section) of the body pub body: u32, } #[derive(Default)] struct ModuleScaffold { pub types: elements::TypeSection, pub import: elements::ImportSection, pub functions: elements::FunctionSection, pub table: elements::TableSection, pub memory: elements::MemorySection, pub global: elements::GlobalSection, pub export: elements::ExportSection, pub start: Option, pub element: elements::ElementSection, pub code: elements::CodeSection, pub data: elements::DataSection, pub other: Vec, } impl From for ModuleScaffold { fn from(module: elements::Module) -> Self { let mut types: Option = None; let mut import: Option = None; let mut funcs: Option = None; let mut table: Option = None; let mut memory: Option = None; let mut global: Option = None; let mut export: Option = None; let mut start: Option = None; let mut element: Option = None; let mut code: Option = None; let mut data: Option = None; let mut sections = module.into_sections(); while let Some(section) = sections.pop() { match section { elements::Section::Type(sect) => { types = Some(sect); } elements::Section::Import(sect) => { import = Some(sect); } elements::Section::Function(sect) => { funcs = Some(sect); } elements::Section::Table(sect) => { table = Some(sect); } elements::Section::Memory(sect) => { memory = Some(sect); } elements::Section::Global(sect) => { global = Some(sect); } elements::Section::Export(sect) => { export = Some(sect); } elements::Section::Start(index) => { start = Some(index); } elements::Section::Element(sect) => { element = Some(sect); } elements::Section::Code(sect) => { code = Some(sect); } elements::Section::Data(sect) => { data = Some(sect); } _ => {} } } ModuleScaffold { types: types.unwrap_or_default(), import: import.unwrap_or_default(), functions: funcs.unwrap_or_default(), table: table.unwrap_or_default(), memory: memory.unwrap_or_default(), global: global.unwrap_or_default(), export: export.unwrap_or_default(), start: start, element: element.unwrap_or_default(), code: code.unwrap_or_default(), data: data.unwrap_or_default(), other: sections, } } } impl From for elements::Module { fn from(module: ModuleScaffold) -> Self { let mut sections = Vec::new(); let types = module.types; if types.types().len() > 0 { sections.push(elements::Section::Type(types)); } let import = module.import; if import.entries().len() > 0 { sections.push(elements::Section::Import(import)); } let functions = module.functions; if functions.entries().len() > 0 { sections.push(elements::Section::Function(functions)); } let table = module.table; if table.entries().len() > 0 { sections.push(elements::Section::Table(table)); } let memory = module.memory; if memory.entries().len() > 0 { sections.push(elements::Section::Memory(memory)); } let global = module.global; if global.entries().len() > 0 { sections.push(elements::Section::Global(global)); } let export = module.export; if export.entries().len() > 0 { sections.push(elements::Section::Export(export)); } if let Some(start) = module.start { sections.push(elements::Section::Start(start)); } let element = module.element; if element.entries().len() > 0 { sections.push(elements::Section::Element(element)); } let code = module.code; if code.bodies().len() > 0 { sections.push(elements::Section::Code(code)); } let data = module.data; if data.entries().len() > 0 { sections.push(elements::Section::Data(data)); } sections.extend(module.other); elements::Module::new(sections) } } impl ModuleBuilder { /// New empty module builder pub fn new() -> Self { ModuleBuilder::with_callback(Identity) } } impl ModuleBuilder where F: Invoke { /// New module builder with bound callback pub fn with_callback(callback: F) -> Self { ModuleBuilder { callback: callback, module: Default::default(), } } /// Builder from raw module pub fn with_module(mut self, module: elements::Module) -> Self { self.module = module.into(); self } /// Fill module with sections from iterator pub fn with_sections(mut self, sections: I) -> Self where I: IntoIterator { self.module.other.extend(sections); self } /// Add additional section pub fn with_section(mut self, section: elements::Section) -> Self { self.module.other.push(section); self } /// Binds to the type section, creates additional types when required pub fn with_signatures(mut self, bindings: code::SignatureBindings) -> Self { self.push_signatures(bindings); self } /// Push stand-alone function definition, creating sections, signature and code blocks /// in corresponding sections. /// `FunctionDefinition` can be build using `builder::function` builder pub fn push_function(&mut self, func: code::FunctionDefinition) -> CodeLocation { let signature = func.signature; let body = func.code; let type_ref = self.resolve_type_ref(signature); self.module.functions.entries_mut().push(elements::Func::new(type_ref)); let signature_index = self.module.functions.entries_mut().len() as u32 - 1; self.module.code.bodies_mut().push(body); let body_index = self.module.code.bodies_mut().len() as u32 - 1; if func.is_main { self.module.start = Some(body_index); } CodeLocation { signature: signature_index, body: body_index, } } /// Push linear memory region pub fn push_memory(&mut self, mut memory: memory::MemoryDefinition) -> u32 { let entries = self.module.memory.entries_mut(); entries.push(elements::MemoryType::new(memory.min, memory.max)); let memory_index = (entries.len() - 1) as u32; for data in memory.data.drain(..) { self.module.data.entries_mut() .push(elements::DataSegment::new(memory_index, data.offset, data.values)) } memory_index } /// Push table pub fn push_table(&mut self, mut table: table::TableDefinition) -> u32 { let entries = self.module.table.entries_mut(); entries.push(elements::TableType::new(table.min, Some(table.min))); let table_index = (entries.len() - 1) as u32; for entry in table.elements.drain(..) { self.module.element.entries_mut() .push(elements::ElementSegment::new(table_index, entry.offset, entry.values)) } table_index } fn resolve_type_ref(&mut self, signature: code::Signature) -> u32 { match signature { code::Signature::Inline(func_type) => { // todo: maybe search for existing type self.module.types.types_mut().push(elements::Type::Function(func_type)); self.module.types.types().len() as u32 - 1 } code::Signature::TypeReference(type_ref) => { type_ref } } } /// Push one function signature, returning it's calling index. /// Can create corresponding type in type section. pub fn push_signature(&mut self, signature: code::Signature) -> u32 { self.resolve_type_ref(signature) } /// Push signatures in the module, returning corresponding indices of pushed signatures pub fn push_signatures(&mut self, signatures: code::SignatureBindings) -> Vec { signatures.into_iter().map(|binding| self.resolve_type_ref(binding) ).collect() } /// Push import entry to module. Not that it does not update calling indices in /// function bodies. pub fn push_import(&mut self, import: elements::ImportEntry) -> u32 { self.module.import.entries_mut().push(import); // todo: actually update calling addresses in function bodies // todo: also batch push self.module.import.entries_mut().len() as u32 - 1 } /// Push export entry to module. pub fn push_export(&mut self, export: elements::ExportEntry) -> u32 { self.module.export.entries_mut().push(export); self.module.export.entries_mut().len() as u32 - 1 } /// Add new function using dedicated builder pub fn function(self) -> FunctionBuilder { FunctionBuilder::with_callback(self) } /// Add new linear memory using dedicated builder pub fn memory(self) -> MemoryBuilder { MemoryBuilder::with_callback(self) } /// Add new table using dedicated builder pub fn table(self) -> TableBuilder { TableBuilder::with_callback(self) } /// Define functions section pub fn functions(self) -> SignaturesBuilder { SignaturesBuilder::with_callback(self) } /// With inserted export entry pub fn with_export(mut self, entry: elements::ExportEntry) -> Self { self.module.export.entries_mut().push(entry); self } /// With inserted import entry pub fn with_import(mut self, entry: elements::ImportEntry) -> Self { self.module.import.entries_mut().push(entry); self } /// Import entry builder pub fn import(self) -> import::ImportBuilder { import::ImportBuilder::with_callback(self) } /// With global variable pub fn with_global(mut self, global: elements::GlobalEntry) -> Self { self.module.global.entries_mut().push(global); self } /// With table pub fn with_table(mut self, table: elements::TableType) -> Self { self.module.table.entries_mut().push(table); self } /// Export entry builder pub fn export(self) -> export::ExportBuilder { export::ExportBuilder::with_callback(self) } /// Glboal entry builder pub fn global(self) -> global::GlobalBuilder { global::GlobalBuilder::with_callback(self) } /// Add data segment to the builder pub fn with_data_segment(mut self, segment: elements::DataSegment) -> Self { self.module.data.entries_mut().push(segment); self } /// Data entry builder pub fn data(self) -> data::DataSegmentBuilder { data::DataSegmentBuilder::with_callback(self) } /// Build module (final step) pub fn build(self) -> F::Result { self.callback.invoke(self.module.into()) } } impl Invoke for ModuleBuilder where F: Invoke { type Result = Self; fn invoke(self, section: elements::FunctionSection) -> Self { self.with_section(elements::Section::Function(section)) } } impl Invoke for ModuleBuilder where F: Invoke { type Result = Self; fn invoke(self, bindings: code::SignatureBindings) -> Self { self.with_signatures(bindings) } } impl Invoke for ModuleBuilder where F: Invoke { type Result = Self; fn invoke(self, def: code::FunctionDefinition) -> Self { let mut b = self; b.push_function(def); b } } impl Invoke for ModuleBuilder where F: Invoke { type Result = Self; fn invoke(self, def: memory::MemoryDefinition) -> Self { let mut b = self; b.push_memory(def); b } } impl Invoke for ModuleBuilder where F: Invoke { type Result = Self; fn invoke(self, def: table::TableDefinition) -> Self { let mut b = self; b.push_table(def); b } } impl Invoke for ModuleBuilder where F: Invoke { type Result = Self; fn invoke(self, entry: elements::ImportEntry) -> Self::Result { self.with_import(entry) } } impl Invoke for ModuleBuilder where F: Invoke { type Result = Self; fn invoke(self, entry: elements::ExportEntry) -> Self::Result { self.with_export(entry) } } impl Invoke for ModuleBuilder where F: Invoke { type Result = Self; fn invoke(self, entry: elements::GlobalEntry) -> Self::Result { self.with_global(entry) } } impl Invoke for ModuleBuilder where F: Invoke { type Result = Self; fn invoke(self, segment: elements::DataSegment) -> Self { self.with_data_segment(segment) } } /// Start new module builder pub fn module() -> ModuleBuilder { ModuleBuilder::new() } /// Start builder to extend existing module pub fn from_module(module: elements::Module) -> ModuleBuilder { ModuleBuilder::new().with_module(module) } #[cfg(test)] mod tests { use super::module; #[test] fn smoky() { let module = module().build(); assert_eq!(module.sections().len(), 0); } #[test] fn functions() { let module = module() .function() .signature().param().i32().build() .body().build() .build() .build(); assert_eq!(module.type_section().expect("type section to exist").types().len(), 1); assert_eq!(module.function_section().expect("function section to exist").entries().len(), 1); assert_eq!(module.code_section().expect("code section to exist").bodies().len(), 1); } #[test] fn export() { let module = module() .export().field("call").internal().func(0).build() .build(); assert_eq!(module.export_section().expect("export section to exist").entries().len(), 1); } #[test] fn global() { let module = module() .global().value_type().i64().mutable().init_expr(::elements::Opcode::I64Const(5)).build() .build(); assert_eq!(module.global_section().expect("global section to exist").entries().len(), 1); } #[test] fn data() { let module = module() .data() .offset(::elements::Opcode::I32Const(16)) .value(vec![0u8, 15, 10, 5, 25]) .build() .build(); assert_eq!(module.data_section().expect("data section to exist").entries().len(), 1); } }