Skip to content

Commit

Permalink
feature(neon-runtime): Dynamic module loading
Browse files Browse the repository at this point in the history
  • Loading branch information
kjvalencik committed Dec 1, 2020
1 parent 7422e60 commit 1f1a2e0
Show file tree
Hide file tree
Showing 23 changed files with 618 additions and 182 deletions.
6 changes: 3 additions & 3 deletions crates/neon-macros/src/napi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ pub(crate) fn main(
#vis #sig {
#[no_mangle]
unsafe extern "C" fn napi_register_module_v1(
env: ::neon::macro_internal::runtime::nodejs_sys::napi_env,
m: ::neon::macro_internal::runtime::nodejs_sys::napi_value,
) -> ::neon::macro_internal::runtime::nodejs_sys::napi_value {
env: ::neon::macro_internal::runtime::raw::Env,
m: ::neon::macro_internal::runtime::raw::Local,
) -> ::neon::macro_internal::runtime::raw::Local {
::neon::macro_internal::initialize_module(
env,
::std::mem::transmute(m),
Expand Down
7 changes: 5 additions & 2 deletions crates/neon-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ edition = "2018"

[dependencies]
cfg-if = "0.1.9"
libloading = { version = "0.6.5", optional = true }
neon-sys = { version = "=0.5.3", path = "../neon-sys", optional = true }
nodejs-sys = { version = "0.7.0", optional = true }
smallvec = "1.4.2"

[dev-dependencies]
nodejs-sys = "0.7.0" # Dev dependency for easy copying

[features]
default = []
napi = ["nodejs-sys"]
napi = ["libloading"]
docs-only = ["neon-sys/docs-only"]

[package.metadata.docs.rs]
Expand Down
1 change: 0 additions & 1 deletion crates/neon-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use cfg_if::cfg_if;

cfg_if! {
if #[cfg(feature = "napi")] {
pub use nodejs_sys;
pub mod napi;
}
}
Expand Down
14 changes: 7 additions & 7 deletions crates/neon-runtime/src/napi/array.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
//! Facilities for working with Array `napi_value`s.
//! Facilities for working with Array `value`s.

use crate::raw::{Env, Local};

use nodejs_sys as napi;
use crate::napi::bindings as napi;

pub unsafe extern "C" fn new(out: &mut Local, env: Env, length: u32) {
assert_eq!(
napi::napi_create_array_with_length(env, length as usize, out as *mut _),
napi::napi_status::napi_ok,
napi::create_array_with_length(env, length as usize, out as *mut _),
napi::Status::Ok,
);
}

/// Gets the length of a `napi_value` containing a JavaScript Array.
/// Gets the length of a `value` containing a JavaScript Array.
///
/// # Panics
/// This function panics if `array` is not an Array, or if a previous n-api call caused a pending
/// exception.
pub unsafe extern "C" fn len(env: Env, array: Local) -> u32 {
let mut len = 0;
assert_eq!(
napi::napi_get_array_length(env, array, &mut len as *mut _),
napi::napi_status::napi_ok
napi::get_array_length(env, array, &mut len as *mut _),
napi::Status::Ok
);
len
}
10 changes: 5 additions & 5 deletions crates/neon-runtime/src/napi/arraybuffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ use crate::raw::{Env, Local};
use std::os::raw::c_void;
use std::ptr::null_mut;

use nodejs_sys as napi;
use crate::napi::bindings as napi;

pub unsafe extern "C" fn new(out: &mut Local, env: Env, size: u32) -> bool {
let status = napi::napi_create_arraybuffer(env, size as usize, null_mut(), out as *mut _);
let status = napi::create_arraybuffer(env, size as usize, null_mut(), out as *mut _);

status == napi::napi_status::napi_ok
status == napi::Status::Ok
}

pub unsafe extern "C" fn data<'a, 'b>(env: Env, base_out: &'a mut *mut c_void, obj: Local) -> usize {
let mut size = 0;
assert_eq!(
napi::napi_get_arraybuffer_info(env, obj, base_out as *mut _, &mut size as *mut _),
napi::napi_status::napi_ok,
napi::get_arraybuffer_info(env, obj, base_out as *mut _, &mut size as *mut _),
napi::Status::Ok,
);
size
}
204 changes: 204 additions & 0 deletions crates/neon-runtime/src/napi/bindings/functions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#[cfg(windows)]
use libloading::os::windows::Library;
#[cfg(not(windows))]
use libloading::os::unix::Library;

use std::os::raw::{c_char, c_void};
use super::types::*;

generate!(extern "C" {
fn get_undefined(env: Env, result: *mut Value) -> Status;

fn get_null(env: Env, result: *mut Value) -> Status;

fn get_global(env: Env, result: *mut Value) -> Status;

fn get_boolean(env: Env, value: bool, result: *mut Value) -> Status;

fn create_double(env: Env, value: f64, result: *mut Value) -> Status;

fn create_object(env: Env, result: *mut Value) -> Status;

fn get_value_bool(env: Env, value: Value, result: *mut bool) -> Status;

fn get_value_double(env: Env, value: Value, result: *mut f64) -> Status;

fn create_array_with_length(env: Env, length: usize, result: *mut Value) -> Status;

fn get_array_length(env: Env, value: Value, result: *mut u32)-> Status;

fn get_new_target(env: Env, cbinfo: CallbackInfo, result: *mut Value) -> Status;

fn coerce_to_object(env: Env, value: Value, result: *mut Value) -> Status;

fn coerce_to_string(env: Env, value: Value, result: *mut Value) -> Status;

fn throw(env: Env, error: Value) -> Status;

fn create_error(env: Env, code: Value, msg: Value, result: *mut Value) -> Status;

fn get_and_clear_last_exception(env: Env, result: *mut Value) -> Status;

fn is_exception_pending(env: Env, result: *mut bool) -> Status;

fn get_value_external(env: Env, value: Value, result: *mut *mut c_void) -> Status;

fn typeof_value(env: Env, value: Value, result: *mut ValueType) -> Status;

fn close_escapable_handle_scope(env: Env, scope: EscapableHandleScope) -> Status;

fn open_escapable_handle_scope(env: Env, result: *mut EscapableHandleScope) -> Status;

fn open_handle_scope(env: Env, result: *mut HandleScope) -> Status;

fn close_handle_scope(env: Env, scope: HandleScope) -> Status;

fn is_arraybuffer(env: Env, value: Value, result: *mut bool) -> Status;
fn is_buffer(env: Env, value: Value, result: *mut bool) -> Status;
fn is_error(env: Env, value: Value, result: *mut bool) -> Status;
fn is_array(env: Env, value: Value, result: *mut bool) -> Status;

fn get_value_string_utf8(
env: Env,
value: Value,
buf: *mut c_char,
bufsize: usize,
result: *mut usize,
) -> Status;

fn create_type_error(
env: Env,
code: Value,
msg: Value,
result: *mut Value,
) -> Status;

fn create_range_error(
env: Env,
code: Value,
msg: Value,
result: *mut Value,
) -> Status;

fn create_string_utf8(
env: Env,
str: *const c_char,
length: usize,
result: *mut Value,
) -> Status;

fn create_arraybuffer(
env: Env,
byte_length: usize,
data: *mut *mut c_void,
result: *mut Value,
) -> Status;

fn get_arraybuffer_info(
env: Env,
arraybuffer: Value,
data: *mut *mut c_void,
byte_length: *mut usize,
) -> Status;

fn create_buffer(
env: Env,
length: usize,
data: *mut *mut c_void,
result: *mut Value,
) -> Status;

fn get_buffer_info(
env: Env,
value: Value,
data: *mut *mut c_void,
length: *mut usize,
) -> Status;

fn get_cb_info(
env: Env,
cbinfo: CallbackInfo,
argc: *mut usize,
argv: *mut Value,
this_arg: *mut Value,
data: *mut *mut c_void,
) -> Status;

fn create_external(
env: Env,
data: *mut c_void,
finalize_cb: Finalize,
finalize_hint: *mut c_void,
result: *mut Value,
) -> Status;

fn new_instance(
env: Env,
constructor: Value,
argc: usize,
argv: *const Value,
result: *mut Value,
) -> Status;

fn call_function(
env: Env,
recv: Value,
func: Value,
argc: usize,
argv: *const Value,
result: *mut Value,
) -> Status;

fn create_function(
env: Env,
utf8name: *const c_char,
length: usize,
cb: Callback,
data: *mut c_void,
result: *mut Value,
) -> Status;

fn set_property(
env: Env,
object: Value,
key: Value,
value: Value,
) -> Status;

fn get_property(
env: Env,
object: Value,
key: Value,
result: *mut Value,
) -> Status;

fn set_element(
env: Env,
object: Value,
index: u32,
value: Value,
) -> Status;

fn get_element(
env: Env,
object: Value,
index: u32,
result: *mut Value,
) -> Status;

fn get_all_property_names(
env: Env,
object: Value,
key_mode: KeyCollectionMode,
key_filter: KeyFilter,
key_conversion: KeyConversion,
result: *mut Value,
) -> Status;

fn escape_handle(
env: Env,
scope: EscapableHandleScope,
escapee: Value,
result: *mut Value,
) -> Status;
});
80 changes: 80 additions & 0 deletions crates/neon-runtime/src/napi/bindings/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
macro_rules! napi_name {
(typeof_value) => {
"napi_typeof"
};
($name:ident) => {
concat!("napi_", stringify!($name))
};
}

macro_rules! generate {
(extern "C" {
$(fn $name:ident($($param:ident: $ptype:ty$(,)?)*) -> $rtype:ty;)+
}) => {
pub(crate) struct Napi {
$(
$name: unsafe extern "C" fn(
$($param: $ptype,)*
) -> $rtype,
)*
}

pub(crate) unsafe fn load() -> Result<(), libloading::Error> {
let host = Library::this();
#[cfg(windows)]
let host = host?;

NAPI = Napi {
$(
$name: *host.get(napi_name!($name).as_bytes())?,
)*
};

Ok(())
}

$(
#[inline]
pub(crate) unsafe fn $name($($param: $ptype,)*) -> $rtype {
(NAPI.$name)($($param,)*)
}
)*

fn panic_load<T>() -> T {
panic!("Must load N-API bindings")
}

static mut NAPI: Napi = {
$(
unsafe extern "C" fn $name($(_: $ptype,)*) -> $rtype {
panic_load()
}
)*

Napi {
$(
$name,
)*
}
};
};
}

use std::sync::Once;

pub(crate) use functions::*;
pub(crate) use types::*;

mod types;
mod functions;

static SETUP: Once = Once::new();

/// Loads N-API symbols from host process.
/// Must be called at least once before using any functions in `neon-runtime` or
/// they will panic.
pub fn setup() {
SETUP.call_once(|| unsafe {
load().expect("Failed to load N-API symbols");
});
}
Loading

0 comments on commit 1f1a2e0

Please sign in to comment.