Added function validation.

Fixed issue with function `modify_spu_delay1`.
`dcb_util::fmt_err{_wrapper}` not supports non-static errors.
This commit is contained in:
Filipe Rodrigues 2021-04-30 15:54:49 +01:00
parent 49c8ee53cc
commit 1749b78fed
5 changed files with 92 additions and 23 deletions

View File

@ -6,10 +6,12 @@
//! type.
// Modules
pub mod error;
pub mod kind;
pub mod table;
// Exports
pub use error::ValidateError;
pub use kind::FuncKind;
pub use table::FuncTable;
@ -61,12 +63,40 @@ pub struct Func {
pub kind: FuncKind,
}
// Getters
impl Func {
/// Checks if this function contains `pos`
#[must_use]
pub fn contains(&self, pos: Pos) -> bool {
(self.start_pos..self.end_pos).contains(&pos)
}
/// Validates this function
pub fn validate(&self) -> Result<(), ValidateError<'_>> {
// TODO: Validate name and signature?
// If our positions don't make a proper range, return Err
if self.end_pos < self.start_pos {
return Err(ValidateError::InvalidRange {
start_pos: self.start_pos,
end_pos: self.end_pos,
});
}
// Check all positions of labels and comments are within our range.
for (&pos, comment) in self.inline_comments.iter().chain(&self.comments) {
if !self.contains(pos) {
return Err(ValidateError::CommentPosOutOfBounds { pos, comment });
}
}
for (&pos, label) in &self.labels {
if !self.contains(pos) {
return Err(ValidateError::LabelPosOutOfBounds { pos, label });
}
}
Ok(())
}
}
impl Func {

38
dcb-exe/src/func/error.rs Normal file
View File

@ -0,0 +1,38 @@
//! Errors
// Imports
use crate::Pos;
/// Validation error
#[derive(Debug, thiserror::Error)]
pub enum ValidateError<'a> {
/// Function range is invalid
#[error("Range {start_pos}..{end_pos} is invalid")]
InvalidRange {
/// Start position
start_pos: Pos,
/// End position
end_pos: Pos,
},
/// Label position is outside of function
#[error("Label {label} @ {pos} is outside of function bounds")]
LabelPosOutOfBounds {
/// Position
pos: Pos,
/// Label
label: &'a str,
},
/// Comment position is outside of function
#[error("Comment {comment} @ {pos} is outside of function bounds")]
CommentPosOutOfBounds {
/// Position
pos: Pos,
/// Comment
comment: &'a str,
},
}

View File

@ -17,16 +17,18 @@ pub use error::GetKnownError;
// Imports
use super::Func;
use crate::Pos;
use std::{collections::BTreeSet, fs::File, iter::FromIterator, ops::RangeBounds};
use std::{collections::BTreeSet, iter::FromIterator, ops::RangeBounds};
/// Function table
///
/// Stores all functions sorted by their address.
/// Also guarantees all functions are unique and non-overlapping.
#[derive(PartialEq, Eq, Clone, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(serde::Serialize)]
#[serde(transparent)]
pub struct FuncTable(BTreeSet<Func>);
// Constructors
impl FuncTable {
/// Creates an empty function table
#[must_use]
@ -35,16 +37,6 @@ impl FuncTable {
}
}
// Constructors
impl FuncTable {
/// Returns all known functions
pub fn get_known() -> Result<Self, GetKnownError> {
let file = File::open("resources/game_funcs.yaml").map_err(GetKnownError::File)?;
serde_yaml::from_reader(file).map_err(GetKnownError::Parse)
}
}
// Getters
impl FuncTable {
/// Retrieves the function containing `pos`
@ -70,21 +62,30 @@ impl FuncTable {
// Note: `BTreeSet` already discards duplicates on it's own.
impl Extend<Func> for FuncTable {
fn extend<T: IntoIterator<Item = Func>>(&mut self, funcs: T) {
self.0.extend(funcs);
for func in funcs {
self.extend_one(func);
}
}
fn extend_one(&mut self, func: Func) {
self.0.extend_one(func);
}
fn extend_reserve(&mut self, additional: usize) {
self.0.extend_reserve(additional);
// Validate the function before inserting it.
match func.validate() {
Ok(()) => self.0.extend_one(func),
// If it fails validation, discard it and warn
Err(err) => log::warn!(
"Function {} failed validation, ignoring:\n{}",
func.name,
dcb_util::fmt_err_wrapper(&err)
),
}
}
}
impl FromIterator<Func> for FuncTable {
fn from_iter<T: IntoIterator<Item = Func>>(iter: T) -> Self {
Self(iter.into_iter().collect())
let mut table = Self::new();
table.extend(iter);
table
}
}

View File

@ -125,7 +125,7 @@ pub const fn saturating_signed_offset(a: u64, b: i64) -> u64 {
}
/// Prints an error
pub fn fmt_err(err: &(dyn std::error::Error + 'static), f: &mut fmt::Formatter) -> fmt::Result {
pub fn fmt_err(err: &(dyn std::error::Error + '_), f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "{err}")?;
match err.source() {
@ -139,7 +139,7 @@ pub fn fmt_err(err: &(dyn std::error::Error + 'static), f: &mut fmt::Formatter)
}
/// Returns a wrapper that prints an error
pub fn fmt_err_wrapper<'a>(err: &'a (dyn std::error::Error + 'static)) -> impl fmt::Display + 'a {
pub fn fmt_err_wrapper<'a>(err: &'a (dyn std::error::Error + 'a)) -> impl fmt::Display + 'a {
DisplayWrapper::new(move |f| self::fmt_err(err, f))
}

View File

@ -239,8 +239,8 @@
Sets `SPU_DELAY` to `(SPU_DELAY & 0xf0fffff) | 0x2000ffff`
and returns the new value
inline_comments:
0x8004b45c: "$v0 = *SPU_DELAY"
0x8004b474: "*SPU_DELAY = $v0"
0x8004b434: "$v0 = *SPU_DELAY"
0x8004b44c: "*SPU_DELAY = $v0"
start_pos: 0x8004b428
end_pos: 0x8004b450
kind: Known