diff options
| author | Henry Gressmann <mail@henrygressmann.de> | 2024-01-29 20:39:31 +0100 |
|---|---|---|
| committer | Henry Gressmann <mail@henrygressmann.de> | 2024-01-29 20:39:31 +0100 |
| commit | f8651619e576ac97209b72660144568b54eb965c (patch) | |
| tree | 6bbf232f2b7444d02ef6cb2192759f628ce2ab8f /crates | |
| parent | 4c9385df1bcc0098c7e1db7a2f7c7e3dbeb00c8b (diff) | |
feat: pre-processed wasm
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/parser/Cargo.toml | 4 | ||||
| -rw-r--r-- | crates/parser/src/lib.rs | 22 | ||||
| -rw-r--r-- | crates/tinywasm/Cargo.toml | 5 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/data.rs | 27 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/element.rs | 19 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/function.rs | 11 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/global.rs | 39 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/memory.rs | 162 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/mod.rs | 218 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/table.rs | 123 | ||||
| -rw-r--r-- | crates/tinywasm/tests/testsuite/run.rs | 6 | ||||
| -rw-r--r-- | crates/types/Cargo.toml | 10 | ||||
| -rw-r--r-- | crates/types/src/archive.rs | 92 | ||||
| -rw-r--r-- | crates/types/src/instructions.rs | 14 | ||||
| -rw-r--r-- | crates/types/src/lib.rs | 114 |
15 files changed, 512 insertions, 354 deletions
diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index 1592f85..df8acce 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -11,9 +11,9 @@ repository.workspace=true # fork of wasmparser with no_std support, see https://github.com/bytecodealliance/wasmtime/issues/3495 wasmparser={version="0.100", package="wasmparser-nostd", default-features=false} log={version="0.4", optional=true} -tinywasm-types={version="0.3.0-alpha.0", path="../types"} +tinywasm-types={version="0.3.0-alpha.0", path="../types", default-features=false} [features] default=["std", "logging"] logging=["log"] -std=[] +std=["tinywasm-types/std"] diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index edcd280..c608232 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -25,7 +25,7 @@ mod module; use alloc::{string::ToString, vec::Vec}; pub use error::*; use module::ModuleReader; -use tinywasm_types::WasmFunction; +use tinywasm_types::{TypedWasmFunction, WasmFunction}; use wasmparser::Validator; pub use tinywasm_types::TinyWasmModule; @@ -116,19 +116,13 @@ impl TryFrom<ModuleReader> for TinyWasmModule { .code .into_iter() .zip(code_type_addrs) - .map(|(f, ty_idx)| { - ( - ty_idx, - WasmFunction { - instructions: f.body, - locals: f.locals, - ty: reader - .func_types - .get(ty_idx as usize) - .expect("No func type for func, this is a bug") - .clone(), - }, - ) + .map(|(f, ty_idx)| TypedWasmFunction { + type_addr: ty_idx, + wasm_function: WasmFunction { + instructions: f.body, + locals: f.locals, + ty: reader.func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone(), + }, }) .collect::<Vec<_>>(); diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml index a9e24bf..dd76f3d 100644 --- a/crates/tinywasm/Cargo.toml +++ b/crates/tinywasm/Cargo.toml @@ -29,11 +29,12 @@ plotters={version="0.3"} pretty_env_logger="0.5" [features] -default=["std", "parser", "logging"] +default=["std", "parser", "logging", "archive"] logging=["log", "tinywasm-types/logging", "tinywasm-parser?/logging"] std=["tinywasm-parser?/std", "tinywasm-types/std"] parser=["tinywasm-parser"] -unsafe=[] +unsafe=["tinywasm-types/unsafe"] +archive=["tinywasm-types/archive"] [[test]] name="generate-charts" diff --git a/crates/tinywasm/src/store/data.rs b/crates/tinywasm/src/store/data.rs new file mode 100644 index 0000000..efbb858 --- /dev/null +++ b/crates/tinywasm/src/store/data.rs @@ -0,0 +1,27 @@ +use alloc::vec::Vec; +use tinywasm_types::*; + +/// A WebAssembly Data Instance +/// +/// See <https://webassembly.github.io/spec/core/exec/runtime.html#data-instances> +#[derive(Debug)] +pub(crate) struct DataInstance { + pub(crate) data: Option<Vec<u8>>, + pub(crate) _owner: ModuleInstanceAddr, // index into store.module_instances +} + +impl DataInstance { + pub(crate) fn new(data: Option<Vec<u8>>, owner: ModuleInstanceAddr) -> Self { + Self { data, _owner: owner } + } + + pub(crate) fn drop(&mut self) -> Option<()> { + match self.data { + None => None, + Some(_) => { + let _ = self.data.take(); + Some(()) + } + } + } +} diff --git a/crates/tinywasm/src/store/element.rs b/crates/tinywasm/src/store/element.rs new file mode 100644 index 0000000..6563dff --- /dev/null +++ b/crates/tinywasm/src/store/element.rs @@ -0,0 +1,19 @@ +use crate::TableElement; +use alloc::vec::Vec; +use tinywasm_types::*; + +/// A WebAssembly Element Instance +/// +/// See <https://webassembly.github.io/spec/core/exec/runtime.html#element-instances> +#[derive(Debug)] +pub(crate) struct ElementInstance { + pub(crate) kind: ElementKind, + pub(crate) items: Option<Vec<TableElement>>, // none is the element was dropped + _owner: ModuleInstanceAddr, // index into store.module_instances +} + +impl ElementInstance { + pub(crate) fn new(kind: ElementKind, owner: ModuleInstanceAddr, items: Option<Vec<TableElement>>) -> Self { + Self { kind, _owner: owner, items } + } +} diff --git a/crates/tinywasm/src/store/function.rs b/crates/tinywasm/src/store/function.rs new file mode 100644 index 0000000..7508d00 --- /dev/null +++ b/crates/tinywasm/src/store/function.rs @@ -0,0 +1,11 @@ +use crate::Function; +use tinywasm_types::*; + +#[derive(Debug, Clone)] +/// A WebAssembly Function Instance +/// +/// See <https://webassembly.github.io/spec/core/exec/runtime.html#function-instances> +pub(crate) struct FunctionInstance { + pub(crate) func: Function, + pub(crate) owner: ModuleInstanceAddr, // index into store.module_instances, none for host functions +} diff --git a/crates/tinywasm/src/store/global.rs b/crates/tinywasm/src/store/global.rs new file mode 100644 index 0000000..fbcc402 --- /dev/null +++ b/crates/tinywasm/src/store/global.rs @@ -0,0 +1,39 @@ +use alloc::{format, string::ToString}; +use tinywasm_types::*; + +use crate::{runtime::RawWasmValue, Error, Result}; + +/// A WebAssembly Global Instance +/// +/// See <https://webassembly.github.io/spec/core/exec/runtime.html#global-instances> +#[derive(Debug)] +pub(crate) struct GlobalInstance { + pub(crate) value: RawWasmValue, + pub(crate) ty: GlobalType, + pub(crate) _owner: ModuleInstanceAddr, // index into store.module_instances +} + +impl GlobalInstance { + pub(crate) fn new(ty: GlobalType, value: RawWasmValue, owner: ModuleInstanceAddr) -> Self { + Self { ty, value, _owner: owner } + } + + pub(crate) fn get(&self) -> WasmValue { + self.value.attach_type(self.ty.ty) + } + + pub(crate) fn set(&mut self, val: WasmValue) -> Result<()> { + if val.val_type() != self.ty.ty { + return Err(Error::Other(format!( + "global type mismatch: expected {:?}, got {:?}", + self.ty.ty, + val.val_type() + ))); + } + if !self.ty.mutable { + return Err(Error::Other("global is immutable".to_string())); + } + self.value = val.into(); + Ok(()) + } +} diff --git a/crates/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs index a087b1d..9b527d3 100644 --- a/crates/tinywasm/src/store/memory.rs +++ b/crates/tinywasm/src/store/memory.rs @@ -205,122 +205,92 @@ pub(crate) unsafe trait MemLoadable<const T: usize>: Sized + Copy { fn from_be_bytes(bytes: [u8; T]) -> Self; } -#[allow(unsafe_code)] -unsafe impl MemLoadable<1> for u8 { - fn from_le_bytes(bytes: [u8; 1]) -> Self { - bytes[0] - } - fn from_be_bytes(bytes: [u8; 1]) -> Self { - bytes[0] - } -} +macro_rules! impl_mem_loadable_for_primitive { + ($($type:ty, $size:expr),*) => { + $( + #[allow(unsafe_code)] + unsafe impl MemLoadable<$size> for $type { + fn from_le_bytes(bytes: [u8; $size]) -> Self { + <$type>::from_le_bytes(bytes) + } -#[allow(unsafe_code)] -unsafe impl MemLoadable<2> for u16 { - fn from_le_bytes(bytes: [u8; 2]) -> Self { - Self::from_le_bytes(bytes) - } - fn from_be_bytes(bytes: [u8; 2]) -> Self { - Self::from_be_bytes(bytes) + fn from_be_bytes(bytes: [u8; $size]) -> Self { + <$type>::from_be_bytes(bytes) + } + } + )* } } -#[allow(unsafe_code)] -unsafe impl MemLoadable<4> for u32 { - fn from_le_bytes(bytes: [u8; 4]) -> Self { - Self::from_le_bytes(bytes) - } - fn from_be_bytes(bytes: [u8; 4]) -> Self { - Self::from_be_bytes(bytes) - } -} +impl_mem_loadable_for_primitive!( + u8, 1, i8, 1, u16, 2, i16, 2, u32, 4, i32, 4, f32, 4, u64, 8, i64, 8, f64, 8, u128, 16, i128, 16 +); -#[allow(unsafe_code)] -unsafe impl MemLoadable<8> for u64 { - fn from_le_bytes(bytes: [u8; 8]) -> Self { - Self::from_le_bytes(bytes) - } - fn from_be_bytes(bytes: [u8; 8]) -> Self { - Self::from_be_bytes(bytes) - } -} +#[cfg(test)] +mod memory_instance_tests { + use super::*; + use tinywasm_types::{MemoryArch, MemoryType, ModuleInstanceAddr}; -#[allow(unsafe_code)] -unsafe impl MemLoadable<16> for u128 { - fn from_le_bytes(bytes: [u8; 16]) -> Self { - Self::from_le_bytes(bytes) + fn create_test_memory() -> MemoryInstance { + let kind = MemoryType { arch: MemoryArch::I32, page_count_initial: 1, page_count_max: Some(2) }; + let owner = ModuleInstanceAddr::default(); + MemoryInstance::new(kind, owner) } - fn from_be_bytes(bytes: [u8; 16]) -> Self { - Self::from_be_bytes(bytes) - } -} -#[allow(unsafe_code)] -unsafe impl MemLoadable<1> for i8 { - fn from_le_bytes(bytes: [u8; 1]) -> Self { - bytes[0] as i8 - } - fn from_be_bytes(bytes: [u8; 1]) -> Self { - bytes[0] as i8 + #[test] + fn test_memory_store_and_load() { + let mut memory = create_test_memory(); + let data_to_store = [1, 2, 3, 4]; + assert!(memory.store(0, 0, &data_to_store, data_to_store.len()).is_ok()); + let loaded_data = memory.load(0, 0, data_to_store.len()).unwrap(); + assert_eq!(loaded_data, &data_to_store); } -} -#[allow(unsafe_code)] -unsafe impl MemLoadable<2> for i16 { - fn from_le_bytes(bytes: [u8; 2]) -> Self { - Self::from_le_bytes(bytes) - } - fn from_be_bytes(bytes: [u8; 2]) -> Self { - Self::from_be_bytes(bytes) + #[test] + fn test_memory_store_out_of_bounds() { + let mut memory = create_test_memory(); + let data_to_store = [1, 2, 3, 4]; + assert!(memory.store(memory.data.len(), 0, &data_to_store, data_to_store.len()).is_err()); } -} -#[allow(unsafe_code)] -unsafe impl MemLoadable<4> for i32 { - fn from_le_bytes(bytes: [u8; 4]) -> Self { - Self::from_le_bytes(bytes) + #[test] + fn test_memory_fill() { + let mut memory = create_test_memory(); + assert!(memory.fill(0, 10, 42).is_ok()); + assert_eq!(&memory.data[0..10], &[42; 10]); } - fn from_be_bytes(bytes: [u8; 4]) -> Self { - Self::from_be_bytes(bytes) - } -} -#[allow(unsafe_code)] -unsafe impl MemLoadable<8> for i64 { - fn from_le_bytes(bytes: [u8; 8]) -> Self { - Self::from_le_bytes(bytes) - } - fn from_be_bytes(bytes: [u8; 8]) -> Self { - Self::from_be_bytes(bytes) + #[test] + fn test_memory_fill_out_of_bounds() { + let mut memory = create_test_memory(); + assert!(memory.fill(memory.data.len(), 10, 42).is_err()); } -} -#[allow(unsafe_code)] -unsafe impl MemLoadable<16> for i128 { - fn from_le_bytes(bytes: [u8; 16]) -> Self { - Self::from_le_bytes(bytes) - } - fn from_be_bytes(bytes: [u8; 16]) -> Self { - Self::from_be_bytes(bytes) + #[test] + fn test_memory_copy_within() { + let mut memory = create_test_memory(); + memory.fill(0, 10, 1).unwrap(); + assert!(memory.copy_within(10, 0, 10).is_ok()); + assert_eq!(&memory.data[10..20], &[1; 10]); } -} -#[allow(unsafe_code)] -unsafe impl MemLoadable<4> for f32 { - fn from_le_bytes(bytes: [u8; 4]) -> Self { - Self::from_le_bytes(bytes) + #[test] + fn test_memory_copy_within_out_of_bounds() { + let mut memory = create_test_memory(); + assert!(memory.copy_within(memory.data.len(), 0, 10).is_err()); } - fn from_be_bytes(bytes: [u8; 4]) -> Self { - Self::from_be_bytes(bytes) - } -} -#[allow(unsafe_code)] -unsafe impl MemLoadable<8> for f64 { - fn from_le_bytes(bytes: [u8; 8]) -> Self { - Self::from_le_bytes(bytes) + #[test] + fn test_memory_grow() { + let mut memory = create_test_memory(); + let original_pages = memory.page_count(); + assert_eq!(memory.grow(1), Some(original_pages as i32)); + assert_eq!(memory.page_count(), original_pages + 1); } - fn from_be_bytes(bytes: [u8; 8]) -> Self { - Self::from_be_bytes(bytes) + + #[test] + fn test_memory_grow_out_of_bounds() { + let mut memory = create_test_memory(); + assert!(memory.grow(MAX_PAGES as i32 + 1).is_none()); } } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index 1526eb1..be885a7 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -1,5 +1,5 @@ use crate::log; -use alloc::{boxed::Box, format, rc::Rc, string::ToString, vec, vec::Vec}; +use alloc::{boxed::Box, format, rc::Rc, string::ToString, vec::Vec}; use core::{ cell::RefCell, sync::atomic::{AtomicUsize, Ordering}, @@ -11,8 +11,18 @@ use crate::{ Error, Function, ModuleInstance, Result, Trap, }; +mod data; +mod element; +mod function; +mod global; mod memory; +mod table; +pub(crate) use data::*; +pub(crate) use element::*; +pub(crate) use function::*; +pub(crate) use global::*; pub(crate) use memory::*; +pub(crate) use table::*; // global store id counter static STORE_ID: AtomicUsize = AtomicUsize::new(0); @@ -118,14 +128,14 @@ impl Store { /// Add functions to the store, returning their addresses in the store pub(crate) fn init_funcs( &mut self, - funcs: Vec<(u32, WasmFunction)>, + funcs: Vec<TypedWasmFunction>, idx: ModuleInstanceAddr, ) -> Result<Vec<FuncAddr>> { let func_count = self.data.funcs.len(); let mut func_addrs = Vec::with_capacity(func_count); - for (i, (_, func)) in funcs.into_iter().enumerate() { - self.data.funcs.push(FunctionInstance { func: Function::Wasm(Rc::new(func)), owner: idx }); + for (i, func) in funcs.into_iter().enumerate() { + self.data.funcs.push(FunctionInstance { func: Function::Wasm(Rc::new(func.wasm_function)), owner: idx }); func_addrs.push((i + func_count) as FuncAddr); } @@ -448,203 +458,3 @@ impl Store { .map(|global| global.borrow_mut().value = value) } } - -#[derive(Debug, Clone)] -/// A WebAssembly Function Instance -/// -/// See <https://webassembly.github.io/spec/core/exec/runtime.html#function-instances> -pub(crate) struct FunctionInstance { - pub(crate) func: Function, - pub(crate) owner: ModuleInstanceAddr, // index into store.module_instances, none for host functions -} - -#[derive(Debug, Clone, Copy)] -pub(crate) enum TableElement { - Uninitialized, - Initialized(Addr), -} - -impl From<Option<Addr>> for TableElement { - fn from(addr: Option<Addr>) -> Self { - match addr { - None => TableElement::Uninitialized, - Some(addr) => TableElement::Initialized(addr), - } - } -} - -impl TableElement { - pub(crate) fn addr(&self) -> Option<Addr> { - match self { - TableElement::Uninitialized => None, - TableElement::Initialized(addr) => Some(*addr), - } - } - - pub(crate) fn map<F: FnOnce(Addr) -> Addr>(self, f: F) -> Self { - match self { - TableElement::Uninitialized => TableElement::Uninitialized, - TableElement::Initialized(addr) => TableElement::Initialized(f(addr)), - } - } -} - -const MAX_TABLE_SIZE: u32 = 10000000; - -/// A WebAssembly Table Instance -/// -/// See <https://webassembly.github.io/spec/core/exec/runtime.html#table-instances> -#[derive(Debug)] -pub(crate) struct TableInstance { - pub(crate) elements: Vec<TableElement>, - pub(crate) kind: TableType, - pub(crate) _owner: ModuleInstanceAddr, // index into store.module_instances -} - -impl TableInstance { - pub(crate) fn new(kind: TableType, owner: ModuleInstanceAddr) -> Self { - Self { elements: vec![TableElement::Uninitialized; kind.size_initial as usize], kind, _owner: owner } - } - - pub(crate) fn get_wasm_val(&self, addr: usize) -> Result<WasmValue> { - let val = self.get(addr)?.addr(); - - Ok(match self.kind.element_type { - ValType::RefFunc => val.map(WasmValue::RefFunc).unwrap_or(WasmValue::RefNull(ValType::RefFunc)), - ValType::RefExtern => val.map(WasmValue::RefExtern).unwrap_or(WasmValue::RefNull(ValType::RefExtern)), - _ => unimplemented!("unsupported table type: {:?}", self.kind.element_type), - }) - } - - pub(crate) fn get(&self, addr: usize) -> Result<&TableElement> { - self.elements.get(addr).ok_or_else(|| Error::Trap(Trap::UndefinedElement { index: addr })) - } - - pub(crate) fn set(&mut self, table_idx: usize, value: Addr) -> Result<()> { - self.grow_to_fit(table_idx + 1).map(|_| self.elements[table_idx] = TableElement::Initialized(value)) - } - - pub(crate) fn grow_to_fit(&mut self, new_size: usize) -> Result<()> { - if new_size > self.elements.len() { - if new_size > self.kind.size_max.unwrap_or(MAX_TABLE_SIZE) as usize { - return Err(crate::Trap::TableOutOfBounds { offset: new_size, len: 1, max: self.elements.len() }.into()); - } - - self.elements.resize(new_size, TableElement::Uninitialized); - } - Ok(()) - } - - pub(crate) fn size(&self) -> i32 { - self.elements.len() as i32 - } - - fn resolve_func_ref(&self, func_addrs: &[u32], addr: Addr) -> Addr { - if self.kind.element_type != ValType::RefFunc { - return addr; - } - - *func_addrs - .get(addr as usize) - .expect("error initializing table: function not found. This should have been caught by the validator") - } - - // Initialize the table with the given elements - pub(crate) fn init_raw(&mut self, offset: i32, init: &[TableElement]) -> Result<()> { - let offset = offset as usize; - let end = offset.checked_add(init.len()).ok_or_else(|| { - Error::Trap(crate::Trap::TableOutOfBounds { offset, len: init.len(), max: self.elements.len() }) - })?; - - if end > self.elements.len() || end < offset { - return Err(crate::Trap::TableOutOfBounds { offset, len: init.len(), max: self.elements.len() }.into()); - } - - self.elements[offset..end].copy_from_slice(init); - log::debug!("table: {:?}", self.elements); - Ok(()) - } - - // Initialize the table with the given elements (resolves function references) - pub(crate) fn init(&mut self, func_addrs: &[u32], offset: i32, init: &[TableElement]) -> Result<()> { - let init = init.iter().map(|item| item.map(|addr| self.resolve_func_ref(func_addrs, addr))).collect::<Vec<_>>(); - - self.init_raw(offset, &init) - } -} - -/// A WebAssembly Global Instance -/// -/// See <https://webassembly.github.io/spec/core/exec/runtime.html#global-instances> -#[derive(Debug)] -pub(crate) struct GlobalInstance { - pub(crate) value: RawWasmValue, - pub(crate) ty: GlobalType, - pub(crate) _owner: ModuleInstanceAddr, // index into store.module_instances -} - -impl GlobalInstance { - pub(crate) fn new(ty: GlobalType, value: RawWasmValue, owner: ModuleInstanceAddr) -> Self { - Self { ty, value, _owner: owner } - } - - pub(crate) fn get(&self) -> WasmValue { - self.value.attach_type(self.ty.ty) - } - - pub(crate) fn set(&mut self, val: WasmValue) -> Result<()> { - if val.val_type() != self.ty.ty { - return Err(Error::Other(format!( - "global type mismatch: expected {:?}, got {:?}", - self.ty.ty, - val.val_type() - ))); - } - if !self.ty.mutable { - return Err(Error::Other("global is immutable".to_string())); - } - self.value = val.into(); - Ok(()) - } -} - -/// A WebAssembly Element Instance -/// -/// See <https://webassembly.github.io/spec/core/exec/runtime.html#element-instances> -#[derive(Debug)] -pub(crate) struct ElementInstance { - pub(crate) kind: ElementKind, - pub(crate) items: Option<Vec<TableElement>>, // none is the element was dropped - _owner: ModuleInstanceAddr, // index into store.module_instances -} - -impl ElementInstance { - pub(crate) fn new(kind: ElementKind, owner: ModuleInstanceAddr, items: Option<Vec<TableElement>>) -> Self { - Self { kind, _owner: owner, items } - } -} - -/// A WebAssembly Data Instance -/// -/// See <https://webassembly.github.io/spec/core/exec/runtime.html#data-instances> -#[derive(Debug)] -pub(crate) struct DataInstance { - pub(crate) data: Option<Vec<u8>>, - pub(crate) _owner: ModuleInstanceAddr, // index into store.module_instances -} - -impl DataInstance { - pub(crate) fn new(data: Option<Vec<u8>>, owner: ModuleInstanceAddr) -> Self { - Self { data, _owner: owner } - } - - pub(crate) fn drop(&mut self) -> Option<()> { - match self.data { - None => None, - Some(_) => { - let _ = self.data.take(); - Some(()) - } - } - } -} diff --git a/crates/tinywasm/src/store/table.rs b/crates/tinywasm/src/store/table.rs new file mode 100644 index 0000000..7b4c568 --- /dev/null +++ b/crates/tinywasm/src/store/table.rs @@ -0,0 +1,123 @@ +use crate::log; +use alloc::{vec, vec::Vec}; + +use tinywasm_types::*; + +use crate::{ + Error, Result, Trap, +}; + +const MAX_TABLE_SIZE: u32 = 10000000; + +/// A WebAssembly Table Instance +/// +/// See <https://webassembly.github.io/spec/core/exec/runtime.html#table-instances> +#[derive(Debug)] +pub(crate) struct TableInstance { + pub(crate) elements: Vec<TableElement>, + pub(crate) kind: TableType, + pub(crate) _owner: ModuleInstanceAddr, // index into store.module_instances +} + +impl TableInstance { + pub(crate) fn new(kind: TableType, owner: ModuleInstanceAddr) -> Self { + Self { elements: vec![TableElement::Uninitialized; kind.size_initial as usize], kind, _owner: owner } + } + + pub(crate) fn get_wasm_val(&self, addr: usize) -> Result<WasmValue> { + let val = self.get(addr)?.addr(); + + Ok(match self.kind.element_type { + ValType::RefFunc => val.map(WasmValue::RefFunc).unwrap_or(WasmValue::RefNull(ValType::RefFunc)), + ValType::RefExtern => val.map(WasmValue::RefExtern).unwrap_or(WasmValue::RefNull(ValType::RefExtern)), + _ => unimplemented!("unsupported table type: {:?}", self.kind.element_type), + }) + } + + pub(crate) fn get(&self, addr: usize) -> Result<&TableElement> { + self.elements.get(addr).ok_or_else(|| Error::Trap(Trap::UndefinedElement { index: addr })) + } + + pub(crate) fn set(&mut self, table_idx: usize, value: Addr) -> Result<()> { + self.grow_to_fit(table_idx + 1).map(|_| self.elements[table_idx] = TableElement::Initialized(value)) + } + + pub(crate) fn grow_to_fit(&mut self, new_size: usize) -> Result<()> { + if new_size > self.elements.len() { + if new_size > self.kind.size_max.unwrap_or(MAX_TABLE_SIZE) as usize { + return Err(crate::Trap::TableOutOfBounds { offset: new_size, len: 1, max: self.elements.len() }.into()); + } + + self.elements.resize(new_size, TableElement::Uninitialized); + } + Ok(()) + } + + pub(crate) fn size(&self) -> i32 { + self.elements.len() as i32 + } + + fn resolve_func_ref(&self, func_addrs: &[u32], addr: Addr) -> Addr { + if self.kind.element_type != ValType::RefFunc { + return addr; + } + + *func_addrs + .get(addr as usize) + .expect("error initializing table: function not found. This should have been caught by the validator") + } + + // Initialize the table with the given elements + pub(crate) fn init_raw(&mut self, offset: i32, init: &[TableElement]) -> Result<()> { + let offset = offset as usize; + let end = offset.checked_add(init.len()).ok_or_else(|| { + Error::Trap(crate::Trap::TableOutOfBounds { offset, len: init.len(), max: self.elements.len() }) + })?; + + if end > self.elements.len() || end < offset { + return Err(crate::Trap::TableOutOfBounds { offset, len: init.len(), max: self.elements.len() }.into()); + } + + self.elements[offset..end].copy_from_slice(init); + log::debug!("table: {:?}", self.elements); + Ok(()) + } + + // Initialize the table with the given elements (resolves function references) + pub(crate) fn init(&mut self, func_addrs: &[u32], offset: i32, init: &[TableElement]) -> Result<()> { + let init = init.iter().map(|item| item.map(|addr| self.resolve_func_ref(func_addrs, addr))).collect::<Vec<_>>(); + + self.init_raw(offset, &init) + } +} + +#[derive(Debug, Clone, Copy)] +pub(crate) enum TableElement { + Uninitialized, + Initialized(TableAddr), +} + +impl From<Option<Addr>> for TableElement { + fn from(addr: Option<Addr>) -> Self { + match addr { + None => TableElement::Uninitialized, + Some(addr) => TableElement::Initialized(addr), + } + } +} + +impl TableElement { + pub(crate) fn addr(&self) -> Option<Addr> { + match self { + TableElement::Uninitialized => None, + TableElement::Initialized(addr) => Some(*addr), + } + } + + pub(crate) fn map<F: FnOnce(Addr) -> Addr>(self, f: F) -> Self { + match self { + TableElement::Uninitialized => TableElement::Uninitialized, + TableElement::Initialized(addr) => TableElement::Initialized(f(addr)), + } + } +} diff --git a/crates/tinywasm/tests/testsuite/run.rs b/crates/tinywasm/tests/testsuite/run.rs index de5d5ab..125a9d6 100644 --- a/crates/tinywasm/tests/testsuite/run.rs +++ b/crates/tinywasm/tests/testsuite/run.rs @@ -137,7 +137,7 @@ impl TestSuite { for (name, addr) in modules { log::debug!("registering module: {}", name); - imports.link_module(&name, *addr)?; + imports.link_module(name, *addr)?; } Ok(imports) @@ -199,7 +199,7 @@ impl TestSuite { QuoteWat::QuoteModule(_, quoted_wat) => { let wat = quoted_wat .iter() - .map(|(_, s)| std::str::from_utf8(&s).expect("failed to convert wast to utf8")) + .map(|(_, s)| std::str::from_utf8(s).expect("failed to convert wast to utf8")) .collect::<Vec<_>>() .join("\n"); @@ -444,7 +444,7 @@ impl TestSuite { continue; } }; - let expected = expected.get(0).expect("expected global value"); + let expected = expected.first().expect("expected global value"); let module_global = module_global.attach_type(expected.val_type()); if !module_global.eq_loose(expected) { diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index bc6769a..76e5679 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -9,10 +9,12 @@ repository.workspace=true [dependencies] log={version="0.4", optional=true} -rkyv={version="0.7", optional=true, default-features=false, features=["size_32"]} +rkyv={version="0.7", optional=true, default-features=false, features=["size_32", "validation"]} +bytecheck={version="0.7", optional=true} [features] -default=["std", "logging"] -std=["rkyv/std"] -serialize=["dep:rkyv", "dep:log"] +default=["std", "logging", "archive", "unsafe"] +std=["rkyv?/std"] +archive=["dep:rkyv", "dep:bytecheck"] logging=["dep:log"] +unsafe=[] diff --git a/crates/types/src/archive.rs b/crates/types/src/archive.rs new file mode 100644 index 0000000..00a9911 --- /dev/null +++ b/crates/types/src/archive.rs @@ -0,0 +1,92 @@ +use crate::TinyWasmModule; +use rkyv::{ + check_archived_root, + ser::{serializers::AllocSerializer, Serializer}, + Deserialize, +}; + +// 16 bytes +const TWASM_MAGIC_PREFIX: &[u8; 4] = b"TWAS"; +const TWASM_VERSION: &[u8; 2] = b"01"; + +#[rustfmt::skip] +const TWASM_MAGIC: [u8; 16] = [ TWASM_MAGIC_PREFIX[0], TWASM_MAGIC_PREFIX[1], TWASM_MAGIC_PREFIX[2], TWASM_MAGIC_PREFIX[3], TWASM_VERSION[0], TWASM_VERSION[1], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + +pub use rkyv::AlignedVec; + +fn validate_magic(wasm: &[u8]) -> Result<usize, &str> { + if wasm.len() < TWASM_MAGIC.len() { + return Err("Invalid twasm: too short"); + } + if &wasm[..TWASM_MAGIC_PREFIX.len()] != TWASM_MAGIC_PREFIX { + return Err("Invalid twasm: invalid magic number"); + } + if &wasm[TWASM_MAGIC_PREFIX.len()..TWASM_MAGIC_PREFIX.len() + TWASM_VERSION.len()] != TWASM_VERSION { + return Err("Invalid twasm: invalid version"); + } + if wasm[TWASM_MAGIC_PREFIX.len() + TWASM_VERSION.len()..TWASM_MAGIC.len()] != [0; 10] { + return Err("Invalid twasm: invalid padding"); + } + + Ok(TWASM_MAGIC.len()) +} + +impl TinyWasmModule { + /// Creates a TinyWasmModule from a slice of bytes. + pub fn from_twasm(wasm: &[u8]) -> Result<TinyWasmModule, &str> { + let len = validate_magic(wasm)?; + let root = check_archived_root::<Self>(&wasm[len..]).map_err(|e| { + log::error!("Error checking archived root: {}", e); + "Error checking archived root" + })?; + + Ok(root.deserialize(&mut rkyv::Infallible).unwrap()) + } + + #[cfg(feature = "unsafe")] + #[allow(unsafe_code)] + /// Creates a TinyWasmModule from a slice of bytes. + /// + /// # Safety + /// This function is only safe to call if the bytes have been created by + /// a trusted source. Otherwise, it may cause undefined behavior. + pub unsafe fn from_twasm_unchecked(wasm: &[u8]) -> Self { + let len = validate_magic(wasm).unwrap(); + rkyv::archived_root::<TinyWasmModule>(&wasm[len..]).deserialize(&mut rkyv::Infallible).unwrap() + } + + /// Serializes the TinyWasmModule into a vector of bytes. + /// AlignedVec can be deferenced as a slice of bytes and + /// implements io::Write when the `std` feature is enabled. + pub fn serialize_twasm(&self) -> rkyv::AlignedVec { + let mut serializer = AllocSerializer::<0>::default(); + serializer.pad(TWASM_MAGIC.len()).unwrap(); + serializer.serialize_value(self).unwrap(); + let mut out = serializer.into_serializer().into_inner(); + out[..TWASM_MAGIC.len()].copy_from_slice(&TWASM_MAGIC); + out + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize() { + let wasm = TinyWasmModule::default(); + let twasm = wasm.serialize_twasm(); + let wasm2 = TinyWasmModule::from_twasm(&twasm).unwrap(); + assert_eq!(wasm, wasm2); + } + + #[cfg(feature = "unsafe")] + #[test] + fn test_serialize_unchecked() { + let wasm = TinyWasmModule::default(); + let twasm = wasm.serialize_twasm(); + #[allow(unsafe_code)] + let wasm2 = unsafe { TinyWasmModule::from_twasm_unchecked(&twasm) }; + assert_eq!(wasm, wasm2); + } +} diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index 1061d10..0e2eafe 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -3,6 +3,8 @@ use crate::{DataAddr, ElemAddr, MemAddr}; use super::{FuncAddr, GlobalAddr, LabelAddr, LocalAddr, TableAddr, TypeAddr, ValType}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub enum BlockArgs { Empty, Type(ValType), @@ -10,7 +12,9 @@ pub enum BlockArgs { } /// Represents a memory immediate in a WebAssembly memory instruction. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub struct MemoryArg { pub mem_addr: MemAddr, pub align: u8, @@ -23,7 +27,9 @@ type BrTableLen = usize; type EndOffset = usize; type ElseOffset = usize; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub enum ConstInstruction { I32Const(i32), I64Const(i64), @@ -46,7 +52,9 @@ pub enum ConstInstruction { /// This makes it easier to implement the label stack iteratively. /// /// See <https://webassembly.github.io/spec/core/binary/instructions.html> -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub enum Instruction { // Custom Instructions BrLabel(LabelAddr), diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 2ccf869..547379c 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -1,10 +1,11 @@ -#![no_std] -#![forbid(unsafe_code)] #![doc(test( no_crate_inject, attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_assignments, unused_variables)) ))] #![warn(missing_debug_implementations, rust_2018_idioms, unreachable_pub)] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "unsafe"), forbid(unsafe_code))] +#![cfg_attr(feature = "unsafe", deny(unused_unsafe))] //! Types used by [`tinywasm`](https://docs.rs/tinywasm) and [`tinywasm_parser`](https://docs.rs/tinywasm_parser). @@ -28,22 +29,25 @@ use core::{fmt::Debug, ops::Range}; use alloc::boxed::Box; pub use instructions::*; +#[cfg(feature = "archive")] +pub mod archive; + /// A TinyWasm WebAssembly Module /// /// This is the internal representation of a WebAssembly module in TinyWasm. /// TinyWasmModules are validated before being created, so they are guaranteed to be valid (as long as they were created by TinyWasm). /// This means you should not trust a TinyWasmModule created by a third party to be valid. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub struct TinyWasmModule { /// The version of the WebAssembly module. pub version: Option<u16>, - /// The start function of the WebAssembly module. pub start_func: Option<FuncAddr>, /// The functions of the WebAssembly module. - pub funcs: Box<[(u32, WasmFunction)]>, - + pub funcs: Box<[TypedWasmFunction]>, /// The types of the WebAssembly module. pub func_types: Box<[FuncType]>, @@ -90,6 +94,7 @@ pub enum WasmValue { } impl WasmValue { + #[inline] pub fn const_instr(&self) -> ConstInstruction { match self { Self::I32(i) => ConstInstruction::I32Const(*i), @@ -106,6 +111,7 @@ impl WasmValue { } /// Get the default value for a given type. + #[inline] pub fn default_for(ty: ValType) -> Self { match ty { ValType::I32 => Self::I32(0), @@ -117,6 +123,7 @@ impl WasmValue { } } + #[inline] pub fn eq_loose(&self, other: &Self) -> bool { match (self, other) { (Self::I32(a), Self::I32(b)) => a == b, @@ -144,36 +151,45 @@ impl WasmValue { } impl From<i32> for WasmValue { + #[inline] fn from(i: i32) -> Self { Self::I32(i) } } impl From<i64> for WasmValue { + #[inline] fn from(i: i64) -> Self { Self::I64(i) } } impl From<f32> for WasmValue { + #[inline] fn from(i: f32) -> Self { Self::F32(i) } } impl From<f64> for WasmValue { + #[inline] fn from(i: f64) -> Self { Self::F64(i) } } +#[cold] +fn cold() {} + impl TryFrom<WasmValue> for i32 { type Error = (); + #[inline] fn try_from(value: WasmValue) -> Result<Self, Self::Error> { match value { WasmValue::I32(i) => Ok(i), _ => { + cold(); crate::log::error!("i32: try_from failed: {:?}", value); Err(()) } @@ -184,10 +200,12 @@ impl TryFrom<WasmValue> for i32 { impl TryFrom<WasmValue> for i64 { type Error = (); + #[inline] fn try_from(value: WasmValue) -> Result<Self, Self::Error> { match value { WasmValue::I64(i) => Ok(i), _ => { + cold(); crate::log::error!("i64: try_from failed: {:?}", value); Err(()) } @@ -198,10 +216,12 @@ impl TryFrom<WasmValue> for i64 { impl TryFrom<WasmValue> for f32 { type Error = (); + #[inline] fn try_from(value: WasmValue) -> Result<Self, Self::Error> { match value { WasmValue::F32(i) => Ok(i), _ => { + cold(); crate::log::error!("f32: try_from failed: {:?}", value); Err(()) } @@ -212,10 +232,12 @@ impl TryFrom<WasmValue> for f32 { impl TryFrom<WasmValue> for f64 { type Error = (); + #[inline] fn try_from(value: WasmValue) -> Result<Self, Self::Error> { match value { WasmValue::F64(i) => Ok(i), _ => { + cold(); crate::log::error!("f64: try_from failed: {:?}", value); Err(()) } @@ -240,6 +262,7 @@ impl Debug for WasmValue { impl WasmValue { /// Get the type of a [`WasmValue`] + #[inline] pub fn val_type(&self) -> ValType { match self { Self::I32(_) => ValType::I32, @@ -255,6 +278,8 @@ impl WasmValue { /// Type of a WebAssembly value. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub enum ValType { /// A 32-bit integer. I32, @@ -271,6 +296,7 @@ pub enum ValType { } impl ValType { + #[inline] pub fn default_value(&self) -> WasmValue { WasmValue::default_for(*self) } @@ -280,6 +306,8 @@ impl ValType { /// /// See <https://webassembly.github.io/spec/core/syntax/types.html#external-types> #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub enum ExternalKind { /// A WebAssembly Function. Func, @@ -322,6 +350,7 @@ pub enum ExternVal { } impl ExternVal { + #[inline] pub fn kind(&self) -> ExternalKind { match self { Self::Func(_) => ExternalKind::Func, @@ -331,6 +360,7 @@ impl ExternVal { } } + #[inline] pub fn new(kind: ExternalKind, addr: Addr) -> Self { match kind { ExternalKind::Func => Self::Func(addr), @@ -345,6 +375,8 @@ impl ExternVal { /// /// See <https://webassembly.github.io/spec/core/syntax/types.html#function-types> #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub struct FuncType { pub params: Box<[ValType]>, pub results: Box<[ValType]>, @@ -352,20 +384,33 @@ pub struct FuncType { impl FuncType { /// Get the number of parameters of a function type. + #[inline] pub fn empty() -> Self { Self { params: Box::new([]), results: Box::new([]) } } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub struct WasmFunction { pub instructions: Box<[Instruction]>, pub locals: Box<[ValType]>, pub ty: FuncType, } +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] +pub struct TypedWasmFunction { + pub type_addr: u32, + pub wasm_function: WasmFunction, +} + /// A WebAssembly Module Export -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub struct Export { /// The name of the export. pub name: Box<str>, @@ -375,19 +420,25 @@ pub struct Export { pub index: u32, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub struct Global { pub ty: GlobalType, pub init: ConstInstruction, } #[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub struct GlobalType { pub mutable: bool, pub ty: ValType, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub struct TableType { pub element_type: ValType, pub size_initial: u32, @@ -404,10 +455,12 @@ impl TableType { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] /// Represents a memory's type. #[derive(Copy)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub struct MemoryType { pub arch: MemoryArch, pub page_count_initial: u64, @@ -418,30 +471,28 @@ impl MemoryType { pub fn new_32(page_count_initial: u64, page_count_max: Option<u64>) -> Self { Self { arch: MemoryArch::I32, page_count_initial, page_count_max } } - - // pub fn new_64(page_count_initial: u64, page_count_max: Option<u64>) -> Self { - // Self { - // arch: MemoryArch::I64, - // page_count_initial, - // page_count_max, - // } - // } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub enum MemoryArch { I32, I64, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub struct Import { pub module: Box<str>, pub name: Box<str>, pub kind: ImportKind, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub enum ImportKind { Function(TypeAddr), Table(TableType), @@ -450,6 +501,7 @@ pub enum ImportKind { } impl From<&ImportKind> for ExternalKind { + #[inline] fn from(kind: &ImportKind) -> Self { match kind { ImportKind::Function(_) => Self::Func, @@ -460,20 +512,26 @@ impl From<&ImportKind> for ExternalKind { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub struct Data { pub data: Box<[u8]>, pub range: Range<usize>, pub kind: DataKind, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub enum DataKind { Active { mem: MemAddr, offset: ConstInstruction }, Passive, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub struct Element { pub kind: ElementKind, pub items: Box<[ElementItem]>, @@ -481,14 +539,18 @@ pub struct Element { pub ty: ValType, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub enum ElementKind { Passive, Active { table: TableAddr, offset: ConstInstruction }, Declared, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "archive", archive(check_bytes))] pub enum ElementItem { Func(FuncAddr), Expr(ConstInstruction), |
