Skip to content

Commit

Permalink
feat: elements
Browse files Browse the repository at this point in the history
Signed-off-by: nerodesu017 <[email protected]>

Signed-off-by: nerodesu017 <[email protected]>
  • Loading branch information
nerodesu017 committed Dec 9, 2024
1 parent 400533d commit dbca01a
Show file tree
Hide file tree
Showing 25 changed files with 5,233 additions and 67 deletions.
99 changes: 98 additions & 1 deletion src/core/error.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use alloc::format;
use alloc::string::{String, ToString};

use crate::core::indices::GlobalIdx;
use crate::validation_stack::LabelKind;
use crate::RefType;
use core::fmt::{Display, Formatter};
use core::str::Utf8Error;

use crate::core::reader::section_header::SectionTy;
use crate::core::reader::types::ValType;

use super::indices::{DataIdx, MemIdx};
use super::indices::{DataIdx, ElemIdx, FuncIdx, MemIdx, TableIdx, TypeIdx};

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum RuntimeError {
Expand All @@ -17,6 +21,18 @@ pub enum RuntimeError {
// https://github.com/wasmi-labs/wasmi/blob/37d1449524a322817c55026eb21eb97dd693b9ce/crates/core/src/trap.rs#L265C5-L265C27
BadConversionToInteger,
MemoryAccessOutOfBounds,
TableAccessOutOfBounds,
ElementAccessOutOfBounds,
UninitializedElement,
SignatureMismatch,
ExpectedAValueOnTheStack,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum StoreInstantiationError {
ActiveDataWriteOutOfBounds,
I64ValueOutOfReach(String),
MissingValueOnTheStack,
}

#[derive(Debug, PartialEq, Eq, Clone)]
Expand Down Expand Up @@ -51,11 +67,23 @@ pub enum Error {
GlobalIsConst,
RuntimeError(RuntimeError),
FoundLabel(LabelKind),
FoundUnspecifiedValTypes,
MemoryIsNotDefined(MemIdx),
// mem.align, wanted alignment
ErroneousAlignment(u32, u32),
NoDataSegments,
DataSegmentNotFound(DataIdx),
UnknownTable,
TableIsNotDefined(TableIdx),
ElementIsNotDefined(ElemIdx),
DifferentRefTypes(RefType, RefType),
ExpectedARefType(ValType),
WrongRefTypeForInteropValue(RefType, RefType),
FunctionIsNotDefined(FuncIdx),
ReferencingAnUnreferencedFunction(FuncIdx),
FunctionTypeIsNotDefined(TypeIdx),
StoreInstantiationError(StoreInstantiationError),
OnlyFuncRefIsAllowed,
}

impl Display for Error {
Expand Down Expand Up @@ -131,6 +159,7 @@ impl Display for Error {
Error::FoundLabel(lk) => f.write_fmt(format_args!(
"Expecting a ValType, a Label was found: {lk:?}"
)),
Error::FoundUnspecifiedValTypes => f.write_str("Found UnspecifiedValTypes"),
Error::ExpectedAnOperand => f.write_str("Expected a ValType"), // Error => f.write_str("Expected an operand (ValType) on the stack")
Error::MemoryIsNotDefined(memidx) => f.write_fmt(format_args!(
"C.mems[{}] is NOT defined when it should be",
Expand All @@ -146,6 +175,41 @@ impl Display for Error {
Error::DataSegmentNotFound(data_idx) => {
f.write_fmt(format_args!("Data Segment {} not found", data_idx))
}
Error::UnknownTable => f.write_str("Unknown Table"),
Error::TableIsNotDefined(table_idx) => f.write_fmt(format_args!(
"C.tables[{}] is NOT defined when it should be",
table_idx
)),
Error::ElementIsNotDefined(elem_idx) => f.write_fmt(format_args!(
"C.elems[{}] is NOT defined when it should be",
elem_idx
)),
Error::DifferentRefTypes(rref1, rref2) => f.write_fmt(format_args!(
"RefType {:?} is NOT equal to RefType {:?}",
rref1, rref2
)),
Error::ExpectedARefType(found_valtype) => f.write_fmt(format_args!(
"Expected a RefType, found a {:?} instead",
found_valtype
)),
Error::WrongRefTypeForInteropValue(ref_given, ref_wanted) => f.write_fmt(format_args!(
"Wrong RefType for InteropValue: Given {:?} - Needed {:?}",
ref_given, ref_wanted
)),
Error::FunctionIsNotDefined(func_idx) => f.write_fmt(format_args!(
"C.functions[{}] is NOT defined when it should be",
func_idx
)),
Error::ReferencingAnUnreferencedFunction(func_idx) => f.write_fmt(format_args!(
"Referenced a function ({}) that was not referenced in validation",
func_idx
)),
Error::FunctionTypeIsNotDefined(func_ty_idx) => f.write_fmt(format_args!(
"C.fn_types[{}] is NOT defined when it should be",
func_ty_idx
)),
Error::StoreInstantiationError(err) => err.fmt(f),
Error::OnlyFuncRefIsAllowed => f.write_str("Only FuncRef is allowed"),
}
}
}
Expand All @@ -159,6 +223,33 @@ impl Display for RuntimeError {
RuntimeError::StackSmash => f.write_str("Stack smashed"),
RuntimeError::BadConversionToInteger => f.write_str("Bad conversion to integer"),
RuntimeError::MemoryAccessOutOfBounds => f.write_str("Memory access out of bounds"),
RuntimeError::TableAccessOutOfBounds => f.write_str("Table access out of bounds"),
RuntimeError::ElementAccessOutOfBounds => f.write_str("Element access out of bounds"),
RuntimeError::UninitializedElement => f.write_str("Uninitialized element"),
RuntimeError::SignatureMismatch => f.write_str("Indirect call signature mismatch"),
RuntimeError::ExpectedAValueOnTheStack => {
f.write_str("Expected a value on the stack, but None was found")
}
}
}
}

impl Display for StoreInstantiationError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
use StoreInstantiationError::*;
match self {
ActiveDataWriteOutOfBounds => {
f.write_str("Active data writing in memory is out of bounds")
}
I64ValueOutOfReach(s) => f.write_fmt(format_args!(
"I64 value {}is out of reach",
if !s.is_empty() {
format!("for {s} ")
} else {
"".to_string()
}
)),
MissingValueOnTheStack => f.write_str(""),
}
}
}
Expand All @@ -170,3 +261,9 @@ impl From<RuntimeError> for Error {
Self::RuntimeError(value)
}
}

impl From<StoreInstantiationError> for Error {
fn from(value: StoreInstantiationError) -> Self {
Self::StoreInstantiationError(value)
}
}
6 changes: 3 additions & 3 deletions src/core/reader/types/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl WasmReadable for DataSegment {
0 => {
// active { memory 0, offset e }
trace!("Data section: active");
let offset = { read_constant_instructions(wasm, None, None)? };
let offset = { read_constant_instructions(wasm, None, None, None)? };

let byte_vec = wasm.read_vec(|el| el.read_u8())?;

Expand Down Expand Up @@ -81,7 +81,8 @@ impl WasmReadable for DataSegment {
0 => {
// active { memory 0, offset e }
trace!("Data section: active");
let offset = { read_constant_instructions(wasm, None, None).unwrap_validated() };
let offset =
{ read_constant_instructions(wasm, None, None, None).unwrap_validated() };

let byte_vec = wasm
.read_vec(|el| Ok(el.read_u8().unwrap_validated()))
Expand Down Expand Up @@ -154,7 +155,6 @@ impl Debug for DataSegment {
/// ;; for hardcoded offsets, we'll usually use i32.const because of wasm being x86 arch
/// )
/// ```
///
/// Since the span has only the start and length and acts a reference, we print the start and end (both inclusive, notice the '..=')
/// We print it in both decimal and hexadecimal so it's easy to trace in something like <https://webassembly.github.io/wabt/demo/wat2wasm/>
Expand Down
164 changes: 164 additions & 0 deletions src/core/reader/types/element.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
use super::RefType;
use crate::core::reader::span::Span;
use crate::core::reader::WasmReader;
use crate::read_constant_expression::read_constant_instructions;
use crate::{Error, Result};

use alloc::collections::btree_set::BTreeSet;
use alloc::vec::Vec;
use core::fmt::Debug;

#[derive(Clone)]
pub struct ElemType {
pub init: ElemItems,
pub mode: ElemMode,
}

impl Debug for ElemType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"ElemType {{\n\tinit: {:?},\n\tmode: {:?},\n\t#ty: {:?}\n}}",
self.init,
self.mode,
self.init.ty()
)
}
}

impl ElemType {
pub fn ty(&self) -> RefType {
self.init.ty()
}

pub fn to_ref_type(&self) -> RefType {
match self.init {
ElemItems::Exprs(rref, _) => rref,
ElemItems::RefFuncs(_) => RefType::FuncRef,
}
}

// TODO: @nerodesu017 maybe split up the validation from the parsing?
/// Here we can't implement WasmReadable because we also want a mutable
/// reference to a [`BTreeSet<u32>`] (`referenced_functions`)
///
/// This comes in handy later on when we are validating the actual code of
/// the functions so that we can make sure we are not referencing invalid
/// functions
pub fn read_from_wasm(
wasm: &mut WasmReader,
functions: &[usize],
referenced_functions: &mut BTreeSet<u32>,
tables_length: usize,
) -> Result<Vec<Self>> {
wasm.read_vec(|wasm| {
let prop = wasm.read_var_u32()?;

// TODO: @nerodesu017 revisit this comment
// https://webassembly.github.io/spec/core/syntax/modules.html#element-segments
// https://webassembly.github.io/spec/core/binary/modules.html#element-section
// We can treat the ty as a 3bit integer
// If it's not 3 bits I am not sure what to do
// bit 0 => diff between passive|declartive and active segment
// bit 1 => presence of an explicit table index for an active segment
// bit 2 => use of element type and element expressions instead of element kind and element indices
// decide if we should

// TODO: @nerodesu017 error, this is a parse error, not validation FYI
// NOTE: This assert breaks my rustfmt :(
// assert!(prop <= 0b111, "Element section is not encoded correctly. The type of this element is over 7 (0b111)");

let elem_mode = if prop & 0b011 == 0b011 {
ElemMode::Declarative
} else if prop & 0b001 == 0b001 {
ElemMode::Passive
} else {
let table_idx = if prop & 0b010 == 0b010 {
wasm.read_var_u32()?
} else {
0
};

if tables_length <= table_idx as usize {
return Err(Error::UnknownTable);
}

let init_expr = read_constant_instructions(wasm, None, None, Some(functions))?;

ElemMode::Active(ActiveElem {
table_idx,
init_expr,
})
};

let third_bit_set = prop & 0b100 == 0b100;

let type_kind = if prop & 0b011 != 0 {
if third_bit_set {
Some(wasm.read_u8()?)
} else {
match wasm.read_u8()? {
0x00 => None,
_ => return Err(Error::OnlyFuncRefIsAllowed),
}
}
} else {
None
};

let reftype_or_elemkind: Option<RefType> = match type_kind {
Some(ty) => Some(RefType::from_byte(ty)?),
None => None,
};

let items: ElemItems = if third_bit_set {
ElemItems::Exprs(
reftype_or_elemkind.unwrap_or(RefType::FuncRef),
wasm.read_vec(|w| read_constant_instructions(w, None, None, Some(functions)))?,
)
} else {
assert!(reftype_or_elemkind.is_none());
ElemItems::RefFuncs(wasm.read_vec(|w| {
let offset = w.read_var_u32()?;
referenced_functions.insert(offset);
Ok(offset)
})?)
};

let el = ElemType {
init: items,
mode: elem_mode,
};

Ok(el)
})
}
}

#[derive(Debug, Clone)]
pub enum ElemItems {
RefFuncs(Vec<u32>),
Exprs(RefType, Vec<Span>),
}

impl ElemItems {
pub fn ty(&self) -> RefType {
match self {
Self::RefFuncs(_) => RefType::FuncRef,
Self::Exprs(rty, _) => *rty,
}
}
}

#[derive(Debug, Clone)]
pub enum ElemMode {
Passive,
Active(ActiveElem),
Declarative,
}

#[derive(Debug, Clone)]
pub struct ActiveElem {
pub table_idx: u32,
pub init_expr: Span,
}
9 changes: 6 additions & 3 deletions src/core/reader/types/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use crate::core::reader::{WasmReadable, WasmReader};
use crate::execution::assert_validated::UnwrapValidatedExt;
use crate::{unreachable_validated, Error, Result};

use super::TableType;

#[derive(Debug)]
pub struct Import {
#[allow(warnings)]
Expand Down Expand Up @@ -47,7 +49,7 @@ pub enum ImportDesc {
#[allow(dead_code)]
Func(TypeIdx),
#[allow(dead_code)]
Table(()),
Table(TableType),
// TODO TableType
#[allow(dead_code)]
Mem(()),
Expand All @@ -60,7 +62,8 @@ impl WasmReadable for ImportDesc {
fn read(wasm: &mut WasmReader) -> Result<Self> {
let desc = match wasm.read_u8()? {
0x00 => Self::Func(wasm.read_var_u32()? as TypeIdx),
0x01 => todo!("read TableType"),
// https://webassembly.github.io/spec/core/binary/types.html#table-types
0x01 => Self::Table(TableType::read(wasm)?),
0x02 => todo!("read MemType"),
0x03 => todo!("read GlobalType"),
other => return Err(Error::InvalidImportDesc(other)),
Expand All @@ -72,7 +75,7 @@ impl WasmReadable for ImportDesc {
fn read_unvalidated(wasm: &mut WasmReader) -> Self {
match wasm.read_u8().unwrap_validated() {
0x00 => Self::Func(wasm.read_var_u32().unwrap_validated() as TypeIdx),
0x01 => todo!("read TableType"),
0x01 => Self::Table(TableType::read_unvalidated(wasm)),
0x02 => todo!("read MemType"),
0x03 => todo!("read GlobalType"),
_ => unreachable_validated!(),
Expand Down
Loading

0 comments on commit dbca01a

Please sign in to comment.