Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic loading sample #645

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ legacy-runtime = ["neon-runtime/neon-sys", "neon-build/neon-sys"]

# Feature flag to enable the experimental N-API runtime. For now, this feature
# is disabled by default.
napi-runtime = ["proc-macros", "neon-macros/napi", "neon-runtime/nodejs-sys"]
napi-runtime = ["proc-macros", "neon-macros/napi", "neon-runtime/napi"]

# Feature flag to disable external dependencies on docs build
docs-only = ["neon-runtime/docs-only"]
Expand Down
4 changes: 4 additions & 0 deletions crates/neon-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@ authors = ["Dave Herman <[email protected]>"]
description = "Bindings to the Node.js native addon API, used by the Neon implementation."
repository = "https://github.com/neon-bindings/neon"
license = "MIT/Apache-2.0"
edition = "2018"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to edition 2018 because I only know how to use macro_rules! in this edition


[dependencies]
cfg-if = "0.1.9"
libloading = { version = "0.6.5", optional = true }
lazy_static = { version = "1.4.0", 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"

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

[package.metadata.docs.rs]
features = ["docs-only"]
6 changes: 1 addition & 5 deletions crates/neon-runtime/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
extern crate cfg_if;
extern crate smallvec;

#[cfg(all(not(feature = "neon-sys"), not(feature = "nodejs-sys")))]
compile_error!("The Neon runtime must have at least one of the `neon-sys` or `nodejs-sys` backends enabled.");

use cfg_if::cfg_if;

cfg_if! {
if #[cfg(feature = "nodejs-sys")] {
pub extern crate nodejs_sys;
pub use nodejs_sys;
pub mod napi;
}
}

cfg_if! {
if #[cfg(feature = "neon-sys")] {
extern crate neon_sys;
pub mod nan;
// The legacy variant is the default API as long as it's present.
pub use nan::*;
Expand Down
2 changes: 1 addition & 1 deletion crates/neon-runtime/src/nan/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Facilities for creating and throwing JS errors.

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

/// Throws an `Error` object in the current context.
pub unsafe fn throw(_: Isolate, val: Local) {
Expand Down
6 changes: 3 additions & 3 deletions crates/neon-runtime/src/nan/scope.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! Facilities for working with `v8::HandleScope`s and `v8::EscapableHandleScope`s.

use raw::{HandleScope, EscapableHandleScope, InheritedHandleScope, Isolate};
use crate::raw::{HandleScope, EscapableHandleScope, InheritedHandleScope, Isolate};

pub trait Root {
unsafe fn allocate() -> Self;
unsafe fn enter(&mut self, Isolate);
unsafe fn exit(&mut self, Isolate);
unsafe fn enter(&mut self, isolate: Isolate);
unsafe fn exit(&mut self, isolate: Isolate);
}

impl Root for HandleScope {
Expand Down
2 changes: 1 addition & 1 deletion crates/neon-runtime/src/napi/array.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Facilities for working with Array `napi_value`s.

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

use nodejs_sys as napi;

Expand Down
2 changes: 1 addition & 1 deletion crates/neon-runtime/src/napi/arraybuffer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use raw::{Env, Local};
use crate::raw::{Env, Local};
use std::os::raw::c_void;
use std::ptr::null_mut;

Expand Down
119 changes: 119 additions & 0 deletions crates/neon-runtime/src/napi/bindings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use std::mem::MaybeUninit;
use lazy_static::lazy_static;
use libloading::{Library, Symbol};

/* Later we should do:
#[repr(C)]
struct NapiEnvStruct {}

#[repr(C)]
struct NapiValueStruct {}

pub(crate) type NapiEnv = *mut NapiEnvStruct;
pub(crate) type NapiValue = *mut NapiValueStruct;

But in this sample we still rely on nodejs_sys's types
*/

pub(crate) type NapiEnv = nodejs_sys::napi_env;
pub(crate) type NapiValue = nodejs_sys::napi_value;

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
pub(crate) enum NapiStatus {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy-paste from the N-API docs; with repr(C) this is fine. N-API will only ever add new values at the end

napi_ok,
napi_invalid_arg,
napi_object_expected,
napi_string_expected,
napi_name_expected,
napi_function_expected,
napi_number_expected,
napi_boolean_expected,
napi_array_expected,
napi_generic_failure,
napi_pending_exception,
napi_cancelled,
napi_escape_called_twice,
napi_handle_scope_mismatch,
napi_callback_scope_mismatch,
napi_queue_full,
napi_closing,
napi_bigint_expected,
napi_date_expected,
napi_arraybuffer_expected,
napi_detachable_arraybuffer_expected,
napi_would_deadlock,
}

pub(crate) struct Napi<'a> {
pub napi_get_undefined: Symbol<'a, unsafe extern "C" fn(env: NapiEnv, out: *mut NapiValue) -> NapiStatus>,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the actual declaration is quite hard to read here, because of the Symbol<> wrapper and because it's in the middle of the line instead of at the start. Could we make it simpler somehow?

pub napi_get_null: Symbol<'a, unsafe extern "C" fn(env: NapiEnv, out: *mut NapiValue) -> NapiStatus>,

pub napi_get_boolean:
Symbol<'a, unsafe extern "C" fn(env: NapiEnv, value: bool, out: *mut NapiValue) -> NapiStatus>,
pub napi_get_value_bool:
Symbol<'a, unsafe extern "C" fn(env: NapiEnv, value: NapiValue, out: *mut bool) -> NapiStatus>,

pub napi_create_double:
Symbol<'a, unsafe extern "C" fn(env: NapiEnv, value: f64, out: *mut NapiValue) -> NapiStatus>,
pub napi_get_value_double:
Symbol<'a, unsafe extern "C" fn(env: NapiEnv, value: NapiValue, out: *mut f64) -> NapiStatus>,
}

#[cfg(not(windows))]
fn get_host_library() -> Library {
use libloading::os::unix::Library;
Library::this().into()
}

#[cfg(windows)]
fn get_host_library() -> Library {
use libloading::os::windows::Library;
Library::this().into()
}

lazy_static! {
static ref HOST: Library = get_host_library();
}

impl Napi<'_> {
fn try_from_host() -> Result<Self, libloading::Error> {
let host = &HOST;

Ok(unsafe {
Self {
napi_get_undefined: host.get(b"napi_get_undefined")?,
napi_get_null: host.get(b"napi_get_null")?,
napi_get_boolean: host.get(b"napi_get_boolean")?,
napi_get_value_bool: host.get(b"napi_get_value_bool")?,
napi_create_double: host.get(b"napi_create_double")?,
napi_get_value_double: host.get(b"napi_get_value_double")?,
}
})
}

pub fn from_host() -> Self {
Self::try_from_host().unwrap()
}
}

pub(crate) static mut NAPI: MaybeUninit<Napi> = MaybeUninit::uninit();

/// Load the N-API symbols we need.
pub(crate) unsafe fn load() {
NAPI.as_mut_ptr().write(Napi::from_host());
}

macro_rules! napi {
( $name:ident ( $($args:expr),* ) ) => {
{
let bindings = $crate::napi::bindings::NAPI.as_ptr();
let result: $crate::napi::bindings::NapiStatus = ((*bindings).$name)(
$($args),*
);
result
}
}
}
2 changes: 1 addition & 1 deletion crates/neon-runtime/src/napi/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use raw::{Env, Local};
use crate::raw::{Env, Local};
use std::os::raw::c_void;
use std::ptr::null_mut;

Expand Down
2 changes: 1 addition & 1 deletion crates/neon-runtime/src/napi/call.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use std::ptr::null_mut;
use raw::{FunctionCallbackInfo, Env, Local};
use crate::raw::{FunctionCallbackInfo, Env, Local};
use smallvec::{smallvec, SmallVec};
use nodejs_sys as napi;

Expand Down
4 changes: 2 additions & 2 deletions crates/neon-runtime/src/napi/class.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::os::raw::c_void;
use call::CCallback;
use raw::{Env, Local};
use crate::call::CCallback;
use crate::raw::{Env, Local};

pub unsafe extern "C" fn get_class_map(_isolate: Env) -> *mut c_void { unimplemented!() }

Expand Down
2 changes: 1 addition & 1 deletion crates/neon-runtime/src/napi/convert.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use nodejs_sys as napi;

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

/// This API is currently unused, see https://github.com/neon-bindings/neon/issues/572
pub unsafe extern "C" fn to_object(out: &mut Local, env: Env, value: Local) -> bool {
Expand Down
2 changes: 1 addition & 1 deletion crates/neon-runtime/src/napi/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::ptr;

use nodejs_sys as napi;

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

pub unsafe fn is_throwing(env: Env) -> bool {
let mut b: MaybeUninit<bool> = MaybeUninit::zeroed();
Expand Down
2 changes: 1 addition & 1 deletion crates/neon-runtime/src/napi/external.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::mem::MaybeUninit;

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

use nodejs_sys as napi;

Expand Down
4 changes: 2 additions & 2 deletions crates/neon-runtime/src/napi/fun.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Facilities for working with JS functions.

use call::CCallback;
use raw::{Env, Local};
use crate::call::CCallback;
use crate::raw::{Env, Local};
use std::os::raw::c_void;
use std::ptr::null;

Expand Down
2 changes: 1 addition & 1 deletion crates/neon-runtime/src/napi/handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use raw::Local;
use crate::raw::Local;
use std::os::raw::c_void;

pub unsafe extern "C" fn new(_isolate: *mut c_void, _this: Local, _callback: Local) -> *mut c_void { unimplemented!() }
Expand Down
2 changes: 1 addition & 1 deletion crates/neon-runtime/src/napi/mem.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
use raw::Local;
use crate::raw::Local;

pub unsafe extern "C" fn same_handle(_h1: Local, _h2: Local) -> bool { unimplemented!() }
9 changes: 9 additions & 0 deletions crates/neon-runtime/src/napi/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[macro_use]
pub(crate) mod bindings;

pub mod array;
pub mod arraybuffer;
pub mod buffer;
Expand All @@ -16,3 +19,9 @@ pub mod string;
pub mod tag;
pub mod task;
pub mod handler;

/// # Safety
/// Must only be called once.
pub unsafe fn setup() {
bindings::load();
}
2 changes: 1 addition & 1 deletion crates/neon-runtime/src/napi/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::mem::MaybeUninit;

use nodejs_sys as napi;

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

/// Mutates the `out` argument to refer to a `napi_value` containing a newly created JavaScript Object.
pub unsafe extern "C" fn new(out: &mut Local, env: Env) {
Expand Down
35 changes: 26 additions & 9 deletions crates/neon-runtime/src/napi/primitive.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@
use raw::{Local, Env};

use nodejs_sys as napi;
use crate::raw::{Local, Env};
use crate::bindings::NapiStatus;

/// Mutates the `out` argument provided to refer to the global `undefined` object.
pub unsafe extern "C" fn undefined(out: &mut Local, env: Env) {
napi::napi_get_undefined(env, out as *mut Local);
assert_eq!(
napi!(napi_get_undefined(env, out as *mut Local)),
NapiStatus::napi_ok,
);
}

/// Mutates the `out` argument provided to refer to the global `null` object.
pub unsafe extern "C" fn null(out: &mut Local, env: Env) {
napi::napi_get_null(env, out as *mut Local);
assert_eq!(
napi!(napi_get_null(env, out as *mut Local)),
NapiStatus::napi_ok,
);
}

/// Mutates the `out` argument provided to refer to one of the global `true` or `false` objects.
pub unsafe extern "C" fn boolean(out: &mut Local, env: Env, b: bool) {
napi::napi_get_boolean(env, b, out as *mut Local);
assert_eq!(
napi!(napi_get_boolean(env, b, out as *mut Local)),
NapiStatus::napi_ok,
);
}

/// Get the boolean value out of a `Local` object. If the `Local` object does not contain a
/// boolean, this function panics.
pub unsafe extern "C" fn boolean_value(env: Env, p: Local) -> bool {
let mut value = false;
assert_eq!(napi::napi_get_value_bool(env, p, &mut value as *mut bool), napi::napi_status::napi_ok);
assert_eq!(
napi!(napi_get_value_bool(env, p, &mut value as *mut bool)),
NapiStatus::napi_ok,
);
value
}

Expand All @@ -38,13 +49,19 @@ pub unsafe extern "C" fn integer_value(_p: Local) -> i64 { unimplemented!() }
/// Mutates the `out` argument provided to refer to a newly created `Local` containing a
/// JavaScript number.
pub unsafe extern "C" fn number(out: &mut Local, env: Env, v: f64) {
napi::napi_create_double(env, v, out as *mut Local);
assert_eq!(
napi!(napi_create_double(env, v, out as *mut Local)),
NapiStatus::napi_ok,
);
}

/// Gets the underlying value of an `Local` object containing a JavaScript number. Panics if
/// the given `Local` is not a number.
pub unsafe extern "C" fn number_value(env: Env, p: Local) -> f64 {
let mut value = 0.0;
assert_eq!(napi::napi_get_value_double(env, p, &mut value as *mut f64), napi::napi_status::napi_ok);
assert_eq!(
napi!(napi_get_value_double(env, p, &mut value as *mut f64)),
NapiStatus::napi_ok,
);
return value;
}
2 changes: 1 addition & 1 deletion crates/neon-runtime/src/napi/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::mem::MaybeUninit;

use nodejs_sys as napi;

use raw::{Env, HandleScope, EscapableHandleScope, InheritedHandleScope};
use crate::raw::{Env, HandleScope, EscapableHandleScope, InheritedHandleScope};

type Local = napi::napi_value;

Expand Down
2 changes: 1 addition & 1 deletion crates/neon-runtime/src/napi/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::ptr;

use nodejs_sys as napi;

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

pub unsafe fn new(out: &mut Local, env: Env, data: *const u8, len: i32) -> bool {
let status = napi::napi_create_string_utf8(
Expand Down
2 changes: 1 addition & 1 deletion crates/neon-runtime/src/napi/tag.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use raw::{Env, Local};
use crate::raw::{Env, Local};

use nodejs_sys as napi;

Expand Down
Loading