From 1fb52089715631f4edb2abdc592547d0f5121161 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Mon, 4 Dec 2023 01:28:08 +0100 Subject: chore: ensure everything still runs on no_std Signed-off-by: Henry Gressmann --- crates/parser/src/lib.rs | 3 +- crates/parser/src/std.rs | 5 + crates/tinywasm/Cargo.toml | 6 +- crates/tinywasm/src/export.rs | 16 +++ crates/tinywasm/src/instance.rs | 149 +++++++++++++++++++++++++ crates/tinywasm/src/lib.rs | 7 +- crates/tinywasm/src/module.rs | 61 ++++++++++ crates/tinywasm/src/module/mod.rs | 222 ------------------------------------- crates/tinywasm/src/runtime/mod.rs | 5 +- crates/tinywasm/src/std.rs | 2 +- crates/tinywasm/src/store.rs | 13 ++- 11 files changed, 251 insertions(+), 238 deletions(-) create mode 100644 crates/parser/src/std.rs create mode 100644 crates/tinywasm/src/export.rs create mode 100644 crates/tinywasm/src/instance.rs create mode 100644 crates/tinywasm/src/module.rs delete mode 100644 crates/tinywasm/src/module/mod.rs (limited to 'crates') diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index d91694b..3cc51d6 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -2,9 +2,8 @@ #![forbid(unsafe_code)] #![cfg_attr(not(feature = "std"), feature(error_in_core))] +mod std; extern crate alloc; -#[cfg(feature = "std")] -extern crate std; mod conversion; mod error; diff --git a/crates/parser/src/std.rs b/crates/parser/src/std.rs new file mode 100644 index 0000000..67152be --- /dev/null +++ b/crates/parser/src/std.rs @@ -0,0 +1,5 @@ +#[cfg(feature = "std")] +extern crate std; + +#[cfg(feature = "std")] +pub use std::*; diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml index db4b659..b54ef8b 100644 --- a/crates/tinywasm/Cargo.toml +++ b/crates/tinywasm/Cargo.toml @@ -8,11 +8,11 @@ path="src/lib.rs" [dependencies] log="0.4.20" -tinywasm-parser={path="../parser"} -tinywasm-types={path="../types"} +tinywasm-parser={path="../parser", default-features=false} +tinywasm-types={path="../types", default-features=false} wasmparser={version="0.100", package="wasmparser-nostd", default-features=false} [features] default=["std"] -std=[] +std=["tinywasm-parser/std", "tinywasm-types/std"] diff --git a/crates/tinywasm/src/export.rs b/crates/tinywasm/src/export.rs new file mode 100644 index 0000000..c2aad88 --- /dev/null +++ b/crates/tinywasm/src/export.rs @@ -0,0 +1,16 @@ +use alloc::{boxed::Box, format}; +use tinywasm_types::{Export, ExternalKind}; + +use crate::{Error, Result}; + +#[derive(Debug)] +pub struct ExportInstance(pub(crate) Box<[Export]>); + +impl ExportInstance { + pub fn func(&self, name: &str) -> Result<&Export> { + self.0 + .iter() + .find(|e| e.name == name.into() && e.kind == ExternalKind::Func) + .ok_or(Error::Other(format!("export {} not found", name))) + } +} diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs new file mode 100644 index 0000000..8b445f1 --- /dev/null +++ b/crates/tinywasm/src/instance.rs @@ -0,0 +1,149 @@ +use alloc::{ + boxed::Box, + format, + string::{String, ToString}, + sync::Arc, + vec, + vec::Vec, +}; +use tinywasm_types::{Export, FuncAddr, FuncType, ModuleInstanceAddr, ValType, WasmValue}; + +use crate::{runtime::Stack, Error, ExportInstance, Result, Store}; + +/// A WebAssembly Module Instance. +/// Addrs are indices into the store's data structures. +/// See https://webassembly.github.io/spec/core/exec/runtime.html#module-instances +#[derive(Debug, Clone)] +pub struct ModuleInstance(Arc); + +#[derive(Debug)] +struct ModuleInstanceInner { + pub(crate) _idx: ModuleInstanceAddr, + pub(crate) func_start: Option, + pub(crate) types: Box<[FuncType]>, + pub exports: ExportInstance, + + pub(crate) func_addrs: Vec, + // pub table_addrs: Vec, + // pub mem_addrs: Vec, + // pub global_addrs: Vec, + // pub elem_addrs: Vec, + // pub data_addrs: Vec, +} + +impl ModuleInstance { + pub(crate) fn new( + types: Box<[FuncType]>, + func_start: Option, + exports: Box<[Export]>, + func_addrs: Vec, + idx: ModuleInstanceAddr, + ) -> Self { + Self(Arc::new(ModuleInstanceInner { + _idx: idx, + types, + func_start, + func_addrs, + exports: ExportInstance(exports), + })) + } + + /// Get an exported function by name + pub fn get_func(&self, store: &Store, name: &str) -> Result { + let export = self.0.exports.func(name)?; + let func_addr = self.0.func_addrs[export.index as usize]; + let func = store.get_func(func_addr as usize)?; + let ty = self.0.types[func.ty_addr() as usize].clone(); + + Ok(FuncHandle { + addr: export.index, + _module: self.clone(), + name: Some(name.to_string()), + ty, + }) + } + + /// Get the start function of the module + pub fn get_start_func(&mut self, store: &Store) -> Result> { + let Some(func_index) = self.0.func_start else { + return Ok(None); + }; + + let func_addr = self.0.func_addrs[func_index as usize]; + let func = store.get_func(func_addr as usize)?; + let ty = self.0.types[func.ty_addr() as usize].clone(); + + Ok(Some(FuncHandle { + _module: self.clone(), + addr: func_addr, + ty, + name: None, + })) + } + + /// 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(&mut self, store: &mut Store) -> Result> { + let Some(func) = self.get_start_func(store)? else { + return Ok(None); + }; + + let _ = func.call(store, vec![]); + Ok(Some(())) + } +} + +#[derive(Debug)] +pub struct FuncHandle { + _module: ModuleInstance, + addr: FuncAddr, + ty: FuncType, + pub name: Option, +} + +impl FuncHandle { + /// Call a function + pub fn call(&self, store: &mut Store, params: Vec) -> Result> { + let func = store + .data + .funcs + .get(self.addr as usize) + .ok_or(Error::Other(format!("function {} not found", self.addr)))?; + + let func_ty = &self.ty; + + // check that params match func_ty params + for (ty, param) in func_ty.params.iter().zip(params.clone()) { + if ty != ¶m.val_type() { + return Err(Error::Other(format!( + "param type mismatch: expected {:?}, got {:?}", + ty, param + ))); + } + } + + let mut local_types: Vec = Vec::new(); + local_types.extend(func_ty.params.iter()); + local_types.extend(func.locals().iter()); + + // let runtime = &mut store.runtime; + + let mut stack = Stack::default(); + stack.locals.extend(params); + + let instrs = func.instructions().iter(); + store.runtime.exec(&mut stack, instrs)?; + + let res = func_ty + .results + .iter() + .map(|_| stack.value_stack.pop()) + .collect::>>() + .ok_or(Error::Other( + "function did not return the correct number of values".into(), + ))?; + + Ok(res) + } +} diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index eecf325..9e976e4 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -13,7 +13,12 @@ pub use store::Store; pub mod module; pub use module::Module; -pub use module::ModuleInstance; + +pub mod instance; +pub use instance::ModuleInstance; + +pub mod export; +pub use export::ExportInstance; pub use tinywasm_parser as parser; pub use tinywasm_types::*; diff --git a/crates/tinywasm/src/module.rs b/crates/tinywasm/src/module.rs new file mode 100644 index 0000000..05bd643 --- /dev/null +++ b/crates/tinywasm/src/module.rs @@ -0,0 +1,61 @@ +use tinywasm_types::TinyWasmModule; + +use crate::{ModuleInstance, Result, Store}; + +#[derive(Debug)] +pub struct Module { + data: TinyWasmModule, +} + +impl From for Module { + fn from(data: TinyWasmModule) -> Self { + Self { data } + } +} + +impl Module { + pub fn parse_bytes(wasm: &[u8]) -> Result { + 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) -> Result { + let parser = tinywasm_parser::Parser::new(); + let data = parser.parse_module_file(path)?; + Ok(data.into()) + } + + #[cfg(feature = "std")] + pub fn parse_stream(stream: impl crate::std::io::Read) -> Result { + let parser = tinywasm_parser::Parser::new(); + let data = parser.parse_module_stream(stream)?; + Ok(data.into()) + } + + /// Instantiate the module in the given store + /// See https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation + /// Runs the start function if it exists + /// If you want to run the start function yourself, use `ModuleInstance::new` + pub fn instantiate( + self, + store: &mut Store, + // imports: Option<()>, + ) -> Result { + let idx = store.next_module_instance_idx(); + + let func_addrs = store.add_funcs(self.data.funcs, idx); + let instance = ModuleInstance::new( + self.data.types, + self.data.start_func, + self.data.exports, + func_addrs, + idx, + ); + + store.add_instance(instance.clone())?; + // let _ = instance.start(store)?; + Ok(instance) + } +} diff --git a/crates/tinywasm/src/module/mod.rs b/crates/tinywasm/src/module/mod.rs deleted file mode 100644 index db0c876..0000000 --- a/crates/tinywasm/src/module/mod.rs +++ /dev/null @@ -1,222 +0,0 @@ -use alloc::{ - boxed::Box, - format, - string::{String, ToString}, - sync::Arc, - vec, - vec::Vec, -}; -use tinywasm_types::{ - Export, ExternalKind, FuncAddr, FuncType, ModuleInstanceAddr, TinyWasmModule, ValType, - WasmValue, -}; - -use crate::{runtime::Stack, store, Error, Result, Store}; - -#[derive(Debug)] -pub struct Module { - data: TinyWasmModule, -} - -impl From for Module { - fn from(data: TinyWasmModule) -> Self { - Self { data } - } -} - -impl Module { - pub fn parse_bytes(wasm: &[u8]) -> Result { - 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) -> Result { - let parser = tinywasm_parser::Parser::new(); - let data = parser.parse_module_file(path)?; - Ok(data.into()) - } - - #[cfg(feature = "std")] - pub fn parse_stream(stream: impl crate::std::io::Read) -> Result { - let parser = tinywasm_parser::Parser::new(); - let data = parser.parse_module_stream(stream)?; - Ok(data.into()) - } - - /// Instantiate the module in the given store - /// See https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation - /// Runs the start function if it exists - /// If you want to run the start function yourself, use `ModuleInstance::new` - pub fn instantiate( - self, - store: &mut Store, - // imports: Option<()>, - ) -> Result { - let idx = store.next_module_instance_idx(); - - let func_addrs = store.add_funcs(self.data.funcs, idx); - let instance = ModuleInstance::new( - self.data.types, - self.data.start_func, - self.data.exports, - func_addrs, - idx, - ); - - store.add_instance(instance.clone())?; - // let _ = instance.start(store)?; - Ok(instance) - } -} - -/// A WebAssembly Module Instance. -/// Addrs are indices into the store's data structures. -/// See https://webassembly.github.io/spec/core/exec/runtime.html#module-instances -#[derive(Debug, Clone)] -pub struct ModuleInstance(Arc); - -#[derive(Debug)] -struct ModuleInstanceInner { - pub(crate) _idx: ModuleInstanceAddr, - pub(crate) func_start: Option, - pub(crate) types: Box<[FuncType]>, - pub exports: ExportInstance, - - pub(crate) func_addrs: Vec, - // pub table_addrs: Vec, - // pub mem_addrs: Vec, - // pub global_addrs: Vec, - // pub elem_addrs: Vec, - // pub data_addrs: Vec, -} - -#[derive(Debug)] -pub struct ExportInstance(Box<[Export]>); - -impl ExportInstance { - pub fn func(&self, name: &str) -> Result<&Export> { - self.0 - .iter() - .find(|e| e.name == name.into() && e.kind == ExternalKind::Func) - .ok_or(Error::Other(format!("export {} not found", name))) - } -} - -impl ModuleInstance { - fn new( - types: Box<[FuncType]>, - func_start: Option, - exports: Box<[Export]>, - func_addrs: Vec, - idx: ModuleInstanceAddr, - ) -> Self { - Self(Arc::new(ModuleInstanceInner { - _idx: idx, - types, - func_start, - func_addrs, - exports: ExportInstance(exports), - })) - } - - /// Get an exported function by name - pub fn get_func(&self, store: &store::Store, name: &str) -> Result { - let export = self.0.exports.func(name)?; - let func_addr = self.0.func_addrs[export.index as usize]; - let func = store.get_func(func_addr as usize)?; - let ty = self.0.types[func.ty_addr() as usize].clone(); - - Ok(FuncHandle { - addr: export.index, - _module: self.clone(), - name: Some(name.to_string()), - ty, - }) - } - - /// Get the start function of the module - pub fn get_start_func(&mut self, store: &store::Store) -> Result> { - let Some(func_index) = self.0.func_start else { - return Ok(None); - }; - - let func_addr = self.0.func_addrs[func_index as usize]; - let func = store.get_func(func_addr as usize)?; - let ty = self.0.types[func.ty_addr() as usize].clone(); - - Ok(Some(FuncHandle { - _module: self.clone(), - addr: func_addr, - ty, - name: None, - })) - } - - /// 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(&mut self, store: &mut store::Store) -> Result> { - let Some(func) = self.get_start_func(store)? else { - return Ok(None); - }; - - let _ = func.call(store, vec![]); - Ok(Some(())) - } -} - -#[derive(Debug)] -pub struct FuncHandle { - _module: ModuleInstance, - addr: FuncAddr, - ty: FuncType, - pub name: Option, -} - -impl FuncHandle { - /// Call a function - pub fn call(&self, store: &mut Store, params: Vec) -> Result> { - let func = store - .data - .funcs - .get(self.addr as usize) - .ok_or(Error::Other(format!("function {} not found", self.addr)))?; - - let func_ty = &self.ty; - - // check that params match func_ty params - for (ty, param) in func_ty.params.iter().zip(params.clone()) { - if ty != ¶m.val_type() { - return Err(Error::Other(format!( - "param type mismatch: expected {:?}, got {:?}", - ty, param - ))); - } - } - - let mut local_types: Vec = Vec::new(); - local_types.extend(func_ty.params.iter()); - local_types.extend(func.locals().iter()); - - // let runtime = &mut store.runtime; - - let mut stack = Stack::default(); - stack.locals.extend(params); - - let instrs = func.instructions().iter(); - store.runtime.exec(&mut stack, instrs)?; - - let res = func_ty - .results - .iter() - .map(|_| stack.value_stack.pop()) - .collect::>>() - .ok_or(Error::Other( - "function did not return the correct number of values".into(), - ))?; - - Ok(res) - } -} diff --git a/crates/tinywasm/src/runtime/mod.rs b/crates/tinywasm/src/runtime/mod.rs index c4c1892..78a98bd 100644 --- a/crates/tinywasm/src/runtime/mod.rs +++ b/crates/tinywasm/src/runtime/mod.rs @@ -1,8 +1,7 @@ mod executer; mod stack; -pub use executer::*; -use log::info; +use log::debug; pub use stack::*; use tinywasm_types::{Instruction, WasmValue}; @@ -25,7 +24,7 @@ impl Runtime { match instr { LocalGet(local_index) => { let val = &locals[*local_index as usize]; - info!("local: {:#?}", val); + debug!("local: {:#?}", val); stack.value_stack.push(val.clone()); } I64Add => { diff --git a/crates/tinywasm/src/std.rs b/crates/tinywasm/src/std.rs index 6c112f1..ba30537 100644 --- a/crates/tinywasm/src/std.rs +++ b/crates/tinywasm/src/std.rs @@ -1,8 +1,8 @@ +#[cfg(not(feature = "std"))] pub use core::*; #[cfg(feature = "std")] extern crate std; - #[cfg(feature = "std")] pub use std::*; diff --git a/crates/tinywasm/src/store.rs b/crates/tinywasm/src/store.rs index d0e4f14..a74163e 100644 --- a/crates/tinywasm/src/store.rs +++ b/crates/tinywasm/src/store.rs @@ -1,9 +1,10 @@ -use alloc::{boxed::Box, format, vec::Vec}; +use alloc::{format, vec::Vec}; use tinywasm_types::{FuncAddr, Function, Instruction, ModuleInstanceAddr, TypeAddr, ValType}; use crate::{runtime::Runtime, Error, ModuleInstance, Result}; /// global state that can be manipulated by WebAssembly programs +/// data should only be addressable by the module that owns it /// https://webassembly.github.io/spec/core/exec/runtime.html#store #[derive(Debug, Default)] pub struct Store { @@ -17,12 +18,12 @@ pub struct Store { #[derive(Debug)] pub struct FunctionInstance { pub(crate) func: Function, - pub(crate) module_instance: ModuleInstanceAddr, // index into store.module_instances + pub(crate) _module_instance: ModuleInstanceAddr, // index into store.module_instances } impl FunctionInstance { - pub(crate) fn module_instance_addr(&self) -> ModuleInstanceAddr { - self.module_instance + pub(crate) fn _module_instance_addr(&self) -> ModuleInstanceAddr { + self._module_instance } pub(crate) fn locals(&self) -> &[ValType] { @@ -68,8 +69,8 @@ impl Store { let mut func_addrs = Vec::with_capacity(funcs.len()); for func in funcs { self.data.funcs.push(FunctionInstance { - func: func, - module_instance: idx, + func, + _module_instance: idx, }); func_addrs.push(idx as FuncAddr); } -- cgit v1.3.1