diff options
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/cli/bin.rs | 11 | ||||
| -rw-r--r-- | crates/parser/src/error.rs | 25 | ||||
| -rw-r--r-- | crates/parser/src/lib.rs | 15 | ||||
| -rw-r--r-- | crates/tinywasm/src/error.rs | 24 | ||||
| -rw-r--r-- | crates/tinywasm/src/module/data.rs | 68 | ||||
| -rw-r--r-- | crates/tinywasm/src/module/instructions.rs | 399 | ||||
| -rw-r--r-- | crates/tinywasm/src/module/mod.rs | 262 | ||||
| -rw-r--r-- | crates/tinywasm/src/module/reader.rs | 177 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/mod.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/types.rs | 54 | ||||
| -rw-r--r-- | crates/tinywasm/src/store.rs | 31 | ||||
| -rw-r--r-- | crates/types/src/lib.rs | 4 |
12 files changed, 223 insertions, 849 deletions
diff --git a/crates/cli/bin.rs b/crates/cli/bin.rs index 9c6908d..015f34a 100644 --- a/crates/cli/bin.rs +++ b/crates/cli/bin.rs @@ -2,7 +2,8 @@ use std::str::FromStr; use argh::FromArgs; use color_eyre::eyre::Result; -use tinywasm::{self}; +use log::info; +use tinywasm::{self, WasmValue}; mod util; #[derive(FromArgs)] @@ -80,11 +81,13 @@ fn main() -> Result<()> { fn run(wasm: &[u8]) -> Result<()> { let mut store = tinywasm::Store::default(); - let module = tinywasm::Module::from_bytes(wasm)?; + let module = tinywasm::Module::parse_bytes(wasm)?; let instance = module.instantiate(&mut store)?; - let func = instance.get_func("add").unwrap(); - println!("func: {:?}", func); + let func = instance.get_func(&mut store, "add")?; + let params = vec![WasmValue::I32(2), WasmValue::I32(2)]; + let res = func.call(&mut store, params)?; + info!("{res:?}"); Ok(()) } diff --git a/crates/parser/src/error.rs b/crates/parser/src/error.rs index ee217c7..6fa9e25 100644 --- a/crates/parser/src/error.rs +++ b/crates/parser/src/error.rs @@ -1,3 +1,5 @@ +use core::fmt::Debug; + use alloc::string::{String, ToString}; use wasmparser::Encoding; @@ -14,6 +16,29 @@ pub enum ParseError { Other(String), } +impl Debug for ParseError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::InvalidType => write!(f, "invalid type"), + Self::UnsupportedSection(section) => write!(f, "unsupported section: {}", section), + Self::DuplicateSection(section) => write!(f, "duplicate section: {}", section), + Self::EmptySection(section) => write!(f, "empty section: {}", section), + Self::UnsupportedOperator(operator) => write!(f, "unsupported operator: {}", operator), + Self::ParseError { message, offset } => { + write!(f, "error parsing module: {} at offset {}", message, offset) + } + Self::InvalidEncoding(encoding) => write!(f, "invalid encoding: {:?}", encoding), + Self::InvalidLocalCount { expected, actual } => write!( + f, + "invalid local count: expected {}, actual {}", + expected, actual + ), + Self::EndNotReached => write!(f, "end of module not reached"), + Self::Other(message) => write!(f, "unknown error: {}", message), + } + } +} + impl From<wasmparser::BinaryReaderError> for ParseError { fn from(value: wasmparser::BinaryReaderError) -> Self { Self::ParseError { diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 5b17fb8..6c49af0 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -18,7 +18,11 @@ use wasmparser::Validator; pub struct Parser {} impl Parser { - pub fn parse_module_bytes(wasm: &[u8]) -> Result<TinyWasmModule> { + pub fn new() -> Self { + Self {} + } + + pub fn parse_module_bytes(&self, wasm: &[u8]) -> Result<TinyWasmModule> { let mut validator = Validator::new(); let mut reader = ModuleReader::new(); @@ -34,18 +38,21 @@ impl Parser { } #[cfg(feature = "std")] - pub fn parse_module_file(path: impl AsRef<crate::std::path::Path>) -> Result<TinyWasmModule> { + pub fn parse_module_file( + &self, + path: impl AsRef<crate::std::path::Path>, + ) -> Result<TinyWasmModule> { 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)) })?; let mut reader = crate::std::io::BufReader::new(f); - Self::parse_module_stream(&mut reader) + self.parse_module_stream(&mut reader) } #[cfg(feature = "std")] - pub fn parse_module_stream(mut stream: impl std::io::Read) -> Result<TinyWasmModule> { + pub fn parse_module_stream(&self, mut stream: impl std::io::Read) -> Result<TinyWasmModule> { use alloc::format; let mut validator = Validator::new(); diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs index 112b2b5..084a49c 100644 --- a/crates/tinywasm/src/error.rs +++ b/crates/tinywasm/src/error.rs @@ -1,15 +1,16 @@ use alloc::string::{String, ToString}; use core::fmt::Display; +use tinywasm_parser::ParseError; #[derive(Debug)] pub enum Error { - ParseError { - message: String, - offset: usize, - }, + ParseError(ParseError), UnsupportedFeature(String), Other(String), + FuncDidNotReturn, + StackUnderflow, + #[cfg(feature = "std")] Io(crate::std::io::Error), } @@ -17,9 +18,9 @@ pub enum Error { impl Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - Self::ParseError { message, offset } => { - write!(f, "error parsing module: {} at offset {}", message, offset) - } + Self::FuncDidNotReturn => write!(f, "function did not return"), + Self::StackUnderflow => write!(f, "stack underflow"), + Self::ParseError(err) => write!(f, "error parsing module: {:?}", err), Self::UnsupportedFeature(feature) => write!(f, "unsupported feature: {}", feature), Self::Other(message) => write!(f, "unknown error: {}", message), #[cfg(feature = "std")] @@ -40,12 +41,9 @@ impl Error { } } -impl From<wasmparser::BinaryReaderError> for Error { - fn from(value: wasmparser::BinaryReaderError) -> Self { - Self::ParseError { - message: value.message().to_string(), - offset: value.offset(), - } +impl From<tinywasm_parser::ParseError> for Error { + fn from(value: tinywasm_parser::ParseError) -> Self { + Self::ParseError(value) } } diff --git a/crates/tinywasm/src/module/data.rs b/crates/tinywasm/src/module/data.rs deleted file mode 100644 index 0fa8a27..0000000 --- a/crates/tinywasm/src/module/data.rs +++ /dev/null @@ -1,68 +0,0 @@ -use alloc::{boxed::Box, string::ToString, vec::Vec}; -use wasmparser::{ExternalKind, FuncType, FunctionBody, ValType}; - -use crate::Result; - -use super::instructions::Instruction; - -/// A WebAssembly Function -pub struct Function { - pub locals: Box<[ValType]>, - pub body: Box<[Instruction]>, -} - -impl Function { - pub fn new(body: FunctionBody) -> Result<Self> { - let locals_reader = body.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| l.1), - ); - - if locals.len() != count as usize { - return Err(crate::Error::Other("Invalid local index".to_string())); - } - - let body_reader = body.get_operators_reader()?; - let body = body_reader - .into_iter() - .map(|op| (op?).try_into()) - .collect::<Result<Vec<Instruction>>>()?; - - Ok(Self { - locals: locals.into_boxed_slice(), - body: body.into_boxed_slice(), - }) - } -} - -/// A WebAssembly Module Export -#[derive(Debug)] -pub struct Export { - /// The name of the export. - pub name: Box<str>, - /// The kind of the export. - pub kind: ExternalKind, - /// The index of the exported item. - pub index: u32, -} - -// TODO: maybe support rkyv serialization -pub struct ModuleData { - pub version: Option<u16>, - pub start_func: Option<u32>, - - pub types: Box<[FuncType]>, - pub functions: Box<[Function]>, - pub exports: Box<[Export]>, - // pub tables: Option<TableType>, - // pub memories: Option<MemoryType>, - // pub globals: Option<GlobalType>, - // pub elements: Option<ElementSectionReader<'a>>, - // pub imports: Option<ImportSectionReader<'a>>, - // pub data_segments: Option<DataSectionReader<'a>>, -} diff --git a/crates/tinywasm/src/module/instructions.rs b/crates/tinywasm/src/module/instructions.rs deleted file mode 100644 index b7fadf5..0000000 --- a/crates/tinywasm/src/module/instructions.rs +++ /dev/null @@ -1,399 +0,0 @@ -use alloc::{format, vec::Vec}; -use wasmparser::{BlockType, MemArg}; - -use crate::{ - runtime::{FuncAddr, GlobalAddr, LabelAddr, LocalAddr, TableAddr, TypeAddr}, - Result, -}; - -/// A WebAssembly Instruction -/// See https://webassembly.github.io/spec/core/binary/instructions.html -/// Currently includes all instructions from the MVP (1.0) spec -pub enum Instruction { - // Control Instructions - // See https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions - Unreachable, - Nop, - Block(BlockType), - Loop(BlockType), - If(BlockType), - Else, - End, - Br(LabelAddr), - BrIf(LabelAddr), - BrTable(Vec<LabelAddr>, LabelAddr), // not to spec, instead of a vector of labels, we have a label and a count - Return, - Call(FuncAddr), - CallIndirect(TypeAddr, TableAddr), - - // Parametric Instructions - // See https://webassembly.github.io/spec/core/binary/instructions.html#parametric-instructions - Drop, - Select, - - // Variable Instructions - // See https://webassembly.github.io/spec/core/binary/instructions.html#variable-instructions - LocalGet(LocalAddr), - LocalSet(LocalAddr), - LocalTee(LocalAddr), - GlobalGet(GlobalAddr), - GlobalSet(GlobalAddr), - - // Memory Instructions - I32Load(MemArg), - I64Load(MemArg), - F32Load(MemArg), - F64Load(MemArg), - I32Load8S(MemArg), - I32Load8U(MemArg), - I32Load16S(MemArg), - I32Load16U(MemArg), - I64Load8S(MemArg), - I64Load8U(MemArg), - I64Load16S(MemArg), - I64Load16U(MemArg), - I64Load32S(MemArg), - I64Load32U(MemArg), - I32Store(MemArg), - I64Store(MemArg), - F32Store(MemArg), - F64Store(MemArg), - I32Store8(MemArg), - I32Store16(MemArg), - I64Store8(MemArg), - I64Store16(MemArg), - I64Store32(MemArg), - MemorySize, - MemoryGrow, - - // Constants - I32Const(i32), - I64Const(i64), - F32Const(f32), - F64Const(f64), - - // Numeric Instructions - // See https://webassembly.github.io/spec/core/binary/instructions.html#numeric-instructions - I32Eqz, - I32Eq, - I32Ne, - I32LtS, - I32LtU, - I32GtS, - I32GtU, - I32LeS, - I32LeU, - I32GeS, - I32GeU, - I64Eqz, - I64Eq, - I64Ne, - I64LtS, - I64LtU, - I64GtS, - I64GtU, - I64LeS, - I64LeU, - I64GeS, - I64GeU, - F32Eq, - F32Ne, - F32Lt, - F32Gt, - F32Le, - F32Ge, - F64Eq, - F64Ne, - F64Lt, - F64Gt, - F64Le, - F64Ge, - I32Clz, - I32Ctz, - I32Popcnt, - I32Add, - I32Sub, - I32Mul, - I32DivS, - I32DivU, - I32RemS, - I32RemU, - I32And, - I32Or, - I32Xor, - I32Shl, - I32ShrS, - I32ShrU, - I32Rotl, - I32Rotr, - I64Clz, - I64Ctz, - I64Popcnt, - I64Add, - I64Sub, - I64Mul, - I64DivS, - I64DivU, - I64RemS, - I64RemU, - I64And, - I64Or, - I64Xor, - I64Shl, - I64ShrS, - I64ShrU, - I64Rotl, - I64Rotr, - F32Abs, - F32Neg, - F32Ceil, - F32Floor, - F32Trunc, - F32Nearest, - F32Sqrt, - F32Add, - F32Sub, - F32Mul, - F32Div, - F32Min, - F32Max, - F32Copysign, - F64Abs, - F64Neg, - F64Ceil, - F64Floor, - F64Trunc, - F64Nearest, - F64Sqrt, - F64Add, - F64Sub, - F64Mul, - F64Div, - F64Min, - F64Max, - F64Copysign, - I32WrapI64, - I32TruncF32S, - I32TruncF32U, - I32TruncF64S, - I32TruncF64U, - I64ExtendI32S, - I64ExtendI32U, - I64TruncF32S, - I64TruncF32U, - I64TruncF64S, - I64TruncF64U, - F32ConvertI32S, - F32ConvertI32U, - F32ConvertI64S, - F32ConvertI64U, - F32DemoteF64, - F64ConvertI32S, - F64ConvertI32U, - F64ConvertI64S, - F64ConvertI64U, - F64PromoteF32, - I32ReinterpretF32, - I64ReinterpretF64, - F32ReinterpretI32, - F64ReinterpretI64, -} - -impl TryFrom<wasmparser::Operator<'_>> for Instruction { - type Error = crate::Error; - - fn try_from(value: wasmparser::Operator<'_>) -> Result<Self> { - use wasmparser::Operator::*; - - let v = match value { - Unreachable => Self::Unreachable, - Nop => Self::Nop, - Block { blockty } => Self::Block(blockty), - Loop { blockty } => Self::Loop(blockty), - If { blockty } => Self::If(blockty), - Else => Self::Else, - End => Self::End, - Br { relative_depth } => Self::Br(relative_depth), - BrIf { relative_depth } => Self::BrIf(relative_depth), - BrTable { targets } => { - let default = targets.default(); - let targets = targets - .targets() - .map(|t| Ok(t?)) - .collect::<Result<Vec<u32>>>()?; - - Self::BrTable(targets, default) - } - Return => Self::Return, - Call { function_index } => Self::Call(function_index), - CallIndirect { - type_index, - table_index, - .. - } => Self::CallIndirect(type_index, table_index), - Drop => Self::Drop, - Select => Self::Select, - LocalGet { local_index } => Self::LocalGet(local_index), - LocalSet { local_index } => Self::LocalSet(local_index), - LocalTee { local_index } => Self::LocalTee(local_index), - GlobalGet { global_index } => Self::GlobalGet(global_index), - GlobalSet { global_index } => Self::GlobalSet(global_index), - MemorySize { .. } => Self::MemorySize, - MemoryGrow { .. } => Self::MemoryGrow, - I32Load { memarg } => Self::I32Load(memarg), - I64Load { memarg } => Self::I64Load(memarg), - F32Load { memarg } => Self::F32Load(memarg), - F64Load { memarg } => Self::F64Load(memarg), - I32Load8S { memarg } => Self::I32Load8S(memarg), - I32Load8U { memarg } => Self::I32Load8U(memarg), - I32Load16S { memarg } => Self::I32Load16S(memarg), - I32Load16U { memarg } => Self::I32Load16U(memarg), - I64Load8S { memarg } => Self::I64Load8S(memarg), - I64Load8U { memarg } => Self::I64Load8U(memarg), - I64Load16S { memarg } => Self::I64Load16S(memarg), - I64Load16U { memarg } => Self::I64Load16U(memarg), - I64Load32S { memarg } => Self::I64Load32S(memarg), - I64Load32U { memarg } => Self::I64Load32U(memarg), - I32Store { memarg } => Self::I32Store(memarg), - I64Store { memarg } => Self::I64Store(memarg), - F32Store { memarg } => Self::F32Store(memarg), - F64Store { memarg } => Self::F64Store(memarg), - I32Store8 { memarg } => Self::I32Store8(memarg), - I32Store16 { memarg } => Self::I32Store16(memarg), - I64Store8 { memarg } => Self::I64Store8(memarg), - I64Store16 { memarg } => Self::I64Store16(memarg), - I64Store32 { memarg } => Self::I64Store32(memarg), - I32Eqz => Self::I32Eqz, - I32Eq => Self::I32Eq, - I32Ne => Self::I32Ne, - I32LtS => Self::I32LtS, - I32LtU => Self::I32LtU, - I32GtS => Self::I32GtS, - I32GtU => Self::I32GtU, - I32LeS => Self::I32LeS, - I32LeU => Self::I32LeU, - I32GeS => Self::I32GeS, - I32GeU => Self::I32GeU, - I64Eqz => Self::I64Eqz, - I64Eq => Self::I64Eq, - I64Ne => Self::I64Ne, - I64LtS => Self::I64LtS, - I64LtU => Self::I64LtU, - I64GtS => Self::I64GtS, - I64GtU => Self::I64GtU, - I64LeS => Self::I64LeS, - I64LeU => Self::I64LeU, - I64GeS => Self::I64GeS, - I64GeU => Self::I64GeU, - F32Eq => Self::F32Eq, - F32Ne => Self::F32Ne, - F32Lt => Self::F32Lt, - F32Gt => Self::F32Gt, - F32Le => Self::F32Le, - F32Ge => Self::F32Ge, - F64Eq => Self::F64Eq, - F64Ne => Self::F64Ne, - F64Lt => Self::F64Lt, - F64Gt => Self::F64Gt, - F64Le => Self::F64Le, - F64Ge => Self::F64Ge, - I32Clz => Self::I32Clz, - I32Ctz => Self::I32Ctz, - I32Popcnt => Self::I32Popcnt, - I32Add => Self::I32Add, - I32Sub => Self::I32Sub, - I32Mul => Self::I32Mul, - I32DivS => Self::I32DivS, - I32DivU => Self::I32DivU, - I32RemS => Self::I32RemS, - I32RemU => Self::I32RemU, - I32And => Self::I32And, - I32Or => Self::I32Or, - I32Xor => Self::I32Xor, - I32Shl => Self::I32Shl, - I32ShrS => Self::I32ShrS, - I32ShrU => Self::I32ShrU, - I32Rotl => Self::I32Rotl, - I32Rotr => Self::I32Rotr, - I64Clz => Self::I64Clz, - I64Ctz => Self::I64Ctz, - I64Popcnt => Self::I64Popcnt, - I64Add => Self::I64Add, - I64Sub => Self::I64Sub, - I64Mul => Self::I64Mul, - I64DivS => Self::I64DivS, - I64DivU => Self::I64DivU, - I64RemS => Self::I64RemS, - I64RemU => Self::I64RemU, - I64And => Self::I64And, - I64Or => Self::I64Or, - I64Xor => Self::I64Xor, - I64Shl => Self::I64Shl, - I64ShrS => Self::I64ShrS, - I64ShrU => Self::I64ShrU, - I64Rotl => Self::I64Rotl, - I64Rotr => Self::I64Rotr, - F32Abs => Self::F32Abs, - F32Neg => Self::F32Neg, - F32Ceil => Self::F32Ceil, - F32Floor => Self::F32Floor, - F32Trunc => Self::F32Trunc, - F32Nearest => Self::F32Nearest, - F32Sqrt => Self::F32Sqrt, - F32Add => Self::F32Add, - F32Sub => Self::F32Sub, - F32Mul => Self::F32Mul, - F32Div => Self::F32Div, - F32Min => Self::F32Min, - F32Max => Self::F32Max, - F32Copysign => Self::F32Copysign, - F64Abs => Self::F64Abs, - F64Neg => Self::F64Neg, - F64Ceil => Self::F64Ceil, - F64Floor => Self::F64Floor, - F64Trunc => Self::F64Trunc, - F64Nearest => Self::F64Nearest, - F64Sqrt => Self::F64Sqrt, - F64Add => Self::F64Add, - F64Sub => Self::F64Sub, - F64Mul => Self::F64Mul, - F64Div => Self::F64Div, - F64Min => Self::F64Min, - F64Max => Self::F64Max, - F64Copysign => Self::F64Copysign, - I32WrapI64 => Self::I32WrapI64, - I32TruncF32S => Self::I32TruncF32S, - I32TruncF32U => Self::I32TruncF32U, - I32TruncF64S => Self::I32TruncF64S, - I32TruncF64U => Self::I32TruncF64U, - I64ExtendI32S => Self::I64ExtendI32S, - I64ExtendI32U => Self::I64ExtendI32U, - I64TruncF32S => Self::I64TruncF32S, - I64TruncF32U => Self::I64TruncF32U, - I64TruncF64S => Self::I64TruncF64S, - I64TruncF64U => Self::I64TruncF64U, - F32ConvertI32S => Self::F32ConvertI32S, - F32ConvertI32U => Self::F32ConvertI32U, - F32ConvertI64S => Self::F32ConvertI64S, - F32ConvertI64U => Self::F32ConvertI64U, - F32DemoteF64 => Self::F32DemoteF64, - F64ConvertI32S => Self::F64ConvertI32S, - F64ConvertI32U => Self::F64ConvertI32U, - F64ConvertI64S => Self::F64ConvertI64S, - F64ConvertI64U => Self::F64ConvertI64U, - F64PromoteF32 => Self::F64PromoteF32, - I32ReinterpretF32 => Self::I32ReinterpretF32, - I64ReinterpretF64 => Self::I64ReinterpretF64, - F32ReinterpretI32 => Self::F32ReinterpretI32, - F64ReinterpretI64 => Self::F64ReinterpretI64, - _ => { - return Err(crate::Error::UnsupportedFeature(format!( - "Unsupported instruction: {:?}", - value - ))) - } - }; - - Ok(v) - } -} diff --git a/crates/tinywasm/src/module/mod.rs b/crates/tinywasm/src/module/mod.rs index 94f4b78..a692175 100644 --- a/crates/tinywasm/src/module/mod.rs +++ b/crates/tinywasm/src/module/mod.rs @@ -1,32 +1,49 @@ -use alloc::{format, vec, vec::Vec}; -use wasmparser::{Export, FuncType, Validator}; +use alloc::{ + boxed::Box, + format, + string::{String, ToString}, + vec, + vec::Vec, +}; +use log::info; +use tinywasm_types::{Export, ExternalKind, FuncAddr, FuncType, TinyWasmModule}; -use crate::{runtime::FuncAddr, Error, Result, Store, WasmValue}; - -use self::reader::ModuleReader; - -pub mod data; -pub mod instructions; -pub mod reader; +use crate::{ + runtime::Runtime, + store::{self, StoreData}, + Error, Result, Store, WasmValue, +}; #[derive(Debug)] -pub struct Module<'data> { - data: ModuleReader<'data>, +pub struct Module { + data: TinyWasmModule, } -impl<'data> Module<'data> { - pub fn from_bytes(wasm: &'data [u8]) -> Result<Module<'data>> { - let mut validator = Validator::new(); - let mut reader = ModuleReader::new(); +impl From<TinyWasmModule> for Module { + fn from(data: TinyWasmModule) -> Self { + Self { data } + } +} - 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"); - } +impl Module { + pub fn parse_bytes(wasm: &[u8]) -> Result<Self> { + let parser = tinywasm_parser::Parser::new(); + let data = parser.parse_module_bytes(wasm)?; + Ok(data.into()) + } + + #[cfg(feature = "std")] + pub fn parse_file(path: impl AsRef<crate::std::path::Path>) -> Result<Self> { + let parser = tinywasm_parser::Parser::new(); + let data = parser.parse_module_file(path)?; + Ok(data.into()) + } - Ok(Self { data: reader }) + #[cfg(feature = "std")] + pub fn parse_stream(stream: impl crate::std::io::Read) -> Result<Self> { + let parser = tinywasm_parser::Parser::new(); + let data = parser.parse_module_stream(stream)?; + Ok(data.into()) } /// Instantiate the module in the given store @@ -34,12 +51,12 @@ impl<'data> Module<'data> { /// Runs the start function if it exists /// If you want to run the start function yourself, use `ModuleInstance::new` pub fn instantiate( - &self, - store: &'data mut Store<'data>, + self, + store: &mut Store, // imports: Option<()>, - ) -> Result<ModuleInstance<'data>> { + ) -> Result<ModuleInstance> { let i = ModuleInstance::new(store, self)?; - let _ = i.start()?; + let _ = i.start(store)?; Ok(i) } } @@ -48,130 +65,149 @@ impl<'data> Module<'data> { /// Addrs are indices into the store's data structures. /// See https://webassembly.github.io/spec/core/exec/runtime.html#module-instances #[derive(Debug)] -pub struct ModuleInstance<'data> { +pub struct ModuleInstance { pub(crate) func_start: Option<FuncAddr>, - pub(crate) types: Vec<FuncType>, + pub(crate) types: Box<[FuncType]>, + pub(crate) exports: Box<[Export]>, // pub(crate) func_addrs: Vec<FuncAddr>, // pub table_addrs: Vec<TableAddr>, // pub mem_addrs: Vec<MemAddr>, // pub global_addrs: Vec<GlobalAddr>, // pub elem_addrs: Vec<ElmAddr>, // pub data_addrs: Vec<DataAddr>, - pub(crate) exports: Vec<Export<'data>>, } -#[derive(Debug)] -pub struct ModuleFunc { - pub ty: FuncType, - pub code: FuncAddr, -} - -impl<'data> ModuleInstance<'data> { +impl ModuleInstance { /// Get an exported function by name - pub fn get_func(&self, name: &str) -> Option<ModuleFunc> { + pub fn get_func(&self, store: &store::Store, name: &str) -> Result<FuncHandle> { let export = self .exports .iter() - .find(|e| e.name == name && e.kind == wasmparser::ExternalKind::Func)?; - // let func_addr = self.func_addrs.get(export.index as usize)?; + .find(|e| e.name == name.into() && e.kind == ExternalKind::Func) + .ok_or(Error::Other(format!("export {} not found", name)))?; + + let func = store.get_func(export.index as usize)?; + let ty = &self.types[func.ty as usize]; - Some(ModuleFunc { - code: export.index, - ty: self.types.get(export.index as usize)?.clone(), + Ok(FuncHandle { + addr: export.index, + module: &self, + name: Some(name.to_string()), + ty: ty.clone(), }) } - pub fn get_start_func(&self) -> Option<ModuleFunc> { - let func_addr = self.func_start?; + /// Get the start function of the module + pub fn get_start_func(&self, store: &store::Store) -> Result<Option<FuncHandle>> { + let Some(func_addr) = self.func_start else { + return Ok(None); + }; - Some(ModuleFunc { - code: func_addr, - ty: self.types.get(func_addr as usize)?.clone(), - }) + let func = store.get_func(func_addr as usize)?; + let ty = &self.types[func.ty as usize]; + Ok(Some(FuncHandle { + module: &self, + addr: func_addr, + ty: ty.clone(), + name: None, + })) } - pub fn new(store: &'data mut Store<'data>, module: &Module<'data>) -> Result<Self> { - let types = module - .data - .type_section - .as_ref() - .map(|s| { - s.clone() - .into_iter() - .map(|ty| { - let wasmparser::Type::Func(func) = ty?; - Ok(func) - }) - .collect::<Result<Vec<_>>>() - }) - .transpose()? - .unwrap_or_default(); - - // let func_addrs = module - // .data - // .function_section - // .as_ref() - // .map(|s| { - // s.clone() - // .into_iter() - // .map(|f| Ok(f?)) - // .collect::<Result<Vec<_>>>() - // }) - // .transpose()? - // .unwrap_or_default(); + pub fn new(store: &mut Store, module: Module) -> Result<Self> { + let store_data = StoreData { + funcs: module.data.funcs, + }; - let exports = module - .data - .export_section - .as_ref() - .map(|s| { - s.clone() - .into_iter() - .map(|e| Ok(e?)) - .collect::<Result<Vec<_>>>() - }) - .transpose()? - .unwrap_or_default(); - let func_start = module.data.start_func; + store.initialize(store_data)?; - store.initialize(&module.data)?; Ok(Self { - types, - func_start, + types: module.data.types, + func_start: module.data.start_func, // table_addrs, // mem_addrs, // global_addrs, // elem_addrs, // data_addrs, - exports, + exports: module.data.exports, }) } - pub fn call(&self, func: ModuleFunc, args: &[WasmValue]) -> Result<Vec<WasmValue>> { - let func_type = func.ty; - let params = func_type.params(); - if params.len() != args.len() { - return Error::other(&format!( - "Function expected {} arguments, got {}", - params.len(), - args.len() - )); - } - - // TODO - // runtime.call(func, args) - Ok(vec![]) - } - /// Invoke the start function of the module /// Returns None if the module has no start function /// https://webassembly.github.io/spec/core/syntax/modules.html#syntax-start - pub fn start(&self) -> Result<Option<()>> { - let Some(func) = self.get_start_func() else { + pub fn start(&self, store: &mut store::Store) -> Result<Option<()>> { + let Some(func) = self.get_start_func(store)? else { return Ok(None); }; - let _ = self.call(func, &[])?; + let _ = func.call(store, vec![]); Ok(Some(())) } } + +#[derive(Debug)] +pub struct FuncHandle<'a> { + module: &'a ModuleInstance, + addr: FuncAddr, + ty: FuncType, + pub name: Option<String>, +} + +impl<'a> FuncHandle<'a> { + /// Call a function + pub fn call(&self, store: &mut Store, params: Vec<WasmValue>) -> Result<Vec<WasmValue>> { + let func = store.get_func(self.addr as usize)?; + let func_ty = &self.ty; + + let mut runtime = Runtime::default(); + let stack = &mut runtime.stack; + let locals = &mut stack.locals; + locals.extend(params); + + let mut instrs = func.body.iter(); + + while let Some(instr) = instrs.next() { + use tinywasm_types::Instruction::*; + match instr { + LocalGet(local_index) => { + let val = &locals[*local_index as usize]; + info!("local: {:#?}", val); + stack.value_stack.push(val.clone()); + } + I64Add => { + let a = stack.value_stack.pop().unwrap(); + let b = stack.value_stack.pop().unwrap(); + let (WasmValue::I64(a), WasmValue::I64(b)) = (a, b) else { + panic!("Invalid type"); + }; + let c = WasmValue::I64(a + b); + stack.value_stack.push(c); + } + I32Add => { + let a = stack.value_stack.pop().unwrap(); + let b = stack.value_stack.pop().unwrap(); + let (WasmValue::I32(a), WasmValue::I32(b)) = (a, b) else { + panic!("Invalid type"); + }; + let c = WasmValue::I32(a + b); + stack.value_stack.push(c); + } + End => { + let res = func_ty + .results + .iter() + .map(|_| runtime.stack.value_stack.pop()) + .collect::<Option<Vec<_>>>() + .ok_or(Error::Other( + "function did not return the correct number of values".into(), + ))?; + + return Ok(res); + } + _ => todo!(), + } + } + + Err(Error::FuncDidNotReturn) + } +} diff --git a/crates/tinywasm/src/module/reader.rs b/crates/tinywasm/src/module/reader.rs deleted file mode 100644 index 062cf45..0000000 --- a/crates/tinywasm/src/module/reader.rs +++ /dev/null @@ -1,177 +0,0 @@ -use alloc::{format, vec::Vec}; -use core::fmt::Debug; -use log::debug; -use wasmparser::{ - ExportSectionReader, FunctionBody, FunctionSectionReader, Payload, TypeSectionReader, Validator, -}; - -use crate::{Error, Result}; - -#[derive(Default)] -pub struct ModuleReader<'a> { - pub version: Option<u16>, - pub start_func: Option<u32>, - - 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("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 Error::other("Component"), - } - } - 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 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); - if self.code_section.is_some() { - return Error::other("Code section already found"); - } - - 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))?, - }; - - Ok(()) - } -} - -/// A WebAssembly code section -/// Can be cloned to read functions multiple times -#[derive(Debug, Clone)] -pub struct CodeSection<'a> { - pub(crate) functions: Vec<FunctionBody<'a>>, -} - -impl<'a> CodeSection<'a> { - fn new() -> Self { - Self { - functions: Vec::new(), - } - } -} diff --git a/crates/tinywasm/src/runtime/mod.rs b/crates/tinywasm/src/runtime/mod.rs index fe2c6e4..76686f7 100644 --- a/crates/tinywasm/src/runtime/mod.rs +++ b/crates/tinywasm/src/runtime/mod.rs @@ -1,10 +1,8 @@ mod executer; mod stack; -mod types; pub use executer::*; pub use stack::*; -pub use types::*; /// A WebAssembly Runtime. /// See https://webassembly.github.io/spec/core/exec/runtime.html diff --git a/crates/tinywasm/src/runtime/types.rs b/crates/tinywasm/src/runtime/types.rs deleted file mode 100644 index 2a09fc4..0000000 --- a/crates/tinywasm/src/runtime/types.rs +++ /dev/null @@ -1,54 +0,0 @@ -use alloc::string::String; - -/// A WebAssembly Address. -/// These are indexes into the respective stores. -/// See https://webassembly.github.io/spec/core/exec/runtime.html#addresses -pub type Addr = u32; -pub type FuncAddr = Addr; -pub type TableAddr = Addr; -pub type MemAddr = Addr; -pub type GlobalAddr = Addr; -pub type ElmAddr = Addr; -pub type DataAddr = Addr; -pub type ExternAddr = Addr; -// additional internal addresses -pub type TypeAddr = Addr; -pub type LocalAddr = Addr; -pub type LabelAddr = Addr; - -/// A WebAssembly Function Instance. -/// See https://webassembly.github.io/spec/core/exec/runtime.html#function-instances -// #[derive(Debug)] -// pub enum FuncInst { -// Host(HostFunc), -// Module(ModuleFunc), -// } -// #[derive(Debug)] -// pub struct HostFunc { -// pub ty: FuncType, -// pub hostcode: fn() -> (), -// } - -// pub struct Func<'a> { -// pub ty: FuncType, -// pub locals: Vec<ValType>, -// pub body: Vec<OperatorsIterator<'a>>, -// } - -/// A WebAssembly Export Instance. -/// https://webassembly.github.io/spec/core/exec/runtime.html#export-instances -#[derive(Debug)] -pub struct ExportInst { - pub name: String, - pub value: ExternVal, -} - -/// A WebAssembly External Value. -/// https://webassembly.github.io/spec/core/exec/runtime.html#external-values -#[derive(Debug)] -pub enum ExternVal { - Func(FuncAddr), - Table(TableAddr), - Mem(MemAddr), - Global(GlobalAddr), -} diff --git a/crates/tinywasm/src/store.rs b/crates/tinywasm/src/store.rs index ff303e1..6fc5860 100644 --- a/crates/tinywasm/src/store.rs +++ b/crates/tinywasm/src/store.rs @@ -1,18 +1,18 @@ -use alloc::vec::Vec; -use wasmparser::FunctionBody; +use alloc::{boxed::Box, format}; +use tinywasm_types::Function; -use crate::{module::reader::ModuleReader, Result}; +use crate::{Error, Result}; /// global state that can be manipulated by WebAssembly programs /// https://webassembly.github.io/spec/core/exec/runtime.html#store #[derive(Debug, Default)] -pub struct Store<'data> { - pub(crate) data: StoreData<'data>, +pub struct Store { + pub(crate) data: StoreData, } #[derive(Debug, Default)] -pub struct StoreData<'data> { - pub funcs: Vec<FunctionBody<'data>>, +pub struct StoreData { + pub funcs: Box<[Function]>, // pub tables: Vec<TableAddr>, // pub mems: Vec<MemAddr>, // pub globals: Vec<GlobalAddr>, @@ -20,14 +20,17 @@ pub struct StoreData<'data> { // pub datas: Vec<DataAddr>, } -impl<'data> Store<'data> { +impl Store { /// Initialize the store with global state from the given module - pub(crate) fn initialize(&'data mut self, reader: &ModuleReader<'data>) -> Result<()> { - let code = reader.code_section.clone().ok_or_else(|| { - crate::Error::Other("Module must have a code section to initialize the store".into()) - })?; - - self.data.funcs = code.functions; + pub(crate) fn initialize(&mut self, data: StoreData) -> Result<()> { + self.data = data; Ok(()) } + + pub(crate) fn get_func(&self, index: usize) -> Result<&Function> { + self.data + .funcs + .get(index) + .ok_or_else(|| Error::Other(format!("function {} not found", index))) + } } diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index af5cf1a..39ced78 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -3,9 +3,10 @@ extern crate alloc; mod instructions; pub use instructions::*; +#[derive(Debug)] pub struct TinyWasmModule { pub version: Option<u16>, - pub start_func: Option<u32>, + pub start_func: Option<FuncAddr>, pub types: Box<[FuncType]>, pub funcs: Box<[Function]>, @@ -97,6 +98,7 @@ pub struct FuncType { } /// A WebAssembly Function +#[derive(Debug)] pub struct Function { pub ty: TypeAddr, pub locals: Box<[ValType]>, |
