From 7f72605405443482add128cb66b7d09e6bb59de9 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Sat, 2 Dec 2023 23:50:14 +0100 Subject: feat: finish parser module, switch from tracing to log Signed-off-by: Henry Gressmann --- crates/parser/src/conversion.rs | 178 +++++++++++++++++++++++++++++----------- crates/parser/src/error.rs | 1 + crates/parser/src/lib.rs | 92 ++++++++++++++++----- crates/parser/src/module.rs | 91 ++++++++++---------- 4 files changed, 246 insertions(+), 116 deletions(-) (limited to 'crates/parser/src') diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index c75aaf1..ee30db9 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -1,18 +1,82 @@ -use alloc::{format, vec::Vec}; -use tinywasm_types::{BlockArgs, Instruction, MemArg, ValType}; +use alloc::{boxed::Box, format, string::ToString, vec::Vec}; +use tinywasm_types::{BlockArgs, Export, ExternalKind, FuncType, Instruction, MemArg, ValType}; -use crate::Result; +use crate::{module::CodeSection, Result}; -fn convert_blocktype(blocktype: wasmparser::BlockType) -> BlockArgs { +pub(crate) fn convert_module_export(export: wasmparser::Export) -> Result { + let kind = match export.kind { + wasmparser::ExternalKind::Func => ExternalKind::Func, + wasmparser::ExternalKind::Table => ExternalKind::Table, + wasmparser::ExternalKind::Memory => ExternalKind::Memory, + wasmparser::ExternalKind::Global => ExternalKind::Global, + wasmparser::ExternalKind::Tag => { + return Err(crate::ParseError::UnsupportedOperator(format!( + "Unsupported export kind: {:?}", + export.kind + ))) + } + }; + + Ok(Export { + index: export.index, + name: Box::from(export.name), + kind, + }) +} + +pub(crate) fn convert_module_code(func: wasmparser::FunctionBody) -> Result { + let locals_reader = func.get_locals_reader()?; + let count = locals_reader.get_count(); + let mut locals = Vec::with_capacity(count as usize); + locals.extend( + locals_reader + .into_iter() + .filter_map(|l| l.ok()) + .map(|l| convert_valtype(&l.1)), + ); + + if locals.len() != count as usize { + return Err(crate::ParseError::Other("Invalid local index".to_string())); + } + + let body_reader = func.get_operators_reader()?; + let body = process_operators(body_reader.into_iter())?; + + Ok(CodeSection { + locals: locals.into_boxed_slice(), + body, + }) +} + +pub(crate) fn convert_module_type(ty: wasmparser::Type) -> Result { + let wasmparser::Type::Func(ty) = ty; + let params = ty + .params() + .iter() + .map(|p| Ok(convert_valtype(p))) + .collect::>>()? + .into_boxed_slice(); + + let results = ty + .results() + .iter() + .map(|p| Ok(convert_valtype(p))) + .collect::>>()? + .into_boxed_slice(); + + Ok(FuncType { params, results }) +} + +pub(crate) fn convert_blocktype(blocktype: &wasmparser::BlockType) -> BlockArgs { use wasmparser::BlockType::*; match blocktype { Empty => BlockArgs::Empty, Type(ty) => BlockArgs::Type(convert_valtype(ty)), - FuncType(ty) => BlockArgs::FuncType(ty), + FuncType(ty) => BlockArgs::FuncType(*ty), } } -fn convert_valtype(valtype: wasmparser::ValType) -> ValType { +pub(crate) fn convert_valtype(valtype: &wasmparser::ValType) -> ValType { use wasmparser::ValType::*; match valtype { I32 => ValType::I32, @@ -25,14 +89,38 @@ fn convert_valtype(valtype: wasmparser::ValType) -> ValType { } } -fn convert_memarg(memarg: wasmparser::MemArg) -> MemArg { +pub(crate) fn convert_memarg(memarg: wasmparser::MemArg) -> MemArg { MemArg { offset: memarg.offset, align: memarg.align, } } -pub fn process_operator(op: wasmparser::Operator<'_>) -> Result { +pub fn process_operators<'a>( + ops: impl Iterator, wasmparser::BinaryReaderError>>, +) -> Result> { + let mut instructions = Vec::new(); + for op in ops { + match op? { + wasmparser::Operator::BrTable { targets } => { + instructions.push(Instruction::BrTable(targets.default())); + instructions.extend( + targets + .targets() + .collect::, wasmparser::BinaryReaderError>>()? + .into_iter() + .map(Instruction::Br), + ); + } + op => instructions.push(process_operator(&op)?), + } + } + + Ok(instructions.into_boxed_slice()) +} + +#[inline] +pub(crate) fn process_operator(op: &wasmparser::Operator) -> Result { use wasmparser::Operator::*; let v = match op { Unreachable => Instruction::Unreachable, @@ -42,56 +130,48 @@ pub fn process_operator(op: wasmparser::Operator<'_>) -> Result { If { blockty } => Instruction::If(convert_blocktype(blockty)), Else => Instruction::Else, End => Instruction::End, - Br { relative_depth } => Instruction::Br(relative_depth), - BrIf { relative_depth } => Instruction::BrIf(relative_depth), - BrTable { targets } => { - let default = targets.default(); - let targets = targets - .targets() - .map(|t| Ok(t?)) - .collect::>>()?; - - Instruction::BrTable(targets, default) - } + Br { relative_depth } => Instruction::Br(*relative_depth), + BrIf { relative_depth } => Instruction::BrIf(*relative_depth), + BrTable { targets } => Instruction::BrTable(targets.default()), Return => Instruction::Return, - Call { function_index } => Instruction::Call(function_index), + Call { function_index } => Instruction::Call(*function_index), CallIndirect { type_index, table_index, .. - } => Instruction::CallIndirect(type_index, table_index), + } => Instruction::CallIndirect(*type_index, *table_index), Drop => Instruction::Drop, Select => Instruction::Select, - LocalGet { local_index } => Instruction::LocalGet(local_index), - LocalSet { local_index } => Instruction::LocalSet(local_index), - LocalTee { local_index } => Instruction::LocalTee(local_index), - GlobalGet { global_index } => Instruction::GlobalGet(global_index), - GlobalSet { global_index } => Instruction::GlobalSet(global_index), + LocalGet { local_index } => Instruction::LocalGet(*local_index), + LocalSet { local_index } => Instruction::LocalSet(*local_index), + LocalTee { local_index } => Instruction::LocalTee(*local_index), + GlobalGet { global_index } => Instruction::GlobalGet(*global_index), + GlobalSet { global_index } => Instruction::GlobalSet(*global_index), MemorySize { .. } => Instruction::MemorySize, MemoryGrow { .. } => Instruction::MemoryGrow, - I32Load { memarg } => Instruction::I32Load(convert_memarg(memarg)), - I64Load { memarg } => Instruction::I64Load(convert_memarg(memarg)), - F32Load { memarg } => Instruction::F32Load(convert_memarg(memarg)), - F64Load { memarg } => Instruction::F64Load(convert_memarg(memarg)), - I32Load8S { memarg } => Instruction::I32Load8S(convert_memarg(memarg)), - I32Load8U { memarg } => Instruction::I32Load8U(convert_memarg(memarg)), - I32Load16S { memarg } => Instruction::I32Load16S(convert_memarg(memarg)), - I32Load16U { memarg } => Instruction::I32Load16U(convert_memarg(memarg)), - I64Load8S { memarg } => Instruction::I64Load8S(convert_memarg(memarg)), - I64Load8U { memarg } => Instruction::I64Load8U(convert_memarg(memarg)), - I64Load16S { memarg } => Instruction::I64Load16S(convert_memarg(memarg)), - I64Load16U { memarg } => Instruction::I64Load16U(convert_memarg(memarg)), - I64Load32S { memarg } => Instruction::I64Load32S(convert_memarg(memarg)), - I64Load32U { memarg } => Instruction::I64Load32U(convert_memarg(memarg)), - I32Store { memarg } => Instruction::I32Store(convert_memarg(memarg)), - I64Store { memarg } => Instruction::I64Store(convert_memarg(memarg)), - F32Store { memarg } => Instruction::F32Store(convert_memarg(memarg)), - F64Store { memarg } => Instruction::F64Store(convert_memarg(memarg)), - I32Store8 { memarg } => Instruction::I32Store8(convert_memarg(memarg)), - I32Store16 { memarg } => Instruction::I32Store16(convert_memarg(memarg)), - I64Store8 { memarg } => Instruction::I64Store8(convert_memarg(memarg)), - I64Store16 { memarg } => Instruction::I64Store16(convert_memarg(memarg)), - I64Store32 { memarg } => Instruction::I64Store32(convert_memarg(memarg)), + I32Load { memarg } => Instruction::I32Load(convert_memarg(*memarg)), + I64Load { memarg } => Instruction::I64Load(convert_memarg(*memarg)), + F32Load { memarg } => Instruction::F32Load(convert_memarg(*memarg)), + F64Load { memarg } => Instruction::F64Load(convert_memarg(*memarg)), + I32Load8S { memarg } => Instruction::I32Load8S(convert_memarg(*memarg)), + I32Load8U { memarg } => Instruction::I32Load8U(convert_memarg(*memarg)), + I32Load16S { memarg } => Instruction::I32Load16S(convert_memarg(*memarg)), + I32Load16U { memarg } => Instruction::I32Load16U(convert_memarg(*memarg)), + I64Load8S { memarg } => Instruction::I64Load8S(convert_memarg(*memarg)), + I64Load8U { memarg } => Instruction::I64Load8U(convert_memarg(*memarg)), + I64Load16S { memarg } => Instruction::I64Load16S(convert_memarg(*memarg)), + I64Load16U { memarg } => Instruction::I64Load16U(convert_memarg(*memarg)), + I64Load32S { memarg } => Instruction::I64Load32S(convert_memarg(*memarg)), + I64Load32U { memarg } => Instruction::I64Load32U(convert_memarg(*memarg)), + I32Store { memarg } => Instruction::I32Store(convert_memarg(*memarg)), + I64Store { memarg } => Instruction::I64Store(convert_memarg(*memarg)), + F32Store { memarg } => Instruction::F32Store(convert_memarg(*memarg)), + F64Store { memarg } => Instruction::F64Store(convert_memarg(*memarg)), + I32Store8 { memarg } => Instruction::I32Store8(convert_memarg(*memarg)), + I32Store16 { memarg } => Instruction::I32Store16(convert_memarg(*memarg)), + I64Store8 { memarg } => Instruction::I64Store8(convert_memarg(*memarg)), + I64Store16 { memarg } => Instruction::I64Store16(convert_memarg(*memarg)), + I64Store32 { memarg } => Instruction::I64Store32(convert_memarg(*memarg)), I32Eqz => Instruction::I32Eqz, I32Eq => Instruction::I32Eq, I32Ne => Instruction::I32Ne, diff --git a/crates/parser/src/error.rs b/crates/parser/src/error.rs index c99fa01..ee217c7 100644 --- a/crates/parser/src/error.rs +++ b/crates/parser/src/error.rs @@ -2,6 +2,7 @@ use alloc::string::{String, ToString}; use wasmparser::Encoding; pub enum ParseError { + InvalidType, UnsupportedSection(String), DuplicateSection(String), EmptySection(String), diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index d2b6d9c..5b17fb8 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -9,49 +9,101 @@ extern crate std; mod conversion; mod error; mod module; +use alloc::vec::Vec; pub use error::*; use module::ModuleReader; -use tinywasm_types::TinyWasmModule; +use tinywasm_types::{Function, TinyWasmModule}; +use wasmparser::Validator; pub struct Parser {} impl Parser { pub fn parse_module_bytes(wasm: &[u8]) -> Result { - let reader = ModuleReader::new(); - reader.try_into() - } + 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); + } - pub fn parse_module_file(file_name: &str) -> Result { - let reader = ModuleReader::new(); reader.try_into() } #[cfg(feature = "std")] - pub fn parse_module_stream(stream: impl std::io::Read) -> Result { - let reader = ModuleReader::new(); - reader.try_into() - } + pub fn parse_module_file(path: impl AsRef) -> Result { + use alloc::format; + let f = crate::std::fs::File::open("log.txt").map_err(|e| { + ParseError::Other(format!("Error opening file {:?}: {}", path.as_ref(), e)) + })?; - pub fn read_module_bytes(bytes: &[u8]) -> Result { - unimplemented!() - } - pub fn read_module_file(file_name: &str) -> Result { - unimplemented!() + let mut reader = crate::std::io::BufReader::new(f); + Self::parse_module_stream(&mut reader) } + #[cfg(feature = "std")] - pub fn read_module_stream(stream: impl std::io::Read) -> Result { - unimplemented!() + pub fn parse_module_stream(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 { +impl TryFrom for TinyWasmModule { type Error = ParseError; - fn try_from(reader: ModuleReader<'_>) -> Result { + fn try_from(reader: ModuleReader) -> Result { if !reader.end_reached { return Err(ParseError::EndNotReached); } - unimplemented!() + let func_types = reader.function_section; + let funcs = reader + .code_section + .into_iter() + .zip(func_types) + .map(|(f, ty)| Function { + body: f.body, + locals: f.locals, + ty, + }) + .collect::>() + .into_boxed_slice(); + + Ok(TinyWasmModule { + version: reader.version, + start_func: reader.start_func, + types: reader.type_section.into_boxed_slice(), + funcs, + exports: reader.export_section.into_boxed_slice(), + }) } } diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs index e2e1bde..268bad7 100644 --- a/crates/parser/src/module.rs +++ b/crates/parser/src/module.rs @@ -1,21 +1,26 @@ -use alloc::{format, vec::Vec}; +use alloc::{boxed::Box, format, vec::Vec}; use core::fmt::Debug; -use tracing::debug; -use wasmparser::{ - ExportSectionReader, FunctionBody, FunctionSectionReader, Payload, TypeSectionReader, Validator, -}; +use log::debug; +use tinywasm_types::{Export, FuncType, Instruction, ValType}; +use wasmparser::{Payload, Validator}; -use crate::{ParseError, Result}; +use crate::{conversion, ParseError, Result}; + +#[derive(Debug, Clone, PartialEq)] +pub struct CodeSection { + pub locals: Box<[ValType]>, + pub body: Box<[Instruction]>, +} #[derive(Default)] -pub struct ModuleReader<'a> { +pub struct ModuleReader { pub version: Option, pub start_func: Option, - pub type_section: Option>, - pub function_section: Option>, - pub export_section: Option>, - pub code_section: Option>, + pub type_section: Vec, + pub function_section: Vec, + pub export_section: Vec, + pub code_section: Vec, // pub table_section: Option>, // pub memory_section: Option>, @@ -26,8 +31,8 @@ pub struct ModuleReader<'a> { pub end_reached: bool, } -impl Debug for ModuleReader<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { +impl Debug for ModuleReader { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct("ModuleReader") .field("version", &self.version) .field("type_section", &self.type_section) @@ -44,16 +49,12 @@ impl Debug for ModuleReader<'_> { } } -impl<'a> ModuleReader<'a> { - pub fn new() -> ModuleReader<'a> { +impl ModuleReader { + pub fn new() -> ModuleReader { Self::default() } - pub fn process_payload( - &mut self, - payload: Payload<'a>, - validator: &mut Validator, - ) -> Result<()> { + pub fn process_payload(&mut self, payload: Payload, validator: &mut Validator) -> Result<()> { use wasmparser::Payload::*; match payload { @@ -79,12 +80,18 @@ impl<'a> ModuleReader<'a> { TypeSection(reader) => { debug!("Found type section"); validator.type_section(&reader)?; - self.type_section = Some(reader); + self.type_section = reader + .into_iter() + .map(|t| conversion::convert_module_type(t?)) + .collect::>>()?; } FunctionSection(reader) => { debug!("Found function section"); validator.function_section(&reader)?; - self.function_section = Some(reader); + self.function_section = reader + .into_iter() + .map(|f| Ok(f?)) + .collect::>>()?; } TableSection(_reader) => { return Err(ParseError::UnsupportedSection("Table section".into())); @@ -118,22 +125,18 @@ impl<'a> ModuleReader<'a> { } CodeSectionStart { count, range, .. } => { debug!("Found code section ({} functions)", count); - if self.code_section.is_some() { + if !self.code_section.is_empty() { return Err(ParseError::DuplicateSection("Code section".into())); } validator.code_section_start(count, &range)?; - self.code_section = Some(CodeSection::new()); } CodeSectionEntry(function) => { debug!("Found code section entry"); validator.code_section_entry(&function)?; - if let Some(code_section) = &mut self.code_section { - code_section.functions.push(function); - } else { - return Err(ParseError::EmptySection("Code section".into())); - } + self.code_section + .push(conversion::convert_module_code(function)?); } ImportSection(_reader) => { return Err(ParseError::UnsupportedSection("Import section".into())); @@ -145,7 +148,10 @@ impl<'a> ModuleReader<'a> { ExportSection(reader) => { debug!("Found export section"); validator.export_section(&reader)?; - self.export_section = Some(reader); + self.export_section = reader + .into_iter() + .map(|e| conversion::convert_module_export(e?)) + .collect::>>()?; } End(offset) => { debug!("Reached end of module"); @@ -156,26 +162,17 @@ impl<'a> ModuleReader<'a> { validator.end(offset)?; self.end_reached = true; } - UnknownSection { .. } | _ => { - return Err(ParseError::UnsupportedSection(format!("Unknown section"))) + UnknownSection { .. } => { + return Err(ParseError::UnsupportedSection("Unknown section".into())) + } + section => { + return Err(ParseError::UnsupportedSection(format!( + "Unsupported section: {:?}", + section + ))) } }; Ok(()) } } - -/// A WebAssembly code section -/// Can be cloned to read functions multiple times -#[derive(Debug, Clone)] -pub struct CodeSection<'a> { - pub(crate) functions: Vec>, -} - -impl<'a> CodeSection<'a> { - fn new() -> Self { - Self { - functions: Vec::new(), - } - } -} -- cgit v1.3.1