From 871398ff821255debdbd9452e46ca55a05e8cb92 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Fri, 1 Dec 2023 14:00:46 +0100 Subject: feat: wip: seperate parser and types crates Signed-off-by: Henry Gressmann --- crates/parser/src/conversion.rs | 227 ++++++++++++++++++++++++++++++++++++++++ crates/parser/src/error.rs | 25 +++++ crates/parser/src/lib.rs | 73 +++++++++++++ crates/parser/src/module.rs | 181 ++++++++++++++++++++++++++++++++ 4 files changed, 506 insertions(+) create mode 100644 crates/parser/src/conversion.rs create mode 100644 crates/parser/src/error.rs create mode 100644 crates/parser/src/lib.rs create mode 100644 crates/parser/src/module.rs (limited to 'crates/parser/src') diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs new file mode 100644 index 0000000..c75aaf1 --- /dev/null +++ b/crates/parser/src/conversion.rs @@ -0,0 +1,227 @@ +use alloc::{format, vec::Vec}; +use tinywasm_types::{BlockArgs, Instruction, MemArg, ValType}; + +use crate::Result; + +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), + } +} + +fn convert_valtype(valtype: wasmparser::ValType) -> ValType { + use wasmparser::ValType::*; + match valtype { + I32 => ValType::I32, + I64 => ValType::I64, + F32 => ValType::F32, + F64 => ValType::F64, + V128 => ValType::V128, + FuncRef => ValType::FuncRef, + ExternRef => ValType::ExternRef, + } +} + +fn convert_memarg(memarg: wasmparser::MemArg) -> MemArg { + MemArg { + offset: memarg.offset, + align: memarg.align, + } +} + +pub fn process_operator(op: wasmparser::Operator<'_>) -> Result { + use wasmparser::Operator::*; + let v = match op { + Unreachable => Instruction::Unreachable, + Nop => Instruction::Nop, + Block { blockty } => Instruction::Block(convert_blocktype(blockty)), + Loop { blockty } => Instruction::Loop(convert_blocktype(blockty)), + 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) + } + Return => Instruction::Return, + Call { function_index } => Instruction::Call(function_index), + 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), + 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)), + I32Eqz => Instruction::I32Eqz, + I32Eq => Instruction::I32Eq, + I32Ne => Instruction::I32Ne, + I32LtS => Instruction::I32LtS, + I32LtU => Instruction::I32LtU, + I32GtS => Instruction::I32GtS, + I32GtU => Instruction::I32GtU, + I32LeS => Instruction::I32LeS, + I32LeU => Instruction::I32LeU, + I32GeS => Instruction::I32GeS, + I32GeU => Instruction::I32GeU, + I64Eqz => Instruction::I64Eqz, + I64Eq => Instruction::I64Eq, + I64Ne => Instruction::I64Ne, + I64LtS => Instruction::I64LtS, + I64LtU => Instruction::I64LtU, + I64GtS => Instruction::I64GtS, + I64GtU => Instruction::I64GtU, + I64LeS => Instruction::I64LeS, + I64LeU => Instruction::I64LeU, + I64GeS => Instruction::I64GeS, + I64GeU => Instruction::I64GeU, + F32Eq => Instruction::F32Eq, + F32Ne => Instruction::F32Ne, + F32Lt => Instruction::F32Lt, + F32Gt => Instruction::F32Gt, + F32Le => Instruction::F32Le, + F32Ge => Instruction::F32Ge, + F64Eq => Instruction::F64Eq, + F64Ne => Instruction::F64Ne, + F64Lt => Instruction::F64Lt, + F64Gt => Instruction::F64Gt, + F64Le => Instruction::F64Le, + F64Ge => Instruction::F64Ge, + I32Clz => Instruction::I32Clz, + I32Ctz => Instruction::I32Ctz, + I32Popcnt => Instruction::I32Popcnt, + I32Add => Instruction::I32Add, + I32Sub => Instruction::I32Sub, + I32Mul => Instruction::I32Mul, + I32DivS => Instruction::I32DivS, + I32DivU => Instruction::I32DivU, + I32RemS => Instruction::I32RemS, + I32RemU => Instruction::I32RemU, + I32And => Instruction::I32And, + I32Or => Instruction::I32Or, + I32Xor => Instruction::I32Xor, + I32Shl => Instruction::I32Shl, + I32ShrS => Instruction::I32ShrS, + I32ShrU => Instruction::I32ShrU, + I32Rotl => Instruction::I32Rotl, + I32Rotr => Instruction::I32Rotr, + I64Clz => Instruction::I64Clz, + I64Ctz => Instruction::I64Ctz, + I64Popcnt => Instruction::I64Popcnt, + I64Add => Instruction::I64Add, + I64Sub => Instruction::I64Sub, + I64Mul => Instruction::I64Mul, + I64DivS => Instruction::I64DivS, + I64DivU => Instruction::I64DivU, + I64RemS => Instruction::I64RemS, + I64RemU => Instruction::I64RemU, + I64And => Instruction::I64And, + I64Or => Instruction::I64Or, + I64Xor => Instruction::I64Xor, + I64Shl => Instruction::I64Shl, + I64ShrS => Instruction::I64ShrS, + I64ShrU => Instruction::I64ShrU, + I64Rotl => Instruction::I64Rotl, + I64Rotr => Instruction::I64Rotr, + F32Abs => Instruction::F32Abs, + F32Neg => Instruction::F32Neg, + F32Ceil => Instruction::F32Ceil, + F32Floor => Instruction::F32Floor, + F32Trunc => Instruction::F32Trunc, + F32Nearest => Instruction::F32Nearest, + F32Sqrt => Instruction::F32Sqrt, + F32Add => Instruction::F32Add, + F32Sub => Instruction::F32Sub, + F32Mul => Instruction::F32Mul, + F32Div => Instruction::F32Div, + F32Min => Instruction::F32Min, + F32Max => Instruction::F32Max, + F32Copysign => Instruction::F32Copysign, + F64Abs => Instruction::F64Abs, + F64Neg => Instruction::F64Neg, + F64Ceil => Instruction::F64Ceil, + F64Floor => Instruction::F64Floor, + F64Trunc => Instruction::F64Trunc, + F64Nearest => Instruction::F64Nearest, + F64Sqrt => Instruction::F64Sqrt, + F64Add => Instruction::F64Add, + F64Sub => Instruction::F64Sub, + F64Mul => Instruction::F64Mul, + F64Div => Instruction::F64Div, + F64Min => Instruction::F64Min, + F64Max => Instruction::F64Max, + F64Copysign => Instruction::F64Copysign, + I32WrapI64 => Instruction::I32WrapI64, + I32TruncF32S => Instruction::I32TruncF32S, + I32TruncF32U => Instruction::I32TruncF32U, + I32TruncF64S => Instruction::I32TruncF64S, + I32TruncF64U => Instruction::I32TruncF64U, + I64ExtendI32S => Instruction::I64ExtendI32S, + I64ExtendI32U => Instruction::I64ExtendI32U, + I64TruncF32S => Instruction::I64TruncF32S, + I64TruncF32U => Instruction::I64TruncF32U, + I64TruncF64S => Instruction::I64TruncF64S, + I64TruncF64U => Instruction::I64TruncF64U, + F32ConvertI32S => Instruction::F32ConvertI32S, + F32ConvertI32U => Instruction::F32ConvertI32U, + F32ConvertI64S => Instruction::F32ConvertI64S, + F32ConvertI64U => Instruction::F32ConvertI64U, + F32DemoteF64 => Instruction::F32DemoteF64, + F64ConvertI32S => Instruction::F64ConvertI32S, + F64ConvertI32U => Instruction::F64ConvertI32U, + F64ConvertI64S => Instruction::F64ConvertI64S, + F64ConvertI64U => Instruction::F64ConvertI64U, + F64PromoteF32 => Instruction::F64PromoteF32, + I32ReinterpretF32 => Instruction::I32ReinterpretF32, + I64ReinterpretF64 => Instruction::I64ReinterpretF64, + F32ReinterpretI32 => Instruction::F32ReinterpretI32, + F64ReinterpretI64 => Instruction::F64ReinterpretI64, + _ => { + return Err(crate::ParseError::UnsupportedOperator(format!( + "Unsupported instruction: {:?}", + op + ))) + } + }; + + Ok(v) +} diff --git a/crates/parser/src/error.rs b/crates/parser/src/error.rs new file mode 100644 index 0000000..c99fa01 --- /dev/null +++ b/crates/parser/src/error.rs @@ -0,0 +1,25 @@ +use alloc::string::{String, ToString}; +use wasmparser::Encoding; + +pub enum ParseError { + UnsupportedSection(String), + DuplicateSection(String), + EmptySection(String), + UnsupportedOperator(String), + ParseError { message: String, offset: usize }, + InvalidEncoding(Encoding), + InvalidLocalCount { expected: u32, actual: u32 }, + EndNotReached, + Other(String), +} + +impl From for ParseError { + fn from(value: wasmparser::BinaryReaderError) -> Self { + Self::ParseError { + message: value.message().to_string(), + offset: value.offset(), + } + } +} + +pub type Result = core::result::Result; diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs new file mode 100644 index 0000000..39bb1c6 --- /dev/null +++ b/crates/parser/src/lib.rs @@ -0,0 +1,73 @@ +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(feature = "std"), feature(error_in_core))] + +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + +mod conversion; +mod error; +mod module; +use alloc::boxed::Box; +pub use error::*; +use module::ModuleReader; +use tinywasm_types::{Export, FuncType, Function}; + +pub struct Parser {} + +impl Parser { + pub fn parse_module_bytes(wasm: &[u8]) -> Result { + let reader = ModuleReader::new(); + reader.try_into() + } + + 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 read_module_bytes(bytes: &[u8]) -> Result { + unimplemented!() + } + pub fn read_module_file(file_name: &str) -> Result { + unimplemented!() + } + #[cfg(feature = "std")] + pub fn read_module_stream(stream: impl std::io::Read) -> Result { + unimplemented!() + } +} + +pub struct TinyWasmModule { + pub version: Option, + pub start_func: Option, + + pub types: Option>, + pub funcs: Option>, + pub exports: Option>, + // pub tables: Option, + // pub memories: Option, + // pub globals: Option, + // pub elements: Option>, + // pub imports: Option>, + // pub data_segments: Option>, +} + +impl TryFrom> for TinyWasmModule { + type Error = ParseError; + + fn try_from(reader: ModuleReader<'_>) -> Result { + if !reader.end_reached { + return Err(ParseError::EndNotReached); + } + + unimplemented!() + } +} diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs new file mode 100644 index 0000000..e2e1bde --- /dev/null +++ b/crates/parser/src/module.rs @@ -0,0 +1,181 @@ +use alloc::{format, vec::Vec}; +use core::fmt::Debug; +use tracing::debug; +use wasmparser::{ + ExportSectionReader, FunctionBody, FunctionSectionReader, Payload, TypeSectionReader, Validator, +}; + +use crate::{ParseError, Result}; + +#[derive(Default)] +pub struct ModuleReader<'a> { + pub version: Option, + pub start_func: Option, + + pub type_section: Option>, + pub function_section: Option>, + pub export_section: Option>, + pub code_section: Option>, + + // pub table_section: Option>, + // pub memory_section: Option>, + // pub global_section: Option>, + // pub element_section: Option>, + // pub data_section: Option>, + // pub import_section: Option>, + pub end_reached: bool, +} + +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) + .field("function_section", &self.function_section) + .field("code_section", &self.code_section) + .field("export_section", &self.export_section) + // .field("table_section", &self.table_section) + // .field("memory_section", &self.memory_section) + // .field("global_section", &self.global_section) + // .field("element_section", &self.element_section) + // .field("data_section", &self.data_section) + // .field("import_section", &self.import_section) + .finish() + } +} + +impl<'a> ModuleReader<'a> { + pub fn new() -> ModuleReader<'a> { + Self::default() + } + + pub fn process_payload( + &mut self, + payload: Payload<'a>, + validator: &mut Validator, + ) -> Result<()> { + use wasmparser::Payload::*; + + match payload { + Version { + num, + encoding, + range, + } => { + validator.version(num, encoding, &range)?; + self.version = Some(num); + match encoding { + wasmparser::Encoding::Module => {} + wasmparser::Encoding::Component => { + return Err(ParseError::InvalidEncoding(encoding)) + } + } + } + StartSection { func, range } => { + debug!("Found start section"); + validator.start_section(func, &range)?; + self.start_func = Some(func); + } + TypeSection(reader) => { + debug!("Found type section"); + validator.type_section(&reader)?; + self.type_section = Some(reader); + } + FunctionSection(reader) => { + debug!("Found function section"); + validator.function_section(&reader)?; + self.function_section = Some(reader); + } + TableSection(_reader) => { + return Err(ParseError::UnsupportedSection("Table section".into())); + // debug!("Found table section"); + // validator.table_section(&reader)?; + // self.table_section = Some(reader); + } + MemorySection(_reader) => { + return Err(ParseError::UnsupportedSection("Memory section".into())); + // debug!("Found memory section"); + // validator.memory_section(&reader)?; + // self.memory_section = Some(reader); + } + GlobalSection(_reader) => { + return Err(ParseError::UnsupportedSection("Global section".into())); + // debug!("Found global section"); + // validator.global_section(&reader)?; + // self.global_section = Some(reader); + } + ElementSection(_reader) => { + return Err(ParseError::UnsupportedSection("Element section".into())); + // debug!("Found element section"); + // validator.element_section(&reader)?; + // self.element_section = Some(reader); + } + DataSection(_reader) => { + return Err(ParseError::UnsupportedSection("Data section".into())); + // debug!("Found data section"); + // validator.data_section(&reader)?; + // self.data_section = Some(reader); + } + CodeSectionStart { count, range, .. } => { + debug!("Found code section ({} functions)", count); + if self.code_section.is_some() { + 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())); + } + } + ImportSection(_reader) => { + return Err(ParseError::UnsupportedSection("Import section".into())); + + // debug!("Found import section"); + // validator.import_section(&reader)?; + // self.import_section = Some(reader); + } + ExportSection(reader) => { + debug!("Found export section"); + validator.export_section(&reader)?; + self.export_section = Some(reader); + } + End(offset) => { + debug!("Reached end of module"); + if self.end_reached { + return Err(ParseError::DuplicateSection("End section".into())); + } + + validator.end(offset)?; + self.end_reached = true; + } + UnknownSection { .. } | _ => { + return Err(ParseError::UnsupportedSection(format!("Unknown 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