summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/parser/src/conversion.rs1
-rw-r--r--crates/parser/src/module.rs8
-rw-r--r--crates/tinywasm/src/imports.rs15
-rw-r--r--crates/tinywasm/src/instance.rs30
-rw-r--r--crates/tinywasm/src/reference.rs126
-rw-r--r--crates/tinywasm/src/runtime/interpreter/mod.rs39
-rw-r--r--crates/tinywasm/src/runtime/interpreter/no_std_floats.rs16
-rw-r--r--crates/tinywasm/src/store/mod.rs76
-rw-r--r--crates/tinywasm/tests/generated/2.0.csv2
9 files changed, 217 insertions, 96 deletions
diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs
index 79069e1..7c10d62 100644
--- a/crates/parser/src/conversion.rs
+++ b/crates/parser/src/conversion.rs
@@ -107,7 +107,6 @@ pub(crate) fn convert_module_tables<T: IntoIterator<Item = wasmparser::Result<wa
table_types: T,
) -> Result<Vec<TableType>> {
let table_type = table_types.into_iter().map(|table| convert_module_table(table?)).collect::<Result<Vec<_>>>()?;
-
Ok(table_type)
}
diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs
index 5bc6a7e..f5c01ac 100644
--- a/crates/parser/src/module.rs
+++ b/crates/parser/src/module.rs
@@ -112,7 +112,6 @@ impl ModuleReader {
if !self.table_types.is_empty() {
return Err(ParseError::DuplicateSection("Table section".into()));
}
-
debug!("Found table section");
validator.table_section(&reader)?;
self.table_types = conversion::convert_module_tables(reader)?;
@@ -140,6 +139,13 @@ impl ModuleReader {
validator.data_section(&reader)?;
self.data = conversion::convert_module_data_sections(reader)?;
}
+ DataCountSection { count, range } => {
+ debug!("Found data count section");
+ if !self.data.is_empty() {
+ return Err(ParseError::DuplicateSection("Data count section".into()));
+ }
+ validator.data_count_section(count, &range)?;
+ }
CodeSectionStart { count, range, .. } => {
debug!("Found code section ({} functions)", count);
if !self.code.is_empty() {
diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs
index 0b16970..38d0707 100644
--- a/crates/tinywasm/src/imports.rs
+++ b/crates/tinywasm/src/imports.rs
@@ -62,13 +62,13 @@ pub struct FuncContext<'a> {
}
impl FuncContext<'_> {
- /// Get a mutable reference to the store
- pub fn store_mut(&mut self) -> &mut crate::Store {
+ /// Get a reference to the store
+ pub fn store(&self) -> &crate::Store {
self.store
}
- /// Get a reference to the store
- pub fn store(&self) -> &crate::Store {
+ /// Get a mutable reference to the store
+ pub fn store_mut(&mut self) -> &mut crate::Store {
self.store
}
@@ -78,9 +78,14 @@ impl FuncContext<'_> {
}
/// Get a reference to an exported memory
- pub fn memory(&mut self, name: &str) -> Result<crate::MemoryRef> {
+ pub fn exported_memory(&mut self, name: &str) -> Result<crate::MemoryRef<'_>> {
self.module().exported_memory(self.store, name)
}
+
+ /// Get a reference to an exported memory
+ pub fn exported_memory_mut(&mut self, name: &str) -> Result<crate::MemoryRefMut<'_>> {
+ self.module().exported_memory_mut(self.store, name)
+ }
}
impl Debug for HostFunction {
diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs
index ecd6ce8..ad6c0f1 100644
--- a/crates/tinywasm/src/instance.rs
+++ b/crates/tinywasm/src/instance.rs
@@ -3,7 +3,7 @@ use tinywasm_types::*;
use crate::{
func::{FromWasmValueTuple, IntoWasmValueTuple},
- log, Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, Module, Result, Store,
+ log, Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, MemoryRefMut, Module, Result, Store,
};
/// An instanciated WebAssembly module
@@ -152,6 +152,11 @@ impl ModuleInstance {
*self.0.mem_addrs.get(addr as usize).expect("No mem addr for mem, this is a bug")
}
+ // resolve a data address to the global store address
+ pub(crate) fn resolve_data_addr(&self, addr: DataAddr) -> MemAddr {
+ *self.0.data_addrs.get(addr as usize).expect("No data addr for data, this is a bug")
+ }
+
// resolve a memory address to the global store address
pub(crate) fn resolve_elem_addr(&self, addr: ElemAddr) -> ElemAddr {
*self.0.elem_addrs.get(addr as usize).expect("No elem addr for elem, this is a bug")
@@ -190,7 +195,7 @@ impl ModuleInstance {
}
/// Get an exported memory by name
- pub fn exported_memory(&self, store: &mut Store, name: &str) -> Result<MemoryRef> {
+ pub fn exported_memory<'a>(&self, store: &'a mut Store, name: &str) -> Result<MemoryRef<'a>> {
let export = self.export_addr(name).ok_or_else(|| Error::Other(format!("Export not found: {}", name)))?;
let ExternVal::Memory(mem_addr) = export else {
return Err(Error::Other(format!("Export is not a memory: {}", name)));
@@ -199,11 +204,28 @@ impl ModuleInstance {
Ok(mem)
}
+ /// Get an exported memory by name
+ pub fn exported_memory_mut<'a>(&self, store: &'a mut Store, name: &str) -> Result<MemoryRefMut<'a>> {
+ let export = self.export_addr(name).ok_or_else(|| Error::Other(format!("Export not found: {}", name)))?;
+ let ExternVal::Memory(mem_addr) = export else {
+ return Err(Error::Other(format!("Export is not a memory: {}", name)));
+ };
+ let mem = self.memory_mut(store, mem_addr)?;
+ Ok(mem)
+ }
+
/// Get a memory by address
- pub fn memory(&self, store: &Store, addr: MemAddr) -> Result<MemoryRef> {
+ pub fn memory<'a>(&self, store: &'a mut Store, addr: MemAddr) -> Result<MemoryRef<'a>> {
+ let addr = self.resolve_mem_addr(addr);
+ let mem = store.get_mem(addr as usize)?;
+ Ok(MemoryRef { instance: mem.borrow() })
+ }
+
+ /// Get a memory by address (mutable)
+ pub fn memory_mut<'a>(&self, store: &'a mut Store, addr: MemAddr) -> Result<MemoryRefMut<'a>> {
let addr = self.resolve_mem_addr(addr);
let mem = store.get_mem(addr as usize)?;
- Ok(MemoryRef { instance: mem.clone() })
+ Ok(MemoryRefMut { instance: mem.borrow_mut() })
}
/// Get the start function of the module
diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs
index ed09530..a34e30b 100644
--- a/crates/tinywasm/src/reference.rs
+++ b/crates/tinywasm/src/reference.rs
@@ -1,5 +1,5 @@
use core::{
- cell::{Ref, RefCell},
+ cell::{Ref, RefCell, RefMut},
ffi::CStr,
};
@@ -15,91 +15,121 @@ use tinywasm_types::WasmValue;
// This module essentially contains the public APIs to interact with the data stored in the store
/// A reference to a memory instance
-#[derive(Debug, Clone)]
-pub struct MemoryRef {
- pub(crate) instance: Rc<RefCell<MemoryInstance>>,
+#[derive(Debug)]
+pub struct MemoryRef<'a> {
+ pub(crate) instance: Ref<'a, MemoryInstance>,
}
/// A borrowed reference to a memory instance
#[derive(Debug)]
-pub struct BorrowedMemory<'a> {
- pub(crate) instance: Ref<'a, MemoryInstance>,
+pub struct MemoryRefMut<'a> {
+ pub(crate) instance: RefMut<'a, MemoryInstance>,
}
-impl<'a> BorrowedMemory<'a> {
+impl<'a> MemoryRefLoad for MemoryRef<'a> {
/// Load a slice of memory
- pub fn load(&self, offset: usize, len: usize) -> Result<&[u8]> {
+ fn load(&self, offset: usize, len: usize) -> Result<&[u8]> {
self.instance.load(offset, 0, len)
}
+}
- /// Load a C-style string from memory
- pub fn load_cstr(&self, offset: usize, len: usize) -> Result<&CStr> {
- let bytes = self.load(offset, len)?;
- CStr::from_bytes_with_nul(bytes).map_err(|_| crate::Error::Other("Invalid C-style string".to_string()))
+impl<'a> MemoryRefLoad for MemoryRefMut<'a> {
+ /// Load a slice of memory
+ fn load(&self, offset: usize, len: usize) -> Result<&[u8]> {
+ self.instance.load(offset, 0, len)
}
+}
- /// Load a C-style string from memory, stopping at the first nul byte
- pub fn load_cstr_until_nul(&self, offset: usize, max_len: usize) -> Result<&CStr> {
- let bytes = self.load(offset, max_len)?;
- CStr::from_bytes_until_nul(bytes).map_err(|_| crate::Error::Other("Invalid C-style string".to_string()))
+impl MemoryRef<'_> {
+ /// Load a slice of memory
+ pub fn load(&self, offset: usize, len: usize) -> Result<&[u8]> {
+ self.instance.load(offset, 0, len)
}
-}
-impl MemoryRef {
- /// Borrow the memory instance
- ///
- /// This is useful for when you want to load only a reference to a slice of memory
- /// without copying the data. The borrow should be dropped before any other memory
- /// operations are performed.
- pub fn borrow(&self) -> BorrowedMemory<'_> {
- BorrowedMemory { instance: self.instance.borrow() }
+ /// Load a slice of memory as a vector
+ pub fn load_vec(&self, offset: usize, len: usize) -> Result<Vec<u8>> {
+ self.load(offset, len).map(|x| x.to_vec())
}
+}
+impl MemoryRefMut<'_> {
/// Load a slice of memory
+ pub fn load(&self, offset: usize, len: usize) -> Result<&[u8]> {
+ self.instance.load(offset, 0, len)
+ }
+
+ /// Load a slice of memory as a vector
pub fn load_vec(&self, offset: usize, len: usize) -> Result<Vec<u8>> {
- self.instance.borrow().load(offset, 0, len).map(|x| x.to_vec())
+ self.load(offset, len).map(|x| x.to_vec())
}
/// Grow the memory by the given number of pages
- pub fn grow(&self, delta_pages: i32) -> Option<i32> {
- self.instance.borrow_mut().grow(delta_pages)
+ pub fn grow(&mut self, delta_pages: i32) -> Option<i32> {
+ self.instance.grow(delta_pages)
}
/// Get the current size of the memory in pages
- pub fn page_count(&self) -> usize {
- self.instance.borrow().page_count()
+ pub fn page_count(&mut self) -> usize {
+ self.instance.page_count()
}
/// Copy a slice of memory to another place in memory
- pub fn copy_within(&self, src: usize, dst: usize, len: usize) -> Result<()> {
- self.instance.borrow_mut().copy_within(src, dst, len)
+ pub fn copy_within(&mut self, src: usize, dst: usize, len: usize) -> Result<()> {
+ self.instance.copy_within(src, dst, len)
}
/// Fill a slice of memory with a value
- pub fn fill(&self, offset: usize, len: usize, val: u8) -> Result<()> {
- self.instance.borrow_mut().fill(offset, len, val)
+ pub fn fill(&mut self, offset: usize, len: usize, val: u8) -> Result<()> {
+ self.instance.fill(offset, len, val)
+ }
+
+ /// Store a slice of memory
+ pub fn store(&mut self, offset: usize, len: usize, data: &[u8]) -> Result<()> {
+ self.instance.store(offset, 0, data, len)
+ }
+}
+
+#[doc(hidden)]
+pub trait MemoryRefLoad {
+ fn load(&self, offset: usize, len: usize) -> Result<&[u8]>;
+ fn load_vec(&self, offset: usize, len: usize) -> Result<Vec<u8>> {
+ self.load(offset, len).map(|x| x.to_vec())
+ }
+}
+
+/// Convenience methods for loading strings from memory
+pub trait MemoryStringExt: MemoryRefLoad {
+ /// Load a C-style string from memory
+ fn load_cstr(&self, offset: usize, len: usize) -> Result<&CStr> {
+ let bytes = self.load(offset, len)?;
+ CStr::from_bytes_with_nul(bytes).map_err(|_| crate::Error::Other("Invalid C-style string".to_string()))
+ }
+
+ /// Load a C-style string from memory, stopping at the first nul byte
+ fn load_cstr_until_nul(&self, offset: usize, max_len: usize) -> Result<&CStr> {
+ let bytes = self.load(offset, max_len)?;
+ CStr::from_bytes_until_nul(bytes).map_err(|_| crate::Error::Other("Invalid C-style string".to_string()))
}
/// Load a UTF-8 string from memory
- pub fn load_string(&self, offset: usize, len: usize) -> Result<String> {
- let bytes = self.load_vec(offset, len)?;
- String::from_utf8(bytes).map_err(|_| crate::Error::Other("Invalid UTF-8 string".to_string()))
+ fn load_string(&self, offset: usize, len: usize) -> Result<String> {
+ let bytes = self.load(offset, len)?;
+ String::from_utf8(bytes.to_vec()).map_err(|_| crate::Error::Other("Invalid UTF-8 string".to_string()))
}
/// Load a C-style string from memory
- pub fn load_cstring(&self, offset: usize, len: usize) -> Result<CString> {
- Ok(CString::from(self.borrow().load_cstr(offset, len)?))
+ fn load_cstring(&self, offset: usize, len: usize) -> Result<CString> {
+ Ok(CString::from(self.load_cstr(offset, len)?))
}
/// Load a C-style string from memory, stopping at the first nul byte
- pub fn load_cstring_until_nul(&self, offset: usize, max_len: usize) -> Result<CString> {
- Ok(CString::from(self.borrow().load_cstr_until_nul(offset, max_len)?))
+ fn load_cstring_until_nul(&self, offset: usize, max_len: usize) -> Result<CString> {
+ Ok(CString::from(self.load_cstr_until_nul(offset, max_len)?))
}
/// Load a JavaScript-style utf-16 string from memory
- pub fn load_js_string(&self, offset: usize, len: usize) -> Result<String> {
- let memref = self.borrow();
- let bytes = memref.load(offset, len)?;
+ fn load_js_string(&self, offset: usize, len: usize) -> Result<String> {
+ let bytes = self.load(offset, len)?;
let mut string = String::new();
for i in 0..(len / 2) {
let c = u16::from_le_bytes([bytes[i * 2], bytes[i * 2 + 1]]);
@@ -109,13 +139,11 @@ impl MemoryRef {
}
Ok(string)
}
-
- /// Store a slice of memory
- pub fn store(&self, offset: usize, len: usize, data: &[u8]) -> Result<()> {
- self.instance.borrow_mut().store(offset, 0, data, len)
- }
}
+impl MemoryStringExt for MemoryRef<'_> {}
+impl MemoryStringExt for MemoryRefMut<'_> {}
+
/// A reference to a global instance
#[derive(Debug, Clone)]
pub struct GlobalRef {
diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs
index 3c3b776..79abbf3 100644
--- a/crates/tinywasm/src/runtime/interpreter/mod.rs
+++ b/crates/tinywasm/src/runtime/interpreter/mod.rs
@@ -418,12 +418,39 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
mem.fill(dst as usize, size as usize, val as u8)?;
}
- // MemoryInit(data_index, mem_index) => {}
- // DataDrop(data_index) => {
- // // let data_idx = module.resolve_data_addr(*data_index);
- // // let data = store.get_data(data_idx as usize)?;
- // // data.borrow_mut().drop()?;
- // }
+ MemoryInit(data_index, mem_index) => {
+ let size = stack.values.pop_t::<i32>()? as usize;
+ let offset = stack.values.pop_t::<i32>()? as usize;
+ let dst = stack.values.pop_t::<i32>()? as usize;
+
+ let data_idx = module.resolve_data_addr(*data_index);
+ let Some(ref data) = store.get_data(data_idx as usize)?.data else {
+ cold();
+ return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into());
+ };
+
+ let mem_idx = module.resolve_mem_addr(*mem_index);
+ let mem = store.get_mem(mem_idx as usize)?;
+
+ let data_len = data.len();
+ if offset + size > data_len {
+ cold();
+ return Err(Trap::MemoryOutOfBounds { offset, len: size, max: data_len }.into());
+ }
+
+ let mut mem = mem.borrow_mut();
+ let data = &data[offset..(offset + size)];
+
+ // mem.store checks bounds
+ mem.store(dst, 0, data, size)?;
+ }
+
+ DataDrop(data_index) => {
+ let data_idx = module.resolve_data_addr(*data_index);
+ let data = store.get_data_mut(data_idx as usize)?;
+ data.drop();
+ }
+
I32Store(arg) => mem_store!(i32, arg, stack, store, module),
I64Store(arg) => mem_store!(i64, arg, stack, store, module),
F32Store(arg) => mem_store!(f32, arg, stack, store, module),
diff --git a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs b/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs
index 095fe9f..5620249 100644
--- a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs
+++ b/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs
@@ -10,67 +10,83 @@ pub(super) trait FExt {
}
impl FExt for f64 {
+ #[inline]
fn round(self) -> Self {
libm::round(self)
}
+ #[inline]
fn abs(self) -> Self {
libm::fabs(self)
}
+ #[inline]
fn signum(self) -> Self {
libm::copysign(1.0, self)
}
+ #[inline]
fn ceil(self) -> Self {
libm::ceil(self)
}
+ #[inline]
fn floor(self) -> Self {
libm::floor(self)
}
+ #[inline]
fn trunc(self) -> Self {
libm::trunc(self)
}
+ #[inline]
fn sqrt(self) -> Self {
libm::sqrt(self)
}
+ #[inline]
fn copysign(self, other: Self) -> Self {
libm::copysign(self, other)
}
}
impl FExt for f32 {
+ #[inline]
fn round(self) -> Self {
libm::roundf(self)
}
+ #[inline]
fn abs(self) -> Self {
libm::fabsf(self)
}
+ #[inline]
fn signum(self) -> Self {
libm::copysignf(1.0, self)
}
+ #[inline]
fn ceil(self) -> Self {
libm::ceilf(self)
}
+ #[inline]
fn floor(self) -> Self {
libm::floorf(self)
}
+ #[inline]
fn trunc(self) -> Self {
libm::truncf(self)
}
+ #[inline]
fn sqrt(self) -> Self {
libm::sqrtf(self)
}
+ #[inline]
fn copysign(self, other: Self) -> Self {
libm::copysignf(self, other)
}
diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs
index f89b931..1526eb1 100644
--- a/crates/tinywasm/src/store/mod.rs
+++ b/crates/tinywasm/src/store/mod.rs
@@ -287,40 +287,38 @@ impl Store {
let data_count = self.data.datas.len();
let mut data_addrs = Vec::with_capacity(data_count);
for (i, data) in datas.into_iter().enumerate() {
- use tinywasm_types::DataKind::*;
- match data.kind {
- Active { mem: mem_addr, offset } => {
- // a. Assert: memidx == 0
- if mem_addr != 0 {
- return Err(Error::UnsupportedFeature("data segments for non-zero memories".to_string()));
- }
+ let data_val =
+ match data.kind {
+ tinywasm_types::DataKind::Active { mem: mem_addr, offset } => {
+ // a. Assert: memidx == 0
+ if mem_addr != 0 {
+ return Err(Error::UnsupportedFeature("data segments for non-zero memories".to_string()));
+ }
- let mem_addr = mem_addrs
- .get(mem_addr as usize)
- .copied()
- .ok_or_else(|| Error::Other(format!("memory {} not found for data segment {}", mem_addr, i)))?;
+ let mem_addr = mem_addrs.get(mem_addr as usize).copied().ok_or_else(|| {
+ Error::Other(format!("memory {} not found for data segment {}", mem_addr, i))
+ })?;
- let offset = self.eval_i32_const(&offset)?;
+ let offset = self.eval_i32_const(&offset)?;
- let mem =
- self.data.memories.get_mut(mem_addr as usize).ok_or_else(|| {
+ let mem = self.data.memories.get_mut(mem_addr as usize).ok_or_else(|| {
Error::Other(format!("memory {} not found for data segment {}", mem_addr, i))
})?;
- // See comment for active element sections in the function above why we need to do this here
- if let Err(Error::Trap(trap)) =
- mem.borrow_mut().store(offset as usize, 0, &data.data, data.data.len())
- {
- return Ok((data_addrs.into_boxed_slice(), Some(trap)));
- }
+ // See comment for active element sections in the function above why we need to do this here
+ if let Err(Error::Trap(trap)) =
+ mem.borrow_mut().store(offset as usize, 0, &data.data, data.data.len())
+ {
+ return Ok((data_addrs.into_boxed_slice(), Some(trap)));
+ }
- // drop the data
- continue;
- }
- Passive => {}
- }
+ // drop the data
+ None
+ }
+ tinywasm_types::DataKind::Passive => Some(data.data.to_vec()),
+ };
- self.data.datas.push(DataInstance::new(data.data.to_vec(), idx));
+ self.data.datas.push(DataInstance::new(data_val, idx));
data_addrs.push((i + data_count) as Addr);
}
@@ -413,6 +411,16 @@ impl Store {
self.data.tables.get(addr).ok_or_else(|| Error::Other(format!("table {} not found", addr)))
}
+ /// Get the data at the actual index in the store
+ pub(crate) fn get_data(&self, addr: usize) -> Result<&DataInstance> {
+ self.data.datas.get(addr).ok_or_else(|| Error::Other(format!("table {} not found", addr)))
+ }
+
+ /// Get the data at the actual index in the store
+ pub(crate) fn get_data_mut(&mut self, addr: usize) -> Result<&mut DataInstance> {
+ self.data.datas.get_mut(addr).ok_or_else(|| Error::Other(format!("table {} not found", addr)))
+ }
+
/// Get the element at the actual index in the store
pub(crate) fn get_elem(&self, addr: usize) -> Result<&ElementInstance> {
self.data.elements.get(addr).ok_or_else(|| Error::Other(format!("element {} not found", addr)))
@@ -621,12 +629,22 @@ impl ElementInstance {
/// See <https://webassembly.github.io/spec/core/exec/runtime.html#data-instances>
#[derive(Debug)]
pub(crate) struct DataInstance {
- pub(crate) _data: Vec<u8>,
+ pub(crate) data: Option<Vec<u8>>,
pub(crate) _owner: ModuleInstanceAddr, // index into store.module_instances
}
impl DataInstance {
- pub(crate) fn new(data: Vec<u8>, owner: ModuleInstanceAddr) -> Self {
- Self { _data: data, _owner: owner }
+ pub(crate) fn new(data: Option<Vec<u8>>, owner: ModuleInstanceAddr) -> Self {
+ Self { data, _owner: owner }
+ }
+
+ pub(crate) fn drop(&mut self) -> Option<()> {
+ match self.data {
+ None => None,
+ Some(_) => {
+ let _ = self.data.take();
+ Some(())
+ }
+ }
}
}
diff --git a/crates/tinywasm/tests/generated/2.0.csv b/crates/tinywasm/tests/generated/2.0.csv
index 404fe16..fbf18ae 100644
--- a/crates/tinywasm/tests/generated/2.0.csv
+++ b/crates/tinywasm/tests/generated/2.0.csv
@@ -1,2 +1,2 @@
0.3.0,26722,1161,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"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":8,"failed":109},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"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":1},{"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":900,"failed":0},{"name":"float_literals.wast","passed":163,"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":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":171,"failed":12},{"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":79,"failed":0},{"name":"memory_copy.wast","passed":3928,"failed":522},{"name":"memory_fill.wast","passed":64,"failed":36},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":177,"failed":63},{"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":"ref_func.wast","passed":9,"failed":8},{"name":"ref_is_null.wast","passed":4,"failed":12},{"name":"ref_null.wast","passed":1,"failed":2},{"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":1613,"failed":115},{"name":"table_fill.wast","passed":23,"failed":22},{"name":"table_get.wast","passed":10,"failed":6},{"name":"table_grow.wast","passed":21,"failed":29},{"name":"table_init.wast","passed":594,"failed":186},{"name":"table_set.wast","passed":19,"failed":7},{"name":"table_size.wast","passed":8,"failed":31},{"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.4.0-alpha.0,27464,419,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"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":53,"failed":64},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"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":99,"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":900,"failed":0},{"name":"float_literals.wast","passed":163,"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":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":183,"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":79,"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":177,"failed":63},{"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":"ref_func.wast","passed":9,"failed":8},{"name":"ref_is_null.wast","passed":4,"failed":12},{"name":"ref_null.wast","passed":1,"failed":2},{"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":1613,"failed":115},{"name":"table_fill.wast","passed":23,"failed":22},{"name":"table_get.wast","passed":10,"failed":6},{"name":"table_grow.wast","passed":21,"failed":29},{"name":"table_init.wast","passed":720,"failed":60},{"name":"table_set.wast","passed":19,"failed":7},{"name":"table_size.wast","passed":8,"failed":31},{"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.4.0-alpha.0,27549,334,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"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":75,"failed":42},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"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":99,"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":900,"failed":0},{"name":"float_literals.wast","passed":163,"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":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":183,"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":79,"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":"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":"ref_func.wast","passed":9,"failed":8},{"name":"ref_is_null.wast","passed":4,"failed":12},{"name":"ref_null.wast","passed":1,"failed":2},{"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":1613,"failed":115},{"name":"table_fill.wast","passed":23,"failed":22},{"name":"table_get.wast","passed":10,"failed":6},{"name":"table_grow.wast","passed":21,"failed":29},{"name":"table_init.wast","passed":720,"failed":60},{"name":"table_set.wast","passed":19,"failed":7},{"name":"table_size.wast","passed":8,"failed":31},{"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}]