Skip to content

Commit

Permalink
feat: elements
Browse files Browse the repository at this point in the history
  • Loading branch information
nerodesu017 committed Nov 25, 2024
1 parent 400533d commit 5f2da3d
Show file tree
Hide file tree
Showing 23 changed files with 5,314 additions and 46 deletions.
55 changes: 54 additions & 1 deletion src/core/error.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
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 +18,10 @@ pub enum RuntimeError {
// https://github.com/wasmi-labs/wasmi/blob/37d1449524a322817c55026eb21eb97dd693b9ce/crates/core/src/trap.rs#L265C5-L265C27
BadConversionToInteger,
MemoryAccessOutOfBounds,
TableAccessOutOfBounds,
ElementAccessOutOfBounds,
UninitializedElement,
SignatureMismatch,
}

#[derive(Debug, PartialEq, Eq, Clone)]
Expand Down Expand Up @@ -51,11 +56,21 @@ 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),
}

impl Display for Error {
Expand Down Expand Up @@ -131,6 +146,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 +162,39 @@ 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
)),
}
}
}
Expand All @@ -159,6 +208,10 @@ 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"),
}
}
}
Expand Down
5 changes: 3 additions & 2 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
165 changes: 165 additions & 0 deletions src/core/reader/types/element.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
use core::fmt::Debug;

use alloc::vec::Vec;

use crate::{
core::reader::span::Span, read_constant_expression::read_constant_instructions, Error, Result,
};

use super::RefType;

#[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,
}
}

pub fn read_from_wasm(
wasm: &mut crate::core::reader::WasmReader,
functions: &[usize],
referenced_functions: &mut alloc::collections::btree_set::BTreeSet<u32>,
tables_length: usize,
) -> Result<Vec<Self>> {
use crate::core::reader::types::element::*;
use crate::RefType;
// https://webassembly.github.io/spec/core/binary/modules.html#element-section

wasm.read_vec(|wasm| {
let ty = wasm.read_var_u32().unwrap();
// 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 ttype 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
assert!(ty <= 0b111, "Element section is not encoded correctly. The type of this element is over 7 (0b111)");
// decide if we should
let elem_mode = if ty & 0b001 == 0b001 {
if ty & 0b010 == 0b010 {
ElemMode::Declarative
} else {
ElemMode::Passive
}
} else {
let table_idx = if ty & 0b010 == 0b010 {
wasm.read_var_u32()?
} else {
0
};
if tables_length <= table_idx as usize {
return Err(Error::UnknownTable);
}
let expr = read_constant_instructions(wasm, None, None, Some(&functions))?;

ElemMode::Active(ActiveElem {
table: table_idx,
offset: expr,
})
};
let use_of_el_ty_and_el_exprs = ty & 0b100 == 0b100;

let reftype_or_elemkind: Option<RefType> = match if ty & 0b011 != 0 {
if use_of_el_ty_and_el_exprs {
Some(wasm.read_u8()?)
} else {
let read = wasm.read_u8()?;
match read {
0x00 => None,
_ => todo!("Only FuncRefs are allowed"),
}
}
} else {
None
} {
None => None,
Some(ty) => Some(RefType::from_byte(ty)?),
};

match reftype_or_elemkind {
Some(rty) => trace!("REFTYPE: {}", rty),
None => {
trace!("REFTYPE NONE!")
}
};

let items: ElemItems = if use_of_el_ty_and_el_exprs {
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)
})
}
}

/// 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
#[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: u32,
pub offset: 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 5f2da3d

Please sign in to comment.