#![no_std] #![forbid(unsafe_code)] #![cfg_attr(not(feature = "std"), feature(error_in_core))] mod std; extern crate alloc; // log for logging (optional). #[cfg(feature = "logging")] #[allow(clippy::single_component_path_imports)] use log; #[cfg(not(feature = "logging"))] mod log { macro_rules! debug ( ($($tt:tt)*) => {{}} ); pub(crate) use debug; } mod conversion; mod error; mod module; use alloc::vec::Vec; pub use error::*; use module::ModuleReader; use tinywasm_types::Function; use wasmparser::Validator; pub use tinywasm_types::TinyWasmModule; #[derive(Default)] pub struct Parser {} impl Parser { pub fn new() -> Self { Self {} } pub fn parse_module_bytes(&self, wasm: impl AsRef<[u8]>) -> Result { let wasm = wasm.as_ref(); let mut validator = Validator::new(); let mut reader = ModuleReader::new(); for payload in wasmparser::Parser::new(0).parse_all(wasm) { reader.process_payload(payload?, &mut validator)?; } if !reader.end_reached { return Err(ParseError::EndNotReached); } reader.try_into() } #[cfg(feature = "std")] pub fn parse_module_file(&self, path: impl AsRef + Clone) -> Result { use alloc::format; let f = crate::std::fs::File::open(path.clone()) .map_err(|e| ParseError::Other(format!("Error opening file {:?}: {}", path.as_ref(), e)))?; let mut reader = crate::std::io::BufReader::new(f); self.parse_module_stream(&mut reader) } #[cfg(feature = "std")] pub fn parse_module_stream(&self, mut stream: impl std::io::Read) -> Result { use alloc::format; let mut validator = Validator::new(); let mut reader = ModuleReader::new(); let mut buffer = Vec::new(); let mut parser = wasmparser::Parser::new(0); let mut eof = false; loop { match parser.parse(&buffer, eof)? { wasmparser::Chunk::NeedMoreData(hint) => { let len = buffer.len(); buffer.extend((0..hint).map(|_| 0u8)); let read_bytes = stream .read(&mut buffer[len..]) .map_err(|e| ParseError::Other(format!("Error reading from stream: {}", e)))?; buffer.truncate(len + read_bytes); eof = read_bytes == 0; } wasmparser::Chunk::Parsed { consumed, payload } => { reader.process_payload(payload, &mut validator)?; buffer.drain(..consumed); if eof || reader.end_reached { return reader.try_into(); } } }; } } } impl TryFrom for TinyWasmModule { type Error = ParseError; fn try_from(reader: ModuleReader) -> Result { if !reader.end_reached { return Err(ParseError::EndNotReached); } let func_types = reader.function_section; let funcs = reader .code_section .into_iter() .zip(func_types) .map(|(f, ty)| Function { instructions: f.body, locals: f.locals, ty, }) .collect::>(); Ok(TinyWasmModule { version: reader.version, start_func: reader.start_func, types: reader.type_section.into_boxed_slice(), funcs: funcs.into_boxed_slice(), exports: reader.export_section.into_boxed_slice(), }) } }