From 83cddcd1be02d7beff27313a2f6ea3fe1642a550 Mon Sep 17 00:00:00 2001 From: Henry Date: Sat, 1 Aug 2026 21:18:31 +0200 Subject: [PATCH 1/4] chore: add initial typed reference types types Signed-off-by: Henry --- crates/types/src/instructions.rs | 5 +- crates/types/src/lib.rs | 4 +- crates/types/src/reference.rs | 213 +++++++++++++++++++++---------- crates/types/src/value.rs | 91 +++++-------- 4 files changed, 179 insertions(+), 134 deletions(-) diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index cec9d5b..b51df38 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -1,5 +1,5 @@ use super::{FuncAddr, GlobalAddr, LocalAddr, TableAddr, TypeAddr, ValueCounts, WasmType}; -use crate::{ConstIdx, DataAddr, ElemAddr, ExternAddr, MemAddr}; +use crate::{ConstIdx, DataAddr, ElemAddr, MemAddr, RefValue}; /// Represents a memory immediate in a WebAssembly memory instruction. #[derive(Copy, Clone, PartialEq, Eq)] @@ -53,8 +53,7 @@ pub enum ConstInstruction { F64Const(f64), V128Const([u8; 16]), GlobalGet(GlobalAddr), - RefFunc(Option), - RefExtern(Option), + Ref(RefValue), I32Add, I32Sub, I32Mul, diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 0b4b54c..95e59b4 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -408,7 +408,7 @@ impl<'a> FromIterator<&'a WasmType> for ValueCounts { for ty in iter { match ty { - WasmType::I32 | WasmType::F32 | WasmType::RefExtern | WasmType::RefFunc => counts.c32 += 1, + WasmType::I32 | WasmType::F32 | WasmType::Ref(_) => counts.c32 += 1, WasmType::I64 | WasmType::F64 => counts.c64 += 1, WasmType::V128 => counts.c128 += 1, } @@ -511,7 +511,7 @@ pub struct TableType { impl TableType { pub const fn empty() -> Self { - Self::new(WasmType::RefFunc, 0, None) + Self::new(WasmType::Ref(RefType::FUNCREF), 0, None) } /// Create a table with 32-bit indices. diff --git a/crates/types/src/reference.rs b/crates/types/src/reference.rs index 7970dc8..42d9f4b 100644 --- a/crates/types/src/reference.rs +++ b/crates/types/src/reference.rs @@ -1,112 +1,185 @@ -use crate::{ExternAddr, FuncAddr}; - -const NULL_REF: u32 = u32::MAX; +/// An abstract WebAssembly heap type. +/// +/// This contains exactly the abstract heap types in core Wasm 3.0. +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] +pub enum AbstractHeapType { + Any, + Eq, + I31, + Struct, + Array, + None, + + Func, + NoFunc, + + Exn, + NoExn, + + Extern, + NoExtern, +} -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct ExternRef(u32); - -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct FuncRef(u32); +/// A WebAssembly reference type. +/// +/// Packed as: +/// +/// ```text +/// [nullable:1 concrete:1 payload:30] +/// ``` +/// +/// For concrete types, `payload` is a module type index. +/// Otherwise, it is an [`AbstractHeapType`]. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] +pub struct RefType(u32); #[cfg(feature = "debug")] -impl core::fmt::Debug for ExternRef { +impl core::fmt::Debug for RefType { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self.addr() { - Some(addr) => write!(f, "extern({addr:?})"), - None => write!(f, "extern(null)"), + if self.is_concrete() { + write!(f, "concrete({})", self.type_index().unwrap()) + } else { + write!(f, "abstract({:?})", self.abstract_heap_type().unwrap()) } } } -#[cfg(feature = "debug")] -impl core::fmt::Debug for FuncRef { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self.addr() { - Some(addr) => write!(f, "func({addr:?})"), - None => write!(f, "func(null)"), - } +impl RefType { + const NULLABLE: u32 = 1 << 31; + const CONCRETE: u32 = 1 << 30; + const PAYLOAD_MASK: u32 = Self::CONCRETE - 1; + + pub const FUNCREF: Self = Self::abstract_(true, AbstractHeapType::Func); + pub const EXTERNREF: Self = Self::abstract_(true, AbstractHeapType::Extern); + pub const EXNREF: Self = Self::abstract_(true, AbstractHeapType::Exn); + + #[inline] + pub const fn abstract_(nullable: bool, heap_type: AbstractHeapType) -> Self { + Self((nullable as u32) << 31 | heap_type as u32) } -} -impl FuncRef { #[inline] - /// Create a new [`FuncRef`] from a [`FuncAddr`]. - pub const fn new(addr: Option) -> Self { - match addr { - Some(addr) => Self(addr), - None => Self::null(), + pub const fn concrete(nullable: bool, type_index: u32) -> Option { + if type_index <= Self::PAYLOAD_MASK { + Some(Self(((nullable as u32) << 31) | Self::CONCRETE | type_index)) + } else { + None } } #[inline] - /// Create a null [`FuncRef`]. - pub const fn null() -> Self { - Self(NULL_REF) + pub const fn is_nullable(self) -> bool { + self.0 & Self::NULLABLE != 0 } #[inline] - /// Check if the [`FuncRef`] is null. - pub const fn is_null(&self) -> bool { - self.0 == NULL_REF + pub const fn is_concrete(self) -> bool { + self.0 & Self::CONCRETE != 0 } #[inline] - /// Get the [`FuncAddr`] from the [`FuncRef`]. - pub const fn addr(&self) -> Option { - if self.is_null() { None } else { Some(self.0) } + pub const fn type_index(self) -> Option { + if self.is_concrete() { Some(self.0 & Self::PAYLOAD_MASK) } else { None } } #[inline] - #[doc(hidden)] - pub const fn from_raw(raw: u32) -> Self { - Self(raw) + pub const fn abstract_heap_type(self) -> Option { + if self.is_concrete() { + return None; + } + + match self.0 & Self::PAYLOAD_MASK { + 0 => Some(AbstractHeapType::Any), + 1 => Some(AbstractHeapType::Eq), + 2 => Some(AbstractHeapType::I31), + 3 => Some(AbstractHeapType::Struct), + 4 => Some(AbstractHeapType::Array), + 5 => Some(AbstractHeapType::None), + 6 => Some(AbstractHeapType::Func), + 7 => Some(AbstractHeapType::NoFunc), + 8 => Some(AbstractHeapType::Exn), + 9 => Some(AbstractHeapType::NoExn), + 10 => Some(AbstractHeapType::Extern), + 11 => Some(AbstractHeapType::NoExtern), + _ => None, + } } #[inline] - #[doc(hidden)] - pub const fn raw(&self) -> u32 { - self.0 + pub const fn with_nullability(self, nullable: bool) -> Self { + Self((self.0 & !Self::NULLABLE) | ((nullable as u32) << 31)) } } -impl ExternRef { - #[inline] - /// Create a new [`ExternRef`] from an [`ExternAddr`]. - /// Should only be used by the runtime. - pub const fn new(addr: Option) -> Self { - match addr { - Some(addr) => Self(addr), - None => Self::null(), +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] +pub enum RefValue { + Null, + Func(FuncRef), + Extern(ExternRef), + Any(AnyRef), + Exn(ExnRef), +} + +#[cfg(feature = "debug")] +impl core::fmt::Debug for RefValue { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Null => write!(f, "null"), + Self::Func(_i) => write!(f, "func()"), + Self::Extern(_i) => write!(f, "extern()"), + Self::Any(_i) => write!(f, "any()"), + Self::Exn(_i) => write!(f, "exn()"), } } +} - /// Create a null [`ExternRef`]. - #[inline] - pub const fn null() -> Self { - Self(NULL_REF) - } +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] +pub struct FuncRef(u32); - /// Check if the [`ExternRef`] is null. - #[inline] - pub const fn is_null(&self) -> bool { - self.0 == NULL_REF +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] +pub struct ExternRef(u32); + +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] +pub struct ExnRef(u32); + +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] +pub struct AnyRef(u32); + +impl AnyRef { + // Odd values are inline i31s. + // Even values are GC object handles. + + pub const fn from_i31(value: i32) -> Option { + if value < -(1 << 30) || value >= (1 << 30) { + return None; + } + + Some(Self(((value as u32) << 1) | 1)) } - /// Get the [`ExternAddr`] from the [`ExternRef`]. - #[inline] - pub const fn addr(&self) -> Option { - if self.is_null() { None } else { Some(self.0) } + pub const fn as_i31(self) -> Option { + if self.0 & 1 == 1 { Some((self.0 as i32) >> 1) } else { None } } - #[inline] - #[doc(hidden)] - pub const fn from_raw(raw: u32) -> Self { - Self(raw) + pub const fn from_gc_addr(addr: u32) -> Option { + match addr.checked_add(1) { + Some(raw) => match raw.checked_mul(2) { + Some(raw) => Some(Self(raw)), + None => None, + }, + None => None, + } } - #[inline] - #[doc(hidden)] - pub const fn raw(&self) -> u32 { - self.0 + pub const fn gc_addr(self) -> Option { + if self.0 != 0 && self.0 & 1 == 0 { Some(self.0 / 2 - 1) } else { None } } } diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs index 782cdb8..1118282 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -1,6 +1,6 @@ use core::fmt::Debug; -use crate::{ConstInstruction, ExternRef, FuncRef}; +use crate::{ConstInstruction, RefType, RefValue}; /// A WebAssembly value. /// @@ -8,18 +8,22 @@ use crate::{ConstInstruction, ExternRef, FuncRef}; #[derive(Clone, Copy, PartialEq)] pub enum WasmValue { // Num types - /// A 32-bit integer. + /// A 32-bit integer I32(i32), - /// A 64-bit integer. + /// A 64-bit integer I64(i64), - /// A 32-bit float. + /// A 32-bit float F32(f32), - /// A 64-bit float. + /// A 64-bit float F64(f64), - // /// A 128-bit vector + /// A 128-bit vector V128([u8; 16]), - RefExtern(ExternRef), - RefFunc(FuncRef), + /// A reference type + Ref(RefValue), +} + +impl WasmValue { + pub const REF_NULL: Self = Self::Ref(RefValue::Null); } impl Debug for WasmValue { @@ -31,13 +35,9 @@ impl Debug for WasmValue { Self::F64(i) => write!(f, "f64({i})"), Self::V128(i) => write!(f, "v128({i:?})"), #[cfg(feature = "debug")] - Self::RefExtern(i) => write!(f, "ref({i:?})"), - #[cfg(feature = "debug")] - Self::RefFunc(i) => write!(f, "func({i:?})"), - #[cfg(not(feature = "debug"))] - Self::RefExtern(_) => write!(f, "ref()"), + Self::Ref(i) => write!(f, "ref({i:?})"), #[cfg(not(feature = "debug"))] - Self::RefFunc(_) => write!(f, "func()"), + Self::Ref(_) => write!(f, "ref(...)"), } } } @@ -52,22 +52,21 @@ impl WasmValue { Self::F32(i) => ConstInstruction::F32Const(*i), Self::F64(i) => ConstInstruction::F64Const(*i), Self::V128(i) => ConstInstruction::V128Const(*i), - Self::RefFunc(i) => ConstInstruction::RefFunc(i.addr()), - Self::RefExtern(i) => ConstInstruction::RefExtern(i.addr()), + Self::Ref(i) => ConstInstruction::Ref(*i), }]) } #[inline] /// Get the default value for a given type. - pub const fn default_for(ty: WasmType) -> Self { + pub const fn default_for(ty: WasmType) -> Option { match ty { - WasmType::I32 => Self::I32(0), - WasmType::I64 => Self::I64(0), - WasmType::F32 => Self::F32(0.0), - WasmType::F64 => Self::F64(0.0), - WasmType::V128 => Self::V128([0; 16]), - WasmType::RefFunc => Self::RefFunc(FuncRef::null()), - WasmType::RefExtern => Self::RefExtern(ExternRef::null()), + WasmType::I32 => Some(Self::I32(0)), + WasmType::I64 => Some(Self::I64(0)), + WasmType::F32 => Some(Self::F32(0.0)), + WasmType::F64 => Some(Self::F64(0.0)), + WasmType::V128 => Some(Self::V128([0; 16])), + WasmType::Ref(ty) if ty.is_nullable() => Some(Self::REF_NULL), + WasmType::Ref(_) => None, } } @@ -78,8 +77,7 @@ impl WasmValue { (Self::I32(a), Self::I32(b)) => a == b, (Self::I64(a), Self::I64(b)) => a == b, (Self::V128(a), Self::V128(b)) => a == b || Self::v128_nan_eq(*a, *b), - (Self::RefExtern(addr), Self::RefExtern(addr2)) => addr == addr2, - (Self::RefFunc(addr), Self::RefFunc(addr2)) => addr == addr2, + (Self::Ref(a), Self::Ref(b)) => a == b, (Self::F32(a), Self::F32(b)) => a.is_nan() && b.is_nan() || a.to_bits() == b.to_bits(), (Self::F64(a), Self::F64(b)) => a.is_nan() && b.is_nan() || a.to_bits() == b.to_bits(), _ => false, @@ -135,51 +133,27 @@ impl WasmValue { } } -impl From<&WasmValue> for WasmType { - #[inline] - fn from(value: &WasmValue) -> Self { - match value { - WasmValue::I32(_) => WasmType::I32, - WasmValue::I64(_) => WasmType::I64, - WasmValue::F32(_) => WasmType::F32, - WasmValue::F64(_) => WasmType::F64, - WasmValue::V128(_) => WasmType::V128, - WasmValue::RefExtern(_) => WasmType::RefExtern, - WasmValue::RefFunc(_) => WasmType::RefFunc, - } - } -} - -impl From for WasmType { - #[inline] - fn from(value: WasmValue) -> Self { - Self::from(&value) - } -} - /// Type of a WebAssembly value. #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub enum WasmType { - /// A 32-bit integer. + /// A 32-bit integer I32, - /// A 64-bit integer. + /// A 64-bit integer I64, - /// A 32-bit float. + /// A 32-bit float F32, - /// A 64-bit float. + /// A 64-bit float F64, /// A 128-bit vector V128, - /// A reference to a function. - RefFunc, - /// A reference to an external value. - RefExtern, + /// A reference type + Ref(RefType), } impl WasmType { #[inline] - pub const fn default_value(&self) -> WasmValue { + pub const fn default_value(&self) -> Option { WasmValue::default_for(*self) } @@ -229,6 +203,5 @@ impl_conversion_for_wasmvalue! { f32 => F32, as_f32, "Return the `f32` from a `WasmValue`, if it is a `F32`."; f64 => F64, as_f64, "Return the `f64` from a `WasmValue`, if it is a `F64`."; [u8; 16] => V128, as_v128, "Return the raw little-endian bytes from a `WasmValue`, if it is a `V128`."; - ExternRef => RefExtern, as_ref_extern, "Return the [`ExternRef`] from a `WasmValue`, if it is one"; - FuncRef => RefFunc, as_ref_func, "Return the [`FuncRef`] from a `WasmValue`, if it is one"; + RefValue => Ref, as_ref, "Return the `RefValue` from a `WasmValue`, if it is a `Ref`."; } From 9ac8a8eba442487d9681e3be952e826513cf4e52 Mon Sep 17 00:00:00 2001 From: Henry Date: Sun, 2 Aug 2026 14:33:51 +0200 Subject: [PATCH 2/4] chore: continue with types, update parser Signed-off-by: Henry --- crates/parser/src/conversion.rs | 67 +++++++++++++++++++------------- crates/parser/src/macros.rs | 1 + crates/parser/src/optimize.rs | 3 ++ crates/parser/src/visit.rs | 51 +++++++++++++++++++++++- crates/types/src/instructions.rs | 11 ++++-- crates/types/src/reference.rs | 43 +++++++++++--------- crates/types/src/value.rs | 3 +- 7 files changed, 127 insertions(+), 52 deletions(-) diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index 586cac5..7f0a5f5 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -26,7 +26,7 @@ pub(crate) fn convert_module_element(element: wasmparser::Element<'_>) -> Result .collect::>>()? .into_boxed_slice(); - Ok(tinywasm_types::Element { kind, items, ty: WasmType::RefFunc, range: element.range }) + Ok(tinywasm_types::Element { kind, items, ty: WasmType::Ref(RefType::FUNCREF), range: element.range }) } wasmparser::ElementItems::Expressions(ty, exprs) => { @@ -186,14 +186,8 @@ pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result Result { - match reftype { - _ if reftype.is_func_ref() => Ok(WasmType::RefFunc), - _ if reftype.is_extern_ref() => Ok(WasmType::RefExtern), - _ => Err(crate::ParseError::UnsupportedOperator(format!( - "Unsupported reference type: {reftype:?}, {:?}", - reftype.heap_type() - ))), - } + let ty = convert_heaptype(reftype.heap_type())?.with_nullability(reftype.is_nullable()); + Ok(WasmType::Ref(ty)) } pub(crate) fn convert_valtype(valtype: &wasmparser::ValType) -> Result { @@ -221,16 +215,13 @@ pub(crate) fn process_const_operators(ops: OperatorsReader<'_>) -> Result match convert_heaptype(hty)? { - WasmType::RefFunc => ConstInstruction::RefFunc(None), - WasmType::RefExtern => ConstInstruction::RefExtern(None), - other => { - return Err(crate::ParseError::UnsupportedOperator(format!( - "Unsupported ref.null heap type lowered to {other:?}" - ))); - } - }, - wasmparser::Operator::RefFunc { function_index } => ConstInstruction::RefFunc(Some(function_index)), + wasmparser::Operator::RefNull { hty } => { + convert_heaptype(hty)?; + ConstInstruction::Ref(RefValue::Null) + } + wasmparser::Operator::RefFunc { function_index } => { + ConstInstruction::Ref(RefValue::Func(FuncRef::new(function_index))) + } wasmparser::Operator::I32Const { value } => ConstInstruction::I32Const(value), wasmparser::Operator::I64Const { value } => ConstInstruction::I64Const(value), wasmparser::Operator::F32Const { value } => ConstInstruction::F32Const(f32::from_bits(value.bits())), @@ -259,14 +250,36 @@ pub(crate) fn process_const_operators(ops: OperatorsReader<'_>) -> Result Result { - match heap { - wasmparser::HeapType::Abstract { shared: false, ty: wasmparser::AbstractHeapType::Func } => { - Ok(WasmType::RefFunc) +pub(crate) fn convert_heaptype(heap: wasmparser::HeapType) -> Result { + let ty = match heap { + wasmparser::HeapType::Abstract { shared: false, ty } => match ty { + wasmparser::AbstractHeapType::Any => AbstractHeapType::Any, + wasmparser::AbstractHeapType::Eq => AbstractHeapType::Eq, + wasmparser::AbstractHeapType::I31 => AbstractHeapType::I31, + wasmparser::AbstractHeapType::Struct => AbstractHeapType::Struct, + wasmparser::AbstractHeapType::Array => AbstractHeapType::Array, + wasmparser::AbstractHeapType::None => AbstractHeapType::None, + wasmparser::AbstractHeapType::Func => AbstractHeapType::Func, + wasmparser::AbstractHeapType::NoFunc => AbstractHeapType::NoFunc, + wasmparser::AbstractHeapType::Exn => AbstractHeapType::Exn, + wasmparser::AbstractHeapType::NoExn => AbstractHeapType::NoExn, + wasmparser::AbstractHeapType::Extern => AbstractHeapType::Extern, + wasmparser::AbstractHeapType::NoExtern => AbstractHeapType::NoExtern, + wasmparser::AbstractHeapType::Cont | wasmparser::AbstractHeapType::NoCont => { + return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported heap type: {heap:?}"))); + } + }, + wasmparser::HeapType::Concrete(index) => { + let index = index.as_module_index().ok_or_else(|| { + crate::ParseError::UnsupportedOperator(format!("Unsupported non-module heap type index: {index:?}")) + })?; + return RefType::new_concrete(false, index) + .ok_or_else(|| crate::ParseError::Other(format!("heap type index is too large: {index}"))); } - wasmparser::HeapType::Abstract { shared: false, ty: wasmparser::AbstractHeapType::Extern } => { - Ok(WasmType::RefExtern) + wasmparser::HeapType::Abstract { shared: true, .. } | wasmparser::HeapType::Exact(_) => { + return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported heap type: {heap:?}"))); } - _ => Err(crate::ParseError::UnsupportedOperator(format!("Unsupported heap type: {heap:?}"))), - } + }; + + Ok(RefType::new_abstract(false, ty)) } diff --git a/crates/parser/src/macros.rs b/crates/parser/src/macros.rs index ff216a5..723f691 100644 --- a/crates/parser/src/macros.rs +++ b/crates/parser/src/macros.rs @@ -129,6 +129,7 @@ pub(crate) mod visit { (@@wide_arithmetic $($rest:tt)* ) => {}; (@@relaxed_simd $($rest:tt)* ) => {}; (@@tail_call $($rest:tt)* ) => {}; + (@@function_references $($rest:tt)* ) => {}; (@@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*)) => { fn $visit(&mut self $($(,_: $argty)*)?) -> Self::Output { diff --git a/crates/parser/src/optimize.rs b/crates/parser/src/optimize.rs index 80dfa0d..dcb7568 100644 --- a/crates/parser/src/optimize.rs +++ b/crates/parser/src/optimize.rs @@ -856,6 +856,8 @@ fn instruction_target_mut(instr: &mut Instruction) -> Option<&mut u32> { | Instruction::JumpIfNonZero32(ip) | Instruction::JumpIfZero64(ip) | Instruction::JumpIfNonZero64(ip) + | Instruction::JumpIfRefNull(ip) + | Instruction::JumpIfRefNonNull(ip) | Instruction::JumpCmpStackConst32 { target_ip: ip, .. } | Instruction::JumpCmpStackConst64 { target_ip: ip, .. } | Instruction::JumpIfLocalZero32 { target_ip: ip, .. } @@ -906,6 +908,7 @@ fn is_unconditional_terminator(instr: Instruction) -> bool { | Instruction::ReturnCall(_) | Instruction::ReturnCallSelf | Instruction::ReturnCallIndirect(..) + | Instruction::ReturnCallRef(_) ) } diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index dae9ff2..70ead68 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -49,7 +49,7 @@ impl From for OperandSize { impl From<&WasmType> for OperandSize { fn from(ty: &WasmType) -> Self { match ty { - WasmType::I32 | WasmType::F32 | WasmType::RefFunc | WasmType::RefExtern => Self::S32, + WasmType::I32 | WasmType::F32 | WasmType::Ref(_) => Self::S32, WasmType::I64 | WasmType::F64 => Self::S64, WasmType::V128 => Self::S128, } @@ -399,6 +399,13 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder<'_> { self.emit(&inputs, &signature.results, Instruction::CallIndirect(type_index, table_index)) } + fn visit_call_ref(&mut self, type_index: u32) -> Self::Output { + let signature = self.metadata.signature(type_index)?.clone(); + let mut inputs = signature.params; + inputs.push(OperandSize::S32); + self.emit(&inputs, &signature.results, Instruction::CallRef(type_index)) + } + fn visit_return_call(&mut self, function_index: u32) -> Self::Output { let signature = self.metadata.function_signature(function_index)?.clone(); self.apply_effect(&signature.params, &[])?; @@ -417,6 +424,16 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder<'_> { Ok(()) } + fn visit_return_call_ref(&mut self, type_index: u32) -> Self::Output { + let signature = self.metadata.signature(type_index)?.clone(); + let mut inputs = signature.params; + inputs.push(OperandSize::S32); + self.apply_effect(&inputs, &[])?; + self.mark_unreachable(); + self.instructions.push(Instruction::ReturnCallRef(type_index)); + Ok(()) + } + fn visit_global_set(&mut self, global_index: u32) -> Self::Output { let size = self.metadata.global_size(global_index)?; let instruction = size.choose( @@ -672,6 +689,32 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder<'_> { self.emit(&[], &[OperandSize::S32], instruction) } + fn visit_ref_as_non_null(&mut self) -> Self::Output { + self.emit(&[OperandSize::S32], &[OperandSize::S32], Instruction::RefAsNonNull) + } + + fn visit_br_on_null(&mut self, relative_depth: u32) -> Self::Output { + self.pop_expect(OperandSize::S32)?; + let fallthrough_jump = self.instructions.len(); + self.instructions.push(Instruction::JumpIfRefNonNull(0)); + self.emit_dropkeep_to_label(relative_depth)?; + self.emit_branch_jump_or_return(relative_depth)?; + self.patch_jump(fallthrough_jump, self.instructions.len()); + self.push_sizes(&[OperandSize::S32]) + } + + fn visit_br_on_non_null(&mut self, relative_depth: u32) -> Self::Output { + self.pop_expect(OperandSize::S32)?; + let fallthrough_jump = self.instructions.len(); + self.instructions.push(Instruction::JumpIfRefNull(0)); + self.push_sizes(&[OperandSize::S32])?; + self.emit_dropkeep_to_label(relative_depth)?; + self.pop_expect(OperandSize::S32)?; + self.emit_branch_jump_or_return(relative_depth)?; + self.patch_jump(fallthrough_jump, self.instructions.len()); + Ok(()) + } + fn visit_typed_select_multi(&mut self, tys: Vec) -> Self::Output { let sizes: Vec<_> = tys.into_iter().map(OperandSize::from).collect(); let counts = Self::value_counts(&sizes); @@ -1026,7 +1069,11 @@ impl FunctionBuilder<'_> { fn patch_jump(&mut self, jump_ip: usize, target: usize) { match &mut self.instructions[jump_ip] { - Instruction::Jump(ip) | Instruction::JumpIfZero32(ip) | Instruction::JumpIfNonZero32(ip) => { + Instruction::Jump(ip) + | Instruction::JumpIfZero32(ip) + | Instruction::JumpIfNonZero32(ip) + | Instruction::JumpIfRefNull(ip) + | Instruction::JumpIfRefNonNull(ip) => { *ip = target as u32; } _ => {} diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index b51df38..a53576e 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -1,5 +1,5 @@ -use super::{FuncAddr, GlobalAddr, LocalAddr, TableAddr, TypeAddr, ValueCounts, WasmType}; -use crate::{ConstIdx, DataAddr, ElemAddr, MemAddr, RefValue}; +use super::{FuncAddr, GlobalAddr, LocalAddr, TableAddr, TypeAddr, ValueCounts}; +use crate::{ConstIdx, DataAddr, ElemAddr, MemAddr, RefType, RefValue}; /// Represents a memory immediate in a WebAssembly memory instruction. #[derive(Copy, Clone, PartialEq, Eq)] @@ -194,6 +194,8 @@ pub enum Instruction { JumpIfNonZero32(u32), JumpIfZero64(u32), JumpIfNonZero64(u32), + JumpIfRefNull(u32), + JumpIfRefNonNull(u32), JumpIfLocalZero32 { target_ip: u32, local: LocalAddr }, JumpIfLocalNonZero32 { target_ip: u32, local: LocalAddr }, JumpIfLocalZero64 { target_ip: u32, local: LocalAddr }, @@ -214,9 +216,11 @@ pub enum Instruction { Call(FuncAddr), CallSelf, CallIndirect(TypeAddr, TableAddr), + CallRef(TypeAddr), ReturnCall(FuncAddr), ReturnCallSelf, ReturnCallIndirect(TypeAddr, TableAddr), + ReturnCallRef(TypeAddr), // > Parametric Instructions // See @@ -264,9 +268,10 @@ pub enum Instruction { Const64(i64), // > Reference Types - RefNull(WasmType), + RefNull(RefType), RefFunc(FuncAddr), RefIsNull, + RefAsNonNull, // > Numeric Instructions // See diff --git a/crates/types/src/reference.rs b/crates/types/src/reference.rs index 42d9f4b..cbb5f6a 100644 --- a/crates/types/src/reference.rs +++ b/crates/types/src/reference.rs @@ -2,7 +2,8 @@ /// /// This contains exactly the abstract heap types in core Wasm 3.0. #[repr(u8)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "debug", derive(Debug))] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub enum AbstractHeapType { Any, @@ -52,17 +53,17 @@ impl RefType { const CONCRETE: u32 = 1 << 30; const PAYLOAD_MASK: u32 = Self::CONCRETE - 1; - pub const FUNCREF: Self = Self::abstract_(true, AbstractHeapType::Func); - pub const EXTERNREF: Self = Self::abstract_(true, AbstractHeapType::Extern); - pub const EXNREF: Self = Self::abstract_(true, AbstractHeapType::Exn); + pub const FUNCREF: Self = Self::new_abstract(true, AbstractHeapType::Func); + pub const EXTERNREF: Self = Self::new_abstract(true, AbstractHeapType::Extern); + pub const EXNREF: Self = Self::new_abstract(true, AbstractHeapType::Exn); #[inline] - pub const fn abstract_(nullable: bool, heap_type: AbstractHeapType) -> Self { + pub const fn new_abstract(nullable: bool, heap_type: AbstractHeapType) -> Self { Self((nullable as u32) << 31 | heap_type as u32) } #[inline] - pub const fn concrete(nullable: bool, type_index: u32) -> Option { + pub const fn new_concrete(nullable: bool, type_index: u32) -> Option { if type_index <= Self::PAYLOAD_MASK { Some(Self(((nullable as u32) << 31) | Self::CONCRETE | type_index)) } else { @@ -115,6 +116,7 @@ impl RefType { } #[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "debug", derive(Debug))] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub enum RefValue { Null, @@ -124,32 +126,35 @@ pub enum RefValue { Exn(ExnRef), } -#[cfg(feature = "debug")] -impl core::fmt::Debug for RefValue { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::Null => write!(f, "null"), - Self::Func(_i) => write!(f, "func()"), - Self::Extern(_i) => write!(f, "extern()"), - Self::Any(_i) => write!(f, "any()"), - Self::Exn(_i) => write!(f, "exn()"), - } - } -} - #[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "debug", derive(Debug))] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct FuncRef(u32); +impl FuncRef { + #[inline] + pub const fn new(addr: u32) -> Self { + Self(addr) + } + + #[inline] + pub const fn addr(self) -> u32 { + self.0 + } +} + #[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "debug", derive(Debug))] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct ExternRef(u32); #[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "debug", derive(Debug))] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct ExnRef(u32); #[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "debug", derive(Debug))] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct AnyRef(u32); diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs index 1118282..213a556 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -134,7 +134,8 @@ impl WasmValue { } /// Type of a WebAssembly value. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "debug", derive(Debug))] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub enum WasmType { /// A 32-bit integer From 8bb2a291f45240930d9eae5693b1e33a2d88422d Mon Sep 17 00:00:00 2001 From: Henry Date: Sun, 2 Aug 2026 17:34:33 +0200 Subject: [PATCH 3/4] feat: add function references and canonicalize runtime types Signed-off-by: Henry --- CHANGELOG.md | 21 +++ Cargo.lock | 10 +- Cargo.toml | 10 +- crates/cli/src/output.rs | 7 +- crates/cli/src/value_parse.rs | 2 +- crates/cli/src/wast_runner.rs | 89 +++++---- crates/parser/src/conversion.rs | 73 ++++--- crates/parser/src/module.rs | 41 ++-- crates/parser/src/visit.rs | 13 +- crates/tinywasm/src/error.rs | 17 +- crates/tinywasm/src/func.rs | 178 ++++++++++-------- crates/tinywasm/src/imports.rs | 91 ++++++--- crates/tinywasm/src/instance.rs | 107 ++++------- crates/tinywasm/src/interpreter/executor.rs | 154 ++++++++++----- .../src/interpreter/stack/value_stack.rs | 10 +- crates/tinywasm/src/interpreter/values.rs | 22 ++- crates/tinywasm/src/reference.rs | 68 +++---- crates/tinywasm/src/store/function.rs | 28 +-- crates/tinywasm/src/store/mod.rs | 166 +++++++++++++--- crates/tinywasm/src/store/table.rs | 45 ++--- crates/tinywasm/tests/generated/wasm-1.csv | 1 + crates/tinywasm/tests/generated/wasm-2.csv | 1 + crates/tinywasm/tests/generated/wasm-3.csv | 1 + .../tests/generated/wasm-annotations.csv | 1 + .../generated/wasm-custom-page-sizes.csv | 1 + .../tests/generated/wasm-extended-const.csv | 1 + .../generated/wasm-function-references.csv | 1 + .../tests/generated/wasm-memory64.csv | 1 + .../tests/generated/wasm-multi-memory.csv | 1 + ...m-nontrapping-float-to-int-conversions.csv | 1 + .../tests/generated/wasm-reference-types.csv | 1 + .../tests/generated/wasm-relaxed-simd.csv | 1 + .../generated/wasm-sign-extension-ops.csv | 1 + crates/tinywasm/tests/generated/wasm-simd.csv | 1 + .../tests/generated/wasm-tail-call.csv | 1 + .../tests/generated/wasm-wide-arithmetic.csv | 1 + .../tests/host_func_signature_check.rs | 100 +++++++++- crates/tinywasm/tests/imported_table_init.rs | 8 +- crates/tinywasm/tests/internal_refs.rs | 17 +- crates/tinywasm/tests/module_descriptors.rs | 4 +- crates/tinywasm/tests/store_ownership.rs | 2 +- crates/types/README.md | 4 + crates/types/src/lib.rs | 90 +++++++-- crates/types/src/reference.rs | 65 +++++++ crates/types/src/value.rs | 73 ++++++- 45 files changed, 1022 insertions(+), 509 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37f697d..79fb6b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- Added support for the WebAssembly function-references proposal +- Added `WasmValue::ty` and `WasmValue::matches_type` + +### Changed + +- Function types are now stored separately and resolved through `Function::ty(&Store)`. + +### Fixed + +- Tail calls to host functions now return directly to the caller frame. + +### Breaking Changes + +- Renamed `ModuleInstanceAddr` to `ModuleInstanceId` for consistency. +- Removed `HostFunction::ty` and `WasmFunction::ty`. Use `Function::ty(&Store)` for runtime function types. +- Changed `TableType::element_type` and `Element::ty` from `WasmType` to `RefType`, and replaced module `table_types` with `TableDefinition { ty, init }`. + ## [0.10.0] - 2026-07-24 **All Commits**: https://github.com/explodingcamera/tinywasm/compare/v0.9.1...v0.10.0 diff --git a/Cargo.lock b/Cargo.lock index f9ab4c1..680b8fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -870,7 +870,7 @@ dependencies = [ [[package]] name = "tinywasm" -version = "0.10.0" +version = "0.11.0-pre.0" dependencies = [ "criterion", "eyre", @@ -888,7 +888,7 @@ dependencies = [ [[package]] name = "tinywasm-cli" -version = "0.10.0" +version = "0.11.0-pre.0" dependencies = [ "anstream", "assert_cmd", @@ -907,7 +907,7 @@ dependencies = [ [[package]] name = "tinywasm-parser" -version = "0.10.0" +version = "0.11.0-pre.0" dependencies = [ "log", "tinywasm-types", @@ -916,7 +916,7 @@ dependencies = [ [[package]] name = "tinywasm-root" -version = "0.10.0" +version = "0.11.0-pre.0" dependencies = [ "eyre", "pretty_env_logger", @@ -926,7 +926,7 @@ dependencies = [ [[package]] name = "tinywasm-types" -version = "0.10.0" +version = "0.11.0-pre.0" dependencies = [ "log", "postcard", diff --git a/Cargo.toml b/Cargo.toml index b40544d..e57b6ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ members = ["crates/*"] default-members = [".", "crates/parser", "crates/tinywasm", "crates/types"] [workspace.package] -version = "0.10.0" +version = "0.11.0-pre.0" edition = "2024" rust-version = "1.95" repository = "https://github.com/explodingcamera/tinywasm" @@ -20,10 +20,10 @@ keywords = ["interpreter", "no-std", "tinywasm", "wasm", "webassembly"] categories = ["compilers", "embedded", "no-std", "virtualization", "wasm"] [workspace.dependencies] -tinywasm = { path = "crates/tinywasm", version = "0.10.0", default-features = false } -tinywasm-cli = { path = "crates/cli", version = "0.10.0", default-features = false } -tinywasm-parser = { path = "crates/parser", version = "0.10.0", default-features = false } -tinywasm-types = { path = "crates/types", version = "0.10.0", default-features = false } +tinywasm = { path = "crates/tinywasm", version = "0.11.0-pre.0", default-features = false } +tinywasm-cli = { path = "crates/cli", version = "0.11.0-pre.0", default-features = false } +tinywasm-parser = { path = "crates/parser", version = "0.11.0-pre.0", default-features = false } +tinywasm-types = { path = "crates/types", version = "0.11.0-pre.0", default-features = false } eyre = "0.6" indexmap = "2.14" diff --git a/crates/cli/src/output.rs b/crates/cli/src/output.rs index bf568ed..40a271e 100644 --- a/crates/cli/src/output.rs +++ b/crates/cli/src/output.rs @@ -30,8 +30,9 @@ pub fn format_wasm_type(ty: WasmType) -> &'static str { WasmType::F32 => "f32", WasmType::F64 => "f64", WasmType::V128 => "v128", - WasmType::RefFunc => "funcref", - WasmType::RefExtern => "externref", + WasmType::Ref(ty) if ty.is_func() => "funcref", + WasmType::Ref(ty) if ty.is_extern() => "externref", + WasmType::Ref(_) => "ref", } } @@ -62,7 +63,7 @@ pub fn format_table_type(ty: &TableType) -> String { MemoryArch::I64 => "i64", }; let max = ty.size_max.map(|v| v.to_string()).unwrap_or_else(|| "unbounded".to_string()); - format!("table[{arch} {}] initial={} max={max}", format_wasm_type(ty.element_type), ty.size_initial) + format!("table[{arch} {}] initial={} max={max}", format_wasm_type(WasmType::Ref(ty.element_type)), ty.size_initial) } pub fn format_global_type(ty: &GlobalType) -> String { diff --git a/crates/cli/src/value_parse.rs b/crates/cli/src/value_parse.rs index 92c0363..eabbdc4 100644 --- a/crates/cli/src/value_parse.rs +++ b/crates/cli/src/value_parse.rs @@ -21,7 +21,7 @@ fn parse_arg(index: usize, ty: WasmType, value: &str) -> Result { .parse::() .map(|v| WasmValue::V128(v.to_le_bytes())) .map_err(|e| format_error(index, ty, value, e))?, - WasmType::RefFunc | WasmType::RefExtern => { + WasmType::Ref(_) => { bail!( "unsupported CLI argument type at position {}: {}; use the embedding API for reference values", index + 1, diff --git a/crates/cli/src/wast_runner.rs b/crates/cli/src/wast_runner.rs index 99f1efe..c773663 100644 --- a/crates/cli/src/wast_runner.rs +++ b/crates/cli/src/wast_runner.rs @@ -7,7 +7,7 @@ use std::time::Duration; use eyre::{Context, Result, bail, eyre}; use log::{debug, error}; -use tinywasm::types::{ExternRef, FuncRef, MemoryType, TableType, WasmType, WasmValue}; +use tinywasm::types::{ExternRef, FuncRef, MemoryType, RefType, RefValue, TableType, WasmType, WasmValue}; use tinywasm::{ExecProgress, Global, HostFunction, Imports, Memory, Module, ModuleInstance, Store, Table}; use wast::{QuoteWat, core::AbstractHeapType}; @@ -154,16 +154,8 @@ impl WastRunner { fn imports(store: &mut Store, modules: &HashMap) -> Result { let mut imports = Imports::new(); - let table = Table::new( - store, - TableType::new(WasmType::RefFunc, 10, Some(20)), - WasmValue::default_for(WasmType::RefFunc), - )?; - let table64 = Table::new( - store, - TableType::new64(WasmType::RefFunc, 10, Some(20)), - WasmValue::default_for(WasmType::RefFunc), - )?; + let table = Table::new(store, TableType::new(RefType::FUNCREF, 10, Some(20)), RefValue::Null.into())?; + let table64 = Table::new(store, TableType::new64(RefType::FUNCREF, 10, Some(20)), RefValue::Null.into())?; let memory = Memory::new(store, MemoryType::default().with_page_count_initial(1).with_page_count_max(Some(2)))?; let global_i32 = Global::new(store, tinywasm::types::GlobalType::new(WasmType::I32, false), WasmValue::I32(666))?; @@ -467,7 +459,7 @@ impl WastRunner { let expected = expected_alternatives .iter() .filter_map(|alts| alts.first()) - .find(|exp| module_global.eq_loose(exp)); + .find(|exp| exp.matches(&module_global)); if expected.is_none() { test_group.add_result( &format!("AssertReturn(unsupported-{i})"), @@ -516,7 +508,7 @@ impl WastRunner { } if expected_alternatives.iter().any(|expected| { expected.len() == outcomes.len() - && outcomes.iter().zip(expected.iter()).all(|(outcome, exp)| outcome.eq_loose(exp)) + && outcomes.iter().zip(expected.iter()).all(|(outcome, exp)| exp.matches(outcome)) }) { Ok(()) } else { @@ -735,7 +727,7 @@ fn convert_wastargs(args: Vec) -> Result> { args.into_iter().map(wastarg2tinywasmvalue).collect() } -fn convert_wastret<'a>(args: impl Iterator>) -> Result>> { +fn convert_wastret<'a>(args: impl Iterator>) -> Result>> { let mut alternatives = vec![Vec::new()]; for arg in args { let choices = wastret2tinywasmvalues(arg)?; @@ -763,14 +755,10 @@ fn wastarg2tinywasmvalue(arg: wast::WastArg) -> Result { I32(i) => WasmValue::I32(i), I64(i) => WasmValue::I64(i), V128(i) => WasmValue::V128(i.to_le_bytes()), - RefExtern(v) => WasmValue::RefExtern(ExternRef::new(Some(v))), + RefExtern(v) => ExternRef::new(v).into(), RefNull(t) => match t { - wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Func } => { - WasmValue::RefFunc(FuncRef::null()) - } - wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Extern } => { - WasmValue::RefExtern(ExternRef::null()) - } + wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Func } => RefValue::Null.into(), + wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Extern } => RefValue::Null.into(), _ => { bail!("unsupported arg type: refnull: {:?}", t); } @@ -797,7 +785,26 @@ fn wast_v128_to_bytes(i: wast::core::V128Pattern) -> [u8; 16] { res.try_into().unwrap() } -fn wastret2tinywasmvalues(ret: wast::WastRet) -> Result> { +#[derive(Clone, Copy)] +enum ExpectedValue { + Exact(WasmValue), + RefNull, + RefFunc, + RefExtern, +} + +impl ExpectedValue { + fn matches(&self, value: &WasmValue) -> bool { + match self { + Self::Exact(expected) => value.eq_loose(expected), + Self::RefNull => matches!(value, WasmValue::Ref(RefValue::Null)), + Self::RefFunc => matches!(value, WasmValue::Ref(RefValue::Func(_))), + Self::RefExtern => matches!(value, WasmValue::Ref(RefValue::Extern(_))), + } + } +} + +fn wastret2tinywasmvalues(ret: wast::WastRet) -> Result> { let wast::WastRet::Core(ret) = ret else { bail!("unsupported arg type"); }; @@ -809,32 +816,22 @@ fn wastret2tinywasmvalues(ret: wast::WastRet) -> Result> { } } -fn wastretcore2tinywasmvalue(ret: wast::core::WastRetCore) -> Result { +fn wastretcore2tinywasmvalue(ret: wast::core::WastRetCore) -> Result { use wast::core::WastRetCore::{F32, F64, I32, I64, RefExtern, RefFunc, RefNull, V128}; Ok(match ret { - F32(f) => nanpattern2tinywasmvalue(f)?, - F64(f) => nanpattern2tinywasmvalue(f)?, - I32(i) => WasmValue::I32(i), - I64(i) => WasmValue::I64(i), - V128(i) => WasmValue::V128(wast_v128_to_bytes(i)), - RefNull(t) => match t { - Some(wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Func }) => { - WasmValue::RefFunc(FuncRef::null()) - } - Some(wast::core::HeapType::Abstract { shared: false, ty: AbstractHeapType::Extern }) => { - WasmValue::RefExtern(ExternRef::null()) - } - _ => { - bail!("unsupported arg type: refnull: {:?}", t); - } - }, - RefExtern(v) => WasmValue::RefExtern(ExternRef::new(v)), - RefFunc(v) => WasmValue::RefFunc(FuncRef::new(match v { - Some(wast::token::Index::Num(n, _)) => Some(n), - _ => { - bail!("unsupported arg type: reffunc: {:?}", v); - } - })), + F32(f) => ExpectedValue::Exact(nanpattern2tinywasmvalue(f)?), + F64(f) => ExpectedValue::Exact(nanpattern2tinywasmvalue(f)?), + I32(i) => ExpectedValue::Exact(WasmValue::I32(i)), + I64(i) => ExpectedValue::Exact(WasmValue::I64(i)), + V128(i) => ExpectedValue::Exact(WasmValue::V128(wast_v128_to_bytes(i))), + RefNull(_) => ExpectedValue::RefNull, + RefExtern(Some(v)) => ExpectedValue::Exact(ExternRef::new(v).into()), + RefExtern(None) => ExpectedValue::RefExtern, + RefFunc(Some(wast::token::Index::Num(n, _))) => ExpectedValue::Exact(FuncRef::new(n).into()), + RefFunc(None) => ExpectedValue::RefFunc, + RefFunc(v) => { + bail!("unsupported arg type: reffunc: {:?}", v); + } a => { bail!("unsupported arg type {:?}", a); } diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index 7f0a5f5..db8d2aa 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -1,5 +1,3 @@ -use alloc::sync::Arc; - use crate::{Result, module::FunctionCode, visit::process_operators_and_validate}; use alloc::{boxed::Box, format, string::ToString, vec::Vec}; use tinywasm_types::*; @@ -26,7 +24,7 @@ pub(crate) fn convert_module_element(element: wasmparser::Element<'_>) -> Result .collect::>>()? .into_boxed_slice(); - Ok(tinywasm_types::Element { kind, items, ty: WasmType::Ref(RefType::FUNCREF), range: element.range }) + Ok(tinywasm_types::Element { kind, items, ty: RefType::FUNCREF, range: element.range }) } wasmparser::ElementItems::Expressions(ty, exprs) => { @@ -36,7 +34,7 @@ pub(crate) fn convert_module_element(element: wasmparser::Element<'_>) -> Result .collect::>>()? .into_boxed_slice(); - Ok(tinywasm_types::Element { kind, items, ty: convert_reftype(ty)?, range: element.range }) + Ok(tinywasm_types::Element { kind, items, ty: convert_ref_type(ty)?, range: element.range }) } } } @@ -59,7 +57,7 @@ pub(crate) fn convert_module_import(import: wasmparser::Import<'_>) -> Result ImportKind::Function(ty), wasmparser::TypeRef::Table(ty) => { - let element_type = convert_reftype(ty.element_type)?; + let element_type = convert_ref_type(ty.element_type)?; ImportKind::Table(if ty.table64 { TableType::new64(element_type, ty.initial, ty.maximum) } else { @@ -165,8 +163,9 @@ pub(crate) fn convert_module_code( )) } -pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result> { +pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result { let mut types = ty.types(); + // TODO(wasm3): Preserve recursive groups and non-function composite types instead of flattening singleton funcs. if types.len() != 1 { return Err(crate::ParseError::UnsupportedOperator( "Expected exactly one type in the type section".to_string(), @@ -182,12 +181,11 @@ pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result>>()?; let results = ty.results().iter().map(convert_valtype).collect::>>()?; - Ok(FuncType::new(¶ms, &results).into()) + Ok(FuncType::new(¶ms, &results)) } -pub(crate) fn convert_reftype(reftype: wasmparser::RefType) -> Result { - let ty = convert_heaptype(reftype.heap_type())?.with_nullability(reftype.is_nullable()); - Ok(WasmType::Ref(ty)) +pub(crate) fn convert_ref_type(ty: wasmparser::RefType) -> Result { + convert_heap_type(ty.heap_type(), ty.is_nullable()) } pub(crate) fn convert_valtype(valtype: &wasmparser::ValType) -> Result { @@ -197,7 +195,7 @@ pub(crate) fn convert_valtype(valtype: &wasmparser::ValType) -> Result wasmparser::ValType::F32 => Ok(WasmType::F32), wasmparser::ValType::F64 => Ok(WasmType::F64), wasmparser::ValType::V128 => Ok(WasmType::V128), - wasmparser::ValType::Ref(r) => convert_reftype(*r), + wasmparser::ValType::Ref(r) => Ok(WasmType::Ref(convert_ref_type(*r)?)), } } @@ -216,7 +214,7 @@ pub(crate) fn process_const_operators(ops: OperatorsReader<'_>) -> Result { - convert_heaptype(hty)?; + convert_heap_type(hty, false)?; ConstInstruction::Ref(RefValue::Null) } wasmparser::Operator::RefFunc { function_index } => { @@ -250,36 +248,37 @@ pub(crate) fn process_const_operators(ops: OperatorsReader<'_>) -> Result Result { - let ty = match heap { - wasmparser::HeapType::Abstract { shared: false, ty } => match ty { - wasmparser::AbstractHeapType::Any => AbstractHeapType::Any, - wasmparser::AbstractHeapType::Eq => AbstractHeapType::Eq, - wasmparser::AbstractHeapType::I31 => AbstractHeapType::I31, - wasmparser::AbstractHeapType::Struct => AbstractHeapType::Struct, - wasmparser::AbstractHeapType::Array => AbstractHeapType::Array, - wasmparser::AbstractHeapType::None => AbstractHeapType::None, - wasmparser::AbstractHeapType::Func => AbstractHeapType::Func, - wasmparser::AbstractHeapType::NoFunc => AbstractHeapType::NoFunc, - wasmparser::AbstractHeapType::Exn => AbstractHeapType::Exn, - wasmparser::AbstractHeapType::NoExn => AbstractHeapType::NoExn, - wasmparser::AbstractHeapType::Extern => AbstractHeapType::Extern, - wasmparser::AbstractHeapType::NoExtern => AbstractHeapType::NoExtern, - wasmparser::AbstractHeapType::Cont | wasmparser::AbstractHeapType::NoCont => { - return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported heap type: {heap:?}"))); - } - }, +pub(crate) fn convert_heap_type(heap: wasmparser::HeapType, nullable: bool) -> Result { + match heap { + wasmparser::HeapType::Abstract { shared: false, ty } => Ok(RefType::new_abstract( + nullable, + match ty { + wasmparser::AbstractHeapType::Any => AbstractHeapType::Any, + wasmparser::AbstractHeapType::Eq => AbstractHeapType::Eq, + wasmparser::AbstractHeapType::I31 => AbstractHeapType::I31, + wasmparser::AbstractHeapType::Struct => AbstractHeapType::Struct, + wasmparser::AbstractHeapType::Array => AbstractHeapType::Array, + wasmparser::AbstractHeapType::None => AbstractHeapType::None, + wasmparser::AbstractHeapType::Func => AbstractHeapType::Func, + wasmparser::AbstractHeapType::NoFunc => AbstractHeapType::NoFunc, + wasmparser::AbstractHeapType::Exn => AbstractHeapType::Exn, + wasmparser::AbstractHeapType::NoExn => AbstractHeapType::NoExn, + wasmparser::AbstractHeapType::Extern => AbstractHeapType::Extern, + wasmparser::AbstractHeapType::NoExtern => AbstractHeapType::NoExtern, + wasmparser::AbstractHeapType::Cont | wasmparser::AbstractHeapType::NoCont => { + return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported heap type: {heap:?}"))); + } + }, + )), wasmparser::HeapType::Concrete(index) => { let index = index.as_module_index().ok_or_else(|| { crate::ParseError::UnsupportedOperator(format!("Unsupported non-module heap type index: {index:?}")) })?; - return RefType::new_concrete(false, index) - .ok_or_else(|| crate::ParseError::Other(format!("heap type index is too large: {index}"))); + RefType::new_concrete(nullable, index) + .ok_or_else(|| crate::ParseError::Other(format!("heap type index is too large: {index}"))) } wasmparser::HeapType::Abstract { shared: true, .. } | wasmparser::HeapType::Exact(_) => { - return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported heap type: {heap:?}"))); + Err(crate::ParseError::UnsupportedOperator(format!("Unsupported heap type: {heap:?}"))) } - }; - - Ok(RefType::new_abstract(false, ty)) + } } diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs index 05811ec..c2199f3 100644 --- a/crates/parser/src/module.rs +++ b/crates/parser/src/module.rs @@ -45,13 +45,13 @@ pub(crate) struct ModuleReader<'a> { pub(crate) version: Option, pub(crate) start_func: Option, - pub(crate) func_types: Arc<[Arc]>, + pub(crate) func_types: Box<[FuncType]>, pub(crate) code_type_addrs: Box<[u32]>, code_results: Box<[ValueCounts]>, pub(crate) exports: Arc<[Export]>, pub(crate) code: Vec, pub(crate) globals: Box<[Global]>, - pub(crate) table_types: Box<[TableType]>, + pub(crate) tables: Box<[TableDefinition]>, pub(crate) memory_types: Box<[MemoryType]>, pub(crate) imports: Box<[Import]>, pub(crate) data: Box<[Data]>, @@ -73,7 +73,7 @@ impl<'a> ModuleReader<'a> { &self.imports, &self.globals, &self.memory_types, - &self.table_types, + &self.tables, ))); } self.translation_metadata.as_deref().unwrap() @@ -124,22 +124,28 @@ impl<'a> ModuleReader<'a> { self.globals = convert_module_globals(reader)?; } Payload::TableSection(reader) => { - check_section("table", !self.table_types.is_empty())?; + check_section("table", !self.tables.is_empty())?; if let Some(validator) = validator.as_mut() { validator.table_section(&reader)?; } - self.table_types = reader - .into_iter() - .map(|table| { - let table = table?; - let element_type = convert_reftype(table.ty.element_type)?; - Ok(if table.ty.table64 { - TableType::new64(element_type, table.ty.initial, table.ty.maximum) - } else { - TableType::new(element_type, table.ty.initial, table.ty.maximum) - }) - }) - .collect::>()?; + let mut tables = Vec::with_capacity(reader.count() as usize); + for table in reader { + let table = table?; + let element_type = convert_ref_type(table.ty.element_type)?; + let ty = if table.ty.table64 { + TableType::new64(element_type, table.ty.initial, table.ty.maximum) + } else { + TableType::new(element_type, table.ty.initial, table.ty.maximum) + }; + let init = match table.init { + wasmparser::TableInit::RefNull => None, + wasmparser::TableInit::Expr(expr) => { + Some(process_const_operators(expr.get_operators_reader())?) + } + }; + tables.push(TableDefinition { ty, init }); + } + self.tables = tables.into_boxed_slice(); } Payload::MemorySection(reader) => { check_section("memory", !self.memory_types.is_empty())?; @@ -466,7 +472,6 @@ impl<'a> ModuleReader<'a> { locals: code.locals, params, results, - ty, }) }) .collect(); @@ -476,7 +481,7 @@ impl<'a> ModuleReader<'a> { func_types: self.func_types, func_type_idxs, globals: self.globals, - table_types: self.table_types, + tables: self.tables, imports: self.imports, start_func: self.start_func, data: self.data, diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 70ead68..d0e970b 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -1,9 +1,8 @@ -use crate::{Result, conversion::convert_heaptype, macros::visit::*}; +use crate::{Result, conversion::convert_heap_type, macros::visit::*}; use alloc::string::ToString; -use alloc::sync::Arc; use alloc::vec::Vec; use tinywasm_types::{ - FuncType, Global, Import, ImportKind, Instruction, MemoryArch, MemoryArg, MemoryType, TableType, ValueCounts, + FuncType, Global, Import, ImportKind, Instruction, MemoryArch, MemoryArg, MemoryType, TableDefinition, ValueCounts, WasmFunctionData, WasmType, }; use wasmparser::{ @@ -151,12 +150,12 @@ struct ValidateThenVisit<'a, 'm> { impl ModuleMetadata { pub(crate) fn new( - types: &[Arc], + types: &[FuncType], code_type_addrs: &[u32], imports: &[Import], globals: &[Global], memories: &[MemoryType], - tables: &[TableType], + tables: &[TableDefinition], ) -> Self { let mut functions = Vec::with_capacity(imports.len() + code_type_addrs.len()); let mut global_sizes = Vec::with_capacity(imports.len() + globals.len()); @@ -175,7 +174,7 @@ impl ModuleMetadata { functions.extend_from_slice(code_type_addrs); global_sizes.extend(globals.iter().map(|global| OperandSize::from(&global.ty.ty))); memory_sizes.extend(memories.iter().map(|ty| OperandSize::from(ty.arch()))); - table_sizes.extend(tables.iter().map(|ty| OperandSize::from(ty.arch()))); + table_sizes.extend(tables.iter().map(|table| OperandSize::from(table.ty.arch()))); let signatures = types .iter() @@ -685,7 +684,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder<'_> { // Reference Types fn visit_ref_null(&mut self, ty: wasmparser::HeapType) -> Self::Output { - let instruction = Instruction::RefNull(convert_heaptype(ty)?); + let instruction = Instruction::RefNull(convert_heap_type(ty, false)?); self.emit(&[], &[OperandSize::S32], instruction) } diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs index 4f187e5..b5885ca 100644 --- a/crates/tinywasm/src/error.rs +++ b/crates/tinywasm/src/error.rs @@ -1,6 +1,5 @@ use alloc::boxed::Box; use alloc::string::{String, ToString}; -use alloc::sync::Arc; use alloc::vec::Vec; use core::fmt::{Debug, Display}; use tinywasm_types::FuncType; @@ -27,7 +26,7 @@ pub enum Error { /// A host function returned an invalid value InvalidHostFnReturn { /// The expected type - expected: Arc, + expected: Box, /// The actual value actual: Vec, }, @@ -167,12 +166,18 @@ pub enum Trap { index: usize, }, + /// A null reference was used where a non-null reference was required. + NullReference, + + /// A null function reference was called. + NullFunctionReference, + /// Indirect call type mismatch IndirectCallTypeMismatch { /// The expected type - expected: Arc, + expected: Box, /// The actual type - actual: Arc, + actual: Box, }, /// Catch-all for other messages @@ -194,6 +199,8 @@ impl Trap { Self::OutOfMemory => "out of memory", Self::UndefinedElement { .. } => "undefined element", Self::UninitializedElement { .. } => "uninitialized element", + Self::NullReference => "null reference", + Self::NullFunctionReference => "null function reference", Self::IndirectCallTypeMismatch { .. } => "indirect call type mismatch", Self::HostFunction(_) => "host function trap", Self::InvalidStore => "invalid store", @@ -294,6 +301,8 @@ impl Display for Trap { Self::UninitializedElement { index } => { write!(f, "uninitialized element: index={index}") } + Self::NullReference => write!(f, "null reference"), + Self::NullFunctionReference => write!(f, "null function reference"), Self::InvalidStore => write!(f, "invalid store"), #[cfg(feature = "debug")] Self::IndirectCallTypeMismatch { expected, actual } => { diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index 05767d5..b6b7b14 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -1,11 +1,24 @@ use crate::interpreter::stack::{CallFrame, ValueStack}; use crate::reference::StoreItem; use crate::{Error, FunctionInstance, InterpreterRuntime, Result, Store, Trap}; -use alloc::{borrow::Cow, boxed::Box, format, rc::Rc, sync::Arc, vec, vec::Vec}; +use alloc::{borrow::Cow, boxed::Box, format, rc::Rc, vec, vec::Vec}; use core::hint::cold_path; -use tinywasm_types::{ExternRef, FuncRef, FuncType, ModuleInstanceAddr, WasmType, WasmValue}; +use tinywasm_types::{ExternRef, FuncAddr, FuncRef, FuncType, ModuleInstanceId, TypeAddr, WasmType, WasmValue}; impl Function { + #[inline] + pub(crate) const fn addr(&self) -> FuncAddr { + self.item.addr + } + + /// Get this function's canonical type from its store. + /// + /// Concrete reference types are only meaningful in this store. + pub fn ty<'a>(&self, store: &'a Store) -> Result<&'a FuncType> { + self.item.validate_store(store)?; + Ok(store.state.get_func_type(self.addr())) + } + /// Call a function (Invocation) /// /// See @@ -13,12 +26,13 @@ impl Function { pub fn call(&self, store: &mut Store, params: &[WasmValue]) -> Result> { #[inline] fn call_inner(func: &Function, store: &mut Store, params: &[WasmValue]) -> Result> { - let func_instance = store.state.get_func(func.addr); - let wasm_func = match func_instance { - FunctionInstance::Host(host_func) => { - return host_func.clone().call(FuncContext { store, module_addr: func.module_addr }, params); + let func_instance = store.state.get_func(func.addr()).clone(); + let wasm_func = match &func_instance.kind { + crate::store::FunctionKind::Host(host_func) => { + let result = host_func.clone().call(FuncContext { store, module_id: func.module_id }, params)?; + return validate_host_results(store, func_instance.type_addr, result); } - FunctionInstance::Wasm(wasm_func) => wasm_func, + crate::store::FunctionKind::Wasm(wasm_func) => wasm_func, }; // Reset stack, push args, allocate locals, create entry frame. @@ -26,15 +40,15 @@ impl Function { store.value_stack.clear(); store.value_stack.extend_from_wasmvalues(params)?; let locals_base = store.value_stack.enter_locals(&wasm_func.func.params, &wasm_func.func.locals)?; - let callframe = CallFrame::new(func.addr, locals_base, wasm_func.func.locals); + let callframe = CallFrame::new(func.addr(), locals_base, wasm_func.func.locals); // Execute until completion and then collect result values from the stack. InterpreterRuntime::exec(store, callframe, 0)?; - collect_call_results(&mut store.value_stack, &func.ty) + collect_call_results(&mut store.value_stack, store.state.get_type(func_instance.type_addr)) } self.item.validate_store(store)?; - validate_call_params(&self.ty, params)?; + validate_call_params(&store.state, store.state.get_func_type(self.addr()), params)?; store.enter_execution()?; let result = call_inner(self, store, params); @@ -59,29 +73,30 @@ impl Function { store: &mut Store, params: &[WasmValue], ) -> Result { - let func_instance = store.state.get_func(func.addr); - match func_instance { - FunctionInstance::Host(host_func) => host_func - .clone() - .call(FuncContext { store, module_addr: func.module_addr }, params) - .map(|result| FuncExecutionState::Completed { result: Some(result) }), - FunctionInstance::Wasm(wasm_func) => { + let func_instance = store.state.get_func(func.addr()).clone(); + match &func_instance.kind { + crate::store::FunctionKind::Host(host_func) => { + let result = host_func.clone().call(FuncContext { store, module_id: func.module_id }, params)?; + let result = validate_host_results(store, func_instance.type_addr, result)?; + Ok(FuncExecutionState::Completed { result: Some(result) }) + } + crate::store::FunctionKind::Wasm(wasm_func) => { store.call_stack.clear(); store.value_stack.clear(); store.value_stack.extend_from_wasmvalues(params)?; let locals_base = store.value_stack.enter_locals(&wasm_func.func.params, &wasm_func.func.locals)?; - let callframe = CallFrame::new(func.addr, locals_base, wasm_func.func.locals); + let callframe = CallFrame::new(func.addr(), locals_base, wasm_func.func.locals); Ok(FuncExecutionState::Running { exec_state: ExecutionState { callframe }, - root_func_addr: func.addr, + root_func_addr: func.addr(), }) } } } self.item.validate_store(store)?; - validate_call_params(&self.ty, params)?; + validate_call_params(&store.state, store.state.get_func_type(self.addr()), params)?; store.enter_execution()?; let result = call_resumable_inner(self, store, params); @@ -111,9 +126,7 @@ pub(crate) struct ExecutionState { #[cfg_attr(feature = "debug", derive(core::fmt::Debug))] pub struct Function { pub(crate) item: StoreItem, - pub(crate) module_addr: ModuleInstanceAddr, - pub(crate) addr: u32, - pub(crate) ty: Arc, + pub(crate) module_id: ModuleInstanceId, } /// A typed function handle @@ -126,16 +139,10 @@ pub struct FunctionTyped { /// A host function pub struct HostFunction { - pub(crate) ty: Arc, pub(crate) func: HostFuncInner, } impl HostFunction { - /// Get the function's type - pub fn ty(&self) -> &Arc { - &self.ty - } - /// Call the function pub fn call(&self, ctx: FuncContext<'_>, args: &[WasmValue]) -> Result> { (self.func)(ctx, args) @@ -178,25 +185,12 @@ impl HostFunction { ty: &FuncType, func: impl Fn(FuncContext<'_>, &[WasmValue]) -> Result> + 'static, ) -> Function { - let ty = Arc::new(ty.clone()); - let host_ty = ty.clone(); - - let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result> { - let result = func(ctx, args)?; - let expected = host_ty.results(); - - let valid = result.len() == expected.len() - && result.iter().zip(expected).all(|(val, ty)| WasmType::from(val) == *ty); - - if !valid { - return Err(crate::Error::InvalidHostFnReturn { expected: Arc::clone(&host_ty), actual: result }); - } - - Ok(result) - }; - - let addr = store.add_func(FunctionInstance::Host(Rc::new(Self { func: Box::new(inner_func), ty: ty.clone() }))); - Function { item: crate::StoreItem::new(store.id(), addr), module_addr: 0, addr, ty } + let type_addr = store.register_host_type(ty); + let addr = store.add_func(FunctionInstance { + type_addr, + kind: crate::store::FunctionKind::Host(Rc::new(Self { func: Box::new(func) })), + }); + Function { item: crate::StoreItem::new(store.id(), addr), module_id: 0 } } /// Create a new typed host function import. @@ -233,9 +227,13 @@ impl HostFunction { Ok(func(ctx, P::from_wasm_values(args)?)?.into_wasm_values()) }; - let ty = Arc::new(tinywasm_types::FuncType::new(&P::wasm_types(), &R::wasm_types())); - let addr = store.add_func(FunctionInstance::Host(Rc::new(Self { func: Box::new(inner_func), ty: ty.clone() }))); - Function { item: crate::StoreItem::new(store.id(), addr), module_addr: 0, addr, ty } + let ty = tinywasm_types::FuncType::new(&P::wasm_types(), &R::wasm_types()); + let type_addr = store.register_host_type(&ty); + let addr = store.add_func(FunctionInstance { + type_addr, + kind: crate::store::FunctionKind::Host(Rc::new(Self { func: Box::new(inner_func) })), + }); + Function { item: crate::StoreItem::new(store.id(), addr), module_id: 0 } } } @@ -245,7 +243,7 @@ pub(crate) type HostFuncInner = Box, &[WasmValue]) -> Res #[cfg_attr(feature = "debug", derive(core::fmt::Debug))] pub struct FuncContext<'a> { pub(crate) store: &'a mut crate::Store, - pub(crate) module_addr: ModuleInstanceAddr, + pub(crate) module_id: ModuleInstanceId, } impl FuncContext<'_> { @@ -261,9 +259,9 @@ impl FuncContext<'_> { /// Get the module instance. pub fn module(&self) -> crate::ModuleInstance { - self.store.get_module_instance(self.module_addr).unwrap_or_else(|| { - unreachable!("invalid module instance address in host function context: {}", self.module_addr) - }) + self.store + .get_module_instance(self.module_id) + .unwrap_or_else(|| unreachable!("invalid module instance id in host function context: {}", self.module_id)) } /// Get a memory export. @@ -327,14 +325,16 @@ impl FuncContext<'_> { } func.item.validate_store(self.store)?; - validate_call_params(&func.ty, args)?; - - let func_instance = self.store.state.get_func(func.addr).clone(); - match func_instance { - FunctionInstance::Host(host_func) => { - host_func.call(FuncContext { store: &mut *self.store, module_addr: func.module_addr }, args) + validate_call_params(&self.store.state, self.store.state.get_func_type(func.addr()), args)?; + + let func_instance = self.store.state.get_func(func.addr()).clone(); + match func_instance.kind { + crate::store::FunctionKind::Host(host_func) => { + let result = + host_func.call(FuncContext { store: &mut *self.store, module_id: func.module_id }, args)?; + validate_host_results(self.store, func_instance.type_addr, result) } - FunctionInstance::Wasm(wasm_func) => { + crate::store::FunctionKind::Wasm(wasm_func) => { let call_stack_base = self.store.call_stack.len(); let value_stack_base = self.store.value_stack.base(); @@ -348,13 +348,13 @@ impl FuncContext<'_> { .enter_locals(&wasm_func.func.params, &wasm_func.func.locals) .inspect_err(|_| self.store.value_stack.truncate_to_base(value_stack_base))?; - let callframe = CallFrame::new(func.addr, locals_base, wasm_func.func.locals); + let callframe = CallFrame::new(func.addr(), locals_base, wasm_func.func.locals); InterpreterRuntime::exec(self.store, callframe, call_stack_base).inspect_err(|_| { self.store.call_stack.truncate_to(call_stack_base); self.store.value_stack.truncate_to_base(value_stack_base); })?; - collect_call_results(&mut self.store.value_stack, &func.ty) + collect_call_results(&mut self.store.value_stack, self.store.state.get_type(func_instance.type_addr)) } } } @@ -388,15 +388,15 @@ impl core::ops::DerefMut for FuncContext<'_> { impl<'a> FuncContext<'a> { /// Create a new host function context. - pub const fn new(store: &'a mut crate::Store, module_addr: ModuleInstanceAddr) -> Self { - Self { store, module_addr } + pub const fn new(store: &'a mut crate::Store, module_id: ModuleInstanceId) -> Self { + Self { store, module_id } } } #[cfg(feature = "debug")] impl core::fmt::Debug for HostFunction { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("HostFunction").field("ty", &self.ty).field("func", &"...").finish() + f.debug_struct("HostFunction").field("func", &"...").finish() } } @@ -441,9 +441,12 @@ impl<'store> FuncExecution<'store> { match result? { crate::interpreter::ExecState::Completed => { - let result_ty = self.store.state.get_func(root_func_addr).ty().clone(); + let result_ty = self.store.state.get_func(root_func_addr).type_addr; self.state = FuncExecutionState::Completed { result: None }; - Ok(ExecProgress::Completed(collect_call_results(&mut self.store.value_stack, &result_ty)?)) + Ok(ExecProgress::Completed(collect_call_results( + &mut self.store.value_stack, + self.store.state.get_type(result_ty), + )?)) } crate::interpreter::ExecState::Suspended(callframe) => { let FuncExecutionState::Running { exec_state, .. } = &mut self.state else { @@ -492,7 +495,7 @@ impl<'store> FuncExecution<'store> { } } -fn validate_call_params(func_ty: &FuncType, params: &[WasmValue]) -> Result<()> { +fn validate_call_params(state: &crate::store::State, func_ty: &FuncType, params: &[WasmValue]) -> Result<()> { if func_ty.params().len() != params.len() { cold_path(); return Err(Error::Other(format!( @@ -502,13 +505,28 @@ fn validate_call_params(func_ty: &FuncType, params: &[WasmValue]) -> Result<()> ))); } - if !(func_ty.params().iter().zip(params).all(|(ty, param)| ty == ¶m.into())) { + if !func_ty.params().iter().zip(params).all(|(ty, param)| state.value_matches_type(*param, *ty)) { return Err(Error::other("Type mismatch")); } Ok(()) } +pub(crate) fn validate_host_results( + store: &Store, + type_addr: TypeAddr, + result: Vec, +) -> Result> { + let expected = store.state.get_type(type_addr); + if result.len() == expected.results().len() + && result.iter().zip(expected.results()).all(|(&value, &ty)| store.state.value_matches_type(value, ty)) + { + return Ok(result); + } + + Err(Error::InvalidHostFnReturn { expected: Box::new(expected.clone()), actual: result }) +} + fn collect_call_results(value_stack: &mut ValueStack, func_ty: &FuncType) -> Result> { debug_assert!(value_stack.len() >= func_ty.results().len()); // m values are on the top of the stack (Ensured by validation) let mut res: Vec<_> = value_stack.pop_types(func_ty.results().iter().rev()).collect(); // pop in reverse order since the stack is LIFO @@ -612,14 +630,14 @@ pub trait ToWasmType { } macro_rules! impl_scalar_wasm_traits { - ($($T:ty => $val_ty:ident),+ $(,)?) => { + ($($T:ty => $val_ty:expr),+ $(,)?) => { $( impl ToWasmType for $T { - const WASM_TYPE: WasmType = WasmType::$val_ty; + const WASM_TYPE: WasmType = $val_ty; } impl ToWasmTypes for $T { - const WASM_TYPES: Option<&'static [WasmType]> = Some(&[WasmType::$val_ty]); + const WASM_TYPES: Option<&'static [WasmType]> = Some(&[$val_ty]); } impl IntoWasmValues for $T { @@ -705,12 +723,12 @@ macro_rules! impl_tuple { } impl_scalar_wasm_traits!( - i32 => I32, - i64 => I64, - f32 => F32, - f64 => F64, - FuncRef => RefFunc, - ExternRef => RefExtern, + i32 => WasmType::I32, + i64 => WasmType::I64, + f32 => WasmType::F32, + f64 => WasmType::F64, + FuncRef => WasmType::Ref(tinywasm_types::RefType::FUNCREF), + ExternRef => WasmType::Ref(tinywasm_types::RefType::EXTERNREF), ); impl_tuple!(impl_tuple_traits); diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index 056d706..887d107 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -82,8 +82,8 @@ impl From<&Import> for ExternName { /// /// let table = Table::new( /// &mut store, -/// TableType::new(WasmType::RefFunc, 10, Some(20)), -/// WasmValue::default_for(WasmType::RefFunc), +/// TableType::new(tinywasm::types::RefType::FUNCREF, 10, Some(20)), +/// tinywasm::types::RefValue::Null.into(), /// )?; /// let memory = Memory::new( /// &mut store, @@ -157,20 +157,44 @@ impl Imports { Ok(()) } - fn compare_table_types(import: &Import, expected: &TableType, actual: &TableType) -> Result<()> { + fn ref_subtype(actual: RefType, expected: RefType) -> bool { + if actual.is_nullable() && !expected.is_nullable() { + return false; + } + match (actual.type_index(), expected.type_index()) { + (Some(actual), Some(expected)) => actual == expected, + (Some(_), None) => matches!(expected.abstract_heap_type(), Some(AbstractHeapType::Func)), + (None, Some(_)) => matches!(actual.abstract_heap_type(), Some(AbstractHeapType::NoFunc)), + (None, None) => { + actual.abstract_heap_type() == expected.abstract_heap_type() + || actual.is_func() && matches!(expected.abstract_heap_type(), Some(AbstractHeapType::Func)) + || actual.is_extern() && matches!(expected.abstract_heap_type(), Some(AbstractHeapType::Extern)) + || actual.is_exn() && matches!(expected.abstract_heap_type(), Some(AbstractHeapType::Exn)) + } + } + } + + fn value_subtype(actual: WasmType, expected: WasmType) -> bool { + match (actual, expected) { + (WasmType::Ref(actual), WasmType::Ref(expected)) => Self::ref_subtype(actual, expected), + _ => actual == expected, + } + } + + fn compare_table_types(import: &Import, actual: &TableType, expected: &TableType) -> Result<()> { Self::compare_types(import, &actual.arch(), &expected.arch())?; - Self::compare_types(import, &actual.element_type, &expected.element_type)?; - if actual.size_initial > expected.size_initial { + if !Self::ref_subtype(actual.element_type, expected.element_type) + || !Self::ref_subtype(expected.element_type, actual.element_type) + { + return Err(LinkingError::incompatible_import_type(import).into()); + } + if actual.size_initial < expected.size_initial { cold_path(); return Err(LinkingError::incompatible_import_type(import).into()); } - match (expected.size_max, actual.size_max) { - (None, Some(_)) => { - cold_path(); - Err(LinkingError::incompatible_import_type(import).into()) - } - (Some(expected_max), Some(actual_max)) if actual_max < expected_max => { + match expected.size_max { + Some(expected_max) if actual.size_max.is_none_or(|actual_max| actual_max > expected_max) => { cold_path(); Err(LinkingError::incompatible_import_type(import).into()) } @@ -202,7 +226,12 @@ impl Imports { Ok(()) } - pub(crate) fn link(&self, store: &mut crate::Store, module: &Module) -> Result { + pub(crate) fn link( + &self, + store: &mut crate::Store, + module: &Module, + type_addrs: &[TypeAddr], + ) -> Result { let (global_count, table_count, mem_count, func_count) = module.imports.iter().fold((0, 0, 0, 0), |(g, t, m, f), import| match import.kind { ImportKind::Global(_) => (g + 1, t, m, f), @@ -213,7 +242,7 @@ impl Imports { let mut imports = ResolvedImports { globals: Vec::with_capacity(global_count + module.globals.len()), - tables: Vec::with_capacity(table_count + module.table_types.len()), + tables: Vec::with_capacity(table_count + module.tables.len()), memories: Vec::with_capacity(mem_count + module.memory_types.len()), funcs: Vec::with_capacity(func_count + module.funcs.len()), }; @@ -224,7 +253,7 @@ impl Imports { Extern::Global(global) => (ExternVal::Global(global.0.addr), None), Extern::Table(table) => (ExternVal::Table(table.0.addr), None), Extern::Memory(memory) => (ExternVal::Memory(memory.0.addr), None), - Extern::Function(func) => (ExternVal::Func(func.addr), Some(func)), + Extern::Function(func) => (ExternVal::Func(func.addr()), Some(func)), } } else { let name = ExternName::from(import); @@ -244,14 +273,26 @@ impl Imports { match (val, &import.kind) { (ExternVal::Global(global_addr), ImportKind::Global(ty)) => { let global = store.state.get_global(global_addr); - Self::compare_types(import, &global.ty, ty)?; + let expected = ty.with_ty(crate::store::canonicalize_value_type(ty.ty, type_addrs)); + let compatible = global.ty.mutable == ty.mutable + && Self::value_subtype(global.ty.ty, expected.ty) + && (!ty.mutable || Self::value_subtype(expected.ty, global.ty.ty)); + if !compatible { + cold_path(); + return Err(LinkingError::incompatible_import_type(import).into()); + } imports.globals.push(global_addr); } (ExternVal::Table(table_addr), ImportKind::Table(ty)) => { let table = store.state.get_table(table_addr); let mut kind = table.kind; kind.size_initial = table.size() as u64; - Self::compare_table_types(import, &kind, ty)?; + let element_type = crate::store::canonicalize_ref_type(ty.element_type, type_addrs); + let expected = match ty.arch() { + MemoryArch::I32 => TableType::new(element_type, ty.size_initial, ty.size_max), + MemoryArch::I64 => TableType::new64(element_type, ty.size_initial, ty.size_max), + }; + Self::compare_table_types(import, &kind, &expected)?; imports.tables.push(table_addr); } (ExternVal::Memory(memory_addr), ImportKind::Memory(ty)) => { @@ -260,17 +301,15 @@ impl Imports { imports.memories.push(memory_addr); } (ExternVal::Func(func_addr), ImportKind::Function(ty)) => { - let import_func_type = module - .func_types - .get(*ty as usize) - .ok_or_else(|| LinkingError::incompatible_import_type(import))?; - let actual_ty = if let Some(func) = &func_handle { + let expected_type_addr = + type_addrs.get(*ty as usize).ok_or_else(|| LinkingError::incompatible_import_type(import))?; + if let Some(func) = &func_handle { func.item.validate_store(store)?; - &func.ty - } else { - store.state.get_func(func_addr).ty() - }; - Self::compare_types(import, actual_ty, import_func_type)?; + } + if store.state.get_func(func_addr).type_addr != *expected_type_addr { + cold_path(); + return Err(LinkingError::incompatible_import_type(import).into()); + } imports.funcs.push(func_addr); } _ => unreachable!("import kind checked above"), diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index a727638..ecfbacb 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -46,9 +46,8 @@ pub struct ModuleInstance(Rc); #[cfg_attr(feature = "debug", derive(Debug))] struct ModuleInstanceInner { store_id: usize, - idx: ModuleInstanceAddr, - types: Arc<[Arc]>, - func_type_idxs: Arc<[u32]>, + id: ModuleInstanceId, + type_addrs: Box<[TypeAddr]>, func_addrs: Box<[FuncAddr]>, table_addrs: Box<[TableAddr]>, mem_addrs: Box<[MemAddr]>, @@ -61,25 +60,8 @@ struct ModuleInstanceInner { impl ModuleInstance { #[inline] - pub(crate) fn idx(&self) -> ModuleInstanceAddr { - self.0.idx - } - - /// Type indices come from the module type section and are used by indirect calls. - #[inline] - pub(crate) fn func_type_by_type_index(&self, type_idx: u32) -> &Arc { - self.0.types.get(type_idx as usize).unwrap_or_else(|| unreachable!("invalid type index: {type_idx}")) - } - - /// Function indices need their own lookup because they are not type-section indices. - #[inline] - pub(crate) fn func_type_idx(&self, addr: FuncAddr) -> u32 { - *self.0.func_type_idxs.get(addr as usize).unwrap_or_else(|| unreachable!("invalid function address: {addr}")) - } - - #[inline] - pub(crate) fn func_addrs(&self) -> &[FuncAddr] { - &self.0.func_addrs + pub(crate) fn resolve_type_addr(&self, type_addr: TypeAddr) -> TypeAddr { + *self.0.type_addrs.get(type_addr as usize).unwrap_or_else(|| unreachable!("invalid type address: {type_addr}")) } /// resolve a function address to the global store address @@ -128,8 +110,8 @@ impl ModuleInstance { } /// Get the module instance's address - pub fn id(&self) -> ModuleInstanceAddr { - self.0.idx + pub fn id(&self) -> ModuleInstanceId { + self.0.id } /// Instantiate the module in the given store @@ -169,10 +151,14 @@ impl ModuleInstance { /// /// See pub fn instantiate_no_start(store: &mut Store, module: &Module, imports: Option) -> Result { - let idx = store.next_module_instance_idx(); - let mut addrs = imports.unwrap_or_default().link(store, module)?; - addrs.funcs.extend(store.init_funcs(&module.funcs, idx)); - addrs.tables.extend(store.init_tables(&module.table_types)?); + let type_addrs = store.register_module_types(&module.func_types); + let id = store.next_module_instance_id(); + let mut addrs = imports.unwrap_or_default().link(store, module, &type_addrs)?; + let local_type_addrs = module.func_type_idxs[addrs.funcs.len()..] + .iter() + .map(|&addr| type_addrs[addr as usize]) + .collect::>(); + addrs.funcs.extend(store.init_funcs(&module.funcs, id, &local_type_addrs)); match module.local_memory_allocation { LocalMemoryAllocation::Skip => { #[cfg(feature = "guest-debug")] @@ -186,7 +172,8 @@ impl ModuleInstance { } } - store.init_globals(&mut addrs.globals, &module.globals, &addrs.funcs)?; + store.init_globals(&mut addrs.globals, &module.globals, &addrs.funcs, &type_addrs)?; + addrs.tables.extend(store.init_tables(&module.tables, &addrs.globals, &addrs.funcs, &type_addrs)?); let (elem_addrs, elem_trapped) = store.init_elements(&addrs.tables, &addrs.funcs, &addrs.globals, &module.elements)?; let (data_addrs, data_trapped) = @@ -194,9 +181,8 @@ impl ModuleInstance { let instance = ModuleInstanceInner { store_id: store.id(), - idx, - types: module.func_types.clone(), - func_type_idxs: module.func_type_idxs.clone(), + id, + type_addrs, func_addrs: addrs.funcs.into_boxed_slice(), table_addrs: addrs.tables.into_boxed_slice(), mem_addrs: addrs.memories.into_boxed_slice(), @@ -264,9 +250,7 @@ impl ModuleInstance { let func_addr = self.resolve_func_addr(export.index); ExternItem::Func(Function { item: StoreItem::new(self.0.store_id, func_addr), - module_addr: self.id(), - addr: func_addr, - ty: self.func_type_by_type_index(self.func_type_idx(export.index)).clone(), + module_id: self.id(), }) } ExternalKind::Table => { @@ -325,14 +309,7 @@ impl ModuleInstance { pub fn extern_item(&self, name: &str) -> Result { match self.require_export(name)? { ExternVal::Func(addr) => { - let export = self.0.exports.iter().find(|e| e.name == name.into()); - let export = export.ok_or_else(|| Error::Other(format!("Export not found: {name}")))?; - Ok(ExternItem::Func(Function { - item: StoreItem::new(self.0.store_id, addr), - module_addr: self.id(), - addr, - ty: self.func_type_by_type_index(self.func_type_idx(export.index)).clone(), - })) + Ok(ExternItem::Func(Function { item: StoreItem::new(self.0.store_id, addr), module_id: self.id() })) } ExternVal::Memory(addr) => Ok(ExternItem::Memory(Memory(StoreItem::new(self.0.store_id, addr)))), ExternVal::Table(addr) => Ok(ExternItem::Table(Table(StoreItem::new(self.0.store_id, addr)))), @@ -373,12 +350,7 @@ impl ModuleInstance { return Err(Error::Other(format!("Export is not a function: {name}"))); }; - Ok(Function { - item: StoreItem::new(self.0.store_id, func_addr), - addr: func_addr, - module_addr: self.id(), - ty: store.state.get_func(func_addr).ty().clone(), - }) + Ok(Function { item: StoreItem::new(self.0.store_id, func_addr), module_id: self.id() }) } /// Get a function by its module-local index. @@ -393,13 +365,7 @@ impl ModuleInstance { self.validate_store(store)?; let func_addr = Self::index_addr(&self.0.func_addrs, func_index, "function")?; - let ty = store.state.get_func(func_addr).ty(); - Ok(Function { - item: StoreItem::new(self.0.store_id, func_addr), - addr: func_addr, - module_addr: self.id(), - ty: ty.clone(), - }) + Ok(Function { item: StoreItem::new(self.0.store_id, func_addr), module_id: self.id() }) } /// Get a typed function export by name. @@ -440,14 +406,9 @@ impl ModuleInstance { return Err(Error::Other(format!("Export is not a function: {name}"))); }; - let func = Function { - item: StoreItem::new(self.0.store_id, func_addr), - addr: func_addr, - module_addr: self.id(), - ty: store.state.get_func(func_addr).ty().clone(), - }; + let func = Function { item: StoreItem::new(self.0.store_id, func_addr), module_id: self.id() }; - Self::validate_typed_func::(&func, name)?; + Self::validate_typed_func::(store, &func, name)?; Ok(FunctionTyped { func, marker: core::marker::PhantomData }) } @@ -460,22 +421,27 @@ impl ModuleInstance { func_index: FuncAddr, ) -> Result> { let func = self.func_by_index(store, func_index)?; - Self::validate_typed_func::(&func, &format!("function index {func_index}"))?; + Self::validate_typed_func::(store, &func, &format!("function index {func_index}"))?; Ok(FunctionTyped { func, marker: core::marker::PhantomData }) } #[inline] - fn validate_typed_func(func: &Function, func_name: &str) -> Result<()> { + fn validate_typed_func( + store: &Store, + func: &Function, + func_name: &str, + ) -> Result<()> { let params = P::wasm_types(); let results = R::wasm_types(); - if func.ty.params() != params.as_ref() || func.ty.results() != results.as_ref() { + let ty = store.state.get_func_type(func.addr()); + if ty.params() != params.as_ref() || ty.results() != results.as_ref() { cold_path(); #[cfg(feature = "debug")] return Err(Error::Other(format!( "function type mismatch for {func_name}: expected {:?}, actual {:?}", FuncType::new(¶ms, &results), - func.ty + ty ))); #[cfg(not(feature = "debug"))] @@ -590,12 +556,7 @@ impl ModuleInstance { }; let func_addr = self.resolve_func_addr(func_addr); - Ok(Some(Function { - item: StoreItem::new(self.0.store_id, func_addr), - module_addr: self.id(), - addr: func_addr, - ty: store.state.get_func(func_addr).ty().clone(), - })) + Ok(Some(Function { item: StoreItem::new(self.0.store_id, func_addr), module_id: self.id() })) } /// Invoke the start function of the module diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index 739c136..5e78dae 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -225,14 +225,18 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { Call(v) => { self.exec_call_direct(*v)?; return Ok(None); } CallSelf => { self.exec_call_self()?; return Ok(None); } CallIndirect(ty, table) => { self.exec_call_indirect::(*ty, *table)?; return Ok(None); } - ReturnCall(v) => { self.exec_return_call_direct(*v)?; return Ok(None); } + CallRef(ty) => { self.exec_call_ref::(*ty)?; return Ok(None); } + ReturnCall(v) => { if self.exec_return_call_direct(*v)? { return Ok(Some(())); } return Ok(None); } ReturnCallSelf => { self.exec_return_call_self()?; return Ok(None); } - ReturnCallIndirect(ty, table) => { self.exec_call_indirect::(*ty, *table)?; return Ok(None); } + ReturnCallIndirect(ty, table) => { if self.exec_call_indirect::(*ty, *table)? { return Ok(Some(())); } return Ok(None); } + ReturnCallRef(ty) => { if self.exec_call_ref::(*ty)? { return Ok(Some(())); } return Ok(None); } Jump(ip) => { self.cf.instr_ptr = *ip as usize; return Ok(None); } JumpIfZero32(ip) => if self.exec_jump_zero_32(*ip) { return Ok(None) }, JumpIfNonZero32(ip) => if self.exec_jump_non_zero_32(*ip) { return Ok(None) }, JumpIfZero64(ip) => if self.exec_jump_zero_64(*ip) { return Ok(None) }, JumpIfNonZero64(ip) => if self.exec_jump_non_zero_64(*ip) { return Ok(None) }, + JumpIfRefNull(ip) => if self.exec_jump_ref_null(*ip) { return Ok(None) }, + JumpIfRefNonNull(ip) => if self.exec_jump_ref_non_null(*ip) { return Ok(None) }, JumpIfLocalZero32 { target_ip, local } => if self.exec_jump_local_zero_32(*target_ip, *local) { return Ok(None) }, JumpIfLocalNonZero32 { target_ip, local } => if self.exec_jump_local_non_zero_32(*target_ip, *local) { return Ok(None) }, JumpIfLocalZero64 { target_ip, local } => if self.exec_jump_local_zero_64(*target_ip, *local) { return Ok(None) }, @@ -443,6 +447,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { RefFunc(func_idx) => self.exec_const(ValueRef::from_addr(Some(self.module.resolve_func_addr(*func_idx))))?, RefNull(_) => self.exec_const(ValueRef::NULL)?, RefIsNull => self.exec_ref_is_null()?, + RefAsNonNull => self.exec_ref_as_non_null()?, MemorySize(addr) => self.exec_memory_size(*addr)?, MemoryGrow(addr) => self.exec_memory_grow(*addr)?, @@ -846,6 +851,24 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { self.jump_if(cond, target_ip) } + #[inline(always)] + fn exec_jump_ref_null(&mut self, target_ip: u32) -> bool { + let is_null = ValueRef::stack_peek(&self.store.value_stack).is_null(); + if is_null { + ValueRef::stack_pop(&mut self.store.value_stack); + } + self.jump_if(is_null, target_ip) + } + + #[inline(always)] + fn exec_jump_ref_non_null(&mut self, target_ip: u32) -> bool { + let is_non_null = !ValueRef::stack_peek(&self.store.value_stack).is_null(); + if !is_non_null { + ValueRef::stack_pop(&mut self.store.value_stack); + } + self.jump_if(is_non_null, target_ip) + } + #[inline(always)] fn exec_jump_local_zero_32(&mut self, target_ip: u32, local: LocalAddr) -> bool { self.jump_if(Value32::local_get(&self.store.value_stack, &self.cf, local) == 0, target_ip) @@ -934,7 +957,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { self.store.call_stack.push(self.cf)?; self.cf = CallFrame::new(func_addr, locals_base, wasm_func.func.locals); - if wasm_func.owner != self.module.idx() { + if wasm_func.owner != self.module.id() { self.module = self.store.get_module_instance_internal(wasm_func.owner); } @@ -953,17 +976,23 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { return Err(Trap::CallStackOverflow); }; self.cf = CallFrame::new(func_addr, locals_base, wasm_func.func.locals); - if wasm_func.owner != self.module.idx() { + if wasm_func.owner != self.module.id() { self.module = self.store.get_module_instance_internal(wasm_func.owner); } Ok(()) } - fn exec_call_host(&mut self, host_func: Rc) -> Result<(), Trap> { - let mut params = self.store.value_stack.pop_types(host_func.ty.params().iter().rev()).collect::>(); + fn exec_call_host( + &mut self, + host_func: Rc, + type_addr: TypeAddr, + ) -> Result { + let ty = self.store.state.get_type(type_addr); + let mut params = self.store.value_stack.pop_types(ty.params().iter().rev()).collect::>(); params.reverse(); - let res = match host_func.call(FuncContext { store: self.store, module_addr: self.module.idx() }, ¶ms) { + let result = host_func.call(FuncContext { store: self.store, module_id: self.module.id() }, ¶ms); + let res = match result.and_then(|result| crate::func::validate_host_results(self.store, type_addr, result)) { Ok(res) => res, Err(err) => { cold_path(); @@ -972,25 +1001,37 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { }; self.store.value_stack.extend_from_wasmvalues(&res)?; - self.cf.instr_ptr += 1; - Ok(()) + if TAIL { + Ok(self.exec_return()) + } else { + self.cf.instr_ptr += 1; + Ok(false) + } } fn exec_call_direct(&mut self, v: u32) -> Result<(), Trap> { self.charge_call_fuel(FUEL_COST_CALL_TOTAL); let addr = self.module.resolve_func_addr(v); - match self.store.state.get_func(addr) { - crate::FunctionInstance::Wasm(wasm_func) => self.exec_call(wasm_func.clone(), addr), - crate::FunctionInstance::Host(host_func) => self.exec_call_host(host_func.clone()), + let func = self.store.state.get_func(addr).clone(); + match func.kind { + crate::store::FunctionKind::Wasm(wasm_func) => self.exec_call(wasm_func, addr), + crate::store::FunctionKind::Host(host_func) => { + self.exec_call_host::(host_func, func.type_addr)?; + Ok(()) + } } } - fn exec_return_call_direct(&mut self, v: u32) -> Result<(), Trap> { + fn exec_return_call_direct(&mut self, v: u32) -> Result { self.charge_call_fuel(FUEL_COST_CALL_TOTAL); let addr = self.module.resolve_func_addr(v); - match self.store.state.get_func(addr) { - crate::FunctionInstance::Wasm(wasm_func) => self.exec_return_call(wasm_func.clone(), addr), - crate::FunctionInstance::Host(host_func) => self.exec_call_host(host_func.clone()), + let func = self.store.state.get_func(addr).clone(); + match func.kind { + crate::store::FunctionKind::Wasm(wasm_func) => { + self.exec_return_call(wasm_func, addr)?; + Ok(false) + } + crate::store::FunctionKind::Host(host_func) => self.exec_call_host::(host_func, func.type_addr), } } @@ -1019,14 +1060,18 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { Ok(()) } - fn exec_call_indirect(&mut self, type_addr: u32, table_addr: u32) -> Result<(), Trap> { + fn exec_call_indirect( + &mut self, + type_addr: u32, + table_addr: u32, + ) -> Result { self.charge_call_fuel(FUEL_COST_CALL_TOTAL); // verify that the table is of the right type, this should be validated by the parser already let table_addr = self.module.resolve_table_addr(table_addr); let table_idx = self.pop_table_operand(self.store.state.get_table(table_addr).kind.arch())?; let table = self.store.state.get_table(table_addr); - debug_assert!(table.kind.element_type == WasmType::RefFunc, "table is not of type funcref"); + debug_assert!(table.kind.element_type.is_func(), "table is not of type funcref"); let Ok(table) = table.get(table_idx) else { cold_path(); @@ -1038,34 +1083,43 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { return Err(Trap::UninitializedElement { index: table_idx }); }; - let call_ty = self.module.func_type_by_type_index(type_addr); - match self.store.state.get_func(func_ref) { - crate::FunctionInstance::Wasm(wasm_func) => { - if wasm_func.ty() != call_ty { - cold_path(); - return Err(Trap::IndirectCallTypeMismatch { - actual: wasm_func.ty().clone(), - expected: call_ty.clone(), - }); - } + self.exec_typed_call::(func_ref, self.module.resolve_type_addr(type_addr)) + } - match IS_RETURN_CALL { - true => self.exec_return_call(wasm_func.clone(), func_ref), - false => self.exec_call(wasm_func.clone(), func_ref), - } + fn exec_typed_call( + &mut self, + func_addr: FuncAddr, + expected_type_addr: TypeAddr, + ) -> Result { + let func = self.store.state.get_func(func_addr).clone(); + if func.type_addr != expected_type_addr { + cold_path(); + return Err(Trap::IndirectCallTypeMismatch { + actual: Box::new(self.store.state.get_type(func.type_addr).clone()), + expected: Box::new(self.store.state.get_type(expected_type_addr).clone()), + }); + } + match func.kind { + crate::store::FunctionKind::Wasm(wasm_func) => match IS_RETURN_CALL { + true => self.exec_return_call(wasm_func, func_addr), + false => self.exec_call(wasm_func, func_addr), + }, + crate::store::FunctionKind::Host(host_func) => { + return self.exec_call_host::(host_func, func.type_addr); } - crate::FunctionInstance::Host(host_func) => { - if host_func.ty != *call_ty { - cold_path(); - return Err(Trap::IndirectCallTypeMismatch { - actual: host_func.ty.clone(), - expected: call_ty.clone(), - }); - } + }?; + Ok(false) + } - self.exec_call_host(host_func.clone()) - } - } + fn exec_call_ref(&mut self, type_addr: u32) -> Result { + self.charge_call_fuel(FUEL_COST_CALL_TOTAL); + let func_ref = ValueRef::stack_pop(&mut self.store.value_stack); + let Some(func_addr) = func_ref.addr() else { + cold_path(); + return Err(Trap::NullFunctionReference); + }; + + self.exec_typed_call::(func_addr, self.module.resolve_type_addr(type_addr)) } fn exec_return(&mut self) -> bool { @@ -1084,7 +1138,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { } let wasm_func = self.store.state.get_wasm_func(caller.func_addr); self.func = wasm_func.func.clone(); - if wasm_func.owner != self.module.idx() { + if wasm_func.owner != self.module.id() { self.module = self.store.get_module_instance_internal(wasm_func.owner); } self.cf = caller; @@ -1250,7 +1304,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { let raw = ::stack_pop(&mut self.store.value_stack); let value = match self.store.state.get_global(global_addr).ty.ty { WasmType::I32 | WasmType::F32 => TinyWasmValue::Value32(raw), - WasmType::RefExtern | WasmType::RefFunc => TinyWasmValue::ValueRef(ValueRef::from_raw(raw)), + WasmType::Ref(_) => TinyWasmValue::ValueRef(ValueRef::from_raw(raw)), WasmType::I64 | WasmType::F64 | WasmType::V128 => unreachable!("invalid global.set.32 target type"), }; self.store.state.set_global_val(global_addr, value); @@ -1265,6 +1319,14 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { self.store.value_stack.push::(is_null) } + fn exec_ref_as_non_null(&mut self) -> Result<(), Trap> { + if ValueRef::stack_peek(&self.store.value_stack).is_null() { + cold_path(); + return Err(Trap::NullReference); + } + Ok(()) + } + fn exec_memory_size(&mut self, addr: u32) -> Result<(), Trap> { let mem = self.store.state.get_mem(self.module.resolve_mem_addr(addr)); match mem.is_64bit() { @@ -1533,7 +1595,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { let n = self.pop_table_operand(arch)?; let val = ::stack_pop(&mut self.store.value_stack); let i = self.pop_table_operand(arch)?; - self.store.state.get_table_mut(table_addr).fill(self.module.func_addrs(), i, n, val.addr().into()) + self.store.state.get_table_mut(table_addr).fill(i, n, val.addr().into()) } fn pop_table_operand(&mut self, arch: MemoryArch) -> Result { diff --git a/crates/tinywasm/src/interpreter/stack/value_stack.rs b/crates/tinywasm/src/interpreter/stack/value_stack.rs index 2049f8d..853c5e4 100644 --- a/crates/tinywasm/src/interpreter/stack/value_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/value_stack.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; use core::hint::cold_path; -use tinywasm_types::{ExternRef, FuncRef, ValueCounts, WasmType, WasmValue}; +use tinywasm_types::{ValueCounts, WasmType, WasmValue}; use super::StackBase; use crate::engine::{Config, StackConfig}; @@ -252,8 +252,7 @@ impl ValueStack { WasmType::I64 => WasmValue::I64(i64::stack_pop(self)), WasmType::F32 => WasmValue::F32(f32::stack_pop(self)), WasmType::F64 => WasmValue::F64(f64::stack_pop(self)), - WasmType::RefExtern => WasmValue::RefExtern(ExternRef::from_raw(ValueRef::stack_pop(self).raw())), - WasmType::RefFunc => WasmValue::RefFunc(FuncRef::from_raw(ValueRef::stack_pop(self).raw())), + WasmType::Ref(_) => TinyWasmValue::ValueRef(ValueRef::stack_pop(self)).attach_type(val_type).unwrap(), WasmType::V128 => WasmValue::V128(Value128::stack_pop(self).0), } } @@ -265,8 +264,9 @@ impl ValueStack { WasmValue::I64(v) => self.stack_64.push(*v as u64)?, WasmValue::F32(v) => self.stack_32.push(v.to_bits())?, WasmValue::F64(v) => self.stack_64.push(v.to_bits())?, - WasmValue::RefExtern(v) => self.stack_32.push(v.raw())?, - WasmValue::RefFunc(v) => self.stack_32.push(v.raw())?, + WasmValue::Ref(v) => { + self.stack_32.push(TinyWasmValue::from(WasmValue::Ref(*v)).as_ref().unwrap().raw())? + } WasmValue::V128(v) => self.stack_128.push((*v).into())?, } } diff --git a/crates/tinywasm/src/interpreter/values.rs b/crates/tinywasm/src/interpreter/values.rs index 11bc6c6..be2e0a1 100644 --- a/crates/tinywasm/src/interpreter/values.rs +++ b/crates/tinywasm/src/interpreter/values.rs @@ -1,6 +1,6 @@ use super::stack::{CallFrame, ValueStack}; use crate::{Result, interpreter::simd::Value128}; -use tinywasm_types::{ExternRef, FuncRef, LocalAddr, WasmType, WasmValue}; +use tinywasm_types::{AnyRef, ExnRef, ExternRef, FuncRef, LocalAddr, RefValue, WasmType, WasmValue}; pub(crate) type Value32 = u32; pub(crate) type Value64 = u64; @@ -8,6 +8,8 @@ pub(crate) type Value64 = u64; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) struct ValueRef(u32); +// TODO(wasm3): GC references need a tagged representation; this raw address is only sufficient for current refs. + impl Default for ValueRef { fn default() -> Self { Self::NULL @@ -101,12 +103,21 @@ impl TinyWasmValue { (Self::Value64(v), WasmType::I64) => Some(WasmValue::I64(v as i64)), (Self::Value32(v), WasmType::F32) => Some(WasmValue::F32(f32::from_bits(v))), (Self::Value64(v), WasmType::F64) => Some(WasmValue::F64(f64::from_bits(v))), - (Self::ValueRef(v), WasmType::RefExtern) => Some(WasmValue::RefExtern(ExternRef::from_raw(v.raw()))), - (Self::ValueRef(v), WasmType::RefFunc) => Some(WasmValue::RefFunc(FuncRef::from_raw(v.raw()))), + (Self::ValueRef(v), WasmType::Ref(_)) if v.is_null() => Some(RefValue::Null.into()), + (Self::ValueRef(v), WasmType::Ref(ty)) if ty.is_func() => { + Some(WasmValue::Ref(RefValue::Func(FuncRef::new(v.raw())))) + } + (Self::ValueRef(v), WasmType::Ref(ty)) if ty.is_extern() => { + Some(WasmValue::Ref(RefValue::Extern(ExternRef::new(v.raw())))) + } + (Self::ValueRef(v), WasmType::Ref(ty)) if ty.is_exn() => { + Some(WasmValue::Ref(RefValue::Exn(ExnRef::new(v.raw())))) + } + (Self::ValueRef(v), WasmType::Ref(_)) => Some(WasmValue::Ref(RefValue::Any(AnyRef::from_raw(v.raw())))), (Self::Value128(v), WasmType::V128) => Some(WasmValue::V128(v.0)), (_, WasmType::I32 | WasmType::F32) => None, (_, WasmType::I64 | WasmType::F64) => None, - (_, WasmType::RefExtern | WasmType::RefFunc) => None, + (_, WasmType::Ref(_)) => None, (_, WasmType::V128) => None, } } @@ -119,8 +130,7 @@ impl From<&WasmValue> for TinyWasmValue { WasmValue::I64(v) => Self::Value64(*v as u64), WasmValue::F32(v) => Self::Value32(v.to_bits()), WasmValue::F64(v) => Self::Value64(v.to_bits()), - WasmValue::RefExtern(v) => Self::ValueRef(ValueRef::from_addr(v.addr())), - WasmValue::RefFunc(v) => Self::ValueRef(ValueRef::from_addr(v.addr())), + WasmValue::Ref(value) => Self::ValueRef(ValueRef::from_addr(value.raw())), WasmValue::V128(v) => Self::Value128((*v).into()), } } diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs index 004939f..dc54736 100644 --- a/crates/tinywasm/src/reference.rs +++ b/crates/tinywasm/src/reference.rs @@ -7,9 +7,7 @@ use alloc::vec::Vec; use crate::store::{GlobalInstance, TableElement, TableInstance}; use crate::{Error, MemoryInstance, Result, Store, Trap}; -use tinywasm_types::{ - Addr, ExternRef, FuncRef, GlobalAddr, GlobalType, MemAddr, MemoryType, TableAddr, TableType, WasmType, WasmValue, -}; +use tinywasm_types::{Addr, GlobalAddr, GlobalType, MemAddr, MemoryType, TableAddr, TableType, WasmType, WasmValue}; #[derive(Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "debug", derive(Debug))] @@ -344,30 +342,25 @@ impl Memory { } } -fn table_element_to_value(element_type: WasmType, element: TableElement) -> WasmValue { - match element_type { - WasmType::RefFunc => WasmValue::RefFunc(FuncRef::new(element.addr())), - WasmType::RefExtern => WasmValue::RefExtern(ExternRef::new(element.addr())), - _ => unreachable!("table element type must be a reference type"), - } -} - -fn table_value_to_element(element_type: WasmType, value: WasmValue) -> Result { - match (element_type, value) { - (WasmType::RefFunc, WasmValue::RefFunc(func_ref)) => Ok(TableElement::from(func_ref.addr())), - (WasmType::RefExtern, WasmValue::RefExtern(extern_ref)) => Ok(TableElement::from(extern_ref.addr())), - _ => Err(Trap::Other("invalid table value type")), +fn table_value_to_element( + state: &crate::store::State, + element_type: tinywasm_types::RefType, + value: WasmValue, +) -> Result { + if !state.value_matches_type(value, WasmType::Ref(element_type)) { + return Err(Trap::Other("invalid table value type")); } + let WasmValue::Ref(value) = value else { unreachable!() }; + Ok(TableElement::from(value.raw())) } impl Table { /// Create a new table in the given store. pub fn new(store: &mut Store, ty: TableType, init: WasmValue) -> Result { - let init = match (ty.element_type, init) { - (WasmType::RefFunc, WasmValue::RefFunc(func_ref)) => TableElement::from(func_ref.addr()), - (WasmType::RefExtern, WasmValue::RefExtern(extern_ref)) => TableElement::from(extern_ref.addr()), - _ => return Err(Error::other("invalid table init value")), - }; + if ty.element_type.is_concrete() { + return Err(Error::other("host tables cannot use module-relative concrete reference types")); + } + let init = table_value_to_element(&store.state, ty.element_type, init).map_err(Error::from)?; let addr = store.state.tables.len() as TableAddr; store.state.tables.push(TableInstance::new_with_init(ty, init)?); Ok(Self(StoreItem::new(store.id(), addr))) @@ -408,16 +401,17 @@ impl Table { Ok(elements .iter() .copied() - .map(move |element| table_element_to_value(element_type, element)) + .map(move |element| element.to_wasm_value(element_type)) .collect::>() .into_iter()) } /// Set a table element. pub fn set(&self, store: &mut Store, index: TableAddr, value: WasmValue) -> Result<(), Trap> { - let table = self.instance_mut(store)?; - let value = table_value_to_element(table.kind.element_type, value)?; - table.set(index as usize, value) + self.0.validate_store(store)?; + let element_type = store.state.get_table(self.0.addr).kind.element_type; + let value = table_value_to_element(&store.state, element_type, value)?; + store.state.get_table_mut(self.0.addr).set(index as usize, value) } /// Copy elements within the same table. @@ -428,11 +422,11 @@ impl Table { /// Grow the table and return the previous size. pub fn grow(&self, store: &mut Store, delta: i32, init: WasmValue) -> Result { self.0.validate_store(store)?; - let table = store.state.get_table_mut(self.0.addr); + let table = store.state.get_table(self.0.addr); let old_size = table.size(); - let init = table_value_to_element(table.kind.element_type, init)?; + let init = table_value_to_element(&store.state, table.kind.element_type, init)?; let delta = usize::try_from(delta).map_err(|_| Trap::TableOutOfBounds { offset: 0, len: 1, max: old_size })?; - table.grow(delta, init)?; + store.state.get_table_mut(self.0.addr).grow(delta, init)?; Ok(old_size) } } @@ -440,7 +434,10 @@ impl Table { impl Global { /// Create a new global in the given store. pub fn new(store: &mut Store, ty: GlobalType, value: WasmValue) -> Result { - if WasmType::from(value) != ty.ty { + if matches!(ty.ty, WasmType::Ref(ty) if ty.is_concrete()) { + return Err(Error::other("host globals cannot use module-relative concrete reference types")); + } + if !store.state.value_matches_type(value, ty.ty) { cold_path(); return Err(Error::Other("invalid global value type".to_string())); } @@ -455,12 +452,6 @@ impl Global { Ok(store.state.get_global(self.0.addr)) } - #[inline] - fn instance_mut<'a>(&self, store: &'a mut Store) -> Result<&'a mut GlobalInstance> { - self.0.validate_store(store)?; - Ok(store.state.get_global_mut(self.0.addr)) - } - /// Get the type of the global. pub fn ty(&self, store: &Store) -> Result { Ok(self.instance(store)?.ty) @@ -475,16 +466,17 @@ impl Global { /// Set the current value of the global. pub fn set(&self, store: &mut Store, value: WasmValue) -> Result<()> { - let global = self.instance_mut(store)?; + self.0.validate_store(store)?; + let global = store.state.get_global(self.0.addr); if !global.ty.mutable { cold_path(); return Err(Error::Other("global is immutable".to_string())); } - if WasmType::from(value) != global.ty.ty { + if !store.state.value_matches_type(value, global.ty.ty) { cold_path(); return Err(Error::Other("invalid global value type".to_string())); } - global.value = value.into(); + store.state.get_global_mut(self.0.addr).value = value.into(); Ok(()) } } diff --git a/crates/tinywasm/src/store/function.rs b/crates/tinywasm/src/store/function.rs index 5f5a90e..f0e3ed9 100644 --- a/crates/tinywasm/src/store/function.rs +++ b/crates/tinywasm/src/store/function.rs @@ -8,7 +8,14 @@ use crate::func::HostFunction; /// See #[derive(Clone)] #[cfg_attr(feature = "debug", derive(Debug))] -pub(crate) enum FunctionInstance { +pub(crate) struct FunctionInstance { + pub(crate) type_addr: TypeAddr, + pub(crate) kind: FunctionKind, +} + +#[derive(Clone)] +#[cfg_attr(feature = "debug", derive(Debug))] +pub(crate) enum FunctionKind { /// A host function Host(Rc), @@ -16,26 +23,9 @@ pub(crate) enum FunctionInstance { Wasm(WasmFunctionInstance), } -impl FunctionInstance { - #[inline] - pub(crate) fn ty(&self) -> &Arc { - match self { - Self::Host(f) => &f.ty, - Self::Wasm(f) => f.ty(), - } - } -} - #[derive(Clone)] #[cfg_attr(feature = "debug", derive(Debug))] pub(crate) struct WasmFunctionInstance { pub(crate) func: Arc, - pub(crate) owner: ModuleInstanceAddr, -} - -impl WasmFunctionInstance { - #[inline] - pub(crate) fn ty(&self) -> &Arc { - &self.func.ty - } + pub(crate) owner: ModuleInstanceId, } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index 61b09ef..517a561 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -22,6 +22,20 @@ pub(crate) use {data::*, element::*, function::*, global::*, table::*}; // global store id counter static STORE_ID: AtomicUsize = AtomicUsize::new(0); +pub(crate) fn canonicalize_ref_type(ty: RefType, type_addrs: &[TypeAddr]) -> RefType { + let Some(type_addr) = ty.type_index() else { return ty }; + let canonical = + *type_addrs.get(type_addr as usize).unwrap_or_else(|| unreachable!("invalid type address: {type_addr}")); + RefType::new_concrete(ty.is_nullable(), canonical).unwrap() +} + +pub(crate) fn canonicalize_value_type(ty: WasmType, type_addrs: &[TypeAddr]) -> WasmType { + match ty { + WasmType::Ref(ty) => WasmType::Ref(canonicalize_ref_type(ty, type_addrs)), + ty => ty, + } +} + /// Global state that can be manipulated by WebAssembly programs /// /// Note that the state doesn't do any garbage collection - so it will grow @@ -79,16 +93,13 @@ impl Store { } /// Get a module instance by the internal id - pub fn get_module_instance(&self, addr: ModuleInstanceAddr) -> Option { - self.module_instances.get(addr as usize).cloned() + pub fn get_module_instance(&self, id: ModuleInstanceId) -> Option { + self.module_instances.get(id as usize).cloned() } #[inline] - pub(crate) fn get_module_instance_internal(&self, addr: ModuleInstanceAddr) -> ModuleInstance { - self.module_instances - .get(addr as usize) - .unwrap_or_else(|| unreachable!("invalid module instance: {addr}")) - .clone() + pub(crate) fn get_module_instance_internal(&self, id: ModuleInstanceId) -> ModuleInstance { + self.module_instances.get(id as usize).unwrap_or_else(|| unreachable!("invalid module instance: {id}")).clone() } pub(crate) fn enter_execution(&mut self) -> Result<()> { @@ -125,6 +136,8 @@ impl Default for Store { /// Data should only be addressable by the module that owns it /// See pub(crate) struct State { + // Concrete type indexes in store instances address this canonical type space. + canonical_types: Vec, pub(crate) funcs: Vec, pub(crate) tables: Vec, pub(crate) memories: Vec, @@ -134,6 +147,30 @@ pub(crate) struct State { } impl State { + pub(crate) fn value_matches_type(&self, value: WasmValue, expected: WasmType) -> bool { + match (value, expected) { + (WasmValue::Ref(RefValue::Null), WasmType::Ref(expected)) => expected.is_nullable(), + (WasmValue::Ref(RefValue::Func(func)), WasmType::Ref(expected)) => { + self.funcs.get(func.addr() as usize).is_some_and(|func| match expected.type_index() { + Some(expected) => func.type_addr == expected, + None => matches!(expected.abstract_heap_type(), Some(AbstractHeapType::Func)), + }) + } + (_, WasmType::Ref(expected)) if expected.is_concrete() => false, + _ => value.matches_type(expected), + } + } + + #[inline] + pub(crate) fn get_func_type(&self, addr: FuncAddr) -> &FuncType { + let type_addr = self.get_func(addr).type_addr; + Self::get(&self.canonical_types, type_addr, "canonical type") + } + + #[inline] + pub(crate) fn get_type(&self, addr: TypeAddr) -> &FuncType { + Self::get(&self.canonical_types, addr, "canonical type") + } fn get<'a, T>(items: &'a [T], addr: Addr, kind: &str) -> &'a T { items.get(addr as usize).unwrap_or_else(|| unreachable!("invalid {kind} address: {addr}")) } @@ -156,8 +193,8 @@ impl State { /// Get a wasm function at the actual index in the store, panicking if it's a host function (which should be guaranteed by the validator) pub(crate) fn get_wasm_func(&self, addr: FuncAddr) -> &WasmFunctionInstance { - match self.funcs.get(addr as usize) { - Some(FunctionInstance::Wasm(wasm_func)) => wasm_func, + match self.funcs.get(addr as usize).map(|func| &func.kind) { + Some(FunctionKind::Wasm(wasm_func)) => wasm_func, _ => unreachable!("invalid wasm function address: {addr}"), } } @@ -233,12 +270,12 @@ impl Store { self.id } - pub(crate) fn next_module_instance_idx(&self) -> ModuleInstanceAddr { - self.module_instances.len() as ModuleInstanceAddr + pub(crate) fn next_module_instance_id(&self) -> ModuleInstanceId { + self.module_instances.len() as ModuleInstanceId } pub(crate) fn add_instance(&mut self, instance: ModuleInstance) { - debug_assert!(instance.idx() == self.module_instances.len() as ModuleInstanceAddr); + debug_assert!(instance.id() == self.module_instances.len() as ModuleInstanceId); self.module_instances.push(instance); } @@ -257,25 +294,94 @@ impl Store { // Linking related functions impl Store { + pub(crate) fn register_module_types(&mut self, types: &[FuncType]) -> Box<[TypeAddr]> { + let mut type_addrs = Vec::with_capacity(types.len()); + for (local_addr, ty) in types.iter().enumerate() { + if let Some(addr) = self + .state + .canonical_types + .iter() + .position(|registered| ty.equivalent(types, registered, &self.state.canonical_types)) + { + type_addrs.push(addr as TypeAddr); + continue; + } + + let addr = self.state.canonical_types.len(); + assert!(addr <= ((1 << 30) - 1), "too many canonical function types"); + type_addrs.push(addr as TypeAddr); + let canonicalize = |ty: WasmType| match ty { + WasmType::Ref(ty) if ty.is_concrete() => { + let module_ref = ty.type_index().unwrap() as usize; + // A singleton recursive group can refer to the type currently being registered. + let canonical = if module_ref == local_addr { + addr as TypeAddr + } else { + *type_addrs + .get(module_ref) + .unwrap_or_else(|| unreachable!("invalid forward type reference: {module_ref}")) + }; + WasmType::Ref(RefType::new_concrete(ty.is_nullable(), canonical).unwrap()) + } + ty => ty, + }; + let params = ty.params().iter().copied().map(canonicalize).collect::>(); + let results = ty.results().iter().copied().map(canonicalize).collect::>(); + self.state.canonical_types.push(FuncType::new(¶ms, &results)); + } + type_addrs.into_boxed_slice() + } + + pub(crate) fn register_host_type(&mut self, ty: &FuncType) -> TypeAddr { + if let Some(addr) = self.state.canonical_types.iter().position(|registered| ty == registered) { + return addr as TypeAddr; + } + let addr = self.state.canonical_types.len(); + assert!(addr <= ((1 << 30) - 1), "too many canonical function types"); + self.state.canonical_types.push(ty.clone()); + addr as TypeAddr + } + /// Add functions to the store, returning their addresses in the store pub(crate) fn init_funcs( &mut self, funcs: &[Arc], - idx: ModuleInstanceAddr, + owner: ModuleInstanceId, + type_addrs: &[TypeAddr], ) -> impl ExactSizeIterator { let start = self.state.funcs.len() as FuncAddr; - self.state.funcs.extend( - funcs.iter().map(|func| FunctionInstance::Wasm(WasmFunctionInstance { func: func.clone(), owner: idx })), - ); + debug_assert_eq!(funcs.len(), type_addrs.len()); + self.state.funcs.extend(funcs.iter().zip(type_addrs).map(|(func, &type_addr)| FunctionInstance { + type_addr, + kind: FunctionKind::Wasm(WasmFunctionInstance { func: func.clone(), owner }), + })); start..start + funcs.len() as FuncAddr } /// Add tables to the store, returning their addresses in the store - pub(crate) fn init_tables(&mut self, tables: &[TableType]) -> Result> { + pub(crate) fn init_tables( + &mut self, + tables: &[TableDefinition], + global_addrs: &[GlobalAddr], + func_addrs: &[FuncAddr], + type_addrs: &[TypeAddr], + ) -> Result> { let start = self.state.tables.len() as TableAddr; self.state.tables.reserve_exact(tables.len()); - for &table in tables { - self.state.tables.push(TableInstance::new(table)?); + for table in tables { + let init = match &table.init { + Some(expr) => match self.eval_const(expr, global_addrs, func_addrs)? { + TinyWasmValue::ValueRef(value) => TableElement::from(value.addr()), + _ => return Err(Error::other("table initializer is not a reference value")), + }, + None => TableElement::Uninitialized, + }; + let element_type = canonicalize_ref_type(table.ty.element_type, type_addrs); + let ty = match table.ty.arch() { + MemoryArch::I32 => TableType::new(element_type, table.ty.size_initial, table.ty.size_max), + MemoryArch::I64 => TableType::new64(element_type, table.ty.size_initial, table.ty.size_max), + }; + self.state.tables.push(TableInstance::new_with_init(ty, init)?); } Ok(start..start + tables.len() as TableAddr) } @@ -305,6 +411,7 @@ impl Store { out: &mut Vec, globals: &[Global], func_addrs: &[FuncAddr], + type_addrs: &[TypeAddr], ) -> Result<()> { let start = self.state.globals.len() as Addr; out.extend(start..start + globals.len() as Addr); @@ -317,7 +424,8 @@ impl Store { return Err(e); } }; - self.state.globals.push(GlobalInstance::new(global.ty, value)); + let ty = global.ty.with_ty(canonicalize_value_type(global.ty.ty, type_addrs)); + self.state.globals.push(GlobalInstance::new(ty, value)); } Ok(()) @@ -524,9 +632,11 @@ impl Store { I64Const(i) => (*i).into(), V128Const(i) => (*i).into(), GlobalGet(addr) => resolve_global(*addr)?, - RefFunc(None) => TinyWasmValue::ValueRef(ValueRef::NULL), - RefExtern(None) => TinyWasmValue::ValueRef(ValueRef::NULL), - RefFunc(Some(idx)) => TinyWasmValue::ValueRef(ValueRef::from_addr(Some(resolve_func(*idx)?))), + Ref(tinywasm_types::RefValue::Null) => TinyWasmValue::ValueRef(ValueRef::NULL), + Ref(tinywasm_types::RefValue::Func(func)) => { + TinyWasmValue::ValueRef(ValueRef::from_raw(resolve_func(func.addr())?)) + } + Ref(_) => return Err(Error::other("unsupported reference constant")), _ => { cold_path(); return Err(Error::other("unsupported const instruction")); @@ -545,13 +655,13 @@ impl Store { F64Const(f) => stack.push(TinyWasmValue::Value64(f.to_bits())), V128Const(i) => stack.push(TinyWasmValue::Value128((*i).into())), GlobalGet(addr) => stack.push(resolve_global(*addr)?), - RefFunc(None) | RefExtern(None) => stack.push(TinyWasmValue::ValueRef(ValueRef::NULL)), - RefFunc(Some(idx)) => { - stack.push(TinyWasmValue::ValueRef(ValueRef::from_addr(Some(resolve_func(*idx)?)))) + Ref(tinywasm_types::RefValue::Null) => stack.push(TinyWasmValue::ValueRef(ValueRef::NULL)), + Ref(tinywasm_types::RefValue::Func(func)) => { + stack.push(TinyWasmValue::ValueRef(ValueRef::from_raw(resolve_func(func.addr())?))) } - RefExtern(Some(_)) => { + Ref(_) => { cold_path(); - return Err(Error::other("ref.extern constants are not supported in init expressions")); + return Err(Error::other("unsupported reference constant")); } I32Add | I32Sub | I32Mul => { let rhs = stack.pop().ok_or_else(|| Error::other("const stack underflow"))?; diff --git a/crates/tinywasm/src/store/table.rs b/crates/tinywasm/src/store/table.rs index d5f1019..68ed99f 100644 --- a/crates/tinywasm/src/store/table.rs +++ b/crates/tinywasm/src/store/table.rs @@ -15,6 +15,7 @@ pub(crate) struct TableInstance { } impl TableInstance { + #[cfg(test)] pub(crate) fn new(kind: TableType) -> Result { Self::new_with_init(kind, TableElement::Uninitialized) } @@ -45,17 +46,10 @@ impl TableInstance { } pub(crate) fn get_wasm_val(&self, addr: usize) -> Result { - let val = self.get(addr)?.addr(); - - Ok(match self.kind.element_type { - WasmType::RefFunc => WasmValue::RefFunc(FuncRef::new(val)), - WasmType::RefExtern => WasmValue::RefExtern(ExternRef::new(val)), - _ => Err(Trap::Other("non-ref table"))?, - }) + Ok(self.get(addr)?.to_wasm_value(self.kind.element_type)) } - pub(crate) fn fill(&mut self, func_addrs: &[u32], addr: usize, len: usize, val: TableElement) -> Result<(), Trap> { - let val = val.map(|addr| self.resolve_func_ref(func_addrs, addr)); + pub(crate) fn fill(&mut self, addr: usize, len: usize, val: TableElement) -> Result<(), Trap> { let range = self.checked_range(addr, len)?; self.elements[range].fill(val); Ok(()) @@ -105,16 +99,6 @@ impl TableInstance { self.elements.len() } - fn resolve_func_ref(&self, func_addrs: &[u32], addr: Addr) -> Addr { - if self.kind.element_type != WasmType::RefFunc { - return addr; - } - - *func_addrs - .get(addr as usize) - .expect("error initializing table: function not found. This should have been caught by the validator") - } - pub(crate) fn init(&mut self, offset: usize, init: &[TableElement]) -> Result<(), Trap> { let range = self.checked_range(offset, init.len())?; self.elements[range].copy_from_slice(init); @@ -146,11 +130,18 @@ impl TableElement { } } - pub(crate) fn map(self, f: impl FnOnce(Addr) -> Addr) -> Self { - match self { - Self::Uninitialized => Self::Uninitialized, - Self::Initialized(addr) => Self::Initialized(f(addr)), - } + pub(crate) fn to_wasm_value(self, ty: RefType) -> WasmValue { + let Some(addr) = self.addr() else { return RefValue::Null.into() }; + let value = if ty.is_func() { + RefValue::Func(FuncRef::new(addr)) + } else if ty.is_extern() { + RefValue::Extern(ExternRef::new(addr)) + } else if ty.is_exn() { + RefValue::Exn(ExnRef::new(addr)) + } else { + RefValue::Any(AnyRef::from_raw(addr)) + }; + value.into() } } @@ -161,7 +152,7 @@ mod tests { // Helper to create a dummy TableType fn dummy_table_type() -> TableType { - TableType::new(WasmType::RefFunc, 10, Some(20)) + TableType::new(RefType::FUNCREF, 10, Some(20)) } #[test] @@ -180,12 +171,12 @@ mod tests { table_instance.set(1, TableElement::Uninitialized).expect("Setting table element failed"); match table_instance.get_wasm_val(0) { - Ok(WasmValue::RefFunc(_)) => {} + Ok(WasmValue::Ref(RefValue::Func(_))) => {} _ => panic!("get_wasm_val failed to return the correct WasmValue"), } match table_instance.get_wasm_val(1) { - Ok(WasmValue::RefFunc(f)) if f.is_null() => {} + Ok(WasmValue::Ref(RefValue::Null)) => {} _ => panic!("get_wasm_val failed to return the correct WasmValue"), } diff --git a/crates/tinywasm/tests/generated/wasm-1.csv b/crates/tinywasm/tests/generated/wasm-1.csv index 66bf615..8fbf08d 100644 --- a/crates/tinywasm/tests/generated/wasm-1.csv +++ b/crates/tinywasm/tests/generated/wasm-1.csv @@ -8,3 +8,4 @@ 0.9.0,19241,0,[{"name":"address.wast","passed":243,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":77,"failed":0},{"name":"binary.wast","passed":67,"failed":0},{"name":"block.wast","passed":171,"failed":0},{"name":"br.wast","passed":84,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":168,"failed":0},{"name":"break-drop.wast","passed":4,"failed":0},{"name":"call.wast","passed":82,"failed":0},{"name":"call_indirect.wast","passed":152,"failed":0},{"name":"comments.wast","passed":4,"failed":0},{"name":"const.wast","passed":668,"failed":0},{"name":"conversions.wast","passed":435,"failed":0},{"name":"custom.wast","passed":10,"failed":0},{"name":"data.wast","passed":45,"failed":0},{"name":"elem.wast","passed":55,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":82,"failed":0},{"name":"f32.wast","passed":2512,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2512,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":7,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":161,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":121,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"globals.wast","passed":78,"failed":0},{"name":"i32.wast","passed":443,"failed":0},{"name":"i64.wast","passed":389,"failed":0},{"name":"if.wast","passed":151,"failed":0},{"name":"imports.wast","passed":146,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":116,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":81,"failed":0},{"name":"memory.wast","passed":71,"failed":0},{"name":"memory_grow.wast","passed":94,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":173,"failed":0},{"name":"names.wast","passed":483,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":111,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":5,"failed":0},{"name":"start.wast","passed":19,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"token.wast","passed":2,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":62,"failed":0},{"name":"unreached-invalid.wast","passed":110,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.9.1,19241,0,[{"name":"address.wast","passed":243,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":77,"failed":0},{"name":"binary.wast","passed":67,"failed":0},{"name":"block.wast","passed":171,"failed":0},{"name":"br.wast","passed":84,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":168,"failed":0},{"name":"break-drop.wast","passed":4,"failed":0},{"name":"call.wast","passed":82,"failed":0},{"name":"call_indirect.wast","passed":152,"failed":0},{"name":"comments.wast","passed":4,"failed":0},{"name":"const.wast","passed":668,"failed":0},{"name":"conversions.wast","passed":435,"failed":0},{"name":"custom.wast","passed":10,"failed":0},{"name":"data.wast","passed":45,"failed":0},{"name":"elem.wast","passed":55,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":82,"failed":0},{"name":"f32.wast","passed":2512,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2512,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":7,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":161,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":121,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"globals.wast","passed":78,"failed":0},{"name":"i32.wast","passed":443,"failed":0},{"name":"i64.wast","passed":389,"failed":0},{"name":"if.wast","passed":151,"failed":0},{"name":"imports.wast","passed":146,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":116,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":81,"failed":0},{"name":"memory.wast","passed":71,"failed":0},{"name":"memory_grow.wast","passed":94,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":173,"failed":0},{"name":"names.wast","passed":483,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":111,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":5,"failed":0},{"name":"start.wast","passed":19,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"token.wast","passed":2,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":62,"failed":0},{"name":"unreached-invalid.wast","passed":110,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.10.0,19241,0,[{"name":"address.wast","passed":243,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":77,"failed":0},{"name":"binary.wast","passed":67,"failed":0},{"name":"block.wast","passed":171,"failed":0},{"name":"br.wast","passed":84,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":168,"failed":0},{"name":"break-drop.wast","passed":4,"failed":0},{"name":"call.wast","passed":82,"failed":0},{"name":"call_indirect.wast","passed":152,"failed":0},{"name":"comments.wast","passed":4,"failed":0},{"name":"const.wast","passed":668,"failed":0},{"name":"conversions.wast","passed":435,"failed":0},{"name":"custom.wast","passed":10,"failed":0},{"name":"data.wast","passed":45,"failed":0},{"name":"elem.wast","passed":55,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":82,"failed":0},{"name":"f32.wast","passed":2512,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2512,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":7,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":161,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":121,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"globals.wast","passed":78,"failed":0},{"name":"i32.wast","passed":443,"failed":0},{"name":"i64.wast","passed":389,"failed":0},{"name":"if.wast","passed":151,"failed":0},{"name":"imports.wast","passed":146,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":116,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":81,"failed":0},{"name":"memory.wast","passed":71,"failed":0},{"name":"memory_grow.wast","passed":94,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":173,"failed":0},{"name":"names.wast","passed":483,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":111,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":5,"failed":0},{"name":"start.wast","passed":19,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"token.wast","passed":2,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":62,"failed":0},{"name":"unreached-invalid.wast","passed":110,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] +0.11.0-pre.0,19241,0,[{"name":"address.wast","passed":243,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":77,"failed":0},{"name":"binary.wast","passed":67,"failed":0},{"name":"block.wast","passed":171,"failed":0},{"name":"br.wast","passed":84,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":168,"failed":0},{"name":"break-drop.wast","passed":4,"failed":0},{"name":"call.wast","passed":82,"failed":0},{"name":"call_indirect.wast","passed":152,"failed":0},{"name":"comments.wast","passed":4,"failed":0},{"name":"const.wast","passed":668,"failed":0},{"name":"conversions.wast","passed":435,"failed":0},{"name":"custom.wast","passed":10,"failed":0},{"name":"data.wast","passed":45,"failed":0},{"name":"elem.wast","passed":55,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":82,"failed":0},{"name":"f32.wast","passed":2512,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2512,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":7,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":161,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":121,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"globals.wast","passed":78,"failed":0},{"name":"i32.wast","passed":443,"failed":0},{"name":"i64.wast","passed":389,"failed":0},{"name":"if.wast","passed":151,"failed":0},{"name":"imports.wast","passed":146,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":116,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":81,"failed":0},{"name":"memory.wast","passed":71,"failed":0},{"name":"memory_grow.wast","passed":94,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":173,"failed":0},{"name":"names.wast","passed":483,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":111,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":5,"failed":0},{"name":"start.wast","passed":19,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"token.wast","passed":2,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":62,"failed":0},{"name":"unreached-invalid.wast","passed":110,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-2.csv b/crates/tinywasm/tests/generated/wasm-2.csv index 0574a4a..c2472a7 100644 --- a/crates/tinywasm/tests/generated/wasm-2.csv +++ b/crates/tinywasm/tests/generated/wasm-2.csv @@ -13,3 +13,4 @@ 0.9.0,28008,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":89,"failed":0},{"name":"binary.wast","passed":128,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":117,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":172,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":927,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":471,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":178,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":88,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":104,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"obsolete-keywords.wast","passed":11,"failed":0},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":16,"failed":0},{"name":"ref_null.wast","passed":3,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1728,"failed":0},{"name":"table_fill.wast","passed":45,"failed":0},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":58,"failed":0},{"name":"table_init.wast","passed":780,"failed":0},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.9.1,28008,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":89,"failed":0},{"name":"binary.wast","passed":128,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":117,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":172,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":927,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":471,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":178,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":88,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":104,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"obsolete-keywords.wast","passed":11,"failed":0},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":16,"failed":0},{"name":"ref_null.wast","passed":3,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1728,"failed":0},{"name":"table_fill.wast","passed":45,"failed":0},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":58,"failed":0},{"name":"table_init.wast","passed":780,"failed":0},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.10.0,28008,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":89,"failed":0},{"name":"binary.wast","passed":128,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":117,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":172,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":927,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":471,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":178,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":88,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":104,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"obsolete-keywords.wast","passed":11,"failed":0},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":16,"failed":0},{"name":"ref_null.wast","passed":3,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1728,"failed":0},{"name":"table_fill.wast","passed":45,"failed":0},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":58,"failed":0},{"name":"table_init.wast","passed":780,"failed":0},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] +0.11.0-pre.0,28008,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":89,"failed":0},{"name":"binary.wast","passed":128,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":117,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":172,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":927,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":471,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":178,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":88,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":104,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"obsolete-keywords.wast","passed":11,"failed":0},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":16,"failed":0},{"name":"ref_null.wast","passed":3,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1728,"failed":0},{"name":"table_fill.wast","passed":45,"failed":0},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":58,"failed":0},{"name":"table_init.wast","passed":780,"failed":0},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-3.csv b/crates/tinywasm/tests/generated/wasm-3.csv index 89c0e38..9403d27 100644 --- a/crates/tinywasm/tests/generated/wasm-3.csv +++ b/crates/tinywasm/tests/generated/wasm-3.csv @@ -1,2 +1,3 @@ 0.9.0,20776,452,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":165,"failed":0},{"name":"annotations.wast","passed":74,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":127,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":119,"failed":0},{"name":"br_on_non_null.wast","passed":1,"failed":11},{"name":"br_on_null.wast","passed":1,"failed":9},{"name":"br_table.wast","passed":24,"failed":162},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":172,"failed":0},{"name":"call_ref.wast","passed":4,"failed":31},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":63,"failed":2},{"name":"elem.wast","passed":149,"failed":2},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":97,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":927,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":471,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":175,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":116,"failed":8},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"id.wast","passed":7,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":198,"failed":20},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"instance.wast","passed":0,"failed":23},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":142,"failed":21},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_init.wast","passed":10,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":98,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":89,"failed":1},{"name":"memory_grow.wast","passed":106,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"obsolete-keywords.wast","passed":11,"failed":0},{"name":"ref.wast","passed":12,"failed":1},{"name":"ref_as_non_null.wast","passed":1,"failed":6},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":2,"failed":20},{"name":"ref_null.wast","passed":0,"failed":34},{"name":"return.wast","passed":84,"failed":0},{"name":"return_call.wast","passed":47,"failed":0},{"name":"return_call_indirect.wast","passed":79,"failed":0},{"name":"return_call_ref.wast","passed":11,"failed":40},{"name":"select.wast","passed":155,"failed":2},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table.wast","passed":37,"failed":9},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":58,"failed":0},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"token.wast","passed":61,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type-canon.wast","passed":0,"failed":2},{"name":"type-equivalence.wast","passed":12,"failed":20},{"name":"type-rec.wast","passed":10,"failed":17},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":121,"failed":0},{"name":"unreached-valid.wast","passed":2,"failed":11},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.10.0,20778,450,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":165,"failed":0},{"name":"annotations.wast","passed":74,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":127,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":119,"failed":0},{"name":"br_on_non_null.wast","passed":1,"failed":11},{"name":"br_on_null.wast","passed":1,"failed":9},{"name":"br_table.wast","passed":24,"failed":162},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":172,"failed":0},{"name":"call_ref.wast","passed":4,"failed":31},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":63,"failed":2},{"name":"elem.wast","passed":149,"failed":2},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":97,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":927,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":471,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":175,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":116,"failed":8},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"id.wast","passed":7,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":198,"failed":20},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"instance.wast","passed":0,"failed":23},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":142,"failed":21},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_init.wast","passed":10,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":98,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":90,"failed":0},{"name":"memory_grow.wast","passed":106,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"obsolete-keywords.wast","passed":11,"failed":0},{"name":"ref.wast","passed":12,"failed":1},{"name":"ref_as_non_null.wast","passed":1,"failed":6},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":2,"failed":20},{"name":"ref_null.wast","passed":0,"failed":34},{"name":"return.wast","passed":84,"failed":0},{"name":"return_call.wast","passed":47,"failed":0},{"name":"return_call_indirect.wast","passed":79,"failed":0},{"name":"return_call_ref.wast","passed":11,"failed":40},{"name":"select.wast","passed":155,"failed":2},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table.wast","passed":38,"failed":8},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":58,"failed":0},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"token.wast","passed":61,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type-canon.wast","passed":0,"failed":2},{"name":"type-equivalence.wast","passed":12,"failed":20},{"name":"type-rec.wast","passed":10,"failed":17},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":121,"failed":0},{"name":"unreached-valid.wast","passed":2,"failed":11},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] +0.11.0-pre.0,21107,121,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":165,"failed":0},{"name":"annotations.wast","passed":74,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":127,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":119,"failed":0},{"name":"br_on_non_null.wast","passed":12,"failed":0},{"name":"br_on_null.wast","passed":10,"failed":0},{"name":"br_table.wast","passed":186,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":172,"failed":0},{"name":"call_ref.wast","passed":35,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":63,"failed":2},{"name":"elem.wast","passed":149,"failed":2},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":97,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":927,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":471,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":175,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":116,"failed":8},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"id.wast","passed":7,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":198,"failed":20},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"instance.wast","passed":0,"failed":23},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":163,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_init.wast","passed":10,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":98,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":90,"failed":0},{"name":"memory_grow.wast","passed":106,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"obsolete-keywords.wast","passed":11,"failed":0},{"name":"ref.wast","passed":13,"failed":0},{"name":"ref_as_non_null.wast","passed":7,"failed":0},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":22,"failed":0},{"name":"ref_null.wast","passed":0,"failed":34},{"name":"return.wast","passed":84,"failed":0},{"name":"return_call.wast","passed":47,"failed":0},{"name":"return_call_indirect.wast","passed":79,"failed":0},{"name":"return_call_ref.wast","passed":51,"failed":0},{"name":"select.wast","passed":157,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table.wast","passed":46,"failed":0},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":58,"failed":0},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"token.wast","passed":61,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type-canon.wast","passed":0,"failed":2},{"name":"type-equivalence.wast","passed":19,"failed":13},{"name":"type-rec.wast","passed":10,"failed":17},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":121,"failed":0},{"name":"unreached-valid.wast","passed":13,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-annotations.csv b/crates/tinywasm/tests/generated/wasm-annotations.csv index 9ed9be8..f24ba50 100644 --- a/crates/tinywasm/tests/generated/wasm-annotations.csv +++ b/crates/tinywasm/tests/generated/wasm-annotations.csv @@ -2,3 +2,4 @@ 0.9.0,617,0,[{"name":"annotations.wast","passed":74,"failed":0},{"name":"id.wast","passed":7,"failed":0},{"name":"simd_lane.wast","passed":475,"failed":0},{"name":"token.wast","passed":61,"failed":0}] 0.9.1,617,0,[{"name":"annotations.wast","passed":74,"failed":0},{"name":"id.wast","passed":7,"failed":0},{"name":"simd_lane.wast","passed":475,"failed":0},{"name":"token.wast","passed":61,"failed":0}] 0.10.0,617,0,[{"name":"annotations.wast","passed":74,"failed":0},{"name":"id.wast","passed":7,"failed":0},{"name":"simd_lane.wast","passed":475,"failed":0},{"name":"token.wast","passed":61,"failed":0}] +0.11.0-pre.0,617,0,[{"name":"annotations.wast","passed":74,"failed":0},{"name":"id.wast","passed":7,"failed":0},{"name":"simd_lane.wast","passed":475,"failed":0},{"name":"token.wast","passed":61,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-custom-page-sizes.csv b/crates/tinywasm/tests/generated/wasm-custom-page-sizes.csv index a22bb97..e3f7540 100644 --- a/crates/tinywasm/tests/generated/wasm-custom-page-sizes.csv +++ b/crates/tinywasm/tests/generated/wasm-custom-page-sizes.csv @@ -2,3 +2,4 @@ 0.9.0,211,0,[{"name":"binary.wast","passed":127,"failed":0},{"name":"custom-page-sizes-invalid.wast","passed":23,"failed":0},{"name":"custom-page-sizes.wast","passed":45,"failed":0},{"name":"memory_max.wast","passed":8,"failed":0},{"name":"memory_max_i64.wast","passed":8,"failed":0}] 0.9.1,207,0,[{"name":"binary.wast","passed":127,"failed":0},{"name":"custom-page-sizes-invalid.wast","passed":23,"failed":0},{"name":"custom-page-sizes.wast","passed":45,"failed":0},{"name":"memory_max.wast","passed":6,"failed":0},{"name":"memory_max_i64.wast","passed":6,"failed":0}] 0.10.0,207,0,[{"name":"binary.wast","passed":127,"failed":0},{"name":"custom-page-sizes-invalid.wast","passed":23,"failed":0},{"name":"custom-page-sizes.wast","passed":45,"failed":0},{"name":"memory_max.wast","passed":6,"failed":0},{"name":"memory_max_i64.wast","passed":6,"failed":0}] +0.11.0-pre.0,207,0,[{"name":"binary.wast","passed":127,"failed":0},{"name":"custom-page-sizes-invalid.wast","passed":23,"failed":0},{"name":"custom-page-sizes.wast","passed":45,"failed":0},{"name":"memory_max.wast","passed":6,"failed":0},{"name":"memory_max_i64.wast","passed":6,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-extended-const.csv b/crates/tinywasm/tests/generated/wasm-extended-const.csv index ce253d5..8677772 100644 --- a/crates/tinywasm/tests/generated/wasm-extended-const.csv +++ b/crates/tinywasm/tests/generated/wasm-extended-const.csv @@ -2,3 +2,4 @@ 0.9.0,290,0,[{"name":"data.wast","passed":65,"failed":0},{"name":"elem.wast","passed":111,"failed":0},{"name":"global.wast","passed":114,"failed":0}] 0.9.1,290,0,[{"name":"data.wast","passed":65,"failed":0},{"name":"elem.wast","passed":111,"failed":0},{"name":"global.wast","passed":114,"failed":0}] 0.10.0,290,0,[{"name":"data.wast","passed":65,"failed":0},{"name":"elem.wast","passed":111,"failed":0},{"name":"global.wast","passed":114,"failed":0}] +0.11.0-pre.0,290,0,[{"name":"data.wast","passed":65,"failed":0},{"name":"elem.wast","passed":111,"failed":0},{"name":"global.wast","passed":114,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-function-references.csv b/crates/tinywasm/tests/generated/wasm-function-references.csv index b217145..3e59535 100644 --- a/crates/tinywasm/tests/generated/wasm-function-references.csv +++ b/crates/tinywasm/tests/generated/wasm-function-references.csv @@ -1,2 +1,3 @@ 0.9.0,1536,331,[{"name":"binary.wast","passed":128,"failed":0},{"name":"br_on_non_null.wast","passed":0,"failed":9},{"name":"br_on_null.wast","passed":0,"failed":9},{"name":"br_table.wast","passed":24,"failed":162},{"name":"call_ref.wast","passed":3,"failed":31},{"name":"data.wast","passed":59,"failed":0},{"name":"elem.wast","passed":138,"failed":0},{"name":"func.wast","passed":175,"failed":0},{"name":"global.wast","passed":108,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"linking.wast","passed":146,"failed":21},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_init.wast","passed":10,"failed":0},{"name":"ref.wast","passed":12,"failed":1},{"name":"ref_as_non_null.wast","passed":1,"failed":6},{"name":"ref_is_null.wast","passed":2,"failed":20},{"name":"ref_null.wast","passed":0,"failed":4},{"name":"return_call.wast","passed":45,"failed":0},{"name":"return_call_indirect.wast","passed":76,"failed":0},{"name":"return_call_ref.wast","passed":10,"failed":40},{"name":"select.wast","passed":155,"failed":2},{"name":"table-sub.wast","passed":2,"failed":1},{"name":"table.wast","passed":35,"failed":8},{"name":"type-equivalence.wast","passed":7,"failed":7},{"name":"unreached-invalid.wast","passed":121,"failed":0},{"name":"unreached-valid.wast","passed":2,"failed":10}] 0.10.0,1536,331,[{"name":"binary.wast","passed":128,"failed":0},{"name":"br_on_non_null.wast","passed":0,"failed":9},{"name":"br_on_null.wast","passed":0,"failed":9},{"name":"br_table.wast","passed":24,"failed":162},{"name":"call_ref.wast","passed":3,"failed":31},{"name":"data.wast","passed":59,"failed":0},{"name":"elem.wast","passed":138,"failed":0},{"name":"func.wast","passed":175,"failed":0},{"name":"global.wast","passed":108,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"linking.wast","passed":146,"failed":21},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_init.wast","passed":10,"failed":0},{"name":"ref.wast","passed":12,"failed":1},{"name":"ref_as_non_null.wast","passed":1,"failed":6},{"name":"ref_is_null.wast","passed":2,"failed":20},{"name":"ref_null.wast","passed":0,"failed":4},{"name":"return_call.wast","passed":45,"failed":0},{"name":"return_call_indirect.wast","passed":76,"failed":0},{"name":"return_call_ref.wast","passed":10,"failed":40},{"name":"select.wast","passed":155,"failed":2},{"name":"table-sub.wast","passed":2,"failed":1},{"name":"table.wast","passed":35,"failed":8},{"name":"type-equivalence.wast","passed":7,"failed":7},{"name":"unreached-invalid.wast","passed":121,"failed":0},{"name":"unreached-valid.wast","passed":2,"failed":10}] +0.11.0-pre.0,1867,0,[{"name":"binary.wast","passed":128,"failed":0},{"name":"br_on_non_null.wast","passed":9,"failed":0},{"name":"br_on_null.wast","passed":9,"failed":0},{"name":"br_table.wast","passed":186,"failed":0},{"name":"call_ref.wast","passed":34,"failed":0},{"name":"data.wast","passed":59,"failed":0},{"name":"elem.wast","passed":138,"failed":0},{"name":"func.wast","passed":175,"failed":0},{"name":"global.wast","passed":108,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"linking.wast","passed":167,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_init.wast","passed":10,"failed":0},{"name":"ref.wast","passed":13,"failed":0},{"name":"ref_as_non_null.wast","passed":7,"failed":0},{"name":"ref_is_null.wast","passed":22,"failed":0},{"name":"ref_null.wast","passed":4,"failed":0},{"name":"return_call.wast","passed":45,"failed":0},{"name":"return_call_indirect.wast","passed":76,"failed":0},{"name":"return_call_ref.wast","passed":50,"failed":0},{"name":"select.wast","passed":157,"failed":0},{"name":"table-sub.wast","passed":3,"failed":0},{"name":"table.wast","passed":43,"failed":0},{"name":"type-equivalence.wast","passed":14,"failed":0},{"name":"unreached-invalid.wast","passed":121,"failed":0},{"name":"unreached-valid.wast","passed":12,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-memory64.csv b/crates/tinywasm/tests/generated/wasm-memory64.csv index b797727..6f87044 100644 --- a/crates/tinywasm/tests/generated/wasm-memory64.csv +++ b/crates/tinywasm/tests/generated/wasm-memory64.csv @@ -2,3 +2,4 @@ 0.9.0,1598,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"address64.wast","passed":242,"failed":0},{"name":"align64.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":93,"failed":0},{"name":"binary.wast","passed":169,"failed":0},{"name":"endianness64.wast","passed":69,"failed":0},{"name":"float_memory64.wast","passed":90,"failed":0},{"name":"load64.wast","passed":97,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory64.wast","passed":65,"failed":0},{"name":"memory_grow64.wast","passed":49,"failed":0},{"name":"memory_redundancy64.wast","passed":8,"failed":0},{"name":"memory_trap64.wast","passed":172,"failed":0},{"name":"simd_address.wast","passed":49,"failed":0}] 0.9.1,1598,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"address64.wast","passed":242,"failed":0},{"name":"align64.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":93,"failed":0},{"name":"binary.wast","passed":169,"failed":0},{"name":"endianness64.wast","passed":69,"failed":0},{"name":"float_memory64.wast","passed":90,"failed":0},{"name":"load64.wast","passed":97,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory64.wast","passed":65,"failed":0},{"name":"memory_grow64.wast","passed":49,"failed":0},{"name":"memory_redundancy64.wast","passed":8,"failed":0},{"name":"memory_trap64.wast","passed":172,"failed":0},{"name":"simd_address.wast","passed":49,"failed":0}] 0.10.0,1598,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"address64.wast","passed":242,"failed":0},{"name":"align64.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":93,"failed":0},{"name":"binary.wast","passed":169,"failed":0},{"name":"endianness64.wast","passed":69,"failed":0},{"name":"float_memory64.wast","passed":90,"failed":0},{"name":"load64.wast","passed":97,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory64.wast","passed":65,"failed":0},{"name":"memory_grow64.wast","passed":49,"failed":0},{"name":"memory_redundancy64.wast","passed":8,"failed":0},{"name":"memory_trap64.wast","passed":172,"failed":0},{"name":"simd_address.wast","passed":49,"failed":0}] +0.11.0-pre.0,1598,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"address64.wast","passed":242,"failed":0},{"name":"align64.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":93,"failed":0},{"name":"binary.wast","passed":169,"failed":0},{"name":"endianness64.wast","passed":69,"failed":0},{"name":"float_memory64.wast","passed":90,"failed":0},{"name":"load64.wast","passed":97,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory64.wast","passed":65,"failed":0},{"name":"memory_grow64.wast","passed":49,"failed":0},{"name":"memory_redundancy64.wast","passed":8,"failed":0},{"name":"memory_trap64.wast","passed":172,"failed":0},{"name":"simd_address.wast","passed":49,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-multi-memory.csv b/crates/tinywasm/tests/generated/wasm-multi-memory.csv index 17b7ec5..a167d76 100644 --- a/crates/tinywasm/tests/generated/wasm-multi-memory.csv +++ b/crates/tinywasm/tests/generated/wasm-multi-memory.csv @@ -2,3 +2,4 @@ 0.9.0,912,0,[{"name":"address0.wast","passed":92,"failed":0},{"name":"address1.wast","passed":127,"failed":0},{"name":"align0.wast","passed":5,"failed":0},{"name":"binary0.wast","passed":7,"failed":0},{"name":"data0.wast","passed":7,"failed":0},{"name":"data1.wast","passed":14,"failed":0},{"name":"data_drop0.wast","passed":11,"failed":0},{"name":"exports0.wast","passed":8,"failed":0},{"name":"float_exprs0.wast","passed":14,"failed":0},{"name":"float_exprs1.wast","passed":3,"failed":0},{"name":"float_memory0.wast","passed":30,"failed":0},{"name":"imports0.wast","passed":8,"failed":0},{"name":"imports1.wast","passed":5,"failed":0},{"name":"imports2.wast","passed":20,"failed":0},{"name":"imports3.wast","passed":10,"failed":0},{"name":"imports4.wast","passed":16,"failed":0},{"name":"linking0.wast","passed":6,"failed":0},{"name":"linking1.wast","passed":14,"failed":0},{"name":"linking2.wast","passed":11,"failed":0},{"name":"linking3.wast","passed":14,"failed":0},{"name":"load0.wast","passed":3,"failed":0},{"name":"load1.wast","passed":18,"failed":0},{"name":"load2.wast","passed":38,"failed":0},{"name":"memory-multi.wast","passed":6,"failed":0},{"name":"memory_copy0.wast","passed":29,"failed":0},{"name":"memory_copy1.wast","passed":14,"failed":0},{"name":"memory_fill0.wast","passed":16,"failed":0},{"name":"memory_grow.wast","passed":51,"failed":0},{"name":"memory_init0.wast","passed":13,"failed":0},{"name":"memory_size0.wast","passed":8,"failed":0},{"name":"memory_size1.wast","passed":15,"failed":0},{"name":"memory_size2.wast","passed":21,"failed":0},{"name":"memory_size3.wast","passed":2,"failed":0},{"name":"memory_size_import.wast","passed":7,"failed":0},{"name":"memory_trap0.wast","passed":14,"failed":0},{"name":"memory_trap1.wast","passed":168,"failed":0},{"name":"start0.wast","passed":9,"failed":0},{"name":"store0.wast","passed":5,"failed":0},{"name":"store1.wast","passed":13,"failed":0},{"name":"store2.wast","passed":25,"failed":0},{"name":"traps0.wast","passed":15,"failed":0}] 0.9.1,912,0,[{"name":"address0.wast","passed":92,"failed":0},{"name":"address1.wast","passed":127,"failed":0},{"name":"align0.wast","passed":5,"failed":0},{"name":"binary0.wast","passed":7,"failed":0},{"name":"data0.wast","passed":7,"failed":0},{"name":"data1.wast","passed":14,"failed":0},{"name":"data_drop0.wast","passed":11,"failed":0},{"name":"exports0.wast","passed":8,"failed":0},{"name":"float_exprs0.wast","passed":14,"failed":0},{"name":"float_exprs1.wast","passed":3,"failed":0},{"name":"float_memory0.wast","passed":30,"failed":0},{"name":"imports0.wast","passed":8,"failed":0},{"name":"imports1.wast","passed":5,"failed":0},{"name":"imports2.wast","passed":20,"failed":0},{"name":"imports3.wast","passed":10,"failed":0},{"name":"imports4.wast","passed":16,"failed":0},{"name":"linking0.wast","passed":6,"failed":0},{"name":"linking1.wast","passed":14,"failed":0},{"name":"linking2.wast","passed":11,"failed":0},{"name":"linking3.wast","passed":14,"failed":0},{"name":"load0.wast","passed":3,"failed":0},{"name":"load1.wast","passed":18,"failed":0},{"name":"load2.wast","passed":38,"failed":0},{"name":"memory-multi.wast","passed":6,"failed":0},{"name":"memory_copy0.wast","passed":29,"failed":0},{"name":"memory_copy1.wast","passed":14,"failed":0},{"name":"memory_fill0.wast","passed":16,"failed":0},{"name":"memory_grow.wast","passed":51,"failed":0},{"name":"memory_init0.wast","passed":13,"failed":0},{"name":"memory_size0.wast","passed":8,"failed":0},{"name":"memory_size1.wast","passed":15,"failed":0},{"name":"memory_size2.wast","passed":21,"failed":0},{"name":"memory_size3.wast","passed":2,"failed":0},{"name":"memory_size_import.wast","passed":7,"failed":0},{"name":"memory_trap0.wast","passed":14,"failed":0},{"name":"memory_trap1.wast","passed":168,"failed":0},{"name":"start0.wast","passed":9,"failed":0},{"name":"store0.wast","passed":5,"failed":0},{"name":"store1.wast","passed":13,"failed":0},{"name":"store2.wast","passed":25,"failed":0},{"name":"traps0.wast","passed":15,"failed":0}] 0.10.0,912,0,[{"name":"address0.wast","passed":92,"failed":0},{"name":"address1.wast","passed":127,"failed":0},{"name":"align0.wast","passed":5,"failed":0},{"name":"binary0.wast","passed":7,"failed":0},{"name":"data0.wast","passed":7,"failed":0},{"name":"data1.wast","passed":14,"failed":0},{"name":"data_drop0.wast","passed":11,"failed":0},{"name":"exports0.wast","passed":8,"failed":0},{"name":"float_exprs0.wast","passed":14,"failed":0},{"name":"float_exprs1.wast","passed":3,"failed":0},{"name":"float_memory0.wast","passed":30,"failed":0},{"name":"imports0.wast","passed":8,"failed":0},{"name":"imports1.wast","passed":5,"failed":0},{"name":"imports2.wast","passed":20,"failed":0},{"name":"imports3.wast","passed":10,"failed":0},{"name":"imports4.wast","passed":16,"failed":0},{"name":"linking0.wast","passed":6,"failed":0},{"name":"linking1.wast","passed":14,"failed":0},{"name":"linking2.wast","passed":11,"failed":0},{"name":"linking3.wast","passed":14,"failed":0},{"name":"load0.wast","passed":3,"failed":0},{"name":"load1.wast","passed":18,"failed":0},{"name":"load2.wast","passed":38,"failed":0},{"name":"memory-multi.wast","passed":6,"failed":0},{"name":"memory_copy0.wast","passed":29,"failed":0},{"name":"memory_copy1.wast","passed":14,"failed":0},{"name":"memory_fill0.wast","passed":16,"failed":0},{"name":"memory_grow.wast","passed":51,"failed":0},{"name":"memory_init0.wast","passed":13,"failed":0},{"name":"memory_size0.wast","passed":8,"failed":0},{"name":"memory_size1.wast","passed":15,"failed":0},{"name":"memory_size2.wast","passed":21,"failed":0},{"name":"memory_size3.wast","passed":2,"failed":0},{"name":"memory_size_import.wast","passed":7,"failed":0},{"name":"memory_trap0.wast","passed":14,"failed":0},{"name":"memory_trap1.wast","passed":168,"failed":0},{"name":"start0.wast","passed":9,"failed":0},{"name":"store0.wast","passed":5,"failed":0},{"name":"store1.wast","passed":13,"failed":0},{"name":"store2.wast","passed":25,"failed":0},{"name":"traps0.wast","passed":15,"failed":0}] +0.11.0-pre.0,912,0,[{"name":"address0.wast","passed":92,"failed":0},{"name":"address1.wast","passed":127,"failed":0},{"name":"align0.wast","passed":5,"failed":0},{"name":"binary0.wast","passed":7,"failed":0},{"name":"data0.wast","passed":7,"failed":0},{"name":"data1.wast","passed":14,"failed":0},{"name":"data_drop0.wast","passed":11,"failed":0},{"name":"exports0.wast","passed":8,"failed":0},{"name":"float_exprs0.wast","passed":14,"failed":0},{"name":"float_exprs1.wast","passed":3,"failed":0},{"name":"float_memory0.wast","passed":30,"failed":0},{"name":"imports0.wast","passed":8,"failed":0},{"name":"imports1.wast","passed":5,"failed":0},{"name":"imports2.wast","passed":20,"failed":0},{"name":"imports3.wast","passed":10,"failed":0},{"name":"imports4.wast","passed":16,"failed":0},{"name":"linking0.wast","passed":6,"failed":0},{"name":"linking1.wast","passed":14,"failed":0},{"name":"linking2.wast","passed":11,"failed":0},{"name":"linking3.wast","passed":14,"failed":0},{"name":"load0.wast","passed":3,"failed":0},{"name":"load1.wast","passed":18,"failed":0},{"name":"load2.wast","passed":38,"failed":0},{"name":"memory-multi.wast","passed":6,"failed":0},{"name":"memory_copy0.wast","passed":29,"failed":0},{"name":"memory_copy1.wast","passed":14,"failed":0},{"name":"memory_fill0.wast","passed":16,"failed":0},{"name":"memory_grow.wast","passed":51,"failed":0},{"name":"memory_init0.wast","passed":13,"failed":0},{"name":"memory_size0.wast","passed":8,"failed":0},{"name":"memory_size1.wast","passed":15,"failed":0},{"name":"memory_size2.wast","passed":21,"failed":0},{"name":"memory_size3.wast","passed":2,"failed":0},{"name":"memory_size_import.wast","passed":7,"failed":0},{"name":"memory_trap0.wast","passed":14,"failed":0},{"name":"memory_trap1.wast","passed":168,"failed":0},{"name":"start0.wast","passed":9,"failed":0},{"name":"store0.wast","passed":5,"failed":0},{"name":"store1.wast","passed":13,"failed":0},{"name":"store2.wast","passed":25,"failed":0},{"name":"traps0.wast","passed":15,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-nontrapping-float-to-int-conversions.csv b/crates/tinywasm/tests/generated/wasm-nontrapping-float-to-int-conversions.csv index b25a607..66d5329 100644 --- a/crates/tinywasm/tests/generated/wasm-nontrapping-float-to-int-conversions.csv +++ b/crates/tinywasm/tests/generated/wasm-nontrapping-float-to-int-conversions.csv @@ -1,3 +1,4 @@ 0.9.0,615,0,[{"name":"conversions.wast","passed":615,"failed":0}] 0.9.1,615,0,[{"name":"conversions.wast","passed":615,"failed":0}] 0.10.0,615,0,[{"name":"conversions.wast","passed":615,"failed":0}] +0.11.0-pre.0,615,0,[{"name":"conversions.wast","passed":615,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-reference-types.csv b/crates/tinywasm/tests/generated/wasm-reference-types.csv index f308361..92a4650 100644 --- a/crates/tinywasm/tests/generated/wasm-reference-types.csv +++ b/crates/tinywasm/tests/generated/wasm-reference-types.csv @@ -1,3 +1,4 @@ 0.9.0,9124,0,[{"name":"binary-leb128.wast","passed":77,"failed":0},{"name":"binary.wast","passed":134,"failed":0},{"name":"br_table.wast","passed":172,"failed":0},{"name":"bulk.wast","passed":117,"failed":0},{"name":"call_indirect.wast","passed":169,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":45,"failed":0},{"name":"elem.wast","passed":60,"failed":0},{"name":"exports.wast","passed":84,"failed":0},{"name":"global.wast","passed":86,"failed":0},{"name":"imports.wast","passed":158,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":16,"failed":0},{"name":"ref_null.wast","passed":3,"failed":0},{"name":"select.wast","passed":141,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1728,"failed":0},{"name":"table_fill.wast","passed":45,"failed":0},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":50,"failed":0},{"name":"table_init.wast","passed":780,"failed":0},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"unreached-invalid.wast","passed":111,"failed":0}] 0.9.1,9124,0,[{"name":"binary-leb128.wast","passed":77,"failed":0},{"name":"binary.wast","passed":134,"failed":0},{"name":"br_table.wast","passed":172,"failed":0},{"name":"bulk.wast","passed":117,"failed":0},{"name":"call_indirect.wast","passed":169,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":45,"failed":0},{"name":"elem.wast","passed":60,"failed":0},{"name":"exports.wast","passed":84,"failed":0},{"name":"global.wast","passed":86,"failed":0},{"name":"imports.wast","passed":158,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":16,"failed":0},{"name":"ref_null.wast","passed":3,"failed":0},{"name":"select.wast","passed":141,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1728,"failed":0},{"name":"table_fill.wast","passed":45,"failed":0},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":50,"failed":0},{"name":"table_init.wast","passed":780,"failed":0},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"unreached-invalid.wast","passed":111,"failed":0}] 0.10.0,9124,0,[{"name":"binary-leb128.wast","passed":77,"failed":0},{"name":"binary.wast","passed":134,"failed":0},{"name":"br_table.wast","passed":172,"failed":0},{"name":"bulk.wast","passed":117,"failed":0},{"name":"call_indirect.wast","passed":169,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":45,"failed":0},{"name":"elem.wast","passed":60,"failed":0},{"name":"exports.wast","passed":84,"failed":0},{"name":"global.wast","passed":86,"failed":0},{"name":"imports.wast","passed":158,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":16,"failed":0},{"name":"ref_null.wast","passed":3,"failed":0},{"name":"select.wast","passed":141,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1728,"failed":0},{"name":"table_fill.wast","passed":45,"failed":0},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":50,"failed":0},{"name":"table_init.wast","passed":780,"failed":0},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"unreached-invalid.wast","passed":111,"failed":0}] +0.11.0-pre.0,9124,0,[{"name":"binary-leb128.wast","passed":77,"failed":0},{"name":"binary.wast","passed":134,"failed":0},{"name":"br_table.wast","passed":172,"failed":0},{"name":"bulk.wast","passed":117,"failed":0},{"name":"call_indirect.wast","passed":169,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":45,"failed":0},{"name":"elem.wast","passed":60,"failed":0},{"name":"exports.wast","passed":84,"failed":0},{"name":"global.wast","passed":86,"failed":0},{"name":"imports.wast","passed":158,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":16,"failed":0},{"name":"ref_null.wast","passed":3,"failed":0},{"name":"select.wast","passed":141,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1728,"failed":0},{"name":"table_fill.wast","passed":45,"failed":0},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":50,"failed":0},{"name":"table_init.wast","passed":780,"failed":0},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"unreached-invalid.wast","passed":111,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-relaxed-simd.csv b/crates/tinywasm/tests/generated/wasm-relaxed-simd.csv index 9f51695..56ca1b5 100644 --- a/crates/tinywasm/tests/generated/wasm-relaxed-simd.csv +++ b/crates/tinywasm/tests/generated/wasm-relaxed-simd.csv @@ -1,3 +1,4 @@ 0.9.0,77,0,[{"name":"i16x8_relaxed_q15mulr_s.wast","passed":3,"failed":0},{"name":"i32x4_relaxed_trunc.wast","passed":1,"failed":0},{"name":"i8x16_relaxed_swizzle.wast","passed":6,"failed":0},{"name":"relaxed_dot_product.wast","passed":11,"failed":0},{"name":"relaxed_laneselect.wast","passed":12,"failed":0},{"name":"relaxed_madd_nmadd.wast","passed":19,"failed":0},{"name":"relaxed_min_max.wast","passed":25,"failed":0}] 0.9.1,77,0,[{"name":"i16x8_relaxed_q15mulr_s.wast","passed":3,"failed":0},{"name":"i32x4_relaxed_trunc.wast","passed":1,"failed":0},{"name":"i8x16_relaxed_swizzle.wast","passed":6,"failed":0},{"name":"relaxed_dot_product.wast","passed":11,"failed":0},{"name":"relaxed_laneselect.wast","passed":12,"failed":0},{"name":"relaxed_madd_nmadd.wast","passed":19,"failed":0},{"name":"relaxed_min_max.wast","passed":25,"failed":0}] 0.10.0,77,0,[{"name":"i16x8_relaxed_q15mulr_s.wast","passed":3,"failed":0},{"name":"i32x4_relaxed_trunc.wast","passed":1,"failed":0},{"name":"i8x16_relaxed_swizzle.wast","passed":6,"failed":0},{"name":"relaxed_dot_product.wast","passed":11,"failed":0},{"name":"relaxed_laneselect.wast","passed":12,"failed":0},{"name":"relaxed_madd_nmadd.wast","passed":19,"failed":0},{"name":"relaxed_min_max.wast","passed":25,"failed":0}] +0.11.0-pre.0,77,0,[{"name":"i16x8_relaxed_q15mulr_s.wast","passed":3,"failed":0},{"name":"i32x4_relaxed_trunc.wast","passed":1,"failed":0},{"name":"i8x16_relaxed_swizzle.wast","passed":6,"failed":0},{"name":"relaxed_dot_product.wast","passed":11,"failed":0},{"name":"relaxed_laneselect.wast","passed":12,"failed":0},{"name":"relaxed_madd_nmadd.wast","passed":19,"failed":0},{"name":"relaxed_min_max.wast","passed":25,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-sign-extension-ops.csv b/crates/tinywasm/tests/generated/wasm-sign-extension-ops.csv index b32b300..4e7e2ee 100644 --- a/crates/tinywasm/tests/generated/wasm-sign-extension-ops.csv +++ b/crates/tinywasm/tests/generated/wasm-sign-extension-ops.csv @@ -1,3 +1,4 @@ 0.9.0,872,0,[{"name":"i32.wast","passed":458,"failed":0},{"name":"i64.wast","passed":414,"failed":0}] 0.9.1,872,0,[{"name":"i32.wast","passed":458,"failed":0},{"name":"i64.wast","passed":414,"failed":0}] 0.10.0,872,0,[{"name":"i32.wast","passed":458,"failed":0},{"name":"i64.wast","passed":414,"failed":0}] +0.11.0-pre.0,872,0,[{"name":"i32.wast","passed":458,"failed":0},{"name":"i64.wast","passed":414,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-simd.csv b/crates/tinywasm/tests/generated/wasm-simd.csv index 683539f..605c09d 100644 --- a/crates/tinywasm/tests/generated/wasm-simd.csv +++ b/crates/tinywasm/tests/generated/wasm-simd.csv @@ -2,3 +2,4 @@ 0.9.0,25990,0,[{"name":"simd_address.wast","passed":49,"failed":0},{"name":"simd_align.wast","passed":100,"failed":0},{"name":"simd_bit_shift.wast","passed":252,"failed":0},{"name":"simd_bitwise.wast","passed":169,"failed":0},{"name":"simd_boolean.wast","passed":277,"failed":0},{"name":"simd_const.wast","passed":758,"failed":0},{"name":"simd_conversions.wast","passed":282,"failed":0},{"name":"simd_f32x4.wast","passed":790,"failed":0},{"name":"simd_f32x4_arith.wast","passed":1822,"failed":0},{"name":"simd_f32x4_cmp.wast","passed":2607,"failed":0},{"name":"simd_f32x4_pmin_pmax.wast","passed":3887,"failed":0},{"name":"simd_f32x4_rounding.wast","passed":201,"failed":0},{"name":"simd_f64x2.wast","passed":803,"failed":0},{"name":"simd_f64x2_arith.wast","passed":1825,"failed":0},{"name":"simd_f64x2_cmp.wast","passed":2685,"failed":0},{"name":"simd_f64x2_pmin_pmax.wast","passed":3887,"failed":0},{"name":"simd_f64x2_rounding.wast","passed":201,"failed":0},{"name":"simd_i16x8_arith.wast","passed":194,"failed":0},{"name":"simd_i16x8_arith2.wast","passed":172,"failed":0},{"name":"simd_i16x8_cmp.wast","passed":465,"failed":0},{"name":"simd_i16x8_extadd_pairwise_i8x16.wast","passed":21,"failed":0},{"name":"simd_i16x8_extmul_i8x16.wast","passed":117,"failed":0},{"name":"simd_i16x8_q15mulr_sat_s.wast","passed":30,"failed":0},{"name":"simd_i16x8_sat_arith.wast","passed":222,"failed":0},{"name":"simd_i32x4_arith.wast","passed":194,"failed":0},{"name":"simd_i32x4_arith2.wast","passed":149,"failed":0},{"name":"simd_i32x4_cmp.wast","passed":475,"failed":0},{"name":"simd_i32x4_dot_i16x8.wast","passed":32,"failed":0},{"name":"simd_i32x4_extadd_pairwise_i16x8.wast","passed":21,"failed":0},{"name":"simd_i32x4_extmul_i16x8.wast","passed":117,"failed":0},{"name":"simd_i32x4_trunc_sat_f32x4.wast","passed":107,"failed":0},{"name":"simd_i32x4_trunc_sat_f64x2.wast","passed":107,"failed":0},{"name":"simd_i64x2_arith.wast","passed":200,"failed":0},{"name":"simd_i64x2_arith2.wast","passed":25,"failed":0},{"name":"simd_i64x2_cmp.wast","passed":113,"failed":0},{"name":"simd_i64x2_extmul_i32x4.wast","passed":117,"failed":0},{"name":"simd_i8x16_arith.wast","passed":131,"failed":0},{"name":"simd_i8x16_arith2.wast","passed":211,"failed":0},{"name":"simd_i8x16_cmp.wast","passed":445,"failed":0},{"name":"simd_i8x16_sat_arith.wast","passed":214,"failed":0},{"name":"simd_int_to_int_extend.wast","passed":253,"failed":0},{"name":"simd_lane.wast","passed":475,"failed":0},{"name":"simd_linking.wast","passed":3,"failed":0},{"name":"simd_load.wast","passed":39,"failed":0},{"name":"simd_load16_lane.wast","passed":36,"failed":0},{"name":"simd_load32_lane.wast","passed":24,"failed":0},{"name":"simd_load64_lane.wast","passed":16,"failed":0},{"name":"simd_load8_lane.wast","passed":52,"failed":0},{"name":"simd_load_extend.wast","passed":104,"failed":0},{"name":"simd_load_splat.wast","passed":126,"failed":0},{"name":"simd_load_zero.wast","passed":39,"failed":0},{"name":"simd_memory-multi.wast","passed":1,"failed":0},{"name":"simd_select.wast","passed":7,"failed":0},{"name":"simd_splat.wast","passed":185,"failed":0},{"name":"simd_store.wast","passed":28,"failed":0},{"name":"simd_store16_lane.wast","passed":36,"failed":0},{"name":"simd_store32_lane.wast","passed":24,"failed":0},{"name":"simd_store64_lane.wast","passed":16,"failed":0},{"name":"simd_store8_lane.wast","passed":52,"failed":0}] 0.9.1,25990,0,[{"name":"simd_address.wast","passed":49,"failed":0},{"name":"simd_align.wast","passed":100,"failed":0},{"name":"simd_bit_shift.wast","passed":252,"failed":0},{"name":"simd_bitwise.wast","passed":169,"failed":0},{"name":"simd_boolean.wast","passed":277,"failed":0},{"name":"simd_const.wast","passed":758,"failed":0},{"name":"simd_conversions.wast","passed":282,"failed":0},{"name":"simd_f32x4.wast","passed":790,"failed":0},{"name":"simd_f32x4_arith.wast","passed":1822,"failed":0},{"name":"simd_f32x4_cmp.wast","passed":2607,"failed":0},{"name":"simd_f32x4_pmin_pmax.wast","passed":3887,"failed":0},{"name":"simd_f32x4_rounding.wast","passed":201,"failed":0},{"name":"simd_f64x2.wast","passed":803,"failed":0},{"name":"simd_f64x2_arith.wast","passed":1825,"failed":0},{"name":"simd_f64x2_cmp.wast","passed":2685,"failed":0},{"name":"simd_f64x2_pmin_pmax.wast","passed":3887,"failed":0},{"name":"simd_f64x2_rounding.wast","passed":201,"failed":0},{"name":"simd_i16x8_arith.wast","passed":194,"failed":0},{"name":"simd_i16x8_arith2.wast","passed":172,"failed":0},{"name":"simd_i16x8_cmp.wast","passed":465,"failed":0},{"name":"simd_i16x8_extadd_pairwise_i8x16.wast","passed":21,"failed":0},{"name":"simd_i16x8_extmul_i8x16.wast","passed":117,"failed":0},{"name":"simd_i16x8_q15mulr_sat_s.wast","passed":30,"failed":0},{"name":"simd_i16x8_sat_arith.wast","passed":222,"failed":0},{"name":"simd_i32x4_arith.wast","passed":194,"failed":0},{"name":"simd_i32x4_arith2.wast","passed":149,"failed":0},{"name":"simd_i32x4_cmp.wast","passed":475,"failed":0},{"name":"simd_i32x4_dot_i16x8.wast","passed":32,"failed":0},{"name":"simd_i32x4_extadd_pairwise_i16x8.wast","passed":21,"failed":0},{"name":"simd_i32x4_extmul_i16x8.wast","passed":117,"failed":0},{"name":"simd_i32x4_trunc_sat_f32x4.wast","passed":107,"failed":0},{"name":"simd_i32x4_trunc_sat_f64x2.wast","passed":107,"failed":0},{"name":"simd_i64x2_arith.wast","passed":200,"failed":0},{"name":"simd_i64x2_arith2.wast","passed":25,"failed":0},{"name":"simd_i64x2_cmp.wast","passed":113,"failed":0},{"name":"simd_i64x2_extmul_i32x4.wast","passed":117,"failed":0},{"name":"simd_i8x16_arith.wast","passed":131,"failed":0},{"name":"simd_i8x16_arith2.wast","passed":211,"failed":0},{"name":"simd_i8x16_cmp.wast","passed":445,"failed":0},{"name":"simd_i8x16_sat_arith.wast","passed":214,"failed":0},{"name":"simd_int_to_int_extend.wast","passed":253,"failed":0},{"name":"simd_lane.wast","passed":475,"failed":0},{"name":"simd_linking.wast","passed":3,"failed":0},{"name":"simd_load.wast","passed":39,"failed":0},{"name":"simd_load16_lane.wast","passed":36,"failed":0},{"name":"simd_load32_lane.wast","passed":24,"failed":0},{"name":"simd_load64_lane.wast","passed":16,"failed":0},{"name":"simd_load8_lane.wast","passed":52,"failed":0},{"name":"simd_load_extend.wast","passed":104,"failed":0},{"name":"simd_load_splat.wast","passed":126,"failed":0},{"name":"simd_load_zero.wast","passed":39,"failed":0},{"name":"simd_memory-multi.wast","passed":1,"failed":0},{"name":"simd_select.wast","passed":7,"failed":0},{"name":"simd_splat.wast","passed":185,"failed":0},{"name":"simd_store.wast","passed":28,"failed":0},{"name":"simd_store16_lane.wast","passed":36,"failed":0},{"name":"simd_store32_lane.wast","passed":24,"failed":0},{"name":"simd_store64_lane.wast","passed":16,"failed":0},{"name":"simd_store8_lane.wast","passed":52,"failed":0}] 0.10.0,25990,0,[{"name":"simd_address.wast","passed":49,"failed":0},{"name":"simd_align.wast","passed":100,"failed":0},{"name":"simd_bit_shift.wast","passed":252,"failed":0},{"name":"simd_bitwise.wast","passed":169,"failed":0},{"name":"simd_boolean.wast","passed":277,"failed":0},{"name":"simd_const.wast","passed":758,"failed":0},{"name":"simd_conversions.wast","passed":282,"failed":0},{"name":"simd_f32x4.wast","passed":790,"failed":0},{"name":"simd_f32x4_arith.wast","passed":1822,"failed":0},{"name":"simd_f32x4_cmp.wast","passed":2607,"failed":0},{"name":"simd_f32x4_pmin_pmax.wast","passed":3887,"failed":0},{"name":"simd_f32x4_rounding.wast","passed":201,"failed":0},{"name":"simd_f64x2.wast","passed":803,"failed":0},{"name":"simd_f64x2_arith.wast","passed":1825,"failed":0},{"name":"simd_f64x2_cmp.wast","passed":2685,"failed":0},{"name":"simd_f64x2_pmin_pmax.wast","passed":3887,"failed":0},{"name":"simd_f64x2_rounding.wast","passed":201,"failed":0},{"name":"simd_i16x8_arith.wast","passed":194,"failed":0},{"name":"simd_i16x8_arith2.wast","passed":172,"failed":0},{"name":"simd_i16x8_cmp.wast","passed":465,"failed":0},{"name":"simd_i16x8_extadd_pairwise_i8x16.wast","passed":21,"failed":0},{"name":"simd_i16x8_extmul_i8x16.wast","passed":117,"failed":0},{"name":"simd_i16x8_q15mulr_sat_s.wast","passed":30,"failed":0},{"name":"simd_i16x8_sat_arith.wast","passed":222,"failed":0},{"name":"simd_i32x4_arith.wast","passed":194,"failed":0},{"name":"simd_i32x4_arith2.wast","passed":149,"failed":0},{"name":"simd_i32x4_cmp.wast","passed":475,"failed":0},{"name":"simd_i32x4_dot_i16x8.wast","passed":32,"failed":0},{"name":"simd_i32x4_extadd_pairwise_i16x8.wast","passed":21,"failed":0},{"name":"simd_i32x4_extmul_i16x8.wast","passed":117,"failed":0},{"name":"simd_i32x4_trunc_sat_f32x4.wast","passed":107,"failed":0},{"name":"simd_i32x4_trunc_sat_f64x2.wast","passed":107,"failed":0},{"name":"simd_i64x2_arith.wast","passed":200,"failed":0},{"name":"simd_i64x2_arith2.wast","passed":25,"failed":0},{"name":"simd_i64x2_cmp.wast","passed":113,"failed":0},{"name":"simd_i64x2_extmul_i32x4.wast","passed":117,"failed":0},{"name":"simd_i8x16_arith.wast","passed":131,"failed":0},{"name":"simd_i8x16_arith2.wast","passed":211,"failed":0},{"name":"simd_i8x16_cmp.wast","passed":445,"failed":0},{"name":"simd_i8x16_sat_arith.wast","passed":214,"failed":0},{"name":"simd_int_to_int_extend.wast","passed":253,"failed":0},{"name":"simd_lane.wast","passed":475,"failed":0},{"name":"simd_linking.wast","passed":3,"failed":0},{"name":"simd_load.wast","passed":39,"failed":0},{"name":"simd_load16_lane.wast","passed":36,"failed":0},{"name":"simd_load32_lane.wast","passed":24,"failed":0},{"name":"simd_load64_lane.wast","passed":16,"failed":0},{"name":"simd_load8_lane.wast","passed":52,"failed":0},{"name":"simd_load_extend.wast","passed":104,"failed":0},{"name":"simd_load_splat.wast","passed":126,"failed":0},{"name":"simd_load_zero.wast","passed":39,"failed":0},{"name":"simd_memory-multi.wast","passed":1,"failed":0},{"name":"simd_select.wast","passed":7,"failed":0},{"name":"simd_splat.wast","passed":185,"failed":0},{"name":"simd_store.wast","passed":28,"failed":0},{"name":"simd_store16_lane.wast","passed":36,"failed":0},{"name":"simd_store32_lane.wast","passed":24,"failed":0},{"name":"simd_store64_lane.wast","passed":16,"failed":0},{"name":"simd_store8_lane.wast","passed":52,"failed":0}] +0.11.0-pre.0,25990,0,[{"name":"simd_address.wast","passed":49,"failed":0},{"name":"simd_align.wast","passed":100,"failed":0},{"name":"simd_bit_shift.wast","passed":252,"failed":0},{"name":"simd_bitwise.wast","passed":169,"failed":0},{"name":"simd_boolean.wast","passed":277,"failed":0},{"name":"simd_const.wast","passed":758,"failed":0},{"name":"simd_conversions.wast","passed":282,"failed":0},{"name":"simd_f32x4.wast","passed":790,"failed":0},{"name":"simd_f32x4_arith.wast","passed":1822,"failed":0},{"name":"simd_f32x4_cmp.wast","passed":2607,"failed":0},{"name":"simd_f32x4_pmin_pmax.wast","passed":3887,"failed":0},{"name":"simd_f32x4_rounding.wast","passed":201,"failed":0},{"name":"simd_f64x2.wast","passed":803,"failed":0},{"name":"simd_f64x2_arith.wast","passed":1825,"failed":0},{"name":"simd_f64x2_cmp.wast","passed":2685,"failed":0},{"name":"simd_f64x2_pmin_pmax.wast","passed":3887,"failed":0},{"name":"simd_f64x2_rounding.wast","passed":201,"failed":0},{"name":"simd_i16x8_arith.wast","passed":194,"failed":0},{"name":"simd_i16x8_arith2.wast","passed":172,"failed":0},{"name":"simd_i16x8_cmp.wast","passed":465,"failed":0},{"name":"simd_i16x8_extadd_pairwise_i8x16.wast","passed":21,"failed":0},{"name":"simd_i16x8_extmul_i8x16.wast","passed":117,"failed":0},{"name":"simd_i16x8_q15mulr_sat_s.wast","passed":30,"failed":0},{"name":"simd_i16x8_sat_arith.wast","passed":222,"failed":0},{"name":"simd_i32x4_arith.wast","passed":194,"failed":0},{"name":"simd_i32x4_arith2.wast","passed":149,"failed":0},{"name":"simd_i32x4_cmp.wast","passed":475,"failed":0},{"name":"simd_i32x4_dot_i16x8.wast","passed":32,"failed":0},{"name":"simd_i32x4_extadd_pairwise_i16x8.wast","passed":21,"failed":0},{"name":"simd_i32x4_extmul_i16x8.wast","passed":117,"failed":0},{"name":"simd_i32x4_trunc_sat_f32x4.wast","passed":107,"failed":0},{"name":"simd_i32x4_trunc_sat_f64x2.wast","passed":107,"failed":0},{"name":"simd_i64x2_arith.wast","passed":200,"failed":0},{"name":"simd_i64x2_arith2.wast","passed":25,"failed":0},{"name":"simd_i64x2_cmp.wast","passed":113,"failed":0},{"name":"simd_i64x2_extmul_i32x4.wast","passed":117,"failed":0},{"name":"simd_i8x16_arith.wast","passed":131,"failed":0},{"name":"simd_i8x16_arith2.wast","passed":211,"failed":0},{"name":"simd_i8x16_cmp.wast","passed":445,"failed":0},{"name":"simd_i8x16_sat_arith.wast","passed":214,"failed":0},{"name":"simd_int_to_int_extend.wast","passed":253,"failed":0},{"name":"simd_lane.wast","passed":475,"failed":0},{"name":"simd_linking.wast","passed":3,"failed":0},{"name":"simd_load.wast","passed":39,"failed":0},{"name":"simd_load16_lane.wast","passed":36,"failed":0},{"name":"simd_load32_lane.wast","passed":24,"failed":0},{"name":"simd_load64_lane.wast","passed":16,"failed":0},{"name":"simd_load8_lane.wast","passed":52,"failed":0},{"name":"simd_load_extend.wast","passed":104,"failed":0},{"name":"simd_load_splat.wast","passed":126,"failed":0},{"name":"simd_load_zero.wast","passed":39,"failed":0},{"name":"simd_memory-multi.wast","passed":1,"failed":0},{"name":"simd_select.wast","passed":7,"failed":0},{"name":"simd_splat.wast","passed":185,"failed":0},{"name":"simd_store.wast","passed":28,"failed":0},{"name":"simd_store16_lane.wast","passed":36,"failed":0},{"name":"simd_store32_lane.wast","passed":24,"failed":0},{"name":"simd_store64_lane.wast","passed":16,"failed":0},{"name":"simd_store8_lane.wast","passed":52,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-tail-call.csv b/crates/tinywasm/tests/generated/wasm-tail-call.csv index 935f9da..316895d 100644 --- a/crates/tinywasm/tests/generated/wasm-tail-call.csv +++ b/crates/tinywasm/tests/generated/wasm-tail-call.csv @@ -1,3 +1,4 @@ 0.9.0,119,0,[{"name":"return_call.wast","passed":44,"failed":0},{"name":"return_call_indirect.wast","passed":75,"failed":0}] 0.9.1,119,0,[{"name":"return_call.wast","passed":44,"failed":0},{"name":"return_call_indirect.wast","passed":75,"failed":0}] 0.10.0,119,0,[{"name":"return_call.wast","passed":44,"failed":0},{"name":"return_call_indirect.wast","passed":75,"failed":0}] +0.11.0-pre.0,119,0,[{"name":"return_call.wast","passed":44,"failed":0},{"name":"return_call_indirect.wast","passed":75,"failed":0}] diff --git a/crates/tinywasm/tests/generated/wasm-wide-arithmetic.csv b/crates/tinywasm/tests/generated/wasm-wide-arithmetic.csv index 10e194a..dfbff12 100644 --- a/crates/tinywasm/tests/generated/wasm-wide-arithmetic.csv +++ b/crates/tinywasm/tests/generated/wasm-wide-arithmetic.csv @@ -1,3 +1,4 @@ 0.9.0,109,0,[{"name":"wide-arithmetic.wast","passed":109,"failed":0}] 0.9.1,109,0,[{"name":"wide-arithmetic.wast","passed":109,"failed":0}] 0.10.0,109,0,[{"name":"wide-arithmetic.wast","passed":109,"failed":0}] +0.11.0-pre.0,109,0,[{"name":"wide-arithmetic.wast","passed":109,"failed":0}] diff --git a/crates/tinywasm/tests/host_func_signature_check.rs b/crates/tinywasm/tests/host_func_signature_check.rs index b0e50b2..b3b5048 100644 --- a/crates/tinywasm/tests/host_func_signature_check.rs +++ b/crates/tinywasm/tests/host_func_signature_check.rs @@ -1,6 +1,6 @@ use eyre::Result; use std::fmt::Write; -use tinywasm::types::{FuncType, WasmType, WasmValue}; +use tinywasm::types::{FuncType, RefValue, WasmType, WasmValue}; use tinywasm::{FuncContext, HostFunction, Imports, Module, ModuleInstance, Store}; use tinywasm_types::ExternRef; @@ -10,15 +10,15 @@ const VAL_LISTS: &[&[WasmValue]] = &[ &[WasmValue::I32(0), WasmValue::I32(0)], &[WasmValue::I32(0), WasmValue::I32(0), WasmValue::F64(0.0)], &[WasmValue::I32(0), WasmValue::F64(0.0), WasmValue::I32(0)], - &[WasmValue::RefExtern(ExternRef::null()), WasmValue::F64(0.0), WasmValue::I32(0)], + &[WasmValue::Ref(RefValue::Extern(ExternRef::new(0))), WasmValue::F64(0.0), WasmValue::I32(0)], ]; fn module_cases() -> Vec<(Module, FuncType, Vec)> { let mut cases = Vec::<(Module, FuncType, Vec)>::new(); for results in VAL_LISTS { for params in VAL_LISTS { - let param_tys = params.iter().map(WasmType::from).collect::>(); - let result_tys = results.iter().map(WasmType::from).collect::>(); + let param_tys = params.iter().map(|value| value.ty().expect("non-null fixture")).collect::>(); + let result_tys = results.iter().map(|value| value.ty().expect("non-null fixture")).collect::>(); let func_ty = FuncType::new(¶m_tys, &result_tys); cases.push((proxy_module(&func_ty), func_ty, params.to_vec())); } @@ -40,7 +40,8 @@ fn test_return_invalid_type() -> Result<()> { let instance = ModuleInstance::instantiate(&mut store, &module, Some(imports)).unwrap(); let caller = instance.func_untyped(&store, "call_hfn").unwrap(); // Return-type mismatch is only observable at call time. - let should_succeed = returned_values.iter().map(WasmType::from).eq(ty.results().iter().copied()); + let should_succeed = returned_values.len() == ty.results().len() + && returned_values.iter().zip(ty.results()).all(|(value, ty)| value.matches_type(*ty)); let call_res = caller.call(&mut store, &args); assert_eq!(call_res.is_ok(), should_succeed); } @@ -114,6 +115,90 @@ fn test_linking_invalid_typed_func() -> Result<()> { Ok(()) } +#[test] +fn concrete_host_references_use_canonical_types() -> Result<()> { + let wasm = wat::parse_str( + r#" + (module + (type $a (func)) + (type $b (func (param i32))) + (type $takes-a (func (param (ref $a)))) + (type $returns-a (func (result (ref $a)))) + (elem declare func $a-func $b-func) + (func $a-func (type $a)) + (func $b-func (type $b) local.get 0 drop) + (func (export "right-ref") (type $returns-a) ref.func $a-func) + (func (export "wrong-ref") (result (ref $b)) ref.func $b-func) + (func (export "takes-a") (type $takes-a) unreachable)) + "#, + )?; + let module = tinywasm::parse_bytes(&wasm)?; + let mut store = Store::default(); + let instance = ModuleInstance::instantiate(&mut store, &module, None)?; + + let right_ref = instance.func_untyped(&store, "right-ref")?.call(&mut store, &[])?; + let wrong_ref = instance.func_untyped(&store, "wrong-ref")?.call(&mut store, &[])?; + let return_ty = instance.func_untyped(&store, "right-ref")?.ty(&store)?.clone(); + let param_ty = instance.func_untyped(&store, "takes-a")?.ty(&store)?.clone(); + + let wrong_result = wrong_ref.clone(); + let wrong_return = HostFunction::from_untyped(&mut store, &return_ty, move |_, _| Ok(wrong_result.clone())); + assert!(wrong_return.call(&mut store, &[]).is_err()); + + let accept_a = HostFunction::from_untyped(&mut store, ¶m_ty, |_, _| Ok(Vec::new())); + assert!(accept_a.call(&mut store, &wrong_ref).is_err()); + assert!(accept_a.call(&mut store, &right_ref).is_ok()); + + Ok(()) +} + +#[test] +fn host_tail_calls_return_from_the_current_frame() -> Result<()> { + let wasm = wat::parse_str( + r#" + (module + (type $t (func (result i32))) + (import "host" "answer" (func $answer (type $t))) + (elem declare func $answer) + (table 1 funcref) + (elem (i32.const 0) func $answer) + (func $direct (export "direct") (type $t) return_call $answer) + (func (export "indirect") (type $t) + i32.const 0 + return_call_indirect (type $t)) + (func (export "reference") (type $t) + ref.func $answer + return_call_ref $t) + (func (export "nested") (result i32) + call $direct + i32.const 1 + i32.add)) + "#, + )?; + let module = tinywasm::parse_bytes(&wasm)?; + let mut store = Store::default(); + let mut imports = Imports::new(); + imports.define("host", "answer", HostFunction::from(&mut store, |_, ()| Ok(42_i32))); + let instance = ModuleInstance::instantiate(&mut store, &module, Some(imports))?; + + for name in ["direct", "indirect", "reference"] { + assert_eq!(instance.func::<(), i32>(&store, name)?.call(&mut store, ())?, 42); + } + assert_eq!(instance.func::<(), i32>(&store, "nested")?.call(&mut store, ())?, 43); + + Ok(()) +} + +#[test] +fn host_calls_reject_unknown_function_references() { + let mut store = Store::default(); + let ty = FuncType::new(&[WasmType::Ref(tinywasm::types::RefType::FUNCREF)], &[]); + let host = HostFunction::from_untyped(&mut store, &ty, |_, _| Ok(Vec::new())); + let invalid = WasmValue::Ref(RefValue::Func(tinywasm::types::FuncRef::new(u32::MAX))); + + assert!(host.call(&mut store, &[invalid]).is_err()); +} + fn to_name(ty: &WasmType) -> &str { match ty { WasmType::I32 => "i32", @@ -121,8 +206,9 @@ fn to_name(ty: &WasmType) -> &str { WasmType::F32 => "f32", WasmType::F64 => "f64", WasmType::V128 => "v128", - WasmType::RefFunc => "funcref", - WasmType::RefExtern => "externref", + WasmType::Ref(ty) if ty.is_func() => "funcref", + WasmType::Ref(ty) if ty.is_extern() => "externref", + WasmType::Ref(_) => panic!("unsupported reference type fixture"), } } diff --git a/crates/tinywasm/tests/imported_table_init.rs b/crates/tinywasm/tests/imported_table_init.rs index 4e2a587..d7ed636 100644 --- a/crates/tinywasm/tests/imported_table_init.rs +++ b/crates/tinywasm/tests/imported_table_init.rs @@ -1,6 +1,6 @@ use eyre::Result; -use tinywasm::types::{FuncRef, TableType, WasmType, WasmValue}; -use tinywasm::{Imports, ModuleInstance, Store, Table}; +use tinywasm::types::{FuncRef, RefType, TableType}; +use tinywasm::{HostFunction, Imports, ModuleInstance, Store, Table}; #[test] fn imported_table_uses_provided_init_value() -> Result<()> { @@ -19,8 +19,8 @@ fn imported_table_uses_provided_init_value() -> Result<()> { let module = tinywasm::parse_bytes(&wasm)?; let mut store = Store::default(); let mut imports = Imports::new(); - let table = - Table::new(&mut store, TableType::new(WasmType::RefFunc, 3, None), WasmValue::RefFunc(FuncRef::new(Some(0))))?; + let _function_at_zero = HostFunction::from(&mut store, |_, ()| Ok(())); + let table = Table::new(&mut store, TableType::new(RefType::FUNCREF, 3, None), FuncRef::new(0).into())?; imports.define("host", "table", table); let instance = ModuleInstance::instantiate(&mut store, &module, Some(imports))?; diff --git a/crates/tinywasm/tests/internal_refs.rs b/crates/tinywasm/tests/internal_refs.rs index 18033a7..7a68f01 100644 --- a/crates/tinywasm/tests/internal_refs.rs +++ b/crates/tinywasm/tests/internal_refs.rs @@ -1,5 +1,7 @@ use eyre::Result; -use tinywasm::types::{FuncRef, WasmValue}; +use tinywasm::types::WasmValue; +#[cfg(feature = "guest-debug")] +use tinywasm::types::{FuncRef, RefValue}; use tinywasm::{ExternItem, ModuleInstance, Store}; #[test] @@ -29,8 +31,8 @@ fn private_items_are_accessible_by_index() -> Result<()> { assert_eq!(instance.memory_by_index(0)?.read_vec(&store, 0, 4)?, &[1, 2, 3, 4]); assert_eq!(instance.table_by_index(0)?.size(&store)?, 2); - assert_eq!(instance.table_by_index(0)?.get(&store, 0)?, WasmValue::RefFunc(FuncRef::new(Some(0)))); - assert!(matches!(instance.table_by_index(0)?.get(&store, 1)?, WasmValue::RefFunc(func_ref) if func_ref.is_null())); + assert_eq!(instance.table_by_index(0)?.get(&store, 0)?, WasmValue::Ref(RefValue::Func(FuncRef::new(0)))); + assert_eq!(instance.table_by_index(0)?.get(&store, 1)?, WasmValue::Ref(tinywasm::types::RefValue::Null)); assert_eq!(instance.global_by_index(0)?.get(&store)?, WasmValue::I32(11)); instance.global_by_index(0)?.set(&mut store, WasmValue::I32(23))?; @@ -61,9 +63,9 @@ fn exported_tables_and_globals_have_handle_and_helper_apis() -> Result<()> { let table = instance.table("t")?; assert_eq!(table.size(&store)?, 1); - assert!(matches!(table.get(&store, 0)?, WasmValue::RefFunc(func_ref) if func_ref.is_null())); + assert_eq!(table.get(&store, 0)?, WasmValue::Ref(tinywasm::types::RefValue::Null)); - let old_size = instance.table("t")?.grow(&mut store, 1, WasmValue::RefFunc(FuncRef::null()))?; + let old_size = instance.table("t")?.grow(&mut store, 1, tinywasm::types::RefValue::Null.into())?; assert_eq!(old_size, 1); assert_eq!(instance.table("t")?.size(&store)?, 2); @@ -148,10 +150,11 @@ fn export_func_type_index_mismatch_fixture_would_break_old_lookup() -> Result<() let export = module.exports.iter().find(|export| export.name.as_ref() == "f").expect("export f not found"); let old_lookup_ty = module.func_types.get(export.index as usize).expect("old lookup type missing"); + let func_ty = &module.func_types[module.func_type_idxs[export.index as usize] as usize]; assert_eq!(old_lookup_ty.params(), &[tinywasm::types::WasmType::I64]); - assert_eq!(module.funcs[0].ty.params(), &[]); - assert_ne!(old_lookup_ty.params(), module.funcs[0].ty.params()); + assert_eq!(func_ty.params(), &[]); + assert_ne!(old_lookup_ty.params(), func_ty.params()); let mut store = Store::default(); let mut imports = tinywasm::Imports::new(); diff --git a/crates/tinywasm/tests/module_descriptors.rs b/crates/tinywasm/tests/module_descriptors.rs index d7fabfa..81ab5ab 100644 --- a/crates/tinywasm/tests/module_descriptors.rs +++ b/crates/tinywasm/tests/module_descriptors.rs @@ -113,7 +113,7 @@ fn module_descriptors_resolve_imported_and_local_table_and_memory_exports() -> R let itable_export = exports.iter().find(|export| export.name == "itable_export").expect("itable export not found"); match itable_export.ty { ExportType::Table(ty) => { - assert_eq!(ty.element_type, WasmType::RefFunc); + assert_eq!(ty.element_type, tinywasm::types::RefType::FUNCREF); assert_eq!(ty.size_initial, 2); assert_eq!(ty.size_max, Some(4)); } @@ -133,7 +133,7 @@ fn module_descriptors_resolve_imported_and_local_table_and_memory_exports() -> R let ltable_export = exports.iter().find(|export| export.name == "ltable_export").expect("ltable export not found"); match ltable_export.ty { ExportType::Table(ty) => { - assert_eq!(ty.element_type, WasmType::RefFunc); + assert_eq!(ty.element_type, tinywasm::types::RefType::FUNCREF); assert_eq!(ty.size_initial, 5); assert_eq!(ty.size_max, Some(7)); } diff --git a/crates/tinywasm/tests/store_ownership.rs b/crates/tinywasm/tests/store_ownership.rs index 61cc88c..8577d73 100644 --- a/crates/tinywasm/tests/store_ownership.rs +++ b/crates/tinywasm/tests/store_ownership.rs @@ -81,7 +81,7 @@ fn table_grow_rejects_wrong_store_with_invalid_store_error() -> Result<()> { let table = instance.table("t")?; let mut other_store = Store::default(); - let err = table.grow(&mut other_store, 1, tinywasm::types::FuncRef::null().into()).unwrap_err(); + let err = table.grow(&mut other_store, 1, tinywasm::types::RefValue::Null.into()).unwrap_err(); assert_eq!(err, tinywasm::Error::Trap(tinywasm::Trap::InvalidStore)); Ok(()) diff --git a/crates/types/README.md b/crates/types/README.md index 315a340..ed5a641 100644 --- a/crates/types/README.md +++ b/crates/types/README.md @@ -3,3 +3,7 @@ This crate contains the shared module, instruction, value, and archive types used by [`tinywasm`](https://crates.io/crates/tinywasm) and [`tinywasm-parser`](https://crates.io/crates/tinywasm-parser). Most users should depend on `tinywasm` directly. This crate is useful when you need to work with parsed modules, serialized `twasm` archives, or shared type definitions without pulling in the runtime. + +## API stability + +`tinywasm-types` is semver-exempt and may make breaking API changes in any release. Types re-exported by `tinywasm` remain covered by `tinywasm`'s compatibility policy when used through that crate. diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 95e59b4..0b15927 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -84,10 +84,10 @@ pub struct ModuleInner { /// A vector of type definitions, indexed by `TypeAddr` /// /// Corresponds to the `type` section of the original WebAssembly module. - pub func_types: Arc<[Arc]>, + pub func_types: Box<[FuncType]>, /// Function index to type index mapping in module index space, including imports. - pub func_type_idxs: Arc<[u32]>, + pub func_type_idxs: Box<[TypeAddr]>, /// Exported items of the WebAssembly module. /// @@ -102,7 +102,7 @@ pub struct ModuleInner { /// Table components of the WebAssembly module used to initialize tables. /// /// Corresponds to the `table` section of the original WebAssembly module. - pub table_types: Box<[TableType]>, + pub tables: Box<[TableDefinition]>, /// Memory components of the WebAssembly module used to initialize memories. /// @@ -193,7 +193,8 @@ impl Module { if idx < imported_funcs { imported_type(&self.0, ExternalKind::Func, idx)? } else { - ExportType::Func(&self.0.funcs.get(idx - imported_funcs)?.ty) + let type_idx = *self.0.func_type_idxs.get(idx)?; + ExportType::Func(self.0.func_types.get(type_idx as usize)?) } } ExternalKind::Table => { @@ -201,7 +202,7 @@ impl Module { if idx < imported_tables { imported_type(&self.0, ExternalKind::Table, idx)? } else { - ExportType::Table(self.0.table_types.get(idx - imported_tables)?) + ExportType::Table(&self.0.tables.get(idx - imported_tables)?.ty) } } ExternalKind::Memory => { @@ -318,9 +319,12 @@ pub type ExternAddr = Addr; pub type ConstIdx = Addr; // additional internal addresses +/// An address in the current type space. +/// +/// Parsed modules use module-local addresses; instantiated types use their store's canonical addresses. pub type TypeAddr = Addr; pub type LocalAddr = u16; // there can't be more than 50.000 locals in a function -pub type ModuleInstanceAddr = Addr; +pub type ModuleInstanceId = Addr; /// A WebAssembly External Value. /// @@ -383,6 +387,61 @@ impl FuncType { pub fn results(&self) -> &[WasmType] { &self.data[self.param_count as usize..] } + + /// Compare function types while resolving concrete references in their respective type spaces. + pub fn equivalent(&self, types: &[FuncType], other: &Self, other_types: &[FuncType]) -> bool { + fn refs_equal( + left_types: &[FuncType], + left: RefType, + right_types: &[FuncType], + right: RefType, + visited: &mut alloc::vec::Vec<(u32, u32)>, + ) -> bool { + if left.is_nullable() != right.is_nullable() { + return false; + } + match (left.type_index(), right.type_index()) { + (Some(left_idx), Some(right_idx)) => { + if visited.contains(&(left_idx, right_idx)) { + return true; + } + let (Some(left), Some(right)) = + (left_types.get(left_idx as usize), right_types.get(right_idx as usize)) + else { + return false; + }; + visited.push((left_idx, right_idx)); + funcs_equal(left, left_types, right, right_types, visited) + } + (None, None) => left.abstract_heap_type() == right.abstract_heap_type(), + _ => false, + } + } + + fn funcs_equal( + left: &FuncType, + left_types: &[FuncType], + right: &FuncType, + right_types: &[FuncType], + visited: &mut alloc::vec::Vec<(u32, u32)>, + ) -> bool { + left.params().len() == right.params().len() + && left.results().len() == right.results().len() + && left.params().iter().chain(left.results()).zip(right.params().iter().chain(right.results())).all( + |(&left, &right)| match (left, right) { + (WasmType::Ref(left), WasmType::Ref(right)) => { + refs_equal(left_types, left, right_types, right, visited) + } + _ => left == right, + }, + ) + } + + if core::ptr::eq(types, other_types) && self == other { + return true; + } + funcs_equal(self, types, other, other_types, &mut alloc::vec::Vec::new()) + } } #[derive(Default, Clone, Copy, PartialEq, Eq)] @@ -426,7 +485,6 @@ pub struct WasmFunction { pub locals: ValueCounts, pub params: ValueCounts, pub results: ValueCounts, - pub ty: Arc, } #[derive(Clone, PartialEq, Eq, Default)] @@ -504,23 +562,31 @@ impl Default for GlobalType { #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct TableType { arch: MemoryArch, - pub element_type: WasmType, + pub element_type: RefType, pub size_initial: u64, pub size_max: Option, } +#[derive(Clone, PartialEq)] +#[cfg_attr(feature = "debug", derive(Debug))] +#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] +pub struct TableDefinition { + pub ty: TableType, + pub init: Option>, +} + impl TableType { pub const fn empty() -> Self { - Self::new(WasmType::Ref(RefType::FUNCREF), 0, None) + Self::new(RefType::FUNCREF, 0, None) } /// Create a table with 32-bit indices. - pub const fn new(element_type: WasmType, size_initial: u64, size_max: Option) -> Self { + pub const fn new(element_type: RefType, size_initial: u64, size_max: Option) -> Self { Self { arch: MemoryArch::I32, element_type, size_initial, size_max } } /// Create a table with 64-bit indices. - pub const fn new64(element_type: WasmType, size_initial: u64, size_max: Option) -> Self { + pub const fn new64(element_type: RefType, size_initial: u64, size_max: Option) -> Self { Self { arch: MemoryArch::I64, element_type, size_initial, size_max } } @@ -675,7 +741,7 @@ pub struct Element { pub kind: ElementKind, pub items: Box<[ElementItem]>, pub range: Range, - pub ty: WasmType, + pub ty: RefType, } #[derive(Clone, PartialEq)] diff --git a/crates/types/src/reference.rs b/crates/types/src/reference.rs index cbb5f6a..ee66965 100644 --- a/crates/types/src/reference.rs +++ b/crates/types/src/reference.rs @@ -113,6 +113,23 @@ impl RefType { pub const fn with_nullability(self, nullable: bool) -> Self { Self((self.0 & !Self::NULLABLE) | ((nullable as u32) << 31)) } + + #[inline] + pub const fn is_func(self) -> bool { + // TODO(wasm3): Classify concrete refs from the module type definition once GC types are represented. + self.is_concrete() + || matches!(self.abstract_heap_type(), Some(AbstractHeapType::Func | AbstractHeapType::NoFunc)) + } + + #[inline] + pub const fn is_extern(self) -> bool { + matches!(self.abstract_heap_type(), Some(AbstractHeapType::Extern | AbstractHeapType::NoExtern)) + } + + #[inline] + pub const fn is_exn(self) -> bool { + matches!(self.abstract_heap_type(), Some(AbstractHeapType::Exn | AbstractHeapType::NoExn)) + } } #[derive(Clone, Copy, PartialEq, Eq)] @@ -126,6 +143,19 @@ pub enum RefValue { Exn(ExnRef), } +impl RefValue { + /// Return the reference's raw representation, or `None` for null. + pub const fn raw(self) -> Option { + match self { + Self::Null => None, + Self::Func(value) => Some(value.addr()), + Self::Extern(value) => Some(value.addr()), + Self::Any(value) => Some(value.raw()), + Self::Exn(value) => Some(value.addr()), + } + } +} + #[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "debug", derive(Debug))] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] @@ -148,11 +178,35 @@ impl FuncRef { #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct ExternRef(u32); +impl ExternRef { + #[inline] + pub const fn new(addr: u32) -> Self { + Self(addr) + } + + #[inline] + pub const fn addr(self) -> u32 { + self.0 + } +} + #[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "debug", derive(Debug))] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct ExnRef(u32); +impl ExnRef { + #[inline] + pub const fn new(addr: u32) -> Self { + Self(addr) + } + + #[inline] + pub const fn addr(self) -> u32 { + self.0 + } +} + #[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "debug", derive(Debug))] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] @@ -162,6 +216,12 @@ impl AnyRef { // Odd values are inline i31s. // Even values are GC object handles. + #[doc(hidden)] + #[inline] + pub const fn from_raw(raw: u32) -> Self { + Self(raw) + } + pub const fn from_i31(value: i32) -> Option { if value < -(1 << 30) || value >= (1 << 30) { return None; @@ -187,4 +247,9 @@ impl AnyRef { pub const fn gc_addr(self) -> Option { if self.0 != 0 && self.0 & 1 == 0 { Some(self.0 / 2 - 1) } else { None } } + + #[inline] + pub const fn raw(self) -> u32 { + self.0 + } } diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs index 213a556..92c660b 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -23,7 +23,49 @@ pub enum WasmValue { } impl WasmValue { - pub const REF_NULL: Self = Self::Ref(RefValue::Null); + /// Return this value's broad WebAssembly type. + /// + /// A null reference has no intrinsic heap type and returns `None`. + pub const fn ty(self) -> Option { + match self { + Self::I32(_) => Some(WasmType::I32), + Self::I64(_) => Some(WasmType::I64), + Self::F32(_) => Some(WasmType::F32), + Self::F64(_) => Some(WasmType::F64), + Self::V128(_) => Some(WasmType::V128), + Self::Ref(RefValue::Null) => None, + Self::Ref(RefValue::Func(_)) => Some(WasmType::Ref(RefType::FUNCREF)), + Self::Ref(RefValue::Extern(_)) => Some(WasmType::Ref(RefType::EXTERNREF)), + Self::Ref(RefValue::Exn(_)) => Some(WasmType::Ref(RefType::EXNREF)), + Self::Ref(RefValue::Any(_)) => { + Some(WasmType::Ref(RefType::new_abstract(true, crate::AbstractHeapType::Any))) + } + } + } + + #[inline] + pub const fn matches_type(self, ty: WasmType) -> bool { + // Concrete references require a store lookup; this method only classifies their broad heap type. + match (self, ty) { + (Self::I32(_), WasmType::I32) + | (Self::I64(_), WasmType::I64) + | (Self::F32(_), WasmType::F32) + | (Self::F64(_), WasmType::F64) + | (Self::V128(_), WasmType::V128) => true, + (Self::Ref(RefValue::Null), WasmType::Ref(ty)) => ty.is_nullable(), + (Self::Ref(RefValue::Func(_)), WasmType::Ref(ty)) => { + ty.is_concrete() || matches!(ty.abstract_heap_type(), Some(crate::AbstractHeapType::Func)) + } + (Self::Ref(RefValue::Extern(_)), WasmType::Ref(ty)) => { + matches!(ty.abstract_heap_type(), Some(crate::AbstractHeapType::Extern)) + } + (Self::Ref(RefValue::Exn(_)), WasmType::Ref(ty)) => { + matches!(ty.abstract_heap_type(), Some(crate::AbstractHeapType::Exn)) + } + (Self::Ref(RefValue::Any(_)), WasmType::Ref(ty)) => !ty.is_func() && !ty.is_extern() && !ty.is_exn(), + _ => false, + } + } } impl Debug for WasmValue { @@ -65,7 +107,7 @@ impl WasmValue { WasmType::F32 => Some(Self::F32(0.0)), WasmType::F64 => Some(Self::F64(0.0)), WasmType::V128 => Some(Self::V128([0; 16])), - WasmType::Ref(ty) if ty.is_nullable() => Some(Self::REF_NULL), + WasmType::Ref(ty) if ty.is_nullable() => Some(Self::Ref(RefValue::Null)), WasmType::Ref(_) => None, } } @@ -206,3 +248,30 @@ impl_conversion_for_wasmvalue! { [u8; 16] => V128, as_v128, "Return the raw little-endian bytes from a `WasmValue`, if it is a `V128`."; RefValue => Ref, as_ref, "Return the `RefValue` from a `WasmValue`, if it is a `Ref`."; } + +macro_rules! impl_ref_conversion_for_wasmvalue { + ($($ty:ty => $variant:ident);* $(;)?) => { + $( + impl From<$ty> for WasmValue { + fn from(value: $ty) -> Self { + Self::Ref(RefValue::$variant(value)) + } + } + + impl TryFrom for $ty { + type Error = (); + + fn try_from(value: WasmValue) -> Result { + if let WasmValue::Ref(RefValue::$variant(value)) = value { Ok(value) } else { Err(()) } + } + } + )* + }; +} + +impl_ref_conversion_for_wasmvalue! { + crate::FuncRef => Func; + crate::ExternRef => Extern; + crate::AnyRef => Any; + crate::ExnRef => Exn; +} From 08166b190159e8e4747bc0dfa8423e3af621f249 Mon Sep 17 00:00:00 2001 From: Henry Date: Sun, 2 Aug 2026 18:09:33 +0200 Subject: [PATCH 4/4] docs: update readme Signed-off-by: Henry --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 132ee49..77ade90 100644 --- a/README.md +++ b/README.md @@ -87,8 +87,8 @@ The internal `twasm` bytecode format is not currently validated as an untrusted | [**Tail Call**](https://github.com/WebAssembly/tail-call/blob/main/proposals/tail-call/Overview.md) | 🟢 | 0.9.0 | | [**Relaxed SIMD**](https://github.com/WebAssembly/relaxed-simd/blob/main/proposals/relaxed-simd/Overview.md) | 🟢 | 0.9.0 | | [**Wide Arithmetic**](https://github.com/WebAssembly/wide-arithmetic/blob/main/proposals/wide-arithmetic/Overview.md) | 🟢 | 0.9.0 | +| [**Typed Function References**](https://github.com/WebAssembly/function-references/blob/main/proposals/function-references/Overview.md) | 🚧 | `next` | | [**Exception Handling**](https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/Exceptions.md) | 🌑 | - | -| [**Typed Function References**](https://github.com/WebAssembly/function-references/blob/main/proposals/function-references/Overview.md) | 🌑 | - | | [**Garbage Collection**](https://github.com/WebAssembly/gc/blob/main/proposals/gc/Overview.md) | 🌑 | - | | [**Stack Switching**](https://github.com/WebAssembly/stack-switching/blob/main/proposals/stack-switching/Explainer.md) | 🌑 | - | | [**Threads**](https://github.com/WebAssembly/threads/blob/main-legacy/proposals/threads/Overview.md) | 🌑 | - |