Skip to content

Commit

Permalink
Merge pull request #1024 from neon-bindings/kv/extract
Browse files Browse the repository at this point in the history
feat(neon): Extractors
  • Loading branch information
kjvalencik authored Apr 2, 2024
2 parents 50c74c0 + 1b28463 commit 2e0a0f1
Show file tree
Hide file tree
Showing 21 changed files with 1,058 additions and 57 deletions.
8 changes: 4 additions & 4 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[alias]
# Neon defines mutually exclusive feature flags which prevents using `cargo clippy --all-features`
# The following aliases simplify linting the entire workspace
neon-check = " check --all --all-targets --features napi-experimental,futures,external-buffers"
neon-clippy = "clippy --all --all-targets --features napi-experimental,futures,external-buffers -- -A clippy::missing_safety_doc"
neon-test = " test --all --features=doc-dependencies,doc-comment,napi-experimental,futures,external-buffers"
neon-doc = " rustdoc -p neon --features=doc-dependencies,napi-experimental,futures,external-buffers,sys -- --cfg docsrs"
neon-check = " check --all --all-targets --features napi-experimental,futures,external-buffers,serde"
neon-clippy = "clippy --all --all-targets --features napi-experimental,futures,external-buffers,serde -- -A clippy::missing_safety_doc"
neon-test = " test --all --features=doc-dependencies,doc-comment,napi-experimental,futures,external-buffers,serde"
neon-doc = " rustdoc -p neon --features=doc-dependencies,napi-experimental,futures,external-buffers,sys,serde -- --cfg docsrs"
57 changes: 41 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions crates/neon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ aquamarine = { version = "0.3.2", optional = true }
easy-cast = { version = "0.5.2", optional = true }
doc-comment = { version = "0.3.3", optional = true }
send_wrapper = "0.6.0"
serde = { version = "1.0.197", optional = true }
serde_json = { version = "1.0.114", optional = true }

[dependencies.tokio]
version = "1.34.0"
Expand All @@ -42,6 +44,9 @@ optional = true
[features]
default = ["napi-8"]

# Enable extracting values by serializing to JSON
serde = ["dep:serde", "dep:serde_json"]

# Enable the creation of external binary buffers. This is disabled by default
# since these APIs fail at runtime in environments that enable the V8 memory
# cage (such as Electron: https://www.electronjs.org/blog/v8-memory-cage).
Expand Down
2 changes: 1 addition & 1 deletion crates/neon/src/context/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl From<raw::Env> for Env {

thread_local! {
#[allow(unused)]
pub(crate) static IS_RUNNING: RefCell<bool> = RefCell::new(false);
pub(crate) static IS_RUNNING: RefCell<bool> = const { RefCell::new(false) };
}

impl Env {
Expand Down
80 changes: 80 additions & 0 deletions crates/neon/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ use crate::{
types::{
boxed::{Finalize, JsBox},
error::JsError,
extract::FromArgs,
private::ValueInternal,
Deferred, JsArray, JsArrayBuffer, JsBoolean, JsBuffer, JsFunction, JsNull, JsNumber,
JsObject, JsPromise, JsString, JsUndefined, JsValue, StringResult, Value,
Expand Down Expand Up @@ -212,6 +213,36 @@ impl CallbackInfo<'_> {
local
}
}

pub(crate) fn argv_exact<'b, C: Context<'b>, const N: usize>(
&self,
cx: &mut C,
) -> [Handle<'b, JsValue>; N] {
use std::ptr;

let mut argv = [JsValue::new_internal(ptr::null_mut()); N];
let mut argc = argv.len();

// # Safety
// * Node-API fills empty slots with `undefined`
// * `Handle` and `JsValue` are transparent wrappers around a raw pointer
unsafe {
assert_eq!(
sys::get_cb_info(
cx.env().to_raw(),
self.info,
&mut argc,
argv.as_mut_ptr().cast(),
ptr::null_mut(),
ptr::null_mut(),
),
sys::Status::Ok,
);
}

// Empty values will be filled with `undefined`
argv
}
}

/// Indicates whether a function was called with `new`.
Expand Down Expand Up @@ -683,6 +714,55 @@ impl<'a> FunctionContext<'a> {
pub fn this_value(&mut self) -> Handle<'a, JsValue> {
JsValue::new_internal(self.info.this(self))
}

/// Extract Rust data from the JavaScript arguments.
///
/// This is frequently more efficient and ergonomic than getting arguments
/// individually. See the [`extract`](crate::types::extract) module documentation
/// for more examples.
///
/// ```
/// # use neon::{prelude::*, types::extract::*};
/// fn add(mut cx: FunctionContext) -> JsResult<JsNumber> {
/// let (a, b): (f64, f64) = cx.args()?;
///
/// Ok(cx.number(a + b))
/// }
/// ```
pub fn args<T>(&mut self) -> NeonResult<T>
where
T: FromArgs<'a>,
{
T::from_args(self)
}

/// Extract Rust data from the JavaScript arguments.
///
/// Similar to [`FunctionContext::args`], but does not throw a JavaScript exception on errors. Useful
/// for function overloading.
///
/// ```
/// # use neon::{prelude::*, types::extract::*};
/// fn combine(mut cx: FunctionContext) -> JsResult<JsValue> {
/// if let Some((a, b)) = cx.args_opt::<(f64, f64)>()? {
/// return Ok(cx.number(a + b).upcast());
/// }
///
/// let (a, b): (String, String) = cx.args()?;
///
/// Ok(cx.string(a + &b).upcast())
/// }
/// ```
pub fn args_opt<T>(&mut self) -> NeonResult<Option<T>>
where
T: FromArgs<'a>,
{
T::from_args_opt(self)
}

pub(crate) fn argv<const N: usize>(&mut self) -> [Handle<'a, JsValue>; N] {
self.info.argv_exact(self)
}
}

impl<'a> ContextInternal<'a> for FunctionContext<'a> {
Expand Down
6 changes: 6 additions & 0 deletions crates/neon/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub mod reflect;
pub mod result;
#[cfg(not(feature = "sys"))]
mod sys;
#[cfg_attr(docsrs, doc(cfg(feature = "napi-6")))]
#[cfg(feature = "napi-6")]
pub mod thread;
// To use the #[aquamarine] attribute on the top-level neon::types module docs, we have to
Expand Down Expand Up @@ -140,6 +141,7 @@ fn feature_matrix() {

const EXTERNAL_BUFFERS: &str = "external-buffers";
const FUTURES: &str = "futures";
const SERDE: &str = "serde";
const NODE_API_VERSIONS: &[&str] = &[
"napi-1", "napi-2", "napi-3", "napi-4", "napi-5", "napi-6", "napi-7", "napi-8",
];
Expand All @@ -150,7 +152,11 @@ fn feature_matrix() {
&[],
&[EXTERNAL_BUFFERS],
&[FUTURES],
&[SERDE],
&[EXTERNAL_BUFFERS, FUTURES],
&[EXTERNAL_BUFFERS, SERDE],
&[FUTURES, SERDE],
&[EXTERNAL_BUFFERS, FUTURES, SERDE],
];

let cargo = env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
Expand Down
4 changes: 2 additions & 2 deletions crates/neon/src/types_impl/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,8 @@ unsafe impl TransparentNoCopyWrapper for JsBigInt {
}

impl private::ValueInternal for JsBigInt {
fn name() -> String {
"BigInt".to_string()
fn name() -> &'static str {
"BigInt"
}

fn is_typeof<Other: Value>(env: Env, other: &Other) -> bool {
Expand Down
4 changes: 2 additions & 2 deletions crates/neon/src/types_impl/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ unsafe impl<T: 'static> TransparentNoCopyWrapper for JsBox<T> {
}

impl<T: 'static> ValueInternal for JsBox<T> {
fn name() -> String {
any::type_name::<Self>().to_string()
fn name() -> &'static str {
any::type_name::<Self>()
}

fn is_typeof<Other: Value>(env: Env, other: &Other) -> bool {
Expand Down
12 changes: 6 additions & 6 deletions crates/neon/src/types_impl/buffer/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ unsafe impl TransparentNoCopyWrapper for JsBuffer {
}

impl ValueInternal for JsBuffer {
fn name() -> String {
"Buffer".to_string()
fn name() -> &'static str {
"Buffer"
}

fn is_typeof<Other: Value>(env: Env, other: &Other) -> bool {
Expand Down Expand Up @@ -341,8 +341,8 @@ unsafe impl TransparentNoCopyWrapper for JsArrayBuffer {
}

impl ValueInternal for JsArrayBuffer {
fn name() -> String {
"JsArrayBuffer".to_string()
fn name() -> &'static str {
"JsArrayBuffer"
}

fn is_typeof<Other: Value>(env: Env, other: &Other) -> bool {
Expand Down Expand Up @@ -791,8 +791,8 @@ macro_rules! impl_typed_array {
impl Object for JsTypedArray<$etyp> {}

impl ValueInternal for JsTypedArray<$etyp> {
fn name() -> String {
stringify!($typ).to_string()
fn name() -> &'static str {
stringify!($typ)
}

fn is_typeof<Other: Value>(env: Env, other: &Other) -> bool {
Expand Down
4 changes: 2 additions & 2 deletions crates/neon/src/types_impl/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ impl JsDate {
}

impl ValueInternal for JsDate {
fn name() -> String {
"object".to_string()
fn name() -> &'static str {
"object"
}

fn is_typeof<Other: Value>(env: Env, other: &Other) -> bool {
Expand Down
Loading

0 comments on commit 2e0a0f1

Please sign in to comment.