Skip to content

Commit

Permalink
Added VerificationError to replace formatted Strings
Browse files Browse the repository at this point in the history
  • Loading branch information
SSheldon committed Jan 11, 2020
1 parent 7358162 commit b8dc92e
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 31 deletions.
23 changes: 14 additions & 9 deletions src/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ mod platform;
mod platform;

use self::platform::{send_unverified, send_super_unverified};
use self::verify::verify_message_signature;
use self::verify::{VerificationError, verify_message_signature};

/// Specifies the superclass of an instance.
#[repr(C)]
Expand Down Expand Up @@ -100,6 +100,7 @@ pub unsafe trait Message {
where Self: Sized, A: EncodeArguments, R: Encode {
let obj = unsafe { &*(self as *const _ as *const Object) };
verify_message_signature::<A, R>(obj.class(), sel)
.map_err(MessageError::from)
}
}

Expand Down Expand Up @@ -169,6 +170,12 @@ impl Error for MessageError {
}
}

impl<'a> From<VerificationError<'a>> for MessageError {
fn from(err: VerificationError) -> MessageError {
MessageError(err.to_string())
}
}

#[doc(hidden)]
#[inline(always)]
#[cfg(not(feature = "verify_message"))]
Expand All @@ -186,14 +193,13 @@ pub unsafe fn send_message<T, A, R>(obj: *const T, sel: Sel, args: A)
where T: Message, A: MessageArguments + EncodeArguments,
R: Any + Encode {
let cls = if obj.is_null() {
return Err(MessageError(format!("Messaging {:?} to nil", sel)));
return Err(VerificationError::NilReceiver(sel).into());
} else {
(*(obj as *const Object)).class()
};

verify_message_signature::<A, R>(cls, sel).and_then(|_| {
send_unverified(obj, sel, args)
})
verify_message_signature::<A, R>(cls, sel)?;
send_unverified(obj, sel, args)
}

#[doc(hidden)]
Expand All @@ -213,12 +219,11 @@ pub unsafe fn send_super_message<T, A, R>(obj: *const T, superclass: &Class,
where T: Message, A: MessageArguments + EncodeArguments,
R: Any + Encode {
if obj.is_null() {
return Err(MessageError(format!("Messaging {:?} to nil", sel)));
return Err(VerificationError::NilReceiver(sel).into());
}

verify_message_signature::<A, R>(superclass, sel).and_then(|_| {
send_super_unverified(obj, superclass, sel, args)
})
verify_message_signature::<A, R>(superclass, sel)?;
send_super_unverified(obj, superclass, sel, args)
}

#[cfg(test)]
Expand Down
69 changes: 47 additions & 22 deletions src/message/verify.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,56 @@
use crate::runtime::{Class, Object, Sel};
use crate::{Encode, EncodeArguments};
use super::MessageError;
use std::fmt;

use crate::runtime::{Class, Method, Object, Sel};
use crate::{Encode, Encoding, EncodeArguments};

pub enum VerificationError<'a> {
NilReceiver(Sel),
MethodNotFound(&'a Class, Sel),
MismatchedReturn(&'a Method, Encoding<'static>),
MismatchedArgumentsCount(&'a Method, usize),
MismatchedArgument(&'a Method, usize, Encoding<'static>),
}

impl<'a> fmt::Display for VerificationError<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
VerificationError::NilReceiver(sel) => {
write!(f, "Messsaging {:?} to nil", sel)
}
VerificationError::MethodNotFound(cls, sel) => {
write!(f, "Method {:?} not found on class {:?}", sel, cls)
}
VerificationError::MismatchedReturn(method, ret) => {
let expected_ret = method.return_type();
write!(f, "Return type code {} does not match expected {} for method {:?}",
ret, expected_ret, method.name())
}
VerificationError::MismatchedArgumentsCount(method, count) => {
let expected_count = method.arguments_count();
write!(f, "Method {:?} accepts {} arguments, but {} were given",
method.name(), expected_count, count)
}
VerificationError::MismatchedArgument(method, i, arg) => {
let expected = method.argument_type(i).unwrap();
write!(f, "Method {:?} expected argument at index {} with type code {:?} but was given {:?}",
method.name(), i, expected, arg)
}
}
}
}

pub fn verify_message_signature<A, R>(cls: &Class, sel: Sel)
-> Result<(), MessageError>
-> Result<(), VerificationError>
where A: EncodeArguments, R: Encode {
let method = match cls.instance_method(sel) {
Some(method) => method,
None => return Err(MessageError(
format!("Method {:?} not found on class {:?}",
sel, cls)
)),
None => return Err(VerificationError::MethodNotFound(cls, sel)),
};

let ret = R::ENCODING;
let expected_ret = method.return_type();
if ret != *expected_ret {
return Err(MessageError(
format!("Return type code {} does not match expected {} for method {:?}",
ret, expected_ret, method.name())
));
return Err(VerificationError::MismatchedReturn(method, ret));
}

let self_and_cmd = [<*mut Object>::ENCODING, Sel::ENCODING];
Expand All @@ -28,19 +59,13 @@ pub fn verify_message_signature<A, R>(cls: &Class, sel: Sel)
let count = self_and_cmd.len() + args.len();
let expected_count = method.arguments_count();
if count != expected_count {
return Err(MessageError(
format!("Method {:?} accepts {} arguments, but {} were given",
method.name(), expected_count, count)
));
return Err(VerificationError::MismatchedArgumentsCount(method, count));
}

for (i, arg) in self_and_cmd.iter().chain(args).enumerate() {
for (i, arg) in self_and_cmd.iter().chain(args).copied().enumerate() {
let expected = method.argument_type(i).unwrap();
if *arg != *expected {
return Err(MessageError(
format!("Method {:?} expected argument at index {} with type code {:?} but was given {:?}",
method.name(), i, expected, arg)
));
if arg != *expected {
return Err(VerificationError::MismatchedArgument(method, i, arg));
}
}

Expand Down

0 comments on commit b8dc92e

Please # to comment.