diff --git a/src/lib.rs b/src/lib.rs index 8ef0a81..dc73fac 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -use core::ffi::c_void; +use core::{ffi::c_void, mem::MaybeUninit}; mod code; mod exception; @@ -40,16 +40,34 @@ where } } +fn do_execute_proc(mut closure: F) -> Result<(), Exception> +where + F: FnMut(), +{ + let mut exception = Exception::empty(); + let closure = &mut closure as *mut _ as *mut c_void; + + unsafe { + match handler_stub(handled_proc::, closure, &mut exception) { + MS_CATCHED => Err(exception), + MS_DISABLED => panic!("exception handling is not supported in this build of microseh"), + /* MS_SUCCEEDED */ _ => Ok(()), + } + } +} + /// Executes the provided closure in a context where exceptions are handled, catching any\ /// hardware exceptions that occur. /// +/// Any value returned by the closure is returned by this function, if no exceptions occur. +/// /// # Arguments /// /// * `closure` - The closure or function to be executed within the handled context. /// /// # Returns /// -/// * `Ok(())` - If the closure executed without throwing any exceptions. +/// * `Ok(R)` - If the closure executed without throwing any exceptions. /// * `Err(Exception)` - If an exception occurred during the execution of the closure. /// /// # Examples @@ -67,7 +85,7 @@ where /// # Caveats /// /// If an exception occours within the closure, resources that require cleanup via\ -/// the `Drop` trait, may not be properly released. +/// the `Drop` trait, will not be released. /// /// As a rule of thumb, it's recommended not to define resources that implement\ /// the `Drop` trait inside the closure. Instead, allocate and manage these resources\ @@ -77,20 +95,17 @@ where /// /// If exception handling is disabled in the build, which occurs when the library is\ /// not built on Windows with Microsoft Visual C++. -pub fn try_seh(mut closure: F) -> Result<(), Exception> +pub fn try_seh(mut closure: F) -> Result where - F: FnMut(), + F: FnMut() -> R, { - let mut exception = Exception::empty(); - let closure = &mut closure as *mut _ as *mut c_void; - - unsafe { - match handler_stub(handled_proc::, closure, &mut exception) { - MS_CATCHED => Err(exception), - MS_DISABLED => panic!("exception handling is not supported in this build of microseh"), - /* MS_SUCCEEDED */ _ => Ok(()), - } - } + let mut ret_val = MaybeUninit::::uninit(); + do_execute_proc(|| { + ret_val.write(closure()); + }) + // SAFETY: We should only reach this point if the inner closure has returned + // without throwing an exception, so `ret_val` should be initialized. + .map(|_| unsafe { ret_val.assume_init() }) } #[cfg(test)] @@ -230,4 +245,16 @@ mod tests { assert_eq!(ex.unwrap_err().registers().x0(), 0xbadc0debabefffff); } + + #[test] + fn ret_vals() { + let a = try_seh(|| 1337); + assert_eq!(a.unwrap(), 1337); + + let b = try_seh(|| "hello"); + assert_eq!(b.unwrap(), "hello"); + + let c = try_seh(|| {}); + assert_eq!(core::mem::size_of_val(&c.unwrap()), 0x0); + } }