emacs/
func.rs

1//! Machinery for defining and exporting functions to the Lisp runtime. It should be mainly used by
2//! the #[[`defun`]] macro, not module code.
3//!
4//! [`defun`]: attr.defun.html
5
6use std::{
7    os, panic,
8    ffi::CString,
9    ops::{Deref, Range},
10    slice,
11};
12
13use emacs_module::{emacs_value, EmacsSubr};
14
15use crate::{Env, Value, Result, FromLisp, IntoLisp};
16
17#[doc(hidden)]
18#[macro_export]
19macro_rules! __export_functions {
20    // Cut trailing comma in top-level.
21    ($env:expr, $prefix:expr, $mappings:tt,) => {
22        $crate::__export_functions!($env, $prefix, $mappings)
23    };
24    // Cut trailing comma in mappings.
25    ($env:expr, $prefix:expr, {
26        $( $name:expr => $declaration:tt ),+,
27    }) => {
28        $crate::__export_functions!($env, $prefix, {
29            $( $name => $declaration ),*
30        })
31    };
32    // Expand each mapping.
33    ($env:expr, $prefix:expr, {
34        $( $name:expr => $declaration:tt ),*
35    }) => {
36        {
37            use $crate::func::Manage;
38            $( $crate::__export_functions!(decl, $env, $prefix, $name, $declaration)?; )*
39        }
40    };
41
42    // Cut trailing comma in declaration.
43    (decl, $env:expr, $prefix:expr, $name:expr, ($func:path, $( $opt:expr ),+,)) => {
44        $crate::__export_functions!(decl, $env, $prefix, $name, ($func, $( $opt ),*))
45    };
46    // Create a function and set a symbol to it.
47    (decl, $env:expr, $prefix:expr, $name:expr, ($func:path, $( $opt:expr ),+)) => {
48        $env.fset(
49            &format!("{}{}", $prefix, $name),
50            $crate::lambda!($env, $func, $($opt),*)?
51        )
52    };
53}
54
55// TODO: Support closures, storing them in the data pointer, using a single handler to dispatch.
56#[doc(hidden)]
57#[macro_export]
58macro_rules! lambda {
59    // Default doc string is empty.
60    ($env:expr, $func:path, $arities:expr $(,)*) => {
61        $crate::lambda!($env, $func, $arities, "")
62    };
63
64    // Declare a wrapper function.
65    ($env:expr, $func:path, $arities:expr, $doc:expr $(,)*) => {
66        {
67            use $crate::func::HandleCall;
68            use $crate::func::Manage;
69            // TODO: Generate identifier from $func.
70            unsafe extern "C" fn extern_lambda(
71                env: *mut $crate::raw::emacs_env,
72                nargs: isize,
73                args: *mut $crate::raw::emacs_value,
74                _data: *mut ::std::os::raw::c_void,
75            ) -> $crate::raw::emacs_value {
76                let env = $crate::Env::new(env);
77                let env = $crate::CallEnv::new(env, nargs, args);
78                env.handle_call($func)
79            }
80
81            // Safety: The raw pointer is simply ignored.
82            unsafe { $env.make_function(extern_lambda, $arities, $doc, ::std::ptr::null_mut()) }
83        }
84    };
85}
86
87#[deprecated(since = "0.7.0", note = "Please use `emacs::lambda!` instead")]
88#[doc(hidden)]
89#[macro_export]
90macro_rules! emacs_lambda {
91    ($($inner:tt)*) => {
92        $crate::lambda!($($inner)*)
93    };
94}
95
96pub trait Manage {
97    unsafe fn make_function<T: Into<Vec<u8>>>(
98        &self,
99        function: EmacsSubr,
100        arities: Range<usize>,
101        doc: T,
102        data: *mut os::raw::c_void,
103    ) -> Result<Value<'_>>;
104
105    fn fset(&self, name: &str, func: Value<'_>) -> Result<Value<'_>>;
106}
107
108impl Manage for Env {
109    /// # Safety
110    ///
111    /// The `function` must use `data` pointer in safe ways.
112    #[allow(unused_unsafe)]
113    unsafe fn make_function<T: Into<Vec<u8>>>(
114        &self,
115        function: EmacsSubr,
116        arities: Range<usize>,
117        doc: T,
118        data: *mut os::raw::c_void,
119    ) -> Result<Value<'_>> {
120        unsafe_raw_call_value!(
121            self,
122            make_function,
123            arities.start as isize,
124            arities.end as isize,
125            Some(function),
126            CString::new(doc)?.as_ptr(),
127            data
128        )
129    }
130
131    fn fset(&self, name: &str, func: Value<'_>) -> Result<Value<'_>> {
132        let symbol = self.intern(name)?;
133        self.call("fset", [symbol, func])
134    }
135}
136
137/// Like [`Env`], but is available only in exported functions. This has additional methods to handle
138/// arguments passed from Lisp code.
139///
140/// [`Env`]: struct.Env.html
141#[doc(hidden)]
142#[derive(Debug)]
143pub struct CallEnv {
144    env: Env,
145    nargs: usize,
146    args: *mut emacs_value,
147}
148
149// TODO: Iterator and Index
150impl CallEnv {
151    #[doc(hidden)]
152    #[inline]
153    pub unsafe fn new(
154        env: Env,
155        nargs: isize,
156        args: *mut emacs_value,
157    ) -> Self {
158        let nargs = nargs as usize;
159        Self { env, nargs, args }
160    }
161
162    #[doc(hidden)]
163    #[inline]
164    pub fn raw_args(&self) -> &[emacs_value] {
165        // Safety: Emacs assures *args is valid for the duration of the call, with length nargs.
166        unsafe { slice::from_raw_parts(self.args, self.nargs) }
167    }
168
169    pub fn args(&self) -> Vec<Value<'_>> {
170        // Safety: Emacs assures *args are on the stack for the duration of the call.
171        self.raw_args().iter().map(|v| unsafe { Value::new(*v, &self.env) }).collect()
172    }
173
174    #[inline]
175    pub fn get_arg(&self, i: usize) -> Value<'_> {
176        let args: &[emacs_value] = self.raw_args();
177        // Safety: Emacs assures *args are on the stack for the duration of the call.
178        unsafe { Value::new(args[i], &self) }
179    }
180
181    #[inline]
182    pub fn parse_arg<'e, T: FromLisp<'e>>(&'e self, i: usize) -> Result<T> {
183        self.get_arg(i).into_rust()
184    }
185}
186
187/// This allows `Env`'s methods to be called on a `CallEnv`.
188impl Deref for CallEnv {
189    type Target = Env;
190
191    #[doc(hidden)]
192    #[inline(always)]
193    fn deref(&self) -> &Env {
194        &self.env
195    }
196}
197
198pub trait HandleCall {
199    fn handle_call<'e, T, F>(&'e self, f: F) -> emacs_value
200        where
201            F: Fn(&'e CallEnv) -> Result<T> + panic::RefUnwindSafe,
202            T: IntoLisp<'e>;
203}
204
205impl HandleCall for CallEnv {
206    #[inline]
207    fn handle_call<'e, T, F>(&'e self, f: F) -> emacs_value
208    where
209        F: Fn(&'e CallEnv) -> Result<T> + panic::RefUnwindSafe,
210        T: IntoLisp<'e>,
211    {
212        let env = panic::AssertUnwindSafe(self);
213        let result = panic::catch_unwind(|| unsafe {
214            let rust_result = f(&env);
215            let lisp_result = rust_result.and_then(|t| t.into_lisp(&env));
216            env.maybe_exit(lisp_result)
217        });
218        env.handle_panic(result)
219    }
220}