diff options
| author | Henry Gressmann <mail@henrygressmann.de> | 2023-11-29 01:45:05 +0100 |
|---|---|---|
| committer | Henry Gressmann <mail@henrygressmann.de> | 2023-11-29 01:45:05 +0100 |
| commit | 93f8e10a8c15cbcf0d09517869016c32c6bd47eb (patch) | |
| tree | 06f23748b8036a8ff6db431cd22cc3abc4859fa3 | |
| parent | f26d077b5a8141253f807ea14551852da6842dd1 (diff) | |
feat: basic working wasm adder (experiment)
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
| -rw-r--r-- | Cargo.lock | 133 | ||||
| -rw-r--r-- | crates/cli/Cargo.toml | 2 | ||||
| -rw-r--r-- | crates/cli/bin.rs | 19 | ||||
| -rw-r--r-- | crates/cli/util.rs | 14 | ||||
| -rw-r--r-- | crates/tinywasm/Cargo.toml | 1 | ||||
| -rw-r--r-- | crates/tinywasm/src/error.rs | 7 | ||||
| -rw-r--r-- | crates/tinywasm/src/instance.rs | 0 | ||||
| -rw-r--r-- | crates/tinywasm/src/lib.rs | 5 | ||||
| -rw-r--r-- | crates/tinywasm/src/module.rs | 155 | ||||
| -rw-r--r-- | crates/tinywasm/src/module/mod.rs | 231 | ||||
| -rw-r--r-- | crates/tinywasm/src/module/reader.rs | 183 | ||||
| -rw-r--r-- | examples/wasm/add.wasm | bin | 0 -> 65 bytes | |||
| -rw-r--r-- | examples/wasm/add.wat | 14 |
13 files changed, 602 insertions, 162 deletions
@@ -18,6 +18,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] name = "argh" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -122,6 +140,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] name = "indenter" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -146,6 +174,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -161,6 +195,16 @@ dependencies = [ ] [[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] name = "object" version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -176,6 +220,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] name = "owo-colors" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -241,6 +291,12 @@ dependencies = [ ] [[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] name = "syn" version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -285,6 +341,7 @@ dependencies = [ name = "tinywasm" version = "0.0.0" dependencies = [ + "hashbrown", "thiserror-core", "tracing", "wasmparser-nostd", @@ -297,6 +354,8 @@ dependencies = [ "argh", "color-eyre", "tinywasm", + "tracing", + "tracing-subscriber", ] [[package]] @@ -306,10 +365,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] [[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -330,14 +401,28 @@ dependencies = [ ] [[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] name = "tracing-subscriber" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ + "nu-ansi-term", "sharded-slab", + "smallvec", "thread_local", "tracing-core", + "tracing-log", ] [[package]] @@ -353,6 +438,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] name = "wasmparser-nostd" version = "0.100.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -360,3 +451,45 @@ checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" dependencies = [ "indexmap-nostd", ] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zerocopy" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 4a9974b..bfe0391 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -13,3 +13,5 @@ path="bin.rs" tinywasm={path="../tinywasm"} argh="0.1" color-eyre="0.6" +tracing="0.1" +tracing-subscriber="0.3" diff --git a/crates/cli/bin.rs b/crates/cli/bin.rs index 4e85481..38e30da 100644 --- a/crates/cli/bin.rs +++ b/crates/cli/bin.rs @@ -1,6 +1,9 @@ use argh::FromArgs; use color_eyre::eyre::Result; -use tinywasm::{self, Module}; +use tinywasm::{self, module::WasmValue, Module}; +use util::install_tracing; + +mod util; #[derive(FromArgs)] /// TinyWasm CLI @@ -25,6 +28,9 @@ struct Run { } fn main() -> Result<()> { + color_eyre::install()?; + install_tracing(None); + let args: TinyWasmCli = argh::from_env(); match args.nested { @@ -37,7 +43,14 @@ fn main() -> Result<()> { } fn run(wasm: &[u8]) -> Result<()> { - let module = Module::new(wasm)?; - println!("{:#?}", module); + let mut module = Module::new(wasm)?; + let args = [WasmValue::I32(1), WasmValue::I32(2)]; + let res = module.run("add", &args)?; + println!("res: {:?}", res); + + let args = [WasmValue::I64(1), WasmValue::I64(2)]; + let res = module.run("add_64", &args)?; + println!("res: {:?}", res); + Ok(()) } diff --git a/crates/cli/util.rs b/crates/cli/util.rs new file mode 100644 index 0000000..2a8160a --- /dev/null +++ b/crates/cli/util.rs @@ -0,0 +1,14 @@ +pub fn install_tracing(log_level: Option<tracing::Level>) { + use tracing_subscriber::filter::LevelFilter; + use tracing_subscriber::prelude::*; + + tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .compact() + .with_filter(LevelFilter::from( + log_level.unwrap_or(tracing::Level::DEBUG), + )), + ) + .init(); +} diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml index 2f71aa9..807a19f 100644 --- a/crates/tinywasm/Cargo.toml +++ b/crates/tinywasm/Cargo.toml @@ -10,6 +10,7 @@ path="src/lib.rs" thiserror={version="1.0", package="thiserror-core", default-features=false} wasmparser={version="0.100", package="wasmparser-nostd", default-features=false} tracing={version="0.1.38", default-features=false} # logging +hashbrown="0.14" [features] default=["std"] diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs index b9c93c4..a253880 100644 --- a/crates/tinywasm/src/error.rs +++ b/crates/tinywasm/src/error.rs @@ -6,6 +6,9 @@ pub enum Error { #[error("error parsing module")] ParseError { message: String, offset: usize }, + #[error("unsupported feature: {0}")] + UnsupportedFeature(String), + #[error("unknown error: {0}")] Other(String), } @@ -14,6 +17,10 @@ impl Error { pub fn other<T>(message: &str) -> Result<T, Self> { Err(Self::Other(message.to_string())) } + + pub fn unsupported<T>(feature: &str) -> Result<T, Self> { + Err(Self::UnsupportedFeature(feature.to_string())) + } } impl From<wasmparser::BinaryReaderError> for Error { diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/crates/tinywasm/src/instance.rs diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index 3d572a2..0f97863 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -19,15 +19,12 @@ pub struct Instance {} mod tests { use super::*; use crate::{error::Result, Module}; - use std::dbg; #[test] fn it_works() -> Result<()> { - let wasm = include_bytes!("../../../examples/wasm/helloworld.wasm"); + let wasm = include_bytes!("../../../examples/wasm/add.wasm"); let module = Module::new(wasm)?; - dbg!(module); - Ok(()) } } diff --git a/crates/tinywasm/src/module.rs b/crates/tinywasm/src/module.rs deleted file mode 100644 index 1ed42c4..0000000 --- a/crates/tinywasm/src/module.rs +++ /dev/null @@ -1,155 +0,0 @@ -use crate::error::{Error, Result}; -use alloc::{format, vec::Vec}; -use tracing::error; -use wasmparser::*; - -pub struct Module<'a> { - reader: ModuleReader<'a>, -} - -impl<'a> core::fmt::Debug for Module<'a> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("Module") - .field("version", &self.reader.version) - .field("type_section", &self.reader.type_section) - .field("function_section", &self.reader.function_section) - .field("table_section", &self.reader.table_section) - .field("memory_section", &self.reader.memory_section) - .field("global_section", &self.reader.global_section) - .field("element_section", &self.reader.element_section) - .field("data_section", &self.reader.data_section) - .field("code_section", &self.reader.code_section) - .field("import_section", &self.reader.import_section) - .field("export_section", &self.reader.export_section) - .finish() - } -} - -#[derive(Default)] -pub struct ModuleReader<'a> { - pub version: Option<u16>, - pub type_section: Option<TypeSectionReader<'a>>, - pub function_section: Option<FunctionSectionReader<'a>>, - pub table_section: Option<TableSectionReader<'a>>, - pub memory_section: Option<MemorySectionReader<'a>>, - pub global_section: Option<GlobalSectionReader<'a>>, - pub element_section: Option<ElementSectionReader<'a>>, - pub data_section: Option<DataSectionReader<'a>>, - pub code_section: Option<CodeSection<'a>>, - pub import_section: Option<ImportSectionReader<'a>>, - pub export_section: Option<ExportSectionReader<'a>>, -} - -#[derive(Debug)] -pub struct CodeSection<'a> { - pub(crate) functions: Vec<FunctionBody<'a>>, -} - -impl<'a> CodeSection<'a> { - fn new() -> Self { - Self { - functions: Vec::new(), - } - } -} - -impl<'a> Module<'a> { - pub fn new(wasm: &'a [u8]) -> Result<Self> { - 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)?; - } - - Ok(Self { reader }) - } -} - -impl<'a> ModuleReader<'a> { - pub fn new() -> Self { - 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 Error::other("Component"), - } - } - TypeSection(reader) => { - validator.type_section(&reader)?; - self.type_section = Some(reader); - } - FunctionSection(reader) => { - validator.function_section(&reader)?; - self.function_section = Some(reader); - } - TableSection(reader) => { - validator.table_section(&reader)?; - self.table_section = Some(reader); - } - MemorySection(reader) => { - validator.memory_section(&reader)?; - self.memory_section = Some(reader); - } - GlobalSection(reader) => { - validator.global_section(&reader)?; - self.global_section = Some(reader); - } - ElementSection(reader) => { - validator.element_section(&reader)?; - self.element_section = Some(reader); - } - DataSection(reader) => { - validator.data_section(&reader)?; - self.data_section = Some(reader); - } - CodeSectionStart { count, range, .. } => { - validator.code_section_start(count, &range)?; - - self.code_section = Some(CodeSection::new()); - } - CodeSectionEntry(function) => { - validator.code_section_entry(&function)?; - - if let Some(code_section) = &mut self.code_section { - code_section.functions.push(function); - } else { - return Error::other("Empty code section"); - } - } - ImportSection(reader) => { - validator.import_section(&reader)?; - self.import_section = Some(reader); - } - ExportSection(reader) => { - validator.export_section(&reader)?; - self.export_section = Some(reader); - } - - End(offset) => { - validator.end(offset)?; - return Ok(()); - } - x => Error::other(&format!("Unknown payload: {:?}", x))?, - }; - - error!("Missing end"); - - Ok(()) - } -} diff --git a/crates/tinywasm/src/module/mod.rs b/crates/tinywasm/src/module/mod.rs new file mode 100644 index 0000000..9f69b32 --- /dev/null +++ b/crates/tinywasm/src/module/mod.rs @@ -0,0 +1,231 @@ +use core::fmt::Debug; + +use crate::error::{Error, Result}; +use alloc::{format, string::String, vec, vec::Vec}; +use hashbrown::{HashMap, HashSet}; +use tracing::{error, info}; +use wasmparser::*; + +mod reader; +use self::reader::ModuleReader; + +#[derive(Debug)] +pub struct ModuleMetadata { + pub version: u16, +} + +pub struct Module<'data> { + pub meta: ModuleMetadata, + + pub types: Vec<FuncType>, + pub functions: Vec<u32>, + pub exports: Vec<Export<'data>>, + pub code: Vec<FunctionBody<'data>>, + + marker: core::marker::PhantomData<&'data ()>, +} + +impl Debug for Module<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Module") + .field("meta", &self.meta) + .field("types", &self.types) + .field("functions", &self.functions) + .field("exports", &self.exports) + .field("code", &self.code) + .finish() + } +} + +#[derive(Debug)] +#[non_exhaustive] +pub enum WasmValue { + I32(i32), + I64(i64), +} + +impl WasmValue { + fn to_bytes(&self) -> (Vec<u8>, ValType) { + match self { + Self::I32(val) => { + let val = val.to_le_bytes().to_vec(); + (val, ValType::I32) + } + Self::I64(val) => { + let val = val.to_le_bytes().to_vec(); + (val, ValType::I64) + } // _ => { + // panic!("Unsupported return type"); + // } + } + } + + fn from_bytes(bytes: &[u8], ty: &ValType) -> Self { + match ty { + ValType::I32 => { + let val = i32::from_le_bytes(bytes.try_into().unwrap()); + Self::I32(val) + } + ValType::I64 => { + let val = i64::from_le_bytes(bytes.try_into().unwrap()); + Self::I64(val) + } + _ => { + panic!("Unsupported return type"); + } + } + } +} + +impl<'data> Module<'data> { + pub fn new(wasm: &'data [u8]) -> Result<Self> { + 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 Error::other("End not reached"); + } + + Self::from_reader(reader) + } + + pub fn run(&mut self, func_name: &str, args: &[WasmValue]) -> Result<Vec<WasmValue>> { + let func = self + .exports + .iter() + .find(|e| e.name == func_name) + .ok_or_else(|| Error::Other(format!("Function {} not found", func_name)))?; + + let func_type_index = self.functions[func.index as usize]; + let func_type = &self.types[func_type_index as usize]; + + info!("func_type: {:#?}", func_type); + let code = &mut self.code[func.index as usize]; + code.allow_memarg64(false); + + let mut locals = vec![]; + for ty in func_type.params() { + locals.push(ty.clone()); + } + + let mut returns = vec![]; + for ty in func_type.results() { + returns.push(ty.clone()); + } + + let locals_reader = code.get_locals_reader().unwrap(); + for local in locals_reader.into_iter() { + let local = local.unwrap(); + if locals.len() != local.0 as usize { + panic!("Invalid local index"); + } + locals.push(local.1); + } + + let mut body = code.get_operators_reader().unwrap().into_iter(); + + let mut local_values = vec![]; + for (i, arg) in args.iter().enumerate() { + let (val, ty) = arg.to_bytes(); + if locals[i] != ty { + return Error::other(&format!( + "Invalid argument type for {}, index {}: expected {:?}, got {:?}", + func_name, i, locals[i], ty + )); + } + + local_values.push(val); + } + + let mut stack: Vec<Vec<u8>> = Vec::new(); + while let Some(op) = body.next() { + let op = op.unwrap(); + info!("op: {:#?}", op); + + match op { + Operator::LocalGet { local_index } => { + let local = locals.get(local_index as usize).unwrap(); + let val = &local_values[local_index as usize]; + info!("local: {:#?}", local); + stack.push(val.clone()); + } + Operator::I64Add => { + let a = stack.pop().unwrap(); + let b = stack.pop().unwrap(); + let a = i64::from_le_bytes(a.try_into().unwrap()); + let b = i64::from_le_bytes(b.try_into().unwrap()); + let c = (a + b).to_le_bytes().to_vec(); + stack.push(c); + } + Operator::I32Add => { + let a = stack.pop().unwrap(); + let b = stack.pop().unwrap(); + let a = i32::from_le_bytes(a.try_into().unwrap()); + let b = i32::from_le_bytes(b.try_into().unwrap()); + let c = (a + b).to_le_bytes().to_vec(); + stack.push(c); + } + Operator::End => { + info!("stack: {:#?}", stack); + let res = returns + .iter() + .map(|ty| { + let val = stack.pop().unwrap(); + WasmValue::from_bytes(&val, ty) + }) + .collect::<Vec<_>>(); + return Ok(res); + } + _ => {} + } + } + + return Error::other("End not reached"); + } + + fn from_reader(reader: ModuleReader<'data>) -> Result<Self> { + let types = reader + .type_section + .map(|s| { + s.into_iter() + .map(|ty| { + let Type::Func(func) = ty?; + Ok(func) + }) + .collect::<Result<Vec<_>>>() + }) + .transpose()? + .unwrap_or_default(); + + let functions = reader + .function_section + .map(|s| s.into_iter().map(|f| Ok(f?)).collect::<Result<Vec<_>>>()) + .transpose()? + .unwrap_or_default(); + + let exports = reader + .export_section + .map(|s| s.into_iter().map(|e| Ok(e?)).collect::<Result<Vec<_>>>()) + .transpose()? + .unwrap_or_default(); + + let code = reader.code_section.map(|s| s.functions).unwrap_or_default(); + + let meta = ModuleMetadata { + version: reader.version.unwrap_or(1), + }; + + Ok(Self { + marker: core::marker::PhantomData, + meta, + types, + exports, + functions, + code, + }) + } +} diff --git a/crates/tinywasm/src/module/reader.rs b/crates/tinywasm/src/module/reader.rs new file mode 100644 index 0000000..7ecfddf --- /dev/null +++ b/crates/tinywasm/src/module/reader.rs @@ -0,0 +1,183 @@ +use alloc::{format, vec::Vec}; +use core::fmt::Debug; +use tracing::debug; +use wasmparser::{ + DataSectionReader, ElementSectionReader, ExportSectionReader, FunctionBody, + FunctionSectionReader, GlobalSectionReader, ImportSectionReader, MemorySectionReader, Payload, + TableSectionReader, TypeSectionReader, Validator, +}; + +use crate::{Error, Result}; + +#[derive(Default)] +pub struct ModuleReader<'a> { + pub version: Option<u16>, + + pub type_section: Option<TypeSectionReader<'a>>, + pub function_section: Option<FunctionSectionReader<'a>>, + pub export_section: Option<ExportSectionReader<'a>>, + pub code_section: Option<CodeSection<'a>>, + + pub table_section: Option<TableSectionReader<'a>>, + pub memory_section: Option<MemorySectionReader<'a>>, + pub global_section: Option<GlobalSectionReader<'a>>, + pub element_section: Option<ElementSectionReader<'a>>, + pub data_section: Option<DataSectionReader<'a>>, + pub import_section: Option<ImportSectionReader<'a>>, + 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("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("code_section", &self.code_section) + .field("import_section", &self.import_section) + .field("export_section", &self.export_section) + .finish() + } +} + +impl<'a> ModuleReader<'a> { + pub fn new() -> Self { + 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 Error::other("Component"), + } + } + 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 Error::unsupported("Table section"); + // debug!("Found table section"); + // validator.table_section(&reader)?; + // self.table_section = Some(reader); + } + MemorySection(_reader) => { + return Error::unsupported("Memory section"); + // debug!("Found memory section"); + // validator.memory_section(&reader)?; + // self.memory_section = Some(reader); + } + GlobalSection(_reader) => { + return Error::unsupported("Global section"); + // debug!("Found global section"); + // validator.global_section(&reader)?; + // self.global_section = Some(reader); + } + ElementSection(_reader) => { + return Error::unsupported("Element section"); + // debug!("Found element section"); + // validator.element_section(&reader)?; + // self.element_section = Some(reader); + } + DataSection(_reader) => { + return Error::unsupported("Data section"); + // debug!("Found data section"); + // validator.data_section(&reader)?; + // self.data_section = Some(reader); + } + CodeSectionStart { count, range, .. } => { + debug!("Found code section ({} functions)", count); + 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 Error::other("Empty code section"); + } + } + ImportSection(_reader) => { + return Error::unsupported("Import section"); + + // 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 Error::other("End reached twice"); + } + + validator.end(offset)?; + self.end_reached = true; + } + x => Error::other(&format!("Unknown payload: {:?}", x))?, + }; + + return Ok(()); + } + + // fn exports(&mut self) -> Result<Vec<Export>> { + // let mut exports = Vec::new(); + + // if let Some(export_section) = self.export_section { + // for export in export_section.into_iter() { + // let export = export?; + // let name = export.name; + // let kind = export.kind; + // let index = export.index; + // exports.push(Export { name, kind, index }); + // } + // } + + // Ok(exports) + // } +} + +#[derive(Debug)] +pub struct CodeSection<'a> { + pub(crate) functions: Vec<FunctionBody<'a>>, +} + +impl<'a> CodeSection<'a> { + fn new() -> Self { + Self { + functions: Vec::new(), + } + } +} diff --git a/examples/wasm/add.wasm b/examples/wasm/add.wasm Binary files differnew file mode 100644 index 0000000..92e3432 --- /dev/null +++ b/examples/wasm/add.wasm diff --git a/examples/wasm/add.wat b/examples/wasm/add.wat new file mode 100644 index 0000000..a246245 --- /dev/null +++ b/examples/wasm/add.wat @@ -0,0 +1,14 @@ +;; name: add ; description: add two numbers +(module + (func $add (export "add") (param $a i32) (param $b i32) (result i32) + local.get $a + local.get $b + i32.add) + + (func $add_64 (export "add_64") (param $a i64) (param $b i64) (result i64) + local.get $a + local.get $b + i64.add) +) + + |
