diff options
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/cli/README.md | 10 | ||||
| -rw-r--r-- | crates/parser/README.md | 10 | ||||
| -rw-r--r-- | crates/tinywasm/Cargo.toml | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/func.rs | 1 | ||||
| -rw-r--r-- | crates/tinywasm/src/imports.rs | 31 | ||||
| -rw-r--r-- | crates/tinywasm/src/instance.rs | 6 | ||||
| -rw-r--r-- | crates/tinywasm/src/lib.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/global.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/mod.rs | 12 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/table.rs | 8 | ||||
| -rw-r--r-- | crates/tinywasm/tests/test-wast.rs | 4 | ||||
| -rw-r--r-- | crates/types/README.md | 4 | ||||
| -rw-r--r-- | crates/types/src/archive.rs | 45 | ||||
| -rw-r--r-- | crates/types/src/lib.rs | 236 | ||||
| -rw-r--r-- | crates/types/src/value.rs | 175 |
15 files changed, 253 insertions, 295 deletions
diff --git a/crates/cli/README.md b/crates/cli/README.md new file mode 100644 index 0000000..1f7a1bb --- /dev/null +++ b/crates/cli/README.md @@ -0,0 +1,10 @@ +# `tinywasm-cli` + +The `tinywasm-cli` crate contains the command line interface for the `tinywasm` project. See [`tinywasm`](https://crates.io/crates/tinywasm) for more information. + +## Usage + +```bash +$ cargo install tinywasm-cli +$ tinywasm-cli --help +``` diff --git a/crates/parser/README.md b/crates/parser/README.md index 6cf2234..8ac7a30 100644 --- a/crates/parser/README.md +++ b/crates/parser/README.md @@ -1,6 +1,6 @@ # `tinywasm-parser` -This crate provides a parser that can parse WebAssembly modules into a TinyWasm module. It is based on +This crate provides a parser that can parse WebAssembly modules into a TinyWasm module. It is based on [`wasmparser_nostd`](https://crates.io/crates/wasmparser_nostd) and used by [`tinywasm`](https://crates.io/crates/tinywasm). ## Features @@ -11,11 +11,11 @@ This crate provides a parser that can parse WebAssembly modules into a TinyWasm ## Usage ```rust -use tinywasm_parser::{Parser, TinyWasmModule}; +use tinywasm_parser::Parser; let bytes = include_bytes!("./file.wasm"); let parser = Parser::new(); -let module: TinyWasmModule = parser.parse_module_bytes(bytes).unwrap(); -let mudule: TinyWasmModule = parser.parse_module_file("path/to/file.wasm").unwrap(); -let module: TinyWasmModule = parser.parse_module_stream(&mut stream).unwrap(); +let module = parser.parse_module_bytes(bytes).unwrap(); +let mudule = parser.parse_module_file("path/to/file.wasm").unwrap(); +let module = parser.parse_module_stream(&mut stream).unwrap(); ``` diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml index dd76f3d..bbc85e9 100644 --- a/crates/tinywasm/Cargo.toml +++ b/crates/tinywasm/Cargo.toml @@ -30,7 +30,7 @@ pretty_env_logger="0.5" [features] default=["std", "parser", "logging", "archive"] -logging=["log", "tinywasm-types/logging", "tinywasm-parser?/logging"] +logging=["log", "tinywasm-parser?/logging", "tinywasm-types/logging"] std=["tinywasm-parser?/std", "tinywasm-types/std"] parser=["tinywasm-parser"] unsafe=["tinywasm-types/unsafe"] diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index a0c1212..2088494 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -29,7 +29,6 @@ impl FuncHandle { // 4. If the length of the provided argument values is different from the number of expected arguments, then fail if unlikely(func_ty.params.len() != params.len()) { - log::info!("func_ty.params: {:?}", func_ty.params); return Err(Error::Other(format!( "param count mismatch: expected {}, got {}", func_ty.params.len(), diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index 38d0707..e273838 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -286,7 +286,6 @@ impl Imports { if let Some(v) = self.values.get(&name) { return Some(ResolvedExtern::Extern(v.clone())); } - if let Some(addr) = self.modules.get(&name.module) { let instance = store.get_module_instance(*addr)?; return Some(ResolvedExtern::Store(instance.export_addr(&import.name)?)); @@ -295,15 +294,11 @@ impl Imports { None } - fn compare_types<T>(import: &Import, actual: &T, expected: &T) -> Result<()> - where - T: Debug + PartialEq, - { + fn compare_types<T: Debug + PartialEq>(import: &Import, actual: &T, expected: &T) -> Result<()> { if expected != actual { log::error!("failed to link import {}, expected {:?}, got {:?}", import.name, expected, actual); return Err(LinkingError::incompatible_import_type(import).into()); } - Ok(()) } @@ -333,22 +328,20 @@ impl Imports { ) -> Result<()> { Self::compare_types(import, &expected.arch, &actual.arch)?; - if actual.page_count_initial > expected.page_count_initial { - if let Some(real_size) = real_size { - if actual.page_count_initial > real_size as u64 { - return Err(LinkingError::incompatible_import_type(import).into()); - } - } else { - return Err(LinkingError::incompatible_import_type(import).into()); - } + if actual.page_count_initial > expected.page_count_initial + && real_size.map_or(true, |size| actual.page_count_initial > size as u64) + { + return Err(LinkingError::incompatible_import_type(import).into()); } - match (expected.page_count_max, actual.page_count_max) { - (None, Some(_)) => return Err(LinkingError::incompatible_import_type(import).into()), - (Some(expected_max), Some(actual_max)) if actual_max < expected_max => { - return Err(LinkingError::incompatible_import_type(import).into()) + if expected.page_count_max.is_none() && actual.page_count_max.is_some() { + return Err(LinkingError::incompatible_import_type(import).into()); + } + + if let (Some(expected_max), Some(actual_max)) = (expected.page_count_max, actual.page_count_max) { + if actual_max < expected_max { + return Err(LinkingError::incompatible_import_type(import).into()); } - _ => {} } Ok(()) diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index ad6c0f1..cb12f1b 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -124,11 +124,6 @@ impl ModuleInstance { &self.0.func_addrs } - /// Get the module's function types - pub fn func_tys(&self) -> &[FuncType] { - &self.0.types - } - pub(crate) fn new(inner: ModuleInstanceInner) -> Self { Self(Rc::new(inner)) } @@ -232,7 +227,6 @@ impl ModuleInstance { /// /// Returns None if the module has no start function /// If no start function is specified, also checks for a _start function in the exports - /// (which is not part of the spec, but used by some compilers) /// /// See <https://webassembly.github.io/spec/core/syntax/modules.html#start-function> pub fn start_func(&self, store: &Store) -> Result<Option<FuncHandle>> { diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index d786ab8..79b111c 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -58,6 +58,8 @@ //! # Ok::<(), tinywasm::Error>(()) //! ``` //! +//! For more examples, see the [`examples`](https://github.com/explodingcamera/tinywasm/tree/main/examples) directory. +//! //! ## Imports //! //! To provide imports to a module, you can use the [`Imports`] struct. diff --git a/crates/tinywasm/src/store/global.rs b/crates/tinywasm/src/store/global.rs index fbcc402..298a31e 100644 --- a/crates/tinywasm/src/store/global.rs +++ b/crates/tinywasm/src/store/global.rs @@ -30,9 +30,11 @@ impl GlobalInstance { 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/mod.rs b/crates/tinywasm/src/store/mod.rs index be885a7..c8c5d8a 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -17,12 +17,7 @@ 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::*; +pub(crate) use {data::*, element::*, function::*, global::*, memory::*, table::*}; // global store id counter static STORE_ID: AtomicUsize = AtomicUsize::new(0); @@ -205,12 +200,11 @@ impl Store { let addr = globals.get(*addr as usize).copied().ok_or_else(|| { Error::Other(format!("global {} not found. This should have been caught by the validator", addr)) })?; - let global = self.data.globals[addr as usize].clone(); let val = i64::from(global.borrow().value); - log::error!("global: {}", val); + + // check if the global is actually a null reference if val < 0 { - // the global is actually a null reference None } else { Some(val as u32) diff --git a/crates/tinywasm/src/store/table.rs b/crates/tinywasm/src/store/table.rs index 7b4c568..ea520b8 100644 --- a/crates/tinywasm/src/store/table.rs +++ b/crates/tinywasm/src/store/table.rs @@ -1,12 +1,8 @@ use crate::log; +use crate::{Error, Result, Trap}; use alloc::{vec, vec::Vec}; - use tinywasm_types::*; -use crate::{ - Error, Result, Trap, -}; - const MAX_TABLE_SIZE: u32 = 10000000; /// A WebAssembly Table Instance @@ -30,7 +26,7 @@ impl TableInstance { 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), + _ => Err(Error::UnsupportedFeature("non-ref table".into()))?, }) } diff --git a/crates/tinywasm/tests/test-wast.rs b/crates/tinywasm/tests/test-wast.rs index a50a612..1d3fbe3 100644 --- a/crates/tinywasm/tests/test-wast.rs +++ b/crates/tinywasm/tests/test-wast.rs @@ -13,8 +13,8 @@ fn main() -> Result<()> { } if args.len() < 3 { - bail!("usage: cargo test-wast <wast-file>"); - } + bail!("usage: cargo test-wast <wast-file>") + }; // cwd for relative paths, absolute paths are kept as-is let cwd = std::env::current_dir()?; diff --git a/crates/types/README.md b/crates/types/README.md index f2d048b..5a4431e 100644 --- a/crates/types/README.md +++ b/crates/types/README.md @@ -1,3 +1,3 @@ -# `tinywasm_types` +# `tinywasm-types` -This crate contains the types used by the [`tinywasm`](https://crates.io/crates/tinywasm) crate. It is also used by the [`tinywasm_parser`](https://crates.io/crates/tinywasm_parser) crate to parse WebAssembly binaries. +This crate contains the types used by the [`tinywasm`](https://crates.io/crates/tinywasm) crate. It is also used by the [`tinywasm-parser`](https://crates.io/crates/tinywasm-parser) crate to parse WebAssembly binaries. diff --git a/crates/types/src/archive.rs b/crates/types/src/archive.rs index 00a9911..bbd2206 100644 --- a/crates/types/src/archive.rs +++ b/crates/types/src/archive.rs @@ -1,3 +1,5 @@ +use core::fmt::{Display, Formatter}; + use crate::TinyWasmModule; use rkyv::{ check_archived_root, @@ -14,30 +16,49 @@ const TWASM_MAGIC: [u8; 16] = [ TWASM_MAGIC_PREFIX[0], TWASM_MAGIC_PREFIX[1], TW 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"); +fn validate_magic(wasm: &[u8]) -> Result<usize, TwasmError> { + if wasm.len() < TWASM_MAGIC.len() || &wasm[..TWASM_MAGIC_PREFIX.len()] != TWASM_MAGIC_PREFIX { + return Err(TwasmError::InvalidMagic); } if &wasm[TWASM_MAGIC_PREFIX.len()..TWASM_MAGIC_PREFIX.len() + TWASM_VERSION.len()] != TWASM_VERSION { - return Err("Invalid twasm: invalid version"); + return Err(TwasmError::InvalidVersion); } if wasm[TWASM_MAGIC_PREFIX.len() + TWASM_VERSION.len()..TWASM_MAGIC.len()] != [0; 10] { - return Err("Invalid twasm: invalid padding"); + return Err(TwasmError::InvalidPadding); } Ok(TWASM_MAGIC.len()) } +#[derive(Debug)] +pub enum TwasmError { + InvalidMagic, + InvalidVersion, + InvalidPadding, + InvalidArchive, +} + +impl Display for TwasmError { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + TwasmError::InvalidMagic => write!(f, "Invalid twasm: invalid magic number"), + TwasmError::InvalidVersion => write!(f, "Invalid twasm: invalid version"), + TwasmError::InvalidPadding => write!(f, "Invalid twasm: invalid padding"), + TwasmError::InvalidArchive => write!(f, "Invalid twasm: invalid archive"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TwasmError {} + impl TinyWasmModule { /// Creates a TinyWasmModule from a slice of bytes. - pub fn from_twasm(wasm: &[u8]) -> Result<TinyWasmModule, &str> { + pub fn from_twasm(wasm: &[u8]) -> Result<TinyWasmModule, TwasmError> { 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" + let root = check_archived_root::<Self>(&wasm[len..]).map_err(|_e| { + crate::log::error!("Invalid archive: {}", _e); + TwasmError::InvalidArchive })?; Ok(root.deserialize(&mut rkyv::Infallible).unwrap()) diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 547379c..205ec5a 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -10,6 +10,8 @@ //! Types used by [`tinywasm`](https://docs.rs/tinywasm) and [`tinywasm_parser`](https://docs.rs/tinywasm_parser). extern crate alloc; +use alloc::boxed::Box; +use core::{fmt::Debug, ops::Range}; // log for logging (optional). #[cfg(feature = "logging")] @@ -24,10 +26,9 @@ pub(crate) mod log { } mod instructions; -use core::{fmt::Debug, ops::Range}; - -use alloc::boxed::Box; +mod value; pub use instructions::*; +pub use value::*; #[cfg(feature = "archive")] pub mod archive; @@ -73,235 +74,6 @@ pub struct TinyWasmModule { pub elements: Box<[Element]>, } -/// A WebAssembly value. -/// -/// See <https://webassembly.github.io/spec/core/syntax/types.html#value-types> -#[derive(Clone, Copy)] -pub enum WasmValue { - // Num types - /// A 32-bit integer. - I32(i32), - /// A 64-bit integer. - I64(i64), - /// A 32-bit float. - F32(f32), - /// A 64-bit float. - F64(f64), - - RefExtern(ExternAddr), - RefFunc(FuncAddr), - RefNull(ValType), -} - -impl WasmValue { - #[inline] - pub fn const_instr(&self) -> ConstInstruction { - match self { - Self::I32(i) => ConstInstruction::I32Const(*i), - Self::I64(i) => ConstInstruction::I64Const(*i), - Self::F32(i) => ConstInstruction::F32Const(*i), - Self::F64(i) => ConstInstruction::F64Const(*i), - - Self::RefFunc(i) => ConstInstruction::RefFunc(*i), - Self::RefNull(ty) => ConstInstruction::RefNull(*ty), - - // Self::RefExtern(addr) => ConstInstruction::RefExtern(*addr), - _ => unimplemented!("no const_instr for {:?}", self), - } - } - - /// Get the default value for a given type. - #[inline] - pub fn default_for(ty: ValType) -> Self { - match ty { - ValType::I32 => Self::I32(0), - ValType::I64 => Self::I64(0), - ValType::F32 => Self::F32(0.0), - ValType::F64 => Self::F64(0.0), - ValType::RefFunc => Self::RefNull(ValType::RefFunc), - ValType::RefExtern => Self::RefNull(ValType::RefExtern), - } - } - - #[inline] - pub fn eq_loose(&self, other: &Self) -> bool { - match (self, other) { - (Self::I32(a), Self::I32(b)) => a == b, - (Self::I64(a), Self::I64(b)) => a == b, - (Self::RefNull(v), Self::RefNull(v2)) => v == v2, - (Self::RefExtern(addr), Self::RefExtern(addr2)) => addr == addr2, - (Self::RefFunc(addr), Self::RefFunc(addr2)) => addr == addr2, - (Self::F32(a), Self::F32(b)) => { - if a.is_nan() && b.is_nan() { - true // Both are NaN, treat them as equal - } else { - a.to_bits() == b.to_bits() - } - } - (Self::F64(a), Self::F64(b)) => { - if a.is_nan() && b.is_nan() { - true // Both are NaN, treat them as equal - } else { - a.to_bits() == b.to_bits() - } - } - _ => false, - } - } -} - -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(()) - } - } - } -} - -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(()) - } - } - } -} - -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(()) - } - } - } -} - -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(()) - } - } - } -} - -impl Debug for WasmValue { - fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result { - match self { - WasmValue::I32(i) => write!(f, "i32({})", i), - WasmValue::I64(i) => write!(f, "i64({})", i), - WasmValue::F32(i) => write!(f, "f32({})", i), - WasmValue::F64(i) => write!(f, "f64({})", i), - WasmValue::RefExtern(addr) => write!(f, "ref.extern({:?})", addr), - WasmValue::RefFunc(addr) => write!(f, "ref.func({:?})", addr), - WasmValue::RefNull(ty) => write!(f, "ref.null({:?})", ty), - // WasmValue::V128(i) => write!(f, "v128({})", i), - } - } -} - -impl WasmValue { - /// Get the type of a [`WasmValue`] - #[inline] - pub fn val_type(&self) -> ValType { - match self { - Self::I32(_) => ValType::I32, - Self::I64(_) => ValType::I64, - Self::F32(_) => ValType::F32, - Self::F64(_) => ValType::F64, - Self::RefExtern(_) => ValType::RefExtern, - Self::RefFunc(_) => ValType::RefFunc, - Self::RefNull(ty) => *ty, - } - } -} - -/// 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, - /// A 64-bit integer. - I64, - /// A 32-bit float. - F32, - /// A 64-bit float. - F64, - /// A reference to a function. - RefFunc, - /// A reference to an external value. - RefExtern, -} - -impl ValType { - #[inline] - pub fn default_value(&self) -> WasmValue { - WasmValue::default_for(*self) - } -} - /// A WebAssembly External Kind. /// /// See <https://webassembly.github.io/spec/core/syntax/types.html#external-types> diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs new file mode 100644 index 0000000..e46092b --- /dev/null +++ b/crates/types/src/value.rs @@ -0,0 +1,175 @@ +use core::fmt::Debug; + +use crate::{ConstInstruction, ExternAddr, FuncAddr}; + +/// A WebAssembly value. +/// +/// See <https://webassembly.github.io/spec/core/syntax/types.html#value-types> +#[derive(Clone, Copy)] +pub enum WasmValue { + // Num types + /// A 32-bit integer. + I32(i32), + /// A 64-bit integer. + I64(i64), + /// A 32-bit float. + F32(f32), + /// A 64-bit float. + F64(f64), + + RefExtern(ExternAddr), + RefFunc(FuncAddr), + RefNull(ValType), +} + +impl WasmValue { + #[inline] + pub fn const_instr(&self) -> ConstInstruction { + match self { + Self::I32(i) => ConstInstruction::I32Const(*i), + Self::I64(i) => ConstInstruction::I64Const(*i), + Self::F32(i) => ConstInstruction::F32Const(*i), + Self::F64(i) => ConstInstruction::F64Const(*i), + + Self::RefFunc(i) => ConstInstruction::RefFunc(*i), + Self::RefNull(ty) => ConstInstruction::RefNull(*ty), + + // Self::RefExtern(addr) => ConstInstruction::RefExtern(*addr), + _ => unimplemented!("no const_instr for {:?}", self), + } + } + + /// Get the default value for a given type. + #[inline] + pub fn default_for(ty: ValType) -> Self { + match ty { + ValType::I32 => Self::I32(0), + ValType::I64 => Self::I64(0), + ValType::F32 => Self::F32(0.0), + ValType::F64 => Self::F64(0.0), + ValType::RefFunc => Self::RefNull(ValType::RefFunc), + ValType::RefExtern => Self::RefNull(ValType::RefExtern), + } + } + + #[inline] + pub fn eq_loose(&self, other: &Self) -> bool { + match (self, other) { + (Self::I32(a), Self::I32(b)) => a == b, + (Self::I64(a), Self::I64(b)) => a == b, + (Self::RefNull(v), Self::RefNull(v2)) => v == v2, + (Self::RefExtern(addr), Self::RefExtern(addr2)) => addr == addr2, + (Self::RefFunc(addr), Self::RefFunc(addr2)) => addr == addr2, + (Self::F32(a), Self::F32(b)) => { + if a.is_nan() && b.is_nan() { + true // Both are NaN, treat them as equal + } else { + a.to_bits() == b.to_bits() + } + } + (Self::F64(a), Self::F64(b)) => { + if a.is_nan() && b.is_nan() { + true // Both are NaN, treat them as equal + } else { + a.to_bits() == b.to_bits() + } + } + _ => false, + } + } +} + +#[cold] +fn cold() {} + +impl Debug for WasmValue { + fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result { + match self { + WasmValue::I32(i) => write!(f, "i32({})", i), + WasmValue::I64(i) => write!(f, "i64({})", i), + WasmValue::F32(i) => write!(f, "f32({})", i), + WasmValue::F64(i) => write!(f, "f64({})", i), + WasmValue::RefExtern(addr) => write!(f, "ref.extern({:?})", addr), + WasmValue::RefFunc(addr) => write!(f, "ref.func({:?})", addr), + WasmValue::RefNull(ty) => write!(f, "ref.null({:?})", ty), + } + } +} + +impl WasmValue { + /// Get the type of a [`WasmValue`] + #[inline] + pub fn val_type(&self) -> ValType { + match self { + Self::I32(_) => ValType::I32, + Self::I64(_) => ValType::I64, + Self::F32(_) => ValType::F32, + Self::F64(_) => ValType::F64, + Self::RefExtern(_) => ValType::RefExtern, + Self::RefFunc(_) => ValType::RefFunc, + Self::RefNull(ty) => *ty, + } + } +} + +/// 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, + /// A 64-bit integer. + I64, + /// A 32-bit float. + F32, + /// A 64-bit float. + F64, + /// A reference to a function. + RefFunc, + /// A reference to an external value. + RefExtern, +} + +impl ValType { + #[inline] + pub fn default_value(&self) -> WasmValue { + WasmValue::default_for(*self) + } +} + +macro_rules! impl_conversion_for_wasmvalue { + ($($t:ty => $variant:ident),*) => { + $( + // Implementing From<$t> for WasmValue + impl From<$t> for WasmValue { + #[inline] + fn from(i: $t) -> Self { + Self::$variant(i) + } + } + + // Implementing TryFrom<WasmValue> for $t + impl TryFrom<WasmValue> for $t { + type Error = (); + + #[inline] + fn try_from(value: WasmValue) -> Result<Self, Self::Error> { + if let WasmValue::$variant(i) = value { + Ok(i) + } else { + cold(); + Err(()) + } + } + } + )* + } +} + +impl_conversion_for_wasmvalue! { + i32 => I32, + i64 => I64, + f32 => F32, + f64 => F64 +} |
