summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/cli/bin.rs11
-rw-r--r--crates/parser/src/error.rs25
-rw-r--r--crates/parser/src/lib.rs15
-rw-r--r--crates/tinywasm/src/error.rs24
-rw-r--r--crates/tinywasm/src/module/data.rs68
-rw-r--r--crates/tinywasm/src/module/instructions.rs399
-rw-r--r--crates/tinywasm/src/module/mod.rs262
-rw-r--r--crates/tinywasm/src/module/reader.rs177
-rw-r--r--crates/tinywasm/src/runtime/mod.rs2
-rw-r--r--crates/tinywasm/src/runtime/types.rs54
-rw-r--r--crates/tinywasm/src/store.rs31
-rw-r--r--crates/types/src/lib.rs4
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]>,