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
use super::check_extensions;
use crate::{Ctx, Error, Loaded, Loader, Module, ModuleLoadFn, Native, Result};

/// The native module loader
///
/// This loader can be used as the nested backing loader in user-defined loaders.
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "dyn-load")))]
#[derive(Debug)]
pub struct NativeLoader {
    extensions: Vec<String>,
    libs: Vec<dlopen::raw::Library>,
}

impl NativeLoader {
    /// Add library file extension
    pub fn add_extension<X: Into<String>>(&mut self, extension: X) -> &mut Self {
        self.extensions.push(extension.into());
        self
    }

    /// Add library file extension
    pub fn with_extension<X: Into<String>>(&mut self, extension: X) -> &mut Self {
        self.add_extension(extension);
        self
    }
}

impl Default for NativeLoader {
    fn default() -> Self {
        let mut loader = Self {
            extensions: Vec::new(),
            libs: Vec::new(),
        };

        #[cfg(target_family = "windows")]
        loader.add_extension("dll");

        #[cfg(all(target_family = "unix"))]
        loader.add_extension("so");

        #[cfg(target_vendor = "apple")]
        loader.add_extension("dylib");

        loader
    }
}

impl Loader<Native> for NativeLoader {
    fn load<'js>(&mut self, ctx: Ctx<'js>, path: &str) -> Result<Module<'js, Loaded<Native>>> {
        use dlopen::raw::Library;

        if !check_extensions(path, &self.extensions) {
            return Err(Error::new_loading(path));
        }

        let lib = Library::open(&path)
            .map_err(|_| Error::new_loading_message(path, "Unable to open library"))?;
        let load: ModuleLoadFn = unsafe { lib.symbol("js_init_module") }.map_err(|_| {
            Error::new_loading_message(path, "Unable to find symbol `js_init_module`")
        })?;

        let module = unsafe { Module::new_raw(ctx, path, load) }
            .map_err(|_| Error::new_loading_message(path, "Unable to create module"))?;

        self.libs.push(lib);

        Ok(module)
    }
}