1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use std::{
    os,
    any,
    cell::RefCell,
    rc::Rc,
    sync::{Mutex, RwLock, Arc},
};

use emacs_module::emacs_finalizer_function;

use super::*;
use crate::ErrorKind;

/// Allowing a type to be exposed to Lisp, where its values appear as opaque objects, or "embedded
/// user pointers" (printed as `#<user-ptr ...>`).
///
/// When a (boxed) value of this type is transferred to Lisp, the GC becomes its owner. Afterwards,
/// module code can only access it through immutable references.
///
/// The 'static bound disallows transferring short-lived references, which can become invalid while
/// still being held by the Lisp runtime.
///
/// This works, because the returned string is copied into the Lisp runtime.
///
/// ```
/// use emacs::{defun, Result};
///
/// #[defun]
/// fn foo(s: &String) -> Result<&str> {
///     Ok(s)
/// }
/// ```
///
/// This doesn't work, because the function attempts to give the Lisp runtime a temporary reference.
///
/// ```compile_fail
/// use emacs::{defun, Result};
///
/// #[defun(user_ptr)]
/// fn foo(s: &String) -> Result<&str> {
///     Ok(s)
/// }
/// ```
pub trait Transfer: Sized + 'static {
    /// Returns the name of this type. This is used to report runtime type errors, when a function
    /// expects values of this type, but receives values of a different type instead. The default
    /// implementation defers to the [`type_name`] function in `std::any`, which can be overridden
    /// for better error reporting.
    ///
    /// [`type_name`]: https://doc.rust-lang.org/std/any/fn.type_name.html
    fn type_name() -> &'static str {
        any::type_name::<Self>()
    }

    // TODO: Consider using a wrapper struct to carry the type info, to enable better runtime
    // reporting of type error (and to enable something like `rs-module/type-of`).
}

impl<'e, T: Transfer> FromLisp<'e> for &'e T {
    fn from_lisp(value: Value<'e>) -> Result<Self> {
        value.get_raw_pointer().map(|r| unsafe { &*r })
    }
}

/// Finalizes an embedded pointer. This is called by the GC when it discards a `user-ptr`.
///
/// This function also serves as a form of runtime type tag, relying on Rust's mono-morphization.
unsafe extern "C" fn finalize<T: Transfer>(ptr: *mut os::raw::c_void) {
    #[cfg(build = "debug")]
    println!("Finalizing {:#?} {}", ptr, T::type_name());
    drop(Box::from_raw(ptr as *mut T));
}

impl<T: Transfer> IntoLisp<'_> for Box<T> {
    fn into_lisp(self, env: &Env) -> Result<Value<'_>> {
        let raw = Box::into_raw(self);
        let ptr = raw as *mut os::raw::c_void;
        // Safety: self is forgotten by `into_raw`, so it's safe for the GC to take over.
        unsafe { env.make_user_ptr(Some(finalize::<T>), ptr) }
    }
}

macro_rules! enable_transfers {
    ($($name:ident;)*) => {$(
        impl<T: 'static> Transfer for $name<T> {}

        impl<'e, T: 'static> IntoLisp<'e> for $name<T> {
            #[inline]
            fn into_lisp(self, env: &Env) -> Result<Value<'_>> {
                Box::new(self).into_lisp(env)
            }
        }
    )*};
}

enable_transfers! {
    RefCell;
    Mutex;
    RwLock;
    Rc;
    Arc;
}

impl Env {
    /// Creates and returns a `user-ptr` object that wraps the raw pointer `ptr`. When the object is
    /// garbage-collected, `fin` will be called with `ptr` as the only argument. If `fin` is None,
    /// no finalization will be done.
    ///
    /// In general, prefer the `user-ptr` supported provided by the [`defun`] attr macro. Use this
    /// function only for special `user-ptr` types, such as newtypes wrapping opaque pointers.
    ///
    /// # Safety
    ///
    /// The pointer must be valid until the finalizer is called. The finalizer itself must finalize
    /// the pointer in a safe manner.
    ///
    /// [`defun`]: attr.defun.html
    #[allow(unused_unsafe)]
    #[inline]
    pub unsafe fn make_user_ptr(&self, fin: emacs_finalizer_function, ptr: *mut os::raw::c_void) -> Result<Value> {
        unsafe_raw_call_value!(self, make_user_ptr, fin, ptr)
    }
}

impl<'e> Value<'e> {
    /// Returns the raw pointer wrapped in this `user-ptr` object.
    ///
    /// In general, prefer the `user-ptr` supported provided by the [`defun`] attr macro. Use this
    /// function only for special `user-ptr` types, such as newtypes wrapping opaque pointers.
    ///
    /// [`defun`]: attr.defun.html
    #[inline]
    pub fn get_user_ptr(self) -> Result<*mut os::raw::c_void> {
        unsafe_raw_call!(self.env, get_user_ptr, self.raw)
    }

    /// Returns the finalizer function associated with this `user-ptr` object.
    ///
    /// In general, prefer the `user-ptr` supported provided by the [`defun`] attr macro. Use this
    /// function only for special `user-ptr` types, such as newtypes wrapping opaque pointers.
    ///
    /// [`defun`]: attr.defun.html
    #[inline]
    pub fn get_user_finalizer(self) -> Result<emacs_finalizer_function> {
        unsafe_raw_call!(self.env, get_user_finalizer, self.raw)
    }

    pub(crate) fn get_raw_pointer<T: Transfer>(self) -> Result<*mut T> {
        match self.get_user_finalizer()? {
            // TODO: Consider using dynamic dispatch for finalize, and core::any for type checking.
            Some(fin) if fin == finalize::<T> => {
                let ptr = self.get_user_ptr()?;
                Ok(ptr as *mut T)
            }
            _ => {
                let expected = T::type_name();
                Err(ErrorKind::WrongTypeUserPtr { expected }.into())
            }
        }
    }
}