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