-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
wip!: replace validation stack with validation ctxt
- Loading branch information
Cem Onem
committed
Nov 22, 2024
1 parent
f7ba965
commit cb1ded4
Showing
7 changed files
with
515 additions
and
716 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,321 @@ | ||
#![allow(unused)] // TODO remove this once sidetable implementation lands | ||
use super::Result; | ||
use alloc::vec; | ||
use alloc::vec::Vec; | ||
use libm::exp; | ||
|
||
use crate::core::reader::types::{FuncType, ResultType}; | ||
|
||
use crate::{Error, ValType}; | ||
|
||
#[derive(Debug, PartialEq, Eq)] | ||
pub(super) struct ValidationContext { | ||
val_stack: Vec<ValType>, | ||
// TODO hide implementation | ||
pub ctrl_stack: Vec<CtrlStackEntry>, | ||
// TODO sidetable ref? | ||
// | ||
} | ||
|
||
// TODO hide implementation | ||
impl ValidationContext { | ||
/// Initialize a new ValidationStack | ||
pub(super) fn new() -> Self { | ||
Self { | ||
val_stack: Vec::new(), | ||
|
||
//dummy label to account for the function itself, | ||
//in case for return instructions that make the rest unreachable | ||
ctrl_stack: vec![CtrlStackEntry { | ||
label_info: LabelInfo::Func, | ||
// TODO dummy, replace with the types of this function later | ||
block_ty: FuncType { | ||
params: ResultType { | ||
valtypes: Vec::new(), | ||
}, | ||
returns: ResultType { | ||
valtypes: Vec::new(), | ||
}, | ||
}, | ||
// TODO dummy, replace with the arity of this function later | ||
height: 0, | ||
unreachable: false, | ||
}], | ||
} | ||
} | ||
|
||
pub(super) fn val_stack_len(&self) -> usize { | ||
self.val_stack.len() | ||
} | ||
|
||
pub fn ctrl_stack_len(&self) -> usize { | ||
self.ctrl_stack.len() | ||
} | ||
|
||
pub(super) fn push_val_type(&mut self, valtype: ValType) { | ||
self.val_stack.push(valtype); | ||
} | ||
|
||
pub fn push_ctrl(&mut self, ctrl_stack_entry: CtrlStackEntry) { | ||
// TODO order? | ||
// TODO include this or not? | ||
// for &v in ctrl_stack_entry.block_ty.params.valtypes.iter().rev() { | ||
// self.push_val_type(v); | ||
// } | ||
self.ctrl_stack.push(ctrl_stack_entry); | ||
} | ||
|
||
pub fn pop_ctrl(&mut self) -> Result<CtrlStackEntry> { | ||
if self.ctrl_stack_len() == 1 { | ||
return Err(Error::InvalidCtrlStackType(None)); | ||
} | ||
let last_ctrl_stack_entry = self.ctrl_stack.pop().unwrap(); | ||
Ok(last_ctrl_stack_entry) | ||
|
||
// TODO include this or not? | ||
// for &v in last_ctrl_stack_entry.block_ty.params.valtypes.iter() { | ||
// self.assert_pop_val_type(v); | ||
// } | ||
|
||
// // TODO is this necessary? | ||
// if self.val_stack_len() == last_ctrl_stack_entry.height { | ||
// Ok(last_ctrl_stack_entry) | ||
// } | ||
// else { | ||
// Err(Error::EndInvalidValueStack) | ||
// } | ||
} | ||
|
||
pub fn make_unspecified(&mut self) { | ||
let last_ctrl_stack_entry = self.ctrl_stack.last_mut().unwrap(); | ||
last_ctrl_stack_entry.unreachable = true; | ||
self.val_stack.truncate(last_ctrl_stack_entry.height); | ||
} | ||
|
||
pub fn is_unspecified(&self) -> bool { | ||
let last_ctrl_stack_entry = self.ctrl_stack.last().unwrap(); | ||
self.val_stack_len() == last_ctrl_stack_entry.height && last_ctrl_stack_entry.unreachable | ||
} | ||
|
||
/// Similar to [`ValidationStack::pop`], because it pops a value from the stack, | ||
/// but more public and doesn't actually return the popped value. | ||
pub(super) fn drop_val_type(&mut self) -> Result<()> { | ||
self.pop_val_type() | ||
.map_err(|e| Error::EndInvalidValueStack) | ||
.map(|v| ()) | ||
} | ||
|
||
pub fn pop_val_type(&mut self) -> Result<Option<ValType>> { | ||
if self.is_unspecified() { | ||
return Ok(None); | ||
} | ||
let last_ctrl_stack_entry = self.ctrl_stack.last().unwrap(); | ||
if self.val_stack.len() == last_ctrl_stack_entry.height { | ||
Err(Error::EndInvalidValueStack) | ||
} else { | ||
Ok(Some(self.val_stack.pop().unwrap())) | ||
} | ||
} | ||
|
||
pub fn assert_nth_top_val_type(&self, n: usize, expected_ty: ValType) -> Result<()> { | ||
let last_ctrl_stack_entry = self.ctrl_stack.last().unwrap(); | ||
if self.val_stack_len() - n <= last_ctrl_stack_entry.height { | ||
if last_ctrl_stack_entry.unreachable { | ||
return Ok(()); | ||
} else { | ||
return Err((Error::EndInvalidValueStack)); | ||
} | ||
} | ||
|
||
if self.val_stack[self.val_stack_len() - n - 1] == expected_ty { | ||
Ok(()) | ||
} else { | ||
Err(Error::InvalidValidationStackValType(None)) | ||
} | ||
} | ||
|
||
// TODO should this even exist? | ||
//fn raw_pop_val(&mut self) -> Result<ValidationStackEntry> { | ||
// ??? | ||
//} | ||
|
||
pub(super) fn assert_pop_val_type(&mut self, expected_ty: ValType) -> Result<ValType> { | ||
let actual_ty = self.pop_val_type()?; | ||
// TODO we hope to never assert an Unknown type? | ||
match actual_ty { | ||
None => Ok(expected_ty), | ||
Some(v) => { | ||
if v == expected_ty { | ||
Ok(expected_ty) | ||
} else { | ||
Err(Error::InvalidValType) | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub(super) fn assert_pop_val_types(&mut self, expected_val_types: &[ValType]) -> Result<()> { | ||
self.assert_val_types(expected_val_types); | ||
for i in 0..expected_val_types.len() { | ||
self.pop_val_type(); | ||
} | ||
Ok(()) | ||
} | ||
|
||
pub(super) fn assert_val_types(&self, expected_val_types: &[ValType]) -> Result<()> { | ||
let last_ctrl_stack_entry = self.ctrl_stack.last().unwrap(); | ||
for (i, expected_ty) in expected_val_types.iter().rev().enumerate() { | ||
if self.val_stack_len() - i <= last_ctrl_stack_entry.height { | ||
if last_ctrl_stack_entry.unreachable { | ||
return Ok(()); | ||
} else { | ||
return Err(Error::EndInvalidValueStack); | ||
} | ||
} | ||
|
||
if self.val_stack[self.val_stack_len() - i - 1] != *expected_ty { | ||
return Err(Error::InvalidValidationStackValType(None)); | ||
} | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
// TODO replace LabelInfo with this later | ||
// TODO fix what is public or not | ||
// TODO innards are coupled with sidetable | ||
#[derive(Clone, Debug, PartialEq, Eq)] | ||
pub enum LabelInfo { | ||
Block { | ||
stps_to_backpatch: Vec<usize>, | ||
}, | ||
If { | ||
stps_to_backpatch: Vec<usize>, | ||
stp: usize, | ||
}, | ||
Loop { | ||
ip: usize, | ||
stp: usize, | ||
}, | ||
Func, | ||
} | ||
|
||
impl LabelInfo { | ||
//TODO ugly but I am not sure if there is a better rustier way | ||
pub fn get_labelkind(&self) -> LabelKind { | ||
match self { | ||
LabelInfo::Block { .. } => LabelKind::Block, | ||
LabelInfo::Loop { .. } => LabelKind::Loop, | ||
LabelInfo::If { .. } => LabelKind::If, | ||
LabelInfo::Func => LabelKind::Func, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Debug, PartialEq, Eq)] | ||
pub struct CtrlStackEntry { | ||
pub label_info: LabelInfo, | ||
pub block_ty: FuncType, | ||
pub height: usize, | ||
pub unreachable: bool, | ||
} | ||
|
||
impl CtrlStackEntry { | ||
pub fn label_types(&self) -> &ResultType { | ||
match &self.label_info { | ||
LabelInfo::Loop { .. } => &self.block_ty.params, | ||
_ => &self.block_ty.returns, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Debug, PartialEq, Eq)] | ||
pub enum LabelKind { | ||
Func, | ||
Block, | ||
Loop, | ||
If, | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::{NumType, RefType, ValType}; | ||
|
||
use super::{LabelInfo, LabelKind, ValidationContext}; | ||
|
||
#[test] | ||
fn push_then_pop() { | ||
let mut ctxt = ValidationContext::new(); | ||
|
||
ctxt.push_val_type(ValType::NumType(NumType::F64)); | ||
ctxt.push_val_type(ValType::NumType(NumType::I32)); | ||
ctxt.push_val_type(ValType::VecType); | ||
ctxt.push_val_type(ValType::RefType(RefType::ExternRef)); | ||
|
||
ctxt.assert_pop_val_type(ValType::RefType(RefType::ExternRef)) | ||
.unwrap(); | ||
ctxt.assert_pop_val_type(ValType::VecType).unwrap(); | ||
ctxt.assert_pop_val_type(ValType::NumType(NumType::I32)) | ||
.unwrap(); | ||
ctxt.assert_pop_val_type(ValType::NumType(NumType::F64)) | ||
.unwrap(); | ||
} | ||
|
||
#[test] | ||
fn assert_valtypes() { | ||
let mut ctxt = ValidationContext::new(); | ||
|
||
ctxt.push_val_type(ValType::NumType(NumType::F64)); | ||
ctxt.push_val_type(ValType::NumType(NumType::I32)); | ||
ctxt.push_val_type(ValType::NumType(NumType::F32)); | ||
|
||
ctxt.assert_val_types(&[ | ||
ValType::NumType(NumType::F64), | ||
ValType::NumType(NumType::I32), | ||
ValType::NumType(NumType::F32), | ||
]) | ||
.unwrap(); | ||
|
||
ctxt.push_val_type(ValType::NumType(NumType::I32)); | ||
|
||
ctxt.assert_val_types(&[ValType::NumType(NumType::I32)]) | ||
.unwrap(); | ||
} | ||
|
||
#[test] | ||
fn assert_emtpy_valtypes() { | ||
let mut ctxt = ValidationContext::new(); | ||
|
||
ctxt.assert_val_types(&[]).unwrap(); | ||
} | ||
|
||
#[test] | ||
fn assert_valtypes2() { | ||
let mut ctxt = ValidationContext::new(); | ||
|
||
ctxt.assert_val_types(&[]).unwrap(); | ||
|
||
ctxt.push_val_type(ValType::NumType(NumType::I32)); | ||
ctxt.push_val_type(ValType::NumType(NumType::F32)); | ||
ctxt.push_val_type(ValType::NumType(NumType::I64)); | ||
|
||
// There are always zero valtypes on top of the stack | ||
ctxt.assert_val_types(&[]).unwrap(); | ||
|
||
ctxt.assert_val_types(&[ValType::NumType(NumType::I64)]) | ||
.unwrap(); | ||
|
||
ctxt.assert_val_types(&[ | ||
ValType::NumType(NumType::F32), | ||
ValType::NumType(NumType::I64), | ||
]) | ||
.unwrap(); | ||
|
||
ctxt.assert_val_types(&[ | ||
ValType::NumType(NumType::I32), | ||
ValType::NumType(NumType::F32), | ||
ValType::NumType(NumType::I64), | ||
]) | ||
.unwrap(); | ||
} | ||
} |
Oops, something went wrong.