Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions crates/checked/src/store.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use alloc::{string::String, vec::Vec};
use alloc::vec::Vec;
use dlr_wasm_interpreter::{
Config, FuncAddr, FuncType, GlobalAddr, GlobalType, HostResumable, Hostcode, MemAddr, MemType,
Module, ModuleAddr, RuntimeError, TableAddr, TableType, WasmResumable,
Expand Down Expand Up @@ -527,7 +527,7 @@ impl<'b, T: Config> Store<'b, T> {
pub fn instance_exports(
&self,
module_addr: Stored<ModuleAddr>,
) -> Vec<(String, StoredExternVal)> {
) -> impl ExactSizeIterator<Item = (&'b str, StoredExternVal)> + '_ {
// 1. try unwrap
let module_addr = module_addr.try_unwrap_into_bare(self.id);
// 2. call
Expand All @@ -536,13 +536,10 @@ impl<'b, T: Config> Store<'b, T> {
let exports = unsafe { self.inner.instance_exports(module_addr) };
// 3. rewrap
// 4. return
exports
.into_iter()
.map(|(name, externval)| {
// SAFETY: The `ExternVal`s just came from the current store.
let stored_externval = unsafe { StoredExternVal::from_bare(externval, self.id) };
(name, stored_externval)
})
.collect()
exports.map(|(name, externval)| {
// SAFETY: The `ExternVal`s just came from the current store.
let stored_externval = unsafe { StoredExternVal::from_bare(externval, self.id) };
(name, stored_externval)
})
}
}
2 changes: 1 addition & 1 deletion crates/linker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ impl Linker {
// SAFETY: The module and thus also its exported extern values come
// from the same store used now. Therefore, the extern values must
// be valid in this store.
unsafe { self.define(module_name.clone(), export.0, export.1)? };
unsafe { self.define(module_name.clone(), export.0.to_owned(), export.1)? };
}

Ok(())
Expand Down
4 changes: 2 additions & 2 deletions src/core/decoding/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ impl<'a> WasmDecoder<'a> {
///
/// May panic if the closure moved the [`pc`](Self::pc) backwards, e.g. when
/// [move_start_to](Self::move_start_to) is called.
pub fn measure_num_read_bytes<T, E>(
pub fn measure_num_read_bytes<T: 'a, E>(
&mut self,
f: impl FnOnce(&mut WasmDecoder) -> Result<T, E>,
f: impl FnOnce(&mut WasmDecoder<'a>) -> Result<T, E>,
) -> Result<(T, usize), E> {
let before = self.pc;
let ret = f(self)?;
Expand Down
4 changes: 2 additions & 2 deletions src/core/decoding/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub enum DecodingError {
/// Therefore, 33-bit signed integers may never be negative.
I33IsNegative,
/// A function specifies too many locals, i.e. more than 2^32 - 1
TooManyLocals(u64),
TooManyLocals,
/// A section's contents were successfully decoded, but either too many or not enough bytes of
/// the section's bytecode were consumed.
SectionSizeMismatch,
Expand Down Expand Up @@ -75,7 +75,7 @@ impl fmt::Display for DecodingError {
DecodingError::MalformedVariableLengthInteger => write!(f, "Reading a variable-length integer overflowed"),
DecodingError::MalformedElemKindDiscriminator(byte) => write!(f, "Failed to parse {byte:#x} as an element kind discriminator"),
DecodingError::I33IsNegative => f.write_str("An i33 type is negative which is not allowed"),
DecodingError::TooManyLocals(n) => write!(f,"There are {n} locals and this exceeds the maximum allowed number of 2^32-1"),
DecodingError::TooManyLocals => write!(f,"A function specifies too many locals, i.e. more than of 2^32-1"),
DecodingError::SectionSizeMismatch => f.write_str("A section's contents were successfully decoded, but either too many or not enough bytes of the section's bytecode were consumed."),
DecodingError::SectionOutOfOrder(ty) => write!(f, "A section of type `{ty:?}` is defined out of order"),
}
Expand Down
86 changes: 54 additions & 32 deletions src/core/decoding/modules/code_section.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,66 @@
use alloc::vec::Vec;
use core::iter;

use crate::{
core::{decoding::decoder::WasmDecoder, utils::ToUsizeExt},
DecodingError, ValType,
};

pub fn decode_locals(wasm: &mut WasmDecoder) -> Result<Vec<ValType>, DecodingError> {
let locals = wasm.decode_vec(|wasm| {
let n = wasm.decode_var_u32()?.into_usize();
let valtype = ValType::decode(wasm)?;

Ok((n, valtype))
})?;

// these checks are related to the official test suite binary.wast file, the first 2 assert_malformed's starting at line 350
// we check to not have more than 2^32-1 locals, and if that number is okay, we then get to instantiate them all
// this is because the flat_map and collect take an insane amount of time
// in total, these 2 tests take more than 240s
let mut total_no_of_locals: u64 = 0;
for local in &locals {
let temp = local.0 as u64;
if temp > u32::MAX.into() {
return Err(DecodingError::TooManyLocals(total_no_of_locals));
};
total_no_of_locals = match total_no_of_locals.checked_add(temp) {
None => return Err(DecodingError::TooManyLocals(total_no_of_locals)),
Some(n) => n,
}
}
pub fn decode_locals<'a, 'wasm>(
wasm: &'a mut WasmDecoder<'wasm>,
) -> Result<impl ExactSizeIterator<Item = ValType> + use<'a, 'wasm>, DecodingError> {
// First pass to decode all locals and check if their total number exceeds 2^32-1
let mut total_number_of_locals = 0_u32;
let mut wasm_cloned = wasm.clone();
wasm_cloned
.decode_vec_map(|wasm| {
let n = wasm.decode_var_u32()?;
let _valtype = ValType::decode(wasm)?;

total_number_of_locals = total_number_of_locals
.checked_add(n)
.ok_or(DecodingError::TooManyLocals)?;

Ok(())
})?
.collect::<Result<(), DecodingError>>()?;

// Second pass to flatten locals
let locals = wasm
.decode_vec_map::<(u32, ValType), _, DecodingError>(|wasm| {
let n = wasm.decode_var_u32().unwrap();
let valtype = ValType::decode(wasm).unwrap();

Ok((n, valtype))
})
.unwrap()
.map(Result::unwrap)
.flat_map(|res| iter::repeat_n(res.1, res.0.into_usize()));

Ok(WithExactSize {
i: locals,
len: total_number_of_locals.into_usize(),
})
}

if total_no_of_locals > u32::MAX.into() {
return Err(DecodingError::TooManyLocals(total_no_of_locals));
struct WithExactSize<I> {
i: I,
len: usize,
}

impl<T, I: Iterator<Item = T>> Iterator for WithExactSize<I> {
type Item = T;

fn next(&mut self) -> Option<Self::Item> {
self.i.next()
}

// Flatten local types for easier representation where n > 1
let locals = locals
.into_iter()
.flat_map(|entry| iter::repeat_n(entry.1, entry.0))
.collect::<Vec<ValType>>();
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}

Ok(locals)
impl<T, I: Iterator<Item = T>> ExactSizeIterator for WithExactSize<I> {
fn len(&self) -> usize {
self.len
}
}
5 changes: 2 additions & 3 deletions src/core/decoding/modules/custom_section.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
core::decoding::decoder::{span::Span, WasmDecoder},
DecodingError, ValidationError,
DecodingError,
};

#[derive(Debug, Clone)]
Expand All @@ -10,11 +10,10 @@ pub struct CustomSection<'wasm> {
}

impl<'wasm> CustomSection<'wasm> {
// TODO this should return a Result<_, DecodingError>
pub(crate) fn decode(
wasm: &mut WasmDecoder<'wasm>,
section_contents: Span,
) -> Result<CustomSection<'wasm>, ValidationError> {
) -> Result<CustomSection<'wasm>, DecodingError> {
// customsec ::= section_0(custom)
// custom ::= name byte*
// name ::= b*:vec(byte) => name (if utf8(name) = b*)
Expand Down
6 changes: 5 additions & 1 deletion src/core/decoding/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
//! [^validation]: [WebAssembly Specification 2.0 - 3.2. Types](https://www.w3.org/TR/2025/CRD-wasm-core-2-20250616/#types%E2%91%A4).
//! [^binary-format]: [WebAssembly Specification 2.0 - 5.3. Types](https://www.w3.org/TR/2025/CRD-wasm-core-2-20250616/#types%E2%91%A6).

use alloc::vec::Vec;

use crate::{
core::{
decoding::decoder::WasmDecoder,
Expand Down Expand Up @@ -101,7 +103,9 @@ impl ResultType {
/// [^binary-format]: [WebAssembly Specification 2.0 - 5.3.5. Result Types](https://www.w3.org/TR/2025/CRD-wasm-core-2-20250616/#binary-resulttype).
/// [^always-valid]: [WebAssembly Specification 2.0 - 3.2. Types](https://www.w3.org/TR/2025/CRD-wasm-core-2-20250616/#types%E2%91%A4).
pub fn decode(wasm: &mut WasmDecoder) -> Result<Self, DecodingError> {
let valtypes = wasm.decode_vec(ValType::decode)?;
let valtypes = wasm
.decode_vec_map(ValType::decode)?
.collect::<Result<Vec<_>, DecodingError>>()?;

Ok(ResultType { valtypes })
}
Expand Down
59 changes: 38 additions & 21 deletions src/core/decoding/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
//! Note: If any of these methods return `Err`, they may have consumed some bytes from the [WasmDecoder] object and thus consequent calls may result in unexpected behaviour.
//! This is due to the fact that these methods read elemental types which cannot be split.

use alloc::vec::Vec;

use crate::{
core::{decoding::decoder::WasmDecoder, utils::ToUsizeExt},
DecodingError,
Expand Down Expand Up @@ -366,36 +364,55 @@ impl<'wasm> WasmDecoder<'wasm> {
core::str::from_utf8(utf8_str).map_err(DecodingError::MalformedUtf8)
}

// TODO remove, see note on read_vec for more info
pub fn decode_vec_enumerated<T, F, E>(&mut self, mut read_element: F) -> Result<Vec<T>, E>
/// A version of [`WasmDecoder::decode_vec_map`] that enumerates all elements.
pub fn decode_vec_enumerate_map<'a, 'f, T, F, E>(
&'a mut self,
mut read_element: F,
) -> Result<
impl ExactSizeIterator<Item = Result<T, E>> + use<'a, 'wasm, 'f, F, T, E>,
DecodingError,
>
where
T: 'wasm,
F: FnMut(&mut WasmDecoder<'wasm>, u32) -> Result<T, E>,
F: FnMut(&mut WasmDecoder<'wasm>, u32) -> Result<T, E> + 'f,
E: From<DecodingError>,
{
let mut idx = 0;
self.decode_vec(|wasm| {
let ret = read_element(wasm, idx);
idx = idx
.checked_add(1)
.expect("the length of vectors to be encoded as a u32");
ret
})
let len = self.decode_var_u32()?;
let i = (0..len).map(move |n| read_element(self, n));

Ok(i)
}

/// Note: If `Err`, the [WasmDecoder] object is no longer guaranteed to be in a valid state
// TODO make this return `impl ExactSizeIterator<Item = T>` to prevent allocation. This will be
// usedful if we want to decode some information on-demand in the future.
pub fn decode_vec<T, F, E>(&mut self, mut read_element: F) -> Result<Vec<T>, E>
/// Decodes a vector and maps every element with the given closure. An iterator with a maximum
/// fixed size of [`u32::MAX`] is returned.
pub fn decode_vec_map<'a, 'f, T, F, E>(
&'a mut self,
mut read_element: F,
) -> Result<impl Iterator<Item = Result<T, E>> + use<'a, 'wasm, 'f, F, T, E>, DecodingError>
where
T: 'wasm,
F: FnMut(&mut WasmDecoder<'wasm>) -> Result<T, E>,
F: FnMut(&mut WasmDecoder<'wasm>) -> Result<T, E> + 'f,
E: From<DecodingError>,
{
let len = self.decode_var_u32()?;
core::iter::repeat_with(|| read_element(self))
.take(len.into_usize())
.collect()
let all_elements = (0..len).map(move |_| read_element(self));

// End the iterator after the first error was been encountered.
let mut found_err = false;
let elements_until_first_error_inclusive = all_elements.take_while(move |result| {
if result.is_err() {
if found_err {
false
} else {
found_err = true;
true
}
} else {
true
}
});

Ok(elements_until_first_error_inclusive)
}
}

Expand Down
16 changes: 16 additions & 0 deletions src/core/fixed_capacity_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,22 @@ impl<T> FixedCapacityVec<T> {
None
}
}

/// Drops all elements in this vector and sets its length to zero. The capacity of this vector
/// remains unaltered.
pub fn clear(&mut self) {
// SAFETY: self.len is always less or equal to the capacity, i.e. the length of
// self.elements. Therefore, this must always return a valid slice.
let elements_to_drop = unsafe { self.elements.get_unchecked_mut(0..self.len) };
for element in elements_to_drop {
// SAFETY: This is guaranteed to be initialized, as `self.len` accurately tracks the
// number of initialized values and elements_to_drop is exactly the slice starting at
// index 0 and ending at index `self.len` (exclusively).
unsafe { element.assume_init_drop() };
}

self.len = 0;
}
}

impl<T: Clone> FixedCapacityVec<T> {
Expand Down
17 changes: 12 additions & 5 deletions src/core/structure/import_subtyping.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{ExternType, Limits};
use crate::{core::structure::types::ExternTypeRef, ExternType, Limits};

//https://webassembly.github.io/spec/core/valid/types.html#import-subtyping
pub trait ImportSubTypeRelation {
Expand All @@ -21,18 +21,25 @@ impl ImportSubTypeRelation for Limits {
}

impl ImportSubTypeRelation for ExternType {
// https://webassembly.github.io/spec/core/valid/types.html#match-limits
fn is_subtype_of(&self, other: &Self) -> bool {
self.as_ref().is_subtype_of(&other.as_ref())
}
}

impl ImportSubTypeRelation for ExternTypeRef<'_> {
// https://webassembly.github.io/spec/core/valid/types.html#match-limits
fn is_subtype_of(&self, other: &Self) -> bool {
match self {
ExternType::Table(self_table_type) => match other {
ExternType::Table(other_table_type) => {
ExternTypeRef::Table(self_table_type) => match other {
ExternTypeRef::Table(other_table_type) => {
self_table_type.lim.is_subtype_of(&other_table_type.lim)
&& self_table_type.et == other_table_type.et
}
_ => false,
},
ExternType::Mem(self_mem_type) => match other {
ExternType::Mem(other_mem_type) => {
ExternTypeRef::Mem(self_mem_type) => match other {
ExternTypeRef::Mem(other_mem_type) => {
self_mem_type.limits.is_subtype_of(&other_mem_type.limits)
}
_ => false,
Expand Down
Loading
Loading