From fe76d91f33b2441de87597ae5885ea9005f35b1c Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 28 Nov 2023 20:06:41 +0100 Subject: refactor: seperate cli crate Signed-off-by: Henry Gressmann --- Cargo.lock | 358 +++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 8 +- crates/cli/Cargo.toml | 15 ++ crates/cli/bin.rs | 43 +++++ crates/core/Cargo.toml | 17 +- crates/core/helloworld.wasm | Bin 115 -> 0 bytes crates/core/helloworld.wat | 15 -- crates/core/src/bin.rs | 9 -- crates/core/src/error.rs | 28 ++++ crates/core/src/lib.rs | 185 +++------------------- crates/core/src/module.rs | 155 ++++++++++++++++++ crates/core/src/std.rs | 18 +++ examples/wasm/helloworld.wasm | Bin 0 -> 115 bytes examples/wasm/helloworld.wat | 15 ++ 14 files changed, 639 insertions(+), 227 deletions(-) create mode 100644 crates/cli/Cargo.toml create mode 100644 crates/cli/bin.rs delete mode 100644 crates/core/helloworld.wasm delete mode 100644 crates/core/helloworld.wat delete mode 100644 crates/core/src/bin.rs create mode 100644 crates/core/src/error.rs create mode 100644 crates/core/src/module.rs create mode 100644 crates/core/src/std.rs create mode 100644 examples/wasm/helloworld.wasm create mode 100644 examples/wasm/helloworld.wat diff --git a/Cargo.lock b/Cargo.lock index 4a3e0e4..0202089 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,46 +3,360 @@ version = 3 [[package]] -name = "equivalent" -version = "1.0.1" +name = "addr2line" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] [[package]] -name = "hashbrown" -version = "0.14.2" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] -name = "indexmap" -version = "2.1.0" +name = "argh" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219" dependencies = [ - "equivalent", - "hashbrown", + "argh_derive", + "argh_shared", ] [[package]] -name = "semver" -version = "1.0.20" +name = "argh_derive" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a" +dependencies = [ + "argh_shared", + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "wasmcore" -version = "0.1.0" +name = "argh_shared" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531" +dependencies = [ + "serde", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "eyre" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80f656be11ddf91bd709454d15d5bd896fbaf4cc3314e69349e4d1569f5b46cd" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ - "wasmparser", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "wasmparser" -version = "0.118.0" +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror-core" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c001ee18b7e5e3f62cbf58c7fe220119e68d902bb7443179c0c8aef30090e999" +dependencies = [ + "thiserror-core-impl", +] + +[[package]] +name = "thiserror-core-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c60d69f36615a077cc7663b9cb8e42275722d23e58a7fa3d2c7f2915d09d04" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tinywasm" +version = "0.0.0" +dependencies = [ + "thiserror-core", + "tracing", + "wasmparser-nostd", +] + +[[package]] +name = "tinywasm-cli" +version = "0.0.0" +dependencies = [ + "argh", + "color-eyre", + "tinywasm", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "wasmparser-nostd" +version = "0.100.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebbb91574de0011ded32b14db12777e7dd5e9ea2f9d7317a1ab51a9495c75924" +checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" dependencies = [ - "indexmap", - "semver", + "indexmap-nostd", ] diff --git a/Cargo.toml b/Cargo.toml index 0cfbbe1..42abe2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,4 @@ [workspace] -members = [ - "crates/core", -] - -resolver = "2" \ No newline at end of file +members=["crates/core", "crates/cli"] +resolver="2" +default-members=["crates/cli"] diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml new file mode 100644 index 0000000..cdd0266 --- /dev/null +++ b/crates/cli/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name="tinywasm-cli" +version="0.0.0" +edition="2021" + +[[bin]] +name="tinywasm" +path="bin.rs" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tinywasm={path="../core"} +argh="0.1" +color-eyre="0.6" diff --git a/crates/cli/bin.rs b/crates/cli/bin.rs new file mode 100644 index 0000000..4e85481 --- /dev/null +++ b/crates/cli/bin.rs @@ -0,0 +1,43 @@ +use argh::FromArgs; +use color_eyre::eyre::Result; +use tinywasm::{self, Module}; + +#[derive(FromArgs)] +/// TinyWasm CLI +struct TinyWasmCli { + #[argh(subcommand)] + nested: TinyWasmSubcommand, +} + +#[derive(FromArgs)] +#[argh(subcommand)] +enum TinyWasmSubcommand { + Run(Run), +} + +#[derive(FromArgs)] +/// run a wasm file +#[argh(subcommand, name = "run")] +struct Run { + /// wasm file to run + #[argh(positional)] + wasm_file: String, +} + +fn main() -> Result<()> { + let args: TinyWasmCli = argh::from_env(); + + match args.nested { + TinyWasmSubcommand::Run(Run { wasm_file }) => { + let wasm = std::fs::read(wasm_file).unwrap(); + run(&wasm)?; + Ok(()) + } + } +} + +fn run(wasm: &[u8]) -> Result<()> { + let module = Module::new(wasm)?; + println!("{:#?}", module); + Ok(()) +} diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index e8c52b9..2f71aa9 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -1,21 +1,16 @@ [package] -name="wasmcore" -version="0.1.0" +name="tinywasm" +version="0.0.0" edition="2021" [lib] -name="wasmcore" path="src/lib.rs" -[[bin]] -name="wasmcore" -path="src/bin.rs" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] -wasmparser={version="0.118.0", default-features=false} +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 [features] default=["std"] -std=[] +std=["thiserror/std"] diff --git a/crates/core/helloworld.wasm b/crates/core/helloworld.wasm deleted file mode 100644 index a5c95d0..0000000 Binary files a/crates/core/helloworld.wasm and /dev/null differ diff --git a/crates/core/helloworld.wat b/crates/core/helloworld.wat deleted file mode 100644 index b74c98f..0000000 --- a/crates/core/helloworld.wat +++ /dev/null @@ -1,15 +0,0 @@ -(module - ;; Imports from JavaScript namespace - (import "console" "log" (func $log (param i32 i32))) ;; Import log function - (import "js" "mem" (memory 1)) ;; Import 1 page of memory (54kb) - - ;; Data section of our module - (data (i32.const 0) "Hello World from WebAssembly!") - - ;; Function declaration: Exported as helloWorld(), no arguments - (func (export "helloWorld") - i32.const 0 ;; pass offset 0 to log - i32.const 29 ;; pass length 29 to log (strlen of sample text) - call $log - ) -) \ No newline at end of file diff --git a/crates/core/src/bin.rs b/crates/core/src/bin.rs deleted file mode 100644 index c4257e8..0000000 --- a/crates/core/src/bin.rs +++ /dev/null @@ -1,9 +0,0 @@ -use wasmcore::{self, Module}; - -pub static WASM: &'static [u8] = include_bytes!("../helloworld.wasm"); - -fn main() { - let module = Module::new(WASM); - - println!("{:#?}", module); -} diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs new file mode 100644 index 0000000..b9c93c4 --- /dev/null +++ b/crates/core/src/error.rs @@ -0,0 +1,28 @@ +use alloc::string::{String, ToString}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum Error { + #[error("error parsing module")] + ParseError { message: String, offset: usize }, + + #[error("unknown error: {0}")] + Other(String), +} + +impl Error { + pub fn other(message: &str) -> Result { + Err(Self::Other(message.to_string())) + } +} + +impl From for Error { + fn from(value: wasmparser::BinaryReaderError) -> Self { + Self::ParseError { + message: value.message().to_string(), + offset: value.offset(), + } + } +} + +pub type Result = crate::std::result::Result; diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index ee8a3be..3d572a2 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,178 +1,33 @@ #![no_std] #![forbid(unsafe_code)] +#![cfg_attr(not(feature = "std"), feature(error_in_core))] -#[cfg(feature = "std")] -extern crate std; -use std::println; - +mod std; extern crate alloc; -use alloc::vec::Vec; - -use wasmparser::{ - DataSectionReader, ElementSectionReader, ExportSectionReader, FunctionBody, - FunctionSectionReader, GlobalSectionReader, ImportSectionReader, MemorySectionReader, Payload, - TableSectionReader, TypeSectionReader, Validator, -}; -mod instructions; - -struct Store {} - -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, - pub type_section: Option>, - pub function_section: Option>, - pub table_section: Option>, - pub memory_section: Option>, - pub global_section: Option>, - pub element_section: Option>, - pub data_section: Option>, - pub code_section: Option>, - pub import_section: Option>, - pub export_section: Option>, -} - -#[derive(Debug)] -pub struct CodeSection<'a> { - pub(crate) functions: Vec>, -} -impl<'a> CodeSection<'a> { - fn new() -> Self { - Self { - functions: Vec::new(), - } - } -} - -impl<'a> Module<'a> { - pub fn new(wasm: &'a [u8]) -> Result { - let mut validator = Validator::new(); - let mut reader = ModuleReader::new(); - - for payload in wasmparser::Parser::new(0).parse_all(wasm) { - reader.process_payload(payload.unwrap(), &mut validator)?; - } - - Ok(Self { reader }) - } -} - -impl<'a> ModuleReader<'a> { - pub fn new() -> Self { - Self::default() - } +mod error; +pub mod instructions; +pub mod module; +pub use error::*; +pub use module::Module; - 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).map_err(|_| ())?; - self.version = Some(num); - match encoding { - wasmparser::Encoding::Module => {} - wasmparser::Encoding::Component => return Err(()), - } - } - TypeSection(reader) => { - validator.type_section(&reader).map_err(|_| ())?; - self.type_section = Some(reader); - } - FunctionSection(reader) => { - validator.function_section(&reader).map_err(|_| ())?; - self.function_section = Some(reader); - } - TableSection(reader) => { - validator.table_section(&reader).map_err(|_| ())?; - self.table_section = Some(reader); - } - MemorySection(reader) => { - validator.memory_section(&reader).map_err(|_| ())?; - self.memory_section = Some(reader); - } - GlobalSection(reader) => { - validator.global_section(&reader).map_err(|_| ())?; - self.global_section = Some(reader); - } - ElementSection(reader) => { - validator.element_section(&reader).map_err(|_| ())?; - self.element_section = Some(reader); - } - DataSection(reader) => { - validator.data_section(&reader).map_err(|_| ())?; - self.data_section = Some(reader); - } - CodeSectionStart { count, range, .. } => { - validator - .code_section_start(count, &range) - .map_err(|_| ())?; +pub struct Store {} - self.code_section = Some(CodeSection::new()); - } - CodeSectionEntry(function) => { - validator.code_section_entry(&function).map_err(|_| ())?; +pub struct Instance {} - if let Some(code_section) = &mut self.code_section { - code_section.functions.push(function); - } else { - return Err(()); - } - } - ImportSection(reader) => { - validator.import_section(&reader).map_err(|_| ())?; - self.import_section = Some(reader); - } - ExportSection(reader) => { - validator.export_section(&reader).map_err(|_| ())?; - self.export_section = Some(reader); - } +#[cfg(test)] +mod tests { + use super::*; + use crate::{error::Result, Module}; + use std::dbg; - End(offset) => { - validator.end(offset).map_err(|_| ())?; - return Ok(true); - } - x => println!("Unknown payload: {:?}", x), - }; + #[test] + fn it_works() -> Result<()> { + let wasm = include_bytes!("../../../examples/wasm/helloworld.wasm"); + let module = Module::new(wasm)?; - Ok(false) - } -} -struct Instance {} + dbg!(module); -pub fn parse(wasm: &[u8]) -> Result, ()> { - for payload in wasmparser::Parser::new(0).parse_all(wasm) { - return Ok(payload.unwrap()); + Ok(()) } - - return Err(()); } diff --git a/crates/core/src/module.rs b/crates/core/src/module.rs new file mode 100644 index 0000000..1ed42c4 --- /dev/null +++ b/crates/core/src/module.rs @@ -0,0 +1,155 @@ +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, + pub type_section: Option>, + pub function_section: Option>, + pub table_section: Option>, + pub memory_section: Option>, + pub global_section: Option>, + pub element_section: Option>, + pub data_section: Option>, + pub code_section: Option>, + pub import_section: Option>, + pub export_section: Option>, +} + +#[derive(Debug)] +pub struct CodeSection<'a> { + pub(crate) functions: Vec>, +} + +impl<'a> CodeSection<'a> { + fn new() -> Self { + Self { + functions: Vec::new(), + } + } +} + +impl<'a> Module<'a> { + pub fn new(wasm: &'a [u8]) -> Result { + 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/core/src/std.rs b/crates/core/src/std.rs new file mode 100644 index 0000000..6c112f1 --- /dev/null +++ b/crates/core/src/std.rs @@ -0,0 +1,18 @@ +pub use core::*; + +#[cfg(feature = "std")] +extern crate std; + +#[cfg(feature = "std")] +pub use std::*; + +pub mod error { + #[cfg(feature = "std")] + extern crate std; + + #[cfg(feature = "std")] + pub use std::error::Error; + + #[cfg(not(feature = "std"))] + pub use core::error::Error; +} diff --git a/examples/wasm/helloworld.wasm b/examples/wasm/helloworld.wasm new file mode 100644 index 0000000..a5c95d0 Binary files /dev/null and b/examples/wasm/helloworld.wasm differ diff --git a/examples/wasm/helloworld.wat b/examples/wasm/helloworld.wat new file mode 100644 index 0000000..b74c98f --- /dev/null +++ b/examples/wasm/helloworld.wat @@ -0,0 +1,15 @@ +(module + ;; Imports from JavaScript namespace + (import "console" "log" (func $log (param i32 i32))) ;; Import log function + (import "js" "mem" (memory 1)) ;; Import 1 page of memory (54kb) + + ;; Data section of our module + (data (i32.const 0) "Hello World from WebAssembly!") + + ;; Function declaration: Exported as helloWorld(), no arguments + (func (export "helloWorld") + i32.const 0 ;; pass offset 0 to log + i32.const 29 ;; pass length 29 to log (strlen of sample text) + call $log + ) +) \ No newline at end of file -- cgit v1.3.1