1 #[doc(no_inline)] 2 pub use failure::Error; 3 use failure_derive::Fail; 4 use std::mem::MaybeUninit; 5 use std::result; 6 use std::thread; 7 use std::any::Any; 8 9 use super::IntoLisp; 10 use super::{Env, Value}; 11 use emacs_module::*; 12 13 // We use const instead of enum, in case Emacs add more exit statuses in the future. 14 // See https://github.com/rust-lang/rust/issues/36927 15 const RETURN: emacs_funcall_exit = emacs_funcall_exit_return; 16 const SIGNAL: emacs_funcall_exit = emacs_funcall_exit_signal; 17 const THROW: emacs_funcall_exit = emacs_funcall_exit_throw; 18 19 #[derive(Debug)] 20 pub struct TempValue { 21 raw: emacs_value, 22 } 23 24 const WRONG_TYPE_USER_PTR: &str = "rust-wrong-type-user-ptr"; 25 const ERROR: &str = "rust-error"; 26 const PANIC: &str = "rust-panic"; 27 28 /// Error types generic to all Rust dynamic modules. 29 /// 30 /// This list is intended to grow over time and it is not recommended to exhaustively match against 31 /// it. 32 #[derive(Debug, Fail)] 33 pub enum ErrorKind { 34 /// An [error] signaled by Lisp code. 35 /// 36 /// [error]: https://www.gnu.org/software/emacs/manual/html_node/elisp/Signaling-Errors.html 37 #[fail(display = "Non-local signal: symbol={:?} data={:?}", symbol, data)] 38 Signal { symbol: TempValue, data: TempValue }, 39 40 /// A [non-local exit] thrown by Lisp code. 41 /// 42 /// [non-local exit]: https://www.gnu.org/software/emacs/manual/html_node/elisp/Catch-and-Throw.html 43 #[fail(display = "Non-local throw: tag={:?} value={:?}", tag, value)] 44 Throw { tag: TempValue, value: TempValue }, 45 46 /// An error indicating that the given value is not a `user-ptr` of the expected type. 47 /// 48 /// # Examples: 49 /// 50 /// ``` 51 /// # use emacs::*; 52 /// # use std::cell::RefCell; 53 /// #[defun] 54 /// fn wrap(x: i64) -> Result<RefCell<i64>> { 55 /// Ok(RefCell::new(x)) 56 /// } 57 /// 58 /// #[defun] 59 /// fn wrap_f(x: f64) -> Result<RefCell<f64>> { 60 /// Ok(RefCell::new(x)) 61 /// } 62 /// 63 /// #[defun] 64 /// fn unwrap(r: &RefCell<i64>) -> Result<i64> { 65 /// Ok(*r.try_borrow()?) 66 /// } 67 /// ``` 68 /// 69 /// ```emacs-lisp 70 /// (unwrap 7) ; *** Eval error *** Wrong type argument: user-ptrp, 7 71 /// (unwrap (wrap 7)) ; 7 72 /// (unwrap (wrap-f 7)) ; *** Eval error *** Wrong type user-ptr: "expected: RefCell" 73 /// ``` 74 #[fail(display = "expected: {}", expected)] 75 WrongTypeUserPtr { expected: &'static str }, 76 } 77 78 /// A specialized [`Result`] type for Emacs's dynamic modules. 79 /// 80 /// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html 81 pub type Result<T> = result::Result<T, Error>; 82 83 // FIX: Make this into RootedValue (or ProtectedValue), and make it safe. XXX: The problem is that 84 // the raw value will be leaked when RootedValue is dropped, since `free_global_ref` requires an env 85 // (thus cannot be called there). This is likely a mis-design in Emacs (In Erlang, 86 // `enif_keep_resource` and `enif_release_resource` don't require an env). 87 impl TempValue { new(raw: emacs_value) -> Self88 unsafe fn new(raw: emacs_value) -> Self { 89 Self { raw } 90 } 91 92 /// # Safety 93 /// 94 /// This must only be used with the [`Env`] from which the error originated. 95 /// 96 /// [`Env`]: struct.Env.html value<'e>(&self, env: &'e Env) -> Value<'e>97 pub unsafe fn value<'e>(&self, env: &'e Env) -> Value<'e> { 98 Value::new_protected(self.raw, env) 99 } 100 } 101 102 // XXX: Technically these are unsound, but they are necessary to use the `Fail` trait. We ensure 103 // safety by marking TempValue methods as unsafe. 104 unsafe impl Send for TempValue {} 105 unsafe impl Sync for TempValue {} 106 107 impl Env { 108 /// Handles possible non-local exit after calling Lisp code. 109 #[inline] handle_exit<T>(&self, result: T) -> Result<T>110 pub(crate) fn handle_exit<T>(&self, result: T) -> Result<T> { 111 let mut symbol = MaybeUninit::uninit(); 112 let mut data = MaybeUninit::uninit(); 113 // TODO: Check whether calling non_local_exit_check first makes a difference in performance. 114 let status = self.non_local_exit_get(&mut symbol, &mut data); 115 match (status, symbol, data) { 116 (RETURN, ..) => Ok(result), 117 (SIGNAL, symbol, data) => { 118 self.non_local_exit_clear(); 119 Err(ErrorKind::Signal { 120 symbol: unsafe { TempValue::new(symbol.assume_init()) }, 121 data: unsafe { TempValue::new(data.assume_init()) }, 122 } 123 .into()) 124 } 125 (THROW, tag, value) => { 126 self.non_local_exit_clear(); 127 Err(ErrorKind::Throw { 128 tag: unsafe { TempValue::new(tag.assume_init()) }, 129 value: unsafe { TempValue::new(value.assume_init()) }, 130 } 131 .into()) 132 } 133 _ => panic!("Unexpected non local exit status {}", status), 134 } 135 } 136 137 /// Converts a Rust's `Result` to either a normal value, or a non-local exit in Lisp. 138 #[inline] maybe_exit(&self, result: Result<Value<'_>>) -> emacs_value139 pub(crate) unsafe fn maybe_exit(&self, result: Result<Value<'_>>) -> emacs_value { 140 match result { 141 Ok(v) => v.raw, 142 Err(error) => match error.downcast_ref::<ErrorKind>() { 143 Some(err) => self.handle_known(err), 144 _ => self 145 .signal_str(ERROR, &format!("{}", error)) 146 .unwrap_or_else(|_| panic!("Failed to signal {}", error)), 147 }, 148 } 149 } 150 151 #[inline] handle_panic(&self, result: thread::Result<emacs_value>) -> emacs_value152 pub(crate) fn handle_panic(&self, result: thread::Result<emacs_value>) -> emacs_value { 153 match result { 154 Ok(v) => v, 155 Err(error) => { 156 // TODO: Try to check for some common types to display? 157 let mut m: result::Result<String, Box<Any>> = Err(error); 158 if let Err(error) = m { 159 m = error.downcast::<String>().map(|v| *v); 160 } 161 if let Err(error) = m { 162 m = match error.downcast::<ErrorKind>() { 163 // TODO: Explain safety. 164 Ok(err) => unsafe { return self.handle_known(&*err) }, 165 Err(error) => Err(error), 166 } 167 } 168 if let Err(error) = m { 169 m = Ok(format!("{:#?}", error)); 170 } 171 self.signal_str(PANIC, &m.expect("Logic error")).expect("Fail to signal panic") 172 } 173 } 174 } 175 define_errors(&self) -> Result<()>176 pub(crate) fn define_errors(&self) -> Result<()> { 177 // FIX: Make panics louder than errors, by somehow make sure that 'rust-panic is 178 // not a sub-type of 'error. 179 self.define_error(PANIC, "Rust panic", &["error"])?; 180 self.define_error(ERROR, "Rust error", &["error"])?; 181 self.define_error( 182 WRONG_TYPE_USER_PTR, 183 "Wrong type user-ptr", 184 &[ERROR, "wrong-type-argument"], 185 )?; 186 Ok(()) 187 } 188 handle_known(&self, err: &ErrorKind) -> emacs_value189 unsafe fn handle_known(&self, err: &ErrorKind) -> emacs_value { 190 match *err { 191 ErrorKind::Signal { ref symbol, ref data } => self.signal(symbol.raw, data.raw), 192 ErrorKind::Throw { ref tag, ref value } => self.throw(tag.raw, value.raw), 193 ErrorKind::WrongTypeUserPtr { .. } => self 194 .signal_str(WRONG_TYPE_USER_PTR, &format!("{}", err)) 195 .unwrap_or_else(|_| panic!("Failed to signal {}", err)), 196 } 197 } 198 199 // TODO: Prepare static values for the symbols. signal_str(&self, symbol: &str, message: &str) -> Result<emacs_value>200 fn signal_str(&self, symbol: &str, message: &str) -> Result<emacs_value> { 201 let message = message.into_lisp(&self)?; 202 let data = self.list([message])?; 203 let symbol = self.intern(symbol)?; 204 unsafe { Ok(self.signal(symbol.raw, data.raw)) } 205 } 206 define_error(&self, name: &str, message: &str, parents: &[&str]) -> Result<Value<'_>>207 fn define_error(&self, name: &str, message: &str, parents: &[&str]) -> Result<Value<'_>> { 208 let mut parent_symbols = vec![]; 209 for symbol in parents.iter().map(|p| self.intern(p)) { 210 parent_symbols.push(symbol?) 211 } 212 let parents = self.list(&parent_symbols)?; 213 self.call("define-error", (self.intern(name)?, message, parents)) 214 } 215 non_local_exit_get( &self, symbol: &mut MaybeUninit<emacs_value>, data: &mut MaybeUninit<emacs_value>, ) -> emacs_funcall_exit216 fn non_local_exit_get( 217 &self, 218 symbol: &mut MaybeUninit<emacs_value>, 219 data: &mut MaybeUninit<emacs_value>, 220 ) -> emacs_funcall_exit { 221 raw_call_no_exit!(self, non_local_exit_get, symbol.as_mut_ptr(), data.as_mut_ptr()) 222 } 223 non_local_exit_clear(&self)224 fn non_local_exit_clear(&self) { 225 raw_call_no_exit!(self, non_local_exit_clear) 226 } 227 228 /// # Safety 229 /// 230 /// The given raw values must still live. 231 #[allow(unused_unsafe)] throw(&self, tag: emacs_value, value: emacs_value) -> emacs_value232 unsafe fn throw(&self, tag: emacs_value, value: emacs_value) -> emacs_value { 233 raw_call_no_exit!(self, non_local_exit_throw, tag, value); 234 tag 235 } 236 237 /// # Safety 238 /// 239 /// The given raw values must still live. 240 #[allow(unused_unsafe)] signal(&self, symbol: emacs_value, data: emacs_value) -> emacs_value241 unsafe fn signal(&self, symbol: emacs_value, data: emacs_value) -> emacs_value { 242 raw_call_no_exit!(self, non_local_exit_signal, symbol, data); 243 symbol 244 } 245 } 246 247 /// Emacs-specific extension methods for [`Result`]. 248 /// 249 /// [`Result`]: type.Result.html 250 pub trait ResultExt<T, E> { 251 /// Unwraps a result, yielding the content of an [`Ok`]. 252 /// 253 /// # Panics 254 /// 255 /// Panics if the value is an [`Err`], using a sensible panic value. 256 /// 257 /// If the underlying error is an [`ErrorKind`], it will be used as the value of the panic, 258 /// which makes the `#[defun]` behave as if the corresponding non-local exit was propagated. 259 /// Otherwise, tries to use [`Display`] to get a descriptive error message. 260 /// 261 /// This is useful when errors cannot be propagated using [`Result`], e.g. callbacks whose types 262 /// are dictated by 3rd-party libraries. 263 /// 264 /// [`Ok`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok 265 /// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err 266 /// [`ErrorKind`]: enum.ErrorKind.html 267 /// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html 268 /// [`Result`]: type.Result.html unwrap_or_propagate(self) -> T269 fn unwrap_or_propagate(self) -> T; 270 } 271 272 impl<T> ResultExt<T, Error> for Result<T> { 273 #[inline] unwrap_or_propagate(self) -> T274 fn unwrap_or_propagate(self) -> T { 275 self.unwrap_or_else(|error| { 276 match error.downcast::<ErrorKind>() { 277 Ok(err) => panic!(err), 278 Err(error) => panic!("{}", error), 279 }; 280 }) 281 } 282 } 283