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
//! Initialization machinery. It should be mainly used by the #[[`module`]] macro, not module code.
//!
//! [`module`]: /emacs-macros/*/emacs_macros/attr.module.html

use std::{
    os, panic,
    collections::HashMap,
    sync::{Mutex, atomic::AtomicBool},
};

use lazy_static::lazy_static;

use crate::{Env, Value, Result};

/// Registers a function as the initialization hook. #[[`module`]] is preferred over this low-level
/// interface.
///
/// This declares `emacs_module_init` and `emacs_rs_module_init`, by wrapping the given function,
/// whose signature must be `fn(&Env) -> Result<Value>`.
///
/// [`module`]: /emacs-macros/*/emacs_macros/attr.module.html
#[deprecated(since = "0.11.0", note = "Please use `#[emacs::module]` instead")]
#[macro_export]
macro_rules! module_init {
    ($($inner:tt)*) => {
        $crate::__module_init!($($inner)*);
    };
}

#[deprecated(since = "0.7.0", note = "Please use `#[emacs::module]` instead")]
#[doc(hidden)]
#[macro_export]
macro_rules! emacs_module_init {
    ($($inner:tt)*) => {
        $crate::__module_init!($($inner)*);
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! __module_init {
    ($init:ident) => {
        /// Entry point for Emacs's module loader.
        #[no_mangle]
        pub unsafe extern "C" fn emacs_module_init(
            runtime: *mut $crate::raw::emacs_runtime,
        ) -> ::std::os::raw::c_int {
            $crate::init::initialize(&$crate::Env::from_runtime(runtime), $init)
        }

        // TODO: Exclude this in release build.
        /// Entry point for live-reloading (by `rs-module`) during development.
        #[no_mangle]
        pub unsafe extern "C" fn emacs_rs_module_init(
            raw: *mut $crate::raw::emacs_env,
        ) -> ::std::os::raw::c_int {
            $crate::init::initialize(&$crate::Env::new(raw), $init)
        }
    };
}

type InitFn = dyn Fn(&Env) -> Result<()> + Send + 'static;

type FnMap = HashMap<String, Box<InitFn>>;

// TODO: How about defining these in user crate, and requiring #[module] to be at the crate's root?
// TODO: We probably don't need the mutexes.
lazy_static! {
    // Keep these names in-sync with those declared in emacs_macros::util.

    /// Functions to be called when the dynamic module is loaded by the OS, before Emacs calls
    /// `emacs_module_init`. These are only called if #[[`module`]] attribute macro is used,
    /// instead of [`module_init!`] macro.
    ///
    /// [`module_init!`]: macro.module_init.html
    /// [`module`]: /emacs-macros/*/emacs_macros/attr.module.html
    pub static ref __INIT_FNS__: Mutex<FnMap> = Mutex::new(HashMap::new());

    /// Prefix to prepend to name of every Lisp function exposed by the dynamic module through the
    /// attribute macro #[[`defun`]].
    ///
    /// [`defun`]: /emacs-macros/*/emacs_macros/attr.defun.html
    pub static ref __PREFIX__: Mutex<[String; 2]> = Mutex::new(["".to_owned(), "-".to_owned()]);

    pub static ref __MOD_IN_NAME__: AtomicBool = AtomicBool::new(true);
}

#[inline]
pub fn initialize<F>(env: &Env, f: F) -> os::raw::c_int
where
    F: Fn(&Env) -> Result<Value<'_>> + panic::RefUnwindSafe,
{
    let env = panic::AssertUnwindSafe(env);
    let result = panic::catch_unwind(|| match env.define_errors().and_then(|_| f(&env)) {
        Ok(_) => 0,
        Err(e) => {
            env.message(format!("Error during initialization: {:#?}", e))
                .expect("Fail to message Emacs about error");
            1
        }
    });
    match result {
        Ok(v) => v,
        Err(e) => {
            env.message(format!("Panic during initialization: {:#?}", e))
                .expect("Fail to message Emacs about panic");
            2
        }
    }
}

fn lisp_name(s: &str) -> String {
    s.replace("_", "-")
}

pub fn lisp_pkg(module_path: &str) -> String {
    let crate_name = module_path.split("::").nth(0).expect("module_path is empty!");
    lisp_name(&crate_name)
}

pub fn lisp_path(module_path: &str) -> String {
    let split = module_path.split("::");
    let mut path =
        __PREFIX__.try_lock().expect("Failed to acquire read lock of module prefix").join("");
    for segment in split.skip(1) {
        path.push_str(segment);
        path.push('-');
    }
    lisp_name(&path)
}