diff options
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/cli/Cargo.toml | 15 | ||||
| -rw-r--r-- | crates/cli/bin.rs | 43 | ||||
| -rw-r--r-- | crates/core/Cargo.toml | 17 | ||||
| -rw-r--r-- | crates/core/helloworld.wasm | bin | 115 -> 0 bytes | |||
| -rw-r--r-- | crates/core/helloworld.wat | 15 | ||||
| -rw-r--r-- | crates/core/src/bin.rs | 9 | ||||
| -rw-r--r-- | crates/core/src/error.rs | 28 | ||||
| -rw-r--r-- | crates/core/src/lib.rs | 185 | ||||
| -rw-r--r-- | crates/core/src/module.rs | 155 | ||||
| -rw-r--r-- | crates/core/src/std.rs | 18 |
10 files changed, 285 insertions, 200 deletions
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 Binary files differdeleted file mode 100644 index a5c95d0..0000000 --- a/crates/core/helloworld.wasm +++ /dev/null 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<T>(message: &str) -> Result<T, Self> { + Err(Self::Other(message.to_string())) + } +} + +impl From<wasmparser::BinaryReaderError> for Error { + fn from(value: wasmparser::BinaryReaderError) -> Self { + Self::ParseError { + message: value.message().to_string(), + offset: value.offset(), + } + } +} + +pub type Result<T, E = Error> = crate::std::result::Result<T, E>; 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<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<Module, ()> { - 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<bool, ()> { - 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<Payload<'_>, ()> { - 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<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/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; +} |
