From 1ac73a640bbfac09fd4d48f56ac3ca4d3f46b086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 28 Feb 2020 11:19:44 +0100 Subject: [PATCH 01/23] Add `env` parameter to `invoke()` API --- src/context/mod.rs | 7 +++---- src/object/class/internal.rs | 20 ++++++++++---------- src/object/class/mod.rs | 17 ++++++++++++++--- src/types/internal.rs | 4 ++-- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/context/mod.rs b/src/context/mod.rs index 2febcdaf8..e4f9109fc 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -35,8 +35,8 @@ impl CallbackInfo { } } - pub unsafe fn with_cx FnOnce(CallContext<'a, T>) -> U>(&self, f: F) -> U { - CallContext::::with(self, f) + pub unsafe fn with_cx FnOnce(CallContext<'a, T>) -> U>(&self, env: Env, f: F) -> U { + CallContext::::with(env, self, f) } pub fn set_return<'a, 'b, T: Value>(&'a self, value: Handle<'b, T>) { @@ -456,8 +456,7 @@ impl<'a, T: This> CallContext<'a, T> { /// Indicates whether the function was called via the JavaScript `[[Call]]` or `[[Construct]]` semantics. pub fn kind(&self) -> CallKind { self.info.kind() } - pub(crate) fn with FnOnce(CallContext<'b, T>) -> U>(info: &'a CallbackInfo, f: F) -> U { - let env = Env::current(); + pub(crate) fn with FnOnce(CallContext<'b, T>) -> U>(env: Env, info: &'a CallbackInfo, f: F) -> U { Scope::with(env, |scope| { f(CallContext { scope, diff --git a/src/object/class/internal.rs b/src/object/class/internal.rs index 6c564cd90..8ba469627 100644 --- a/src/object/class/internal.rs +++ b/src/object/class/internal.rs @@ -6,7 +6,7 @@ use neon_runtime::raw; use super::{Class, ClassInternal, Callback}; use handle::{Handle, Managed}; use context::{CallbackInfo, CallContext, Context}; -use context::internal::ContextInternal; +use context::internal::{ContextInternal, Env}; use result::{NeonResult, JsResult, Throw}; use types::{JsValue, JsObject, JsFunction, JsUndefined, build}; use types::error::convert_panics; @@ -15,9 +15,9 @@ use types::error::convert_panics; pub struct MethodCallback(pub fn(CallContext) -> JsResult); impl Callback<()> for MethodCallback { - extern "C" fn invoke(info: &CallbackInfo) { + extern "C" fn invoke(env: Env, info: &CallbackInfo) { unsafe { - info.with_cx::(|mut cx| { + info.with_cx::(env, |mut cx| { let data = info.data(); let this: Handle = Handle::new_internal(JsValue::from_raw(info.this(&mut cx))); @@ -39,7 +39,7 @@ impl Callback<()> for MethodCallback { } }) } - } + } fn as_ptr(self) -> *mut c_void { self.0 as *mut c_void @@ -65,9 +65,9 @@ impl ConstructorCallCallback { } impl Callback<()> for ConstructorCallCallback { - extern "C" fn invoke(info: &CallbackInfo) { + extern "C" fn invoke(env: Env, info: &CallbackInfo) { unsafe { - info.with_cx(|cx| { + info.with_cx(env, |cx| { let data = info.data(); let kernel: fn(CallContext) -> JsResult = mem::transmute(neon_runtime::class::get_call_kernel(data.to_raw())); @@ -87,9 +87,9 @@ impl Callback<()> for ConstructorCallCallback { pub struct AllocateCallback(pub fn(CallContext) -> NeonResult); impl Callback<*mut c_void> for AllocateCallback { - extern "C" fn invoke(info: &CallbackInfo) -> *mut c_void { + extern "C" fn invoke(env: Env, info: &CallbackInfo) -> *mut c_void { unsafe { - info.with_cx(|cx| { + info.with_cx(env, |cx| { let data = info.data(); let kernel: fn(CallContext) -> NeonResult = mem::transmute(neon_runtime::class::get_allocate_kernel(data.to_raw())); @@ -112,9 +112,9 @@ impl Callback<*mut c_void> for AllocateCallback { pub struct ConstructCallback(pub fn(CallContext) -> NeonResult>>); impl Callback for ConstructCallback { - extern "C" fn invoke(info: &CallbackInfo) -> bool { + extern "C" fn invoke(env: Env, info: &CallbackInfo) -> bool { unsafe { - info.with_cx(|cx| { + info.with_cx(env, |cx| { let data = info.data(); let kernel: fn(CallContext) -> NeonResult>> = mem::transmute(neon_runtime::class::get_construct_kernel(data.to_raw())); diff --git a/src/object/class/mod.rs b/src/object/class/mod.rs index dd04e196b..b6d016dc2 100644 --- a/src/object/class/mod.rs +++ b/src/object/class/mod.rs @@ -266,11 +266,18 @@ impl<'a, T: Class> BorrowMut for &'a mut T { /// as a pair of 1) a raw pointer to the dynamically computed function, and 2) /// a static function that knows how to transmute that raw pointer and call it. pub(crate) trait Callback: Sized { - /// Extracts the computed Rust function and invokes it. The Neon runtime /// ensures that the computed function is provided as the extra data field, /// wrapped as a V8 External, in the `CallbackInfo` argument. - extern "C" fn invoke(info: &CallbackInfo) -> T; + extern "C" fn invoke(env: Env, info: &CallbackInfo) -> T; + + /// See `invoke`. This is used by the non-n-api implementation, so that every impl for this + /// trait doesn't need to provide two versions of `invoke`. + #[cfg(feature = "legacy-runtime")] + #[doc(hidden)] + extern "C" fn invoke_compat(info: &CallbackInfo) -> T { + Self::invoke(Env::current(), info) + } /// Converts the callback to a raw void pointer. fn as_ptr(self) -> *mut c_void; @@ -278,8 +285,12 @@ pub(crate) trait Callback: Sized { /// Exports the callback as a pair consisting of the static `Self::invoke` /// method and the computed callback, both converted to raw void pointers. fn into_c_callback(self) -> CCallback { + #[cfg(feature = "napi-runtime")] + let invoke = Self::invoke; + #[cfg(feature = "legacy-runtime")] + let invoke = Self::invoke_compat; CCallback { - static_callback: unsafe { mem::transmute(Self::invoke as usize) }, + static_callback: unsafe { mem::transmute(invoke as usize) }, dynamic_callback: self.as_ptr() } } diff --git a/src/types/internal.rs b/src/types/internal.rs index 880b9d4bd..e90a7a1a8 100644 --- a/src/types/internal.rs +++ b/src/types/internal.rs @@ -32,9 +32,9 @@ pub trait ValueInternal: Managed + 'static { pub struct FunctionCallback(pub fn(FunctionContext) -> JsResult); impl Callback<()> for FunctionCallback { - extern "C" fn invoke(info: &CallbackInfo) { + extern "C" fn invoke(env: Env, info: &CallbackInfo) { unsafe { - info.with_cx::(|cx| { + info.with_cx::(env, |cx| { let data = info.data(); let dynamic_callback: fn(FunctionContext) -> JsResult = mem::transmute(neon_runtime::fun::get_dynamic_callback(data.to_raw())); From 6d5c32a8ea93b3d0e7f6ffe8e38d5c1fdbe1d280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 28 Feb 2020 16:04:30 +0100 Subject: [PATCH 02/23] Make CallbackInfo wrap a pointer. --- crates/neon-runtime/src/napi/call.rs | 18 +++++++++++------- crates/neon-runtime/src/napi/fun.rs | 3 ++- crates/neon-runtime/src/napi/raw.rs | 4 ++-- crates/neon-sys/src/lib.rs | 16 ++++++++-------- src/context/mod.rs | 13 +++++++------ src/object/class/internal.rs | 8 ++++---- src/object/class/mod.rs | 4 ++-- src/types/internal.rs | 2 +- 8 files changed, 37 insertions(+), 31 deletions(-) diff --git a/crates/neon-runtime/src/napi/call.rs b/crates/neon-runtime/src/napi/call.rs index b0d4d6c71..a07a6d487 100644 --- a/crates/neon-runtime/src/napi/call.rs +++ b/crates/neon-runtime/src/napi/call.rs @@ -2,6 +2,8 @@ use std::os::raw::c_void; use std::ptr::null_mut; use raw::{FunctionCallbackInfo, Env, Local}; +use nodejs_sys as napi; + #[repr(C)] pub struct CCallback { pub static_callback: *mut c_void, @@ -17,19 +19,21 @@ impl Default for CCallback { } } -pub unsafe extern "C" fn set_return(_info: &FunctionCallbackInfo, _value: Local) { unimplemented!() } +pub unsafe extern "C" fn set_return(_info: FunctionCallbackInfo, _value: Local) { + +} -pub unsafe extern "C" fn get_isolate(_info: &FunctionCallbackInfo) -> Env { unimplemented!() } +pub unsafe extern "C" fn get_isolate(info: FunctionCallbackInfo) -> Env { unimplemented!() } // FIXME: Remove. This will never be implemented pub unsafe extern "C" fn current_isolate() -> Env { panic!("current_isolate won't be implemented in n-api") } -pub unsafe extern "C" fn is_construct(_info: &FunctionCallbackInfo) -> bool { unimplemented!() } +pub unsafe extern "C" fn is_construct(_info: FunctionCallbackInfo) -> bool { unimplemented!() } -pub unsafe extern "C" fn this(_info: &FunctionCallbackInfo, _out: &mut Local) { unimplemented!() } +pub unsafe extern "C" fn this(_info: FunctionCallbackInfo, _out: &mut Local) { unimplemented!() } -pub unsafe extern "C" fn data(_info: &FunctionCallbackInfo, _out: &mut Local) { unimplemented!() } +pub unsafe extern "C" fn data(_info: FunctionCallbackInfo, _out: &mut Local) { unimplemented!() } -pub unsafe extern "C" fn len(_info: &FunctionCallbackInfo) -> i32 { unimplemented!() } +pub unsafe extern "C" fn len(_info: FunctionCallbackInfo) -> i32 { unimplemented!() } -pub unsafe extern "C" fn get(_info: &FunctionCallbackInfo, _i: i32, _out: &mut Local) { unimplemented!() } +pub unsafe extern "C" fn get(_info: FunctionCallbackInfo, _i: i32, _out: &mut Local) { unimplemented!() } diff --git a/crates/neon-runtime/src/napi/fun.rs b/crates/neon-runtime/src/napi/fun.rs index 10963b6fd..4fe37f8f3 100644 --- a/crates/neon-runtime/src/napi/fun.rs +++ b/crates/neon-runtime/src/napi/fun.rs @@ -1,4 +1,5 @@ -use std::os::raw::c_void; +//! Facilities for working with JS functions. + use call::CCallback; use raw::{Env, Local}; diff --git a/crates/neon-runtime/src/napi/raw.rs b/crates/neon-runtime/src/napi/raw.rs index d3a92b974..007c5f601 100644 --- a/crates/neon-runtime/src/napi/raw.rs +++ b/crates/neon-runtime/src/napi/raw.rs @@ -3,9 +3,9 @@ use std::ptr; use nodejs_sys as napi; -pub type Local = napi::napi_value; +pub type Local = napi::napi_value; -pub type FunctionCallbackInfo = c_void; +pub type FunctionCallbackInfo = napi::napi_callback_info; pub type Env = napi::napi_env; diff --git a/crates/neon-sys/src/lib.rs b/crates/neon-sys/src/lib.rs index 136a1b89e..8baba2607 100644 --- a/crates/neon-sys/src/lib.rs +++ b/crates/neon-sys/src/lib.rs @@ -16,7 +16,7 @@ pub struct Local { /// /// It contains the arguments used to invoke the function, the isolate reference, the `this` object /// the function is bound to and a mechanism to return a value to the caller. -pub type FunctionCallbackInfo = c_void; +pub type FunctionCallbackInfo = *const c_void; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -93,14 +93,14 @@ extern "C" { pub fn Neon_Buffer_Uninitialized(out: &mut Local, size: u32) -> bool; pub fn Neon_Buffer_Data<'a, 'b>(base_out: &'a mut *mut c_void, obj: Local) -> usize; - pub fn Neon_Call_SetReturn(info: &FunctionCallbackInfo, value: Local); - pub fn Neon_Call_GetIsolate(info: &FunctionCallbackInfo) -> Isolate; + pub fn Neon_Call_SetReturn(info: FunctionCallbackInfo, value: Local); + pub fn Neon_Call_GetIsolate(info: FunctionCallbackInfo) -> Isolate; pub fn Neon_Call_CurrentIsolate() -> Isolate; - pub fn Neon_Call_IsConstruct(info: &FunctionCallbackInfo) -> bool; - pub fn Neon_Call_This(info: &FunctionCallbackInfo, out: &mut Local); - pub fn Neon_Call_Data(info: &FunctionCallbackInfo, out: &mut Local); - pub fn Neon_Call_Length(info: &FunctionCallbackInfo) -> i32; - pub fn Neon_Call_Get(info: &FunctionCallbackInfo, i: i32, out: &mut Local); + pub fn Neon_Call_IsConstruct(info: FunctionCallbackInfo) -> bool; + pub fn Neon_Call_This(info: FunctionCallbackInfo, out: &mut Local); + pub fn Neon_Call_Data(isolate: Isolate, info: FunctionCallbackInfo, out: &mut Local); + pub fn Neon_Call_Length(info: FunctionCallbackInfo) -> i32; + pub fn Neon_Call_Get(info: FunctionCallbackInfo, i: i32, out: &mut Local); pub fn Neon_Class_GetClassMap(isolate: Isolate) -> *mut c_void; pub fn Neon_Class_SetClassMap(isolate: Isolate, map: *mut c_void, free_map: *mut c_void); diff --git a/src/context/mod.rs b/src/context/mod.rs index e4f9109fc..93589a9b5 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -20,6 +20,7 @@ use object::{Object, This}; use object::class::Class; use result::{NeonResult, JsResult, Throw}; use self::internal::{ContextInternal, Scope, ScopeMetadata}; +use std::os::raw::c_void; #[repr(C)] pub(crate) struct CallbackInfo { @@ -30,7 +31,7 @@ impl CallbackInfo { pub fn data<'a>(&self) -> Handle<'a, JsValue> { unsafe { let mut local: raw::Local = std::mem::zeroed(); - neon_runtime::call::data(&self.info, &mut local); + neon_runtime::call::data(self.info, &mut local); Handle::new_internal(JsValue::from_raw(local)) } } @@ -41,7 +42,7 @@ impl CallbackInfo { pub fn set_return<'a, 'b, T: Value>(&'a self, value: Handle<'b, T>) { unsafe { - neon_runtime::call::set_return(&self.info, value.to_raw()) + neon_runtime::call::set_return(self.info, value.to_raw()) } } @@ -55,7 +56,7 @@ impl CallbackInfo { pub fn len(&self) -> i32 { unsafe { - neon_runtime::call::len(&self.info) + neon_runtime::call::len(self.info) } } @@ -65,7 +66,7 @@ impl CallbackInfo { } unsafe { let mut local: raw::Local = std::mem::zeroed(); - neon_runtime::call::get(&self.info, i, &mut local); + neon_runtime::call::get(self.info, i, &mut local); Some(Handle::new_internal(JsValue::from_raw(local))) } } @@ -76,7 +77,7 @@ impl CallbackInfo { } unsafe { let mut local: raw::Local = std::mem::zeroed(); - neon_runtime::call::get(&self.info, i, &mut local); + neon_runtime::call::get(self.info, i, &mut local); Ok(Handle::new_internal(JsValue::from_raw(local))) } } @@ -84,7 +85,7 @@ impl CallbackInfo { pub fn this<'b, V: Context<'b>>(&self, _: &mut V) -> raw::Local { unsafe { let mut local: raw::Local = std::mem::zeroed(); - neon_runtime::call::this(std::mem::transmute(&self.info), &mut local); + neon_runtime::call::this(std::mem::transmute(self.info), &mut local); local } } diff --git a/src/object/class/internal.rs b/src/object/class/internal.rs index 8ba469627..8966e8075 100644 --- a/src/object/class/internal.rs +++ b/src/object/class/internal.rs @@ -15,7 +15,7 @@ use types::error::convert_panics; pub struct MethodCallback(pub fn(CallContext) -> JsResult); impl Callback<()> for MethodCallback { - extern "C" fn invoke(env: Env, info: &CallbackInfo) { + extern "C" fn invoke(env: Env, info: CallbackInfo) { unsafe { info.with_cx::(env, |mut cx| { let data = info.data(); @@ -65,7 +65,7 @@ impl ConstructorCallCallback { } impl Callback<()> for ConstructorCallCallback { - extern "C" fn invoke(env: Env, info: &CallbackInfo) { + extern "C" fn invoke(env: Env, info: CallbackInfo) { unsafe { info.with_cx(env, |cx| { let data = info.data(); @@ -87,7 +87,7 @@ impl Callback<()> for ConstructorCallCallback { pub struct AllocateCallback(pub fn(CallContext) -> NeonResult); impl Callback<*mut c_void> for AllocateCallback { - extern "C" fn invoke(env: Env, info: &CallbackInfo) -> *mut c_void { + extern "C" fn invoke(env: Env, info: CallbackInfo) -> *mut c_void { unsafe { info.with_cx(env, |cx| { let data = info.data(); @@ -112,7 +112,7 @@ impl Callback<*mut c_void> for AllocateCallback { pub struct ConstructCallback(pub fn(CallContext) -> NeonResult>>); impl Callback for ConstructCallback { - extern "C" fn invoke(env: Env, info: &CallbackInfo) -> bool { + extern "C" fn invoke(env: Env, info: CallbackInfo) -> bool { unsafe { info.with_cx(env, |cx| { let data = info.data(); diff --git a/src/object/class/mod.rs b/src/object/class/mod.rs index b6d016dc2..8bf85fe52 100644 --- a/src/object/class/mod.rs +++ b/src/object/class/mod.rs @@ -269,13 +269,13 @@ pub(crate) trait Callback: Sized { /// Extracts the computed Rust function and invokes it. The Neon runtime /// ensures that the computed function is provided as the extra data field, /// wrapped as a V8 External, in the `CallbackInfo` argument. - extern "C" fn invoke(env: Env, info: &CallbackInfo) -> T; + extern "C" fn invoke(env: Env, info: CallbackInfo) -> T; /// See `invoke`. This is used by the non-n-api implementation, so that every impl for this /// trait doesn't need to provide two versions of `invoke`. #[cfg(feature = "legacy-runtime")] #[doc(hidden)] - extern "C" fn invoke_compat(info: &CallbackInfo) -> T { + extern "C" fn invoke_compat(info: CallbackInfo) -> T { Self::invoke(Env::current(), info) } diff --git a/src/types/internal.rs b/src/types/internal.rs index e90a7a1a8..61fbf69db 100644 --- a/src/types/internal.rs +++ b/src/types/internal.rs @@ -32,7 +32,7 @@ pub trait ValueInternal: Managed + 'static { pub struct FunctionCallback(pub fn(FunctionContext) -> JsResult); impl Callback<()> for FunctionCallback { - extern "C" fn invoke(env: Env, info: &CallbackInfo) { + extern "C" fn invoke(env: Env, info: CallbackInfo) { unsafe { info.with_cx::(env, |cx| { let data = info.data(); From cfc894015bd5d66815923e66cb94fc0c4e46131c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 28 Feb 2020 16:08:46 +0100 Subject: [PATCH 03/23] Unwrap v8::External pointer in C++ napi automatically wraps and unwraps the `data` pointer on functions created through `napi_create_function`, so we don't need to use the `napi_create_external` API in neon-runtime. Instead we can work with the raw pointers. This aligns the C++ and n-api APIs so that the `neon` crate can just assume associated data for a function is always a raw pointer. --- crates/neon-runtime/src/napi/call.rs | 2 +- crates/neon-runtime/src/napi/class.rs | 6 +++--- crates/neon-runtime/src/napi/fun.rs | 3 ++- crates/neon-sys/native/src/neon.cc | 23 +++++++++++++---------- crates/neon-sys/native/src/neon.h | 10 +++++----- crates/neon-sys/src/lib.rs | 10 +++++----- src/context/mod.rs | 8 ++++---- src/object/class/internal.rs | 16 ++++++++-------- src/types/internal.rs | 4 ++-- 9 files changed, 43 insertions(+), 39 deletions(-) diff --git a/crates/neon-runtime/src/napi/call.rs b/crates/neon-runtime/src/napi/call.rs index a07a6d487..0c9dd5062 100644 --- a/crates/neon-runtime/src/napi/call.rs +++ b/crates/neon-runtime/src/napi/call.rs @@ -32,7 +32,7 @@ pub unsafe extern "C" fn is_construct(_info: FunctionCallbackInfo) -> bool { uni pub unsafe extern "C" fn this(_info: FunctionCallbackInfo, _out: &mut Local) { unimplemented!() } -pub unsafe extern "C" fn data(_info: FunctionCallbackInfo, _out: &mut Local) { unimplemented!() } +pub unsafe extern "C" fn data(_env: Env, _info: FunctionCallbackInfo, _out: &mut *mut c_void) { unimplemented!() } pub unsafe extern "C" fn len(_info: FunctionCallbackInfo) -> i32 { unimplemented!() } diff --git a/crates/neon-runtime/src/napi/class.rs b/crates/neon-runtime/src/napi/class.rs index 2a73e5108..e522a6041 100644 --- a/crates/neon-runtime/src/napi/class.rs +++ b/crates/neon-runtime/src/napi/class.rs @@ -26,11 +26,11 @@ pub unsafe extern "C" fn metadata_to_constructor(_out: &mut Local, _isolate: Env // FIXME: get rid of all the "kernel" nomenclature -pub unsafe extern "C" fn get_allocate_kernel(_obj: Local) -> *mut c_void { unimplemented!() } +pub unsafe extern "C" fn get_allocate_kernel(_data: *mut c_void) -> *mut c_void { unimplemented!() } -pub unsafe extern "C" fn get_construct_kernel(_obj: Local) -> *mut c_void { unimplemented!() } +pub unsafe extern "C" fn get_construct_kernel(_data: *mut c_void) -> *mut c_void { unimplemented!() } -pub unsafe extern "C" fn get_call_kernel(_obj: Local) -> *mut c_void { unimplemented!() } +pub unsafe extern "C" fn get_call_kernel(_data: *mut c_void) -> *mut c_void { unimplemented!() } pub unsafe extern "C" fn constructor(_out: &mut Local, _ft: Local) -> bool { unimplemented!() } diff --git a/crates/neon-runtime/src/napi/fun.rs b/crates/neon-runtime/src/napi/fun.rs index 4fe37f8f3..c8be1f464 100644 --- a/crates/neon-runtime/src/napi/fun.rs +++ b/crates/neon-runtime/src/napi/fun.rs @@ -2,12 +2,13 @@ use call::CCallback; use raw::{Env, Local}; +use std::os::raw::c_void; pub unsafe extern "C" fn new(_out: &mut Local, _env: Env, _callback: CCallback) -> bool { unimplemented!() } pub unsafe extern "C" fn new_template(_out: &mut Local, _env: Env, _callback: CCallback) -> bool { unimplemented!() } -pub unsafe extern "C" fn get_dynamic_callback(_obj: Local) -> *mut c_void { unimplemented!() } +pub unsafe extern "C" fn get_dynamic_callback(_env: Env, _data: *mut c_void) -> *mut c_void { unimplemented!() } pub unsafe extern "C" fn call(_out: &mut Local, _env: Env, _fun: Local, _this: Local, _argc: i32, _argv: *mut c_void) -> bool { unimplemented!() } diff --git a/crates/neon-sys/native/src/neon.cc b/crates/neon-sys/native/src/neon.cc index edbc4b3ab..e61d3311b 100644 --- a/crates/neon-sys/native/src/neon.cc +++ b/crates/neon-sys/native/src/neon.cc @@ -30,14 +30,17 @@ extern "C" void Neon_Call_This(v8::FunctionCallbackInfo *info, v8::Lo *out = info->This(); } -extern "C" void Neon_Call_Data(v8::FunctionCallbackInfo *info, v8::Local *out) { +extern "C" void Neon_Call_Data(v8::Isolate *isolate, v8::FunctionCallbackInfo *info, void **out) { /* printf("Call_Data: v8 info = %p\n", *(void **)info); dump((void *)info, 3); printf("Call_Data: v8 info implicit:\n"); dump_implicit((void *)info); */ - *out = info->Data(); + v8::Local external = info->Data(); + if (external->IsExternal()) { + *out = external.As()->Value(); + } } extern "C" int32_t Neon_Call_Length(v8::FunctionCallbackInfo *info) { @@ -330,18 +333,18 @@ extern "C" void Neon_Class_SetClassMap(v8::Isolate *isolate, void *map, Neon_Dro node::AtExit(cleanup_class_map, holder); } -extern "C" void *Neon_Class_GetCallKernel(v8::Local wrapper) { - neon::ClassMetadata *metadata = static_cast(wrapper->Value()); +extern "C" void *Neon_Class_GetCallKernel(void *wrapper) { + neon::ClassMetadata *metadata = static_cast(wrapper); return metadata->GetCallKernel(); } -extern "C" void *Neon_Class_GetConstructKernel(v8::Local wrapper) { - neon::ClassMetadata *metadata = static_cast(wrapper->Value()); +extern "C" void *Neon_Class_GetConstructKernel(void *wrapper) { + neon::ClassMetadata *metadata = static_cast(wrapper); return metadata->GetConstructKernel(); } -extern "C" void *Neon_Class_GetAllocateKernel(v8::Local wrapper) { - neon::BaseClassMetadata *metadata = static_cast(wrapper->Value()); +extern "C" void *Neon_Class_GetAllocateKernel(void *wrapper) { + neon::BaseClassMetadata *metadata = static_cast(wrapper); return metadata->GetAllocateKernel(); } @@ -433,8 +436,8 @@ extern "C" bool Neon_Fun_New(v8::Local *out, v8::Isolate *isolate, return maybe_result.ToLocal(out); } -extern "C" void *Neon_Fun_GetDynamicCallback(v8::Local data) { - return data->Value(); +extern "C" void *Neon_Fun_GetDynamicCallback(v8::Isolate *isolate, void *data) { + return data; } extern "C" bool Neon_Fun_Call(v8::Local *out, v8::Isolate *isolate, v8::Local fun, v8::Local self, int32_t argc, v8::Local argv[]) { diff --git a/crates/neon-sys/native/src/neon.h b/crates/neon-sys/native/src/neon.h index 2b5cf2aad..2f2c11150 100644 --- a/crates/neon-sys/native/src/neon.h +++ b/crates/neon-sys/native/src/neon.h @@ -18,7 +18,7 @@ extern "C" { void *Neon_Call_CurrentIsolate(); bool Neon_Call_IsConstruct(v8::FunctionCallbackInfo *info); void Neon_Call_This(v8::FunctionCallbackInfo *info, v8::Local *out); - void Neon_Call_Data(v8::FunctionCallbackInfo *info, v8::Local *out); + void Neon_Call_Data(v8::Isolate *isolate, v8::FunctionCallbackInfo *info, void **out); int32_t Neon_Call_Length(v8::FunctionCallbackInfo *info); void Neon_Call_Get(v8::FunctionCallbackInfo *info, int32_t i, v8::Local *out); @@ -75,7 +75,7 @@ extern "C" { bool Neon_Fun_New(v8::Local *out, v8::Isolate *isolate, callback_t callback); bool Neon_Fun_Template_New(v8::Local *out, v8::Isolate *isolate, callback_t callback); - void *Neon_Fun_GetDynamicCallback(v8::Local obj); + void *Neon_Fun_GetDynamicCallback(v8::Isolate *isolate, void *obj); bool Neon_Fun_Call(v8::Local *out, v8::Isolate *isolate, v8::Local fun, v8::Local self, int32_t argc, v8::Local argv[]); bool Neon_Fun_Construct(v8::Local *out, v8::Isolate *isolate, v8::Local fun, int32_t argc, v8::Local argv[]); @@ -95,9 +95,9 @@ extern "C" { callback_t call, Neon_DropCallback drop); // FIXME: get rid of all the "kernel" nomenclature - void *Neon_Class_GetCallKernel(v8::Local wrapper); - void *Neon_Class_GetConstructKernel(v8::Local wrapper); - void *Neon_Class_GetAllocateKernel(v8::Local wrapper); + void *Neon_Class_GetCallKernel(void *wrapper); + void *Neon_Class_GetConstructKernel(void *wrapper); + void *Neon_Class_GetAllocateKernel(void *wrapper); bool Neon_Class_Constructor(v8::Local *out, v8::Local ft); bool Neon_Class_HasInstance(void *metadata, v8::Local v); bool Neon_Class_SetName(v8::Isolate *isolate, void *metadata, const char *name, uint32_t byte_length); diff --git a/crates/neon-sys/src/lib.rs b/crates/neon-sys/src/lib.rs index 8baba2607..fa5b72b62 100644 --- a/crates/neon-sys/src/lib.rs +++ b/crates/neon-sys/src/lib.rs @@ -98,7 +98,7 @@ extern "C" { pub fn Neon_Call_CurrentIsolate() -> Isolate; pub fn Neon_Call_IsConstruct(info: FunctionCallbackInfo) -> bool; pub fn Neon_Call_This(info: FunctionCallbackInfo, out: &mut Local); - pub fn Neon_Call_Data(isolate: Isolate, info: FunctionCallbackInfo, out: &mut Local); + pub fn Neon_Call_Data(isolate: Isolate, info: FunctionCallbackInfo, out: &mut *mut c_void); pub fn Neon_Call_Length(info: FunctionCallbackInfo) -> i32; pub fn Neon_Call_Get(info: FunctionCallbackInfo, i: i32, out: &mut Local); @@ -115,9 +115,9 @@ extern "C" { pub fn Neon_Class_ThrowThisError(isolate: Isolate, metadata: *mut c_void); pub fn Neon_Class_AddMethod(isolate: Isolate, metadata: *mut c_void, name: *const u8, byte_length: u32, method: Local) -> bool; pub fn Neon_Class_MetadataToConstructor(out: &mut Local, isolate: Isolate, metadata: *mut c_void) -> bool; - pub fn Neon_Class_GetAllocateKernel(obj: Local) -> *mut c_void; - pub fn Neon_Class_GetConstructKernel(obj: Local) -> *mut c_void; - pub fn Neon_Class_GetCallKernel(obj: Local) -> *mut c_void; + pub fn Neon_Class_GetAllocateKernel(data: *mut c_void) -> *mut c_void; + pub fn Neon_Class_GetConstructKernel(data: *mut c_void) -> *mut c_void; + pub fn Neon_Class_GetCallKernel(data: *mut c_void) -> *mut c_void; pub fn Neon_Class_Constructor(out: &mut Local, ft: Local) -> bool; pub fn Neon_Class_HasInstance(metadata: *mut c_void, v: Local) -> bool; pub fn Neon_Class_GetInstanceInternals(obj: Local) -> *mut c_void; @@ -133,7 +133,7 @@ extern "C" { pub fn Neon_Fun_New(out: &mut Local, isolate: Isolate, callback: CCallback) -> bool; pub fn Neon_Fun_Template_New(out: &mut Local, isolate: Isolate, callback: CCallback) -> bool; - pub fn Neon_Fun_GetDynamicCallback(obj: Local) -> *mut c_void; + pub fn Neon_Fun_GetDynamicCallback(isolate: Isolate, data: *mut c_void) -> *mut c_void; pub fn Neon_Fun_Call(out: &mut Local, isolate: Isolate, fun: Local, this: Local, argc: i32, argv: *mut c_void) -> bool; pub fn Neon_Fun_Construct(out: &mut Local, isolate: Isolate, fun: Local, argc: i32, argv: *mut c_void) -> bool; diff --git a/src/context/mod.rs b/src/context/mod.rs index 93589a9b5..bdd3eee61 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -28,11 +28,11 @@ pub(crate) struct CallbackInfo { } impl CallbackInfo { - pub fn data<'a>(&self) -> Handle<'a, JsValue> { + pub fn data<'a>(&self, env: Env) -> *mut c_void { unsafe { - let mut local: raw::Local = std::mem::zeroed(); - neon_runtime::call::data(self.info, &mut local); - Handle::new_internal(JsValue::from_raw(local)) + let mut raw_data: *mut c_void = std::mem::zeroed(); + neon_runtime::call::data(env.to_raw(), self.info, &mut raw_data); + raw_data } } diff --git a/src/object/class/internal.rs b/src/object/class/internal.rs index 8966e8075..ccd259038 100644 --- a/src/object/class/internal.rs +++ b/src/object/class/internal.rs @@ -18,7 +18,7 @@ impl Callback<()> for MethodCallback { extern "C" fn invoke(env: Env, info: CallbackInfo) { unsafe { info.with_cx::(env, |mut cx| { - let data = info.data(); + let data = info.data(cx.env()); let this: Handle = Handle::new_internal(JsValue::from_raw(info.this(&mut cx))); #[cfg(feature = "legacy-runtime")] @@ -33,7 +33,7 @@ impl Callback<()> for MethodCallback { return; }; let dynamic_callback: fn(CallContext) -> JsResult = - mem::transmute(neon_runtime::fun::get_dynamic_callback(data.to_raw())); + mem::transmute(neon_runtime::fun::get_dynamic_callback(cx.env().to_raw(), data)); if let Ok(value) = convert_panics(|| { dynamic_callback(cx) }) { info.set_return(value); } @@ -68,9 +68,9 @@ impl Callback<()> for ConstructorCallCallback { extern "C" fn invoke(env: Env, info: CallbackInfo) { unsafe { info.with_cx(env, |cx| { - let data = info.data(); + let data = info.data(cx.env()); let kernel: fn(CallContext) -> JsResult = - mem::transmute(neon_runtime::class::get_call_kernel(data.to_raw())); + mem::transmute(neon_runtime::class::get_call_kernel(data)); if let Ok(value) = convert_panics(|| { kernel(cx) }) { info.set_return(value); } @@ -90,9 +90,9 @@ impl Callback<*mut c_void> for AllocateCallback { extern "C" fn invoke(env: Env, info: CallbackInfo) -> *mut c_void { unsafe { info.with_cx(env, |cx| { - let data = info.data(); + let data = info.data(cx.env()); let kernel: fn(CallContext) -> NeonResult = - mem::transmute(neon_runtime::class::get_allocate_kernel(data.to_raw())); + mem::transmute(neon_runtime::class::get_allocate_kernel(data)); if let Ok(value) = convert_panics(|| { kernel(cx) }) { let p = Box::into_raw(Box::new(value)); mem::transmute(p) @@ -115,9 +115,9 @@ impl Callback for ConstructCallback { extern "C" fn invoke(env: Env, info: CallbackInfo) -> bool { unsafe { info.with_cx(env, |cx| { - let data = info.data(); + let data = info.data(cx.env()); let kernel: fn(CallContext) -> NeonResult>> = - mem::transmute(neon_runtime::class::get_construct_kernel(data.to_raw())); + mem::transmute(neon_runtime::class::get_construct_kernel(data)); match convert_panics(|| { kernel(cx) }) { Ok(None) => true, Ok(Some(obj)) => { diff --git a/src/types/internal.rs b/src/types/internal.rs index 61fbf69db..f3ef4b53a 100644 --- a/src/types/internal.rs +++ b/src/types/internal.rs @@ -35,9 +35,9 @@ impl Callback<()> for FunctionCallback { extern "C" fn invoke(env: Env, info: CallbackInfo) { unsafe { info.with_cx::(env, |cx| { - let data = info.data(); + let data = info.data(env); let dynamic_callback: fn(FunctionContext) -> JsResult = - mem::transmute(neon_runtime::fun::get_dynamic_callback(data.to_raw())); + mem::transmute(neon_runtime::fun::get_dynamic_callback(env.to_raw(), data)); if let Ok(value) = convert_panics(|| { dynamic_callback(cx) }) { info.set_return(value); } From 98462ae56dea90632eaea7dffcd20c0b7bfb2cbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 28 Feb 2020 16:15:45 +0100 Subject: [PATCH 04/23] Start work on exporting n-api functions --- crates/neon-runtime/src/napi/call.rs | 17 ++++++- crates/neon-runtime/src/napi/fun.rs | 74 ++++++++++++++++++++++++++-- test/napi/lib/hello.js | 4 ++ test/napi/native/src/lib.rs | 11 +++++ 4 files changed, 100 insertions(+), 6 deletions(-) diff --git a/crates/neon-runtime/src/napi/call.rs b/crates/neon-runtime/src/napi/call.rs index 0c9dd5062..0d399d18a 100644 --- a/crates/neon-runtime/src/napi/call.rs +++ b/crates/neon-runtime/src/napi/call.rs @@ -32,7 +32,22 @@ pub unsafe extern "C" fn is_construct(_info: FunctionCallbackInfo) -> bool { uni pub unsafe extern "C" fn this(_info: FunctionCallbackInfo, _out: &mut Local) { unimplemented!() } -pub unsafe extern "C" fn data(_env: Env, _info: FunctionCallbackInfo, _out: &mut *mut c_void) { unimplemented!() } +pub unsafe extern "C" fn data(env: Env, info: FunctionCallbackInfo, out: &mut *mut c_void) { + let mut data = null_mut(); + let mut argc = 0usize; + let status = napi::napi_get_cb_info( + env, + info, + &mut argc as *mut _, + null_mut(), + null_mut(), + &mut data as *mut _, + ); + println!("data() status = {:?} argc = {}", status, argc); + if status == napi::napi_status::napi_ok { + *out = data; + } +} pub unsafe extern "C" fn len(_info: FunctionCallbackInfo) -> i32 { unimplemented!() } diff --git a/crates/neon-runtime/src/napi/fun.rs b/crates/neon-runtime/src/napi/fun.rs index c8be1f464..a26e61555 100644 --- a/crates/neon-runtime/src/napi/fun.rs +++ b/crates/neon-runtime/src/napi/fun.rs @@ -3,13 +3,77 @@ use call::CCallback; use raw::{Env, Local}; use std::os::raw::c_void; +use std::mem::MaybeUninit; +use std::ptr::{null, null_mut}; -pub unsafe extern "C" fn new(_out: &mut Local, _env: Env, _callback: CCallback) -> bool { unimplemented!() } +use nodejs_sys as napi; -pub unsafe extern "C" fn new_template(_out: &mut Local, _env: Env, _callback: CCallback) -> bool { unimplemented!() } +/// Mutates the `out` argument provided to refer to a newly created `v8::Function`. Returns +/// `false` if the value couldn't be created. +pub unsafe extern "C" fn new(out: &mut Local, env: Env, callback: CCallback) -> bool { + let status = napi::napi_create_function( + env, + null(), + 0, + Some(std::mem::transmute(callback.static_callback)), + callback.dynamic_callback, + out as *mut Local, + ); -pub unsafe extern "C" fn get_dynamic_callback(_env: Env, _data: *mut c_void) -> *mut c_void { unimplemented!() } + status == napi::napi_status::napi_ok +} -pub unsafe extern "C" fn call(_out: &mut Local, _env: Env, _fun: Local, _this: Local, _argc: i32, _argv: *mut c_void) -> bool { unimplemented!() } +pub unsafe extern "C" fn new_template(_out: &mut Local, _env: Env, _callback: CCallback) -> bool { + unimplemented!() +} -pub unsafe extern "C" fn construct(_out: &mut Local, _env: Env, _fun: Local, _argc: i32, _argv: *mut c_void) -> bool { unimplemented!() } +pub unsafe extern "C" fn get_dynamic_callback(env: Env, data: *mut c_void) -> *mut c_void { + data + /* + let mut value = null_mut(); + + // debug stuff + { + let mut ty = napi::napi_valuetype::napi_null; + let r = napi::napi_typeof(env, obj, &mut ty as *mut _); + dbg!(r,ty); + let d = &mut [0u8; 64]; + let l = crate::string::utf8_len(env, obj); + crate::string::data(env, d.as_mut_ptr(), l, obj); + dbg!(std::str::from_utf8(&d[..l as usize])); + } + // end debug stuff + + let status = napi::napi_get_value_external( + env, + obj, + &mut value as *mut _, + ); + println!("get_dynamic_callback() status = {:?}", status); + if status != napi::napi_status::napi_ok { + return null_mut(); + } + value + */ +} + +pub unsafe extern "C" fn call( + _out: &mut Local, + _env: Env, + _fun: Local, + _this: Local, + _argc: i32, + _argv: *mut c_void, +) -> bool { + unimplemented!() +} + +pub unsafe extern "C" fn construct( + _out: &mut Local, + _env: Env, + _fun: Local, + _argc: i32, + _argv: *mut c_void, +) -> bool { + unimplemented!() +} diff --git a/test/napi/lib/hello.js b/test/napi/lib/hello.js index becef4295..df95e5c5d 100644 --- a/test/napi/lib/hello.js +++ b/test/napi/lib/hello.js @@ -27,4 +27,8 @@ describe('hello', function() { whatever: true }); }); + + it('should export a Rust function', function () { + assert.strictEqual(addon.return_js_function(2), 3.0); + }) }); diff --git a/test/napi/native/src/lib.rs b/test/napi/native/src/lib.rs index f79e1692c..d450c6044 100644 --- a/test/napi/native/src/lib.rs +++ b/test/napi/native/src/lib.rs @@ -68,5 +68,16 @@ register_module!(|mut cx| { cx.export_value("rustCreated", rust_created)?; + fn add1(mut cx: FunctionContext) -> JsResult { + let x = cx.argument::(0)?.value(&mut cx); + Ok(cx.number(x + 1.0)) + } + + fn return_js_function(mut cx: FunctionContext) -> JsResult { + JsFunction::new(&mut cx, add1) + } + + cx.export_function("return_js_function", return_js_function)?; + Ok(()) }); From f61b739aff994a32bc1eef02be56092fb777e3ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 28 Feb 2020 16:55:49 +0100 Subject: [PATCH 05/23] napi: Implement receiving call arguments on the Rust side. --- crates/neon-runtime/src/nan/call.rs | 2 +- crates/neon-runtime/src/napi/call.rs | 38 ++++++++++++++++++++++++++-- crates/neon-runtime/src/napi/fun.rs | 26 ------------------- crates/neon-sys/native/src/neon.cc | 4 +-- crates/neon-sys/native/src/neon.h | 4 +-- crates/neon-sys/src/lib.rs | 4 +-- src/context/mod.rs | 16 ++++++------ test/napi/lib/hello.js | 2 +- test/napi/native/src/lib.rs | 6 +---- 9 files changed, 53 insertions(+), 49 deletions(-) diff --git a/crates/neon-runtime/src/nan/call.rs b/crates/neon-runtime/src/nan/call.rs index 285fc27a4..895bd23de 100644 --- a/crates/neon-runtime/src/nan/call.rs +++ b/crates/neon-runtime/src/nan/call.rs @@ -18,7 +18,7 @@ pub use neon_sys::Neon_Call_IsConstruct as is_construct; /// the function is bound to. pub use neon_sys::Neon_Call_This as this; -/// Mutates the `out` argument provided to refer to the `v8::Local` handle value of the +/// Mutates the `out` argument provided to refer to the pointer value of the /// `v8::FunctionCallbackInfo` `Data`. pub use neon_sys::Neon_Call_Data as data; diff --git a/crates/neon-runtime/src/napi/call.rs b/crates/neon-runtime/src/napi/call.rs index 0d399d18a..f3fa44a9b 100644 --- a/crates/neon-runtime/src/napi/call.rs +++ b/crates/neon-runtime/src/napi/call.rs @@ -32,6 +32,8 @@ pub unsafe extern "C" fn is_construct(_info: FunctionCallbackInfo) -> bool { uni pub unsafe extern "C" fn this(_info: FunctionCallbackInfo, _out: &mut Local) { unimplemented!() } +/// Mutates the `out` argument provided to refer to the associated data value of the +/// `napi_callback_info`. pub unsafe extern "C" fn data(env: Env, info: FunctionCallbackInfo, out: &mut *mut c_void) { let mut data = null_mut(); let mut argc = 0usize; @@ -49,6 +51,38 @@ pub unsafe extern "C" fn data(env: Env, info: FunctionCallbackInfo, out: &mut *m } } -pub unsafe extern "C" fn len(_info: FunctionCallbackInfo) -> i32 { unimplemented!() } +/// Gets the number of arguments passed to the function. +pub unsafe extern "C" fn len(env: Env, info: FunctionCallbackInfo) -> i32 { + let mut argc = 0usize; + let status = napi::napi_get_cb_info( + env, + info, + &mut argc as *mut _, + null_mut(), + null_mut(), + null_mut(), + ); + assert_eq!(status, napi::napi_status::napi_ok); + argc as i32 +} + +/// Mutates the `out` argument provided to refer to the `napi_value` of the `i`th argument +/// passed to the function. +pub unsafe extern "C" fn get(env: Env, info: FunctionCallbackInfo, i: i32, out: &mut Local) { + // TODO make this not allocate + // Instead, we can probably get all the arguments at once in `neon` itself? + let mut args = vec![null_mut(); (i + 1) as usize]; + let mut num_args = args.len(); -pub unsafe extern "C" fn get(_info: FunctionCallbackInfo, _i: i32, _out: &mut Local) { unimplemented!() } + let status = napi::napi_get_cb_info( + env, + info, + &mut num_args as *mut _, + args.as_mut_ptr(), + null_mut(), + null_mut(), + ); + assert_eq!(status, napi::napi_status::napi_ok); + assert!(num_args > i as usize); + *out = args[i as usize]; +} diff --git a/crates/neon-runtime/src/napi/fun.rs b/crates/neon-runtime/src/napi/fun.rs index a26e61555..d0621c118 100644 --- a/crates/neon-runtime/src/napi/fun.rs +++ b/crates/neon-runtime/src/napi/fun.rs @@ -29,32 +29,6 @@ pub unsafe extern "C" fn new_template(_out: &mut Local, _env: Env, _callback: CC pub unsafe extern "C" fn get_dynamic_callback(env: Env, data: *mut c_void) -> *mut c_void { data - /* - let mut value = null_mut(); - - // debug stuff - { - let mut ty = napi::napi_valuetype::napi_null; - let r = napi::napi_typeof(env, obj, &mut ty as *mut _); - dbg!(r,ty); - let d = &mut [0u8; 64]; - let l = crate::string::utf8_len(env, obj); - crate::string::data(env, d.as_mut_ptr(), l, obj); - dbg!(std::str::from_utf8(&d[..l as usize])); - } - // end debug stuff - - let status = napi::napi_get_value_external( - env, - obj, - &mut value as *mut _, - ); - println!("get_dynamic_callback() status = {:?}", status); - if status != napi::napi_status::napi_ok { - return null_mut(); - } - value - */ } pub unsafe extern "C" fn call( diff --git a/crates/neon-sys/native/src/neon.cc b/crates/neon-sys/native/src/neon.cc index e61d3311b..ab01c2b49 100644 --- a/crates/neon-sys/native/src/neon.cc +++ b/crates/neon-sys/native/src/neon.cc @@ -43,11 +43,11 @@ extern "C" void Neon_Call_Data(v8::Isolate *isolate, v8::FunctionCallbackInfo *info) { +extern "C" int32_t Neon_Call_Length(v8::Isolate *isolate, v8::FunctionCallbackInfo *info) { return info->Length(); } -extern "C" void Neon_Call_Get(v8::FunctionCallbackInfo *info, int32_t i, v8::Local *out) { +extern "C" void Neon_Call_Get(v8::Isolate *isolate, v8::FunctionCallbackInfo *info, int32_t i, v8::Local *out) { *out = (*info)[i]; } diff --git a/crates/neon-sys/native/src/neon.h b/crates/neon-sys/native/src/neon.h index 2f2c11150..4686733c5 100644 --- a/crates/neon-sys/native/src/neon.h +++ b/crates/neon-sys/native/src/neon.h @@ -19,8 +19,8 @@ extern "C" { bool Neon_Call_IsConstruct(v8::FunctionCallbackInfo *info); void Neon_Call_This(v8::FunctionCallbackInfo *info, v8::Local *out); void Neon_Call_Data(v8::Isolate *isolate, v8::FunctionCallbackInfo *info, void **out); - int32_t Neon_Call_Length(v8::FunctionCallbackInfo *info); - void Neon_Call_Get(v8::FunctionCallbackInfo *info, int32_t i, v8::Local *out); + int32_t Neon_Call_Length(v8::Isolate *isolate, v8::FunctionCallbackInfo *info); + void Neon_Call_Get(v8::Isolate *isolate, v8::FunctionCallbackInfo *info, int32_t i, v8::Local *out); void Neon_Primitive_Number(v8::Local *out, v8::Isolate *isolate, double value); void Neon_Primitive_Undefined(v8::Local *out, v8::Isolate *isolate); diff --git a/crates/neon-sys/src/lib.rs b/crates/neon-sys/src/lib.rs index fa5b72b62..d384a5880 100644 --- a/crates/neon-sys/src/lib.rs +++ b/crates/neon-sys/src/lib.rs @@ -99,8 +99,8 @@ extern "C" { pub fn Neon_Call_IsConstruct(info: FunctionCallbackInfo) -> bool; pub fn Neon_Call_This(info: FunctionCallbackInfo, out: &mut Local); pub fn Neon_Call_Data(isolate: Isolate, info: FunctionCallbackInfo, out: &mut *mut c_void); - pub fn Neon_Call_Length(info: FunctionCallbackInfo) -> i32; - pub fn Neon_Call_Get(info: FunctionCallbackInfo, i: i32, out: &mut Local); + pub fn Neon_Call_Length(isolate: Isolate, info: FunctionCallbackInfo) -> i32; + pub fn Neon_Call_Get(isolate: Isolate, info: FunctionCallbackInfo, i: i32, out: &mut Local); pub fn Neon_Class_GetClassMap(isolate: Isolate) -> *mut c_void; pub fn Neon_Class_SetClassMap(isolate: Isolate, map: *mut c_void, free_map: *mut c_void); diff --git a/src/context/mod.rs b/src/context/mod.rs index bdd3eee61..0e936fab9 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -54,30 +54,30 @@ impl CallbackInfo { } } - pub fn len(&self) -> i32 { + pub fn len<'b, C: Context<'b>>(&self, cx: &C) -> i32 { unsafe { - neon_runtime::call::len(self.info) + neon_runtime::call::len(cx.env().to_raw(), self.info) } } - pub fn get<'b, C: Context<'b>>(&self, _: &mut C, i: i32) -> Option> { - if i < 0 || i >= self.len() { + pub fn get<'b, C: Context<'b>>(&self, cx: &mut C, i: i32) -> Option> { + if i < 0 || i >= self.len(cx) { return None; } unsafe { let mut local: raw::Local = std::mem::zeroed(); - neon_runtime::call::get(self.info, i, &mut local); + neon_runtime::call::get(cx.env().to_raw(), self.info, i, &mut local); Some(Handle::new_internal(JsValue::from_raw(local))) } } pub fn require<'b, C: Context<'b>>(&self, cx: &mut C, i: i32) -> JsResult<'b, JsValue> { - if i < 0 || i >= self.len() { + if i < 0 || i >= self.len(cx) { return cx.throw_type_error("not enough arguments"); } unsafe { let mut local: raw::Local = std::mem::zeroed(); - neon_runtime::call::get(self.info, i, &mut local); + neon_runtime::call::get(cx.env().to_raw(), self.info, i, &mut local); Ok(Handle::new_internal(JsValue::from_raw(local))) } } @@ -468,7 +468,7 @@ impl<'a, T: This> CallContext<'a, T> { } /// Indicates the number of arguments that were passed to the function. - pub fn len(&self) -> i32 { self.info.len() } + pub fn len(&self) -> i32 { self.info.len(self) } /// Produces the `i`th argument, or `None` if `i` is greater than or equal to `self.len()`. pub fn argument_opt(&mut self, i: i32) -> Option> { diff --git a/test/napi/lib/hello.js b/test/napi/lib/hello.js index df95e5c5d..773c06822 100644 --- a/test/napi/lib/hello.js +++ b/test/napi/lib/hello.js @@ -29,6 +29,6 @@ describe('hello', function() { }); it('should export a Rust function', function () { - assert.strictEqual(addon.return_js_function(2), 3.0); + assert.strictEqual(addon.add1(2), 3.0); }) }); diff --git a/test/napi/native/src/lib.rs b/test/napi/native/src/lib.rs index d450c6044..e234e3214 100644 --- a/test/napi/native/src/lib.rs +++ b/test/napi/native/src/lib.rs @@ -73,11 +73,7 @@ register_module!(|mut cx| { Ok(cx.number(x + 1.0)) } - fn return_js_function(mut cx: FunctionContext) -> JsResult { - JsFunction::new(&mut cx, add1) - } - - cx.export_function("return_js_function", return_js_function)?; + cx.export_function("add1", add1)?; Ok(()) }); From a0ec71b633a954aae88c0f5dd73ca0671de0d298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 28 Feb 2020 17:03:53 +0100 Subject: [PATCH 06/23] napi: implement return value napi expects you to return a `napi_value` from your functions, instead of using `SetReturn` like in nan. I ended up having to duplicate these `invoke` implementations after all! --- src/types/internal.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/types/internal.rs b/src/types/internal.rs index f3ef4b53a..d17876371 100644 --- a/src/types/internal.rs +++ b/src/types/internal.rs @@ -31,6 +31,7 @@ pub trait ValueInternal: Managed + 'static { #[repr(C)] pub struct FunctionCallback(pub fn(FunctionContext) -> JsResult); +#[cfg(feature = "legacy-runtime")] impl Callback<()> for FunctionCallback { extern "C" fn invoke(env: Env, info: CallbackInfo) { unsafe { @@ -49,3 +50,26 @@ impl Callback<()> for FunctionCallback { unsafe { mem::transmute(self.0) } } } + +#[cfg(feature = "napi-runtime")] +impl Callback for FunctionCallback { + extern "C" fn invoke(env: Env, info: CallbackInfo) -> raw::Local { + unsafe { + info.with_cx::(env, |cx| { + let data = info.data(env); + let dynamic_callback: fn(FunctionContext) -> JsResult = + mem::transmute(neon_runtime::fun::get_dynamic_callback(env.to_raw(), data)); + if let Ok(value) = convert_panics(|| { dynamic_callback(cx) }) { + value.to_raw() + } else { + // TODO this should probably not be null + std::ptr::null_mut() + } + }) + } + } + + fn as_ptr(self) -> *mut c_void { + unsafe { mem::transmute(self.0) } + } +} From dfebfc61b2b2323a6bdd04f69ff2497a2bafcac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 6 Mar 2020 16:48:45 +0100 Subject: [PATCH 07/23] Start porting function tests from nan to n-api runtime. --- crates/neon-runtime/src/napi/tag.rs | 4 +- test/napi/lib/functions.js | 97 +++++++++++++++++++++++++ test/napi/native/src/js/functions.rs | 103 +++++++++++++++++++++++++++ test/napi/native/src/lib.rs | 21 ++++++ 4 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 test/napi/lib/functions.js create mode 100644 test/napi/native/src/js/functions.rs diff --git a/crates/neon-runtime/src/napi/tag.rs b/crates/neon-runtime/src/napi/tag.rs index 1d51f7a97..e09a6b3ec 100644 --- a/crates/neon-runtime/src/napi/tag.rs +++ b/crates/neon-runtime/src/napi/tag.rs @@ -35,7 +35,9 @@ pub unsafe extern "C" fn is_object(_env: Env, _val: Local) -> bool { unimplement pub unsafe extern "C" fn is_array(_env: Env, _val: Local) -> bool { unimplemented!() } -pub unsafe extern "C" fn is_function(_env: Env, _val: Local) -> bool { unimplemented!() } +pub unsafe extern "C" fn is_function(env: Env, val: Local) -> bool { + is_type(env, val, napi::napi_valuetype::napi_function) +} pub unsafe extern "C" fn is_error(_env: Env, _val: Local) -> bool { unimplemented!() } diff --git a/test/napi/lib/functions.js b/test/napi/lib/functions.js new file mode 100644 index 000000000..d9709787c --- /dev/null +++ b/test/napi/lib/functions.js @@ -0,0 +1,97 @@ +var addon = require('../native'); +var assert = require('chai').assert; + +describe('JsFunction', function() { + it('return a JsFunction built in Rust', function () { + assert.isFunction(addon.return_js_function()); + }); + + it('return a JsFunction built in Rust that implements x => x + 1', function () { + assert.equal(addon.return_js_function()(41), 42); + }); + + // The n-api runtime cannot yet call JS functions. + it.skip('call a JsFunction built in JS that implements x => x + 1', function () { + assert.equal(addon.call_js_function(function(x) { return x + 1 }), 17); + }); + + // The n-api runtime cannot yet call JS functions. + it.skip('new a JsFunction', function () { + assert.equal(addon.construct_js_function(Date), 1970); + }); + + it('got two parameters, a string and a number', function() { + addon.check_string_and_number("string", 42); + }); + + // The n-api runtime doesn't yet handle panics. + it.skip('converts a Rust panic to a throw in a function', function() { + assert.throws(function() { addon.panic() }, Error, /^internal error in Neon module: zomg$/); + }); + + // The n-api runtime doesn't yet handle panics. + it.skip('lets panic override a throw', function() { + assert.throws(function() { addon.panic_after_throw() }, Error, /^internal error in Neon module: this should override the RangeError$/); + }); + + it('computes the right number of arguments', function() { + assert.equal(addon.num_arguments(), 0); + assert.equal(addon.num_arguments('a'), 1); + assert.equal(addon.num_arguments('a', 'b'), 2); + assert.equal(addon.num_arguments('a', 'b', 'c'), 3); + assert.equal(addon.num_arguments('a', 'b', 'c', 'd'), 4); + }); + + // The n-api runtime cannot yet access `this`. + it.skip('gets the right `this`-value', function() { + var o = { iamobject: 'i am object' }; + assert.equal(addon.return_this.call(o), o); + + var d = new Date(); + assert.equal(addon.return_this.call(d), d); + + var n = 19; + assert.notStrictEqual(addon.return_this.call(n), n); + }); + + // The n-api runtime cannot yet access `this`. + it.skip('can manipulate an object `this` binding', function() { + var o = { modified: false }; + addon.require_object_this.call(o); + assert.equal(o.modified, true); + // Doesn't throw because of implicit primitive wrapping: + addon.require_object_this.call(42); + }); + + // The n-api runtime cannot yet access `this`. + it.skip('implicitly gets global', function() { + var global = (new Function("return this"))(); + assert.equal(addon.return_this.call(undefined), global); + }); + + it('exposes an argument via arguments_opt iff it is there', function() { + assert.equal(addon.is_argument_zero_some(), false); + assert.equal(addon.is_argument_zero_some('a'), true); + assert.equal(addon.is_argument_zero_some('a', 'b'), true); + assert.equal(addon.is_argument_zero_some.call(null), false); + assert.equal(addon.is_argument_zero_some.call(null, ['a']), true); + assert.equal(addon.is_argument_zero_some.call(null, ['a', 'b']), true); + }); + + // The n-api runtime cannot yet throw errors. + it.skip('correctly casts an argument via cx.arguments', function() { + assert.equal(addon.require_argument_zero_string('foobar'), 'foobar'); + assert.throws(function() { addon.require_argument_zero_string(new Date()) }, TypeError); + assert.throws(function() { addon.require_argument_zero_string(17) }, TypeError); + }); + + // The n-api runtime does not support scoped computation yet. + it.skip('executes a scoped computation', function() { + assert.equal(addon.execute_scoped(), 99); + }); + + // The n-api runtime does not support scoped computation yet. + it.skip('computes a value in a scoped computation', function() { + assert.equal(addon.compute_scoped(), 99); + }); +}); diff --git a/test/napi/native/src/js/functions.rs b/test/napi/native/src/js/functions.rs new file mode 100644 index 000000000..072bc4911 --- /dev/null +++ b/test/napi/native/src/js/functions.rs @@ -0,0 +1,103 @@ +use neon::prelude::*; +use neon::object::This; + +fn add1(mut cx: FunctionContext) -> JsResult { + let x = cx.argument::(0)?.value(&mut cx); + Ok(cx.number(x + 1.0)) +} + +pub fn return_js_function(mut cx: FunctionContext) -> JsResult { + JsFunction::new(&mut cx, add1) +} + +pub fn call_js_function(mut cx: FunctionContext) -> JsResult { + let f = cx.argument::(0)?; + let args: Vec> = vec![cx.number(16.0)]; + let null = cx.null(); + f.call(&mut cx, null, args)?.downcast::(&mut cx).or_throw(&mut cx) +} + +pub fn construct_js_function(mut cx: FunctionContext) -> JsResult { + let f = cx.argument::(0)?; + let zero = cx.number(0.0); + let o = f.construct(&mut cx, vec![zero])?; + let get_utc_full_year_method = o.get(&mut cx, "getUTCFullYear")?.downcast::(&mut cx).or_throw(&mut cx)?; + let args: Vec> = vec![]; + get_utc_full_year_method.call(&mut cx, o.upcast::(), args)?.downcast::(&mut cx).or_throw(&mut cx) +} + +trait CheckArgument<'a> { + fn check_argument(&mut self, i: i32) -> JsResult<'a, V>; +} + +impl<'a, T: This> CheckArgument<'a> for CallContext<'a, T> { + fn check_argument(&mut self, i: i32) -> JsResult<'a, V> { + self.argument::(i) + } +} + +pub fn check_string_and_number(mut cx: FunctionContext) -> JsResult { + cx.check_argument::(0)?; + cx.check_argument::(1)?; + Ok(cx.undefined()) +} + +pub fn panic(_: FunctionContext) -> JsResult { + panic!("zomg") +} + +pub fn panic_after_throw(mut cx: FunctionContext) -> JsResult { + cx.throw_range_error::<_, ()>("entering throw state with a RangeError").unwrap_err(); + panic!("this should override the RangeError") +} + +pub fn num_arguments(mut cx: FunctionContext) -> JsResult { + let n = cx.len(); + Ok(cx.number(n)) +} + +pub fn return_this(mut cx: FunctionContext) -> JsResult { + Ok(cx.this().upcast()) +} + +pub fn require_object_this(mut cx: FunctionContext) -> JsResult { + let this = cx.this(); + let this = this.downcast::(&mut cx).or_throw(&mut cx)?; + let t = cx.boolean(true); + this.set(&mut cx, "modified", t)?; + Ok(cx.undefined()) +} + +pub fn is_argument_zero_some(mut cx: FunctionContext) -> JsResult { + let b = cx.argument_opt(0).is_some(); + Ok(cx.boolean(b)) +} + +pub fn require_argument_zero_string(mut cx: FunctionContext) -> JsResult { + let s = cx.argument(0)?; + Ok(s) +} + +pub fn execute_scoped(mut cx: FunctionContext) -> JsResult { + let mut i = 0; + for _ in 1..100 { + cx.execute_scoped(|mut cx| { + let n = cx.number(1); + i += n.value(&mut cx) as i32; + }); + } + Ok(cx.number(i)) +} + +pub fn compute_scoped(mut cx: FunctionContext) -> JsResult { + let mut i = cx.number(0); + for _ in 1..100 { + i = cx.compute_scoped(|mut cx| { + let n = cx.number(1); + let left = i.value(&mut cx) as i32; + let right = n.value(&mut cx) as i32; + Ok(cx.number(left + right)) + })?; + } + Ok(i) +} diff --git a/test/napi/native/src/lib.rs b/test/napi/native/src/lib.rs index e234e3214..b73387fe9 100644 --- a/test/napi/native/src/lib.rs +++ b/test/napi/native/src/lib.rs @@ -1,5 +1,11 @@ use neon::prelude::*; +mod js { + pub mod functions; +} + +use js::functions::*; + register_module!(|mut cx| { let greeting = cx.string("Hello, World!"); let greeting_copy = greeting.value(&mut cx); @@ -75,5 +81,20 @@ register_module!(|mut cx| { cx.export_function("add1", add1)?; + cx.export_function("return_js_function", return_js_function)?; + cx.export_function("call_js_function", call_js_function)?; + cx.export_function("construct_js_function", construct_js_function)?; + cx.export_function("num_arguments", num_arguments)?; + cx.export_function("return_this", return_this)?; + cx.export_function("require_object_this", require_object_this)?; + cx.export_function("is_argument_zero_some", is_argument_zero_some)?; + cx.export_function("require_argument_zero_string", require_argument_zero_string)?; + cx.export_function("check_string_and_number", check_string_and_number)?; + cx.export_function("execute_scoped", execute_scoped)?; + cx.export_function("compute_scoped", compute_scoped)?; + + cx.export_function("panic", panic)?; + cx.export_function("panic_after_throw", panic_after_throw)?; + Ok(()) }); From 8d00d716c08bf3e804d9473752d45505572e8683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 13 Mar 2020 15:17:00 +0100 Subject: [PATCH 08/23] Start porting object test to n-api runtime. --- crates/neon-runtime/src/napi/scope.rs | 4 +- test/napi/lib/objects.js | 142 ++++++++++++++++++++++++++ test/napi/native/src/js/objects.rs | 117 +++++++++++++++++++++ test/napi/native/src/lib.rs | 8 ++ 4 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 test/napi/lib/objects.js create mode 100644 test/napi/native/src/js/objects.rs diff --git a/crates/neon-runtime/src/napi/scope.rs b/crates/neon-runtime/src/napi/scope.rs index 47fad3b29..7a8aacbea 100644 --- a/crates/neon-runtime/src/napi/scope.rs +++ b/crates/neon-runtime/src/napi/scope.rs @@ -69,4 +69,6 @@ pub unsafe extern "C" fn escapable_size() -> usize { unimplemented!() } pub unsafe extern "C" fn escapable_alignment() -> usize { unimplemented!() } -pub unsafe extern "C" fn get_global(_env: Env, _out: &mut Local) { unimplemented!() } +pub unsafe extern "C" fn get_global(env: Env, out: &mut Local) { + assert_eq!(napi::napi_get_global(env, out as *mut _), napi::napi_status::napi_ok); +} diff --git a/test/napi/lib/objects.js b/test/napi/lib/objects.js new file mode 100644 index 000000000..cd829c70a --- /dev/null +++ b/test/napi/lib/objects.js @@ -0,0 +1,142 @@ +var addon = require('../native'); +var assert = require('chai').assert; + +describe('JsObject', function() { + it('return the v8::Global object', function () { + assert(global === addon.return_js_global_object()); + }); + + it('return a JsObject built in Rust', function () { + assert.deepEqual({}, addon.return_js_object()); + }); + + it('return a JsObject with a number key value pair', function () { + assert.deepEqual({number: 9000}, addon.return_js_object_with_number()); + }); + + it('return a JsObject with an string key value pair', function () { + assert.deepEqual({string: "hello node"}, addon.return_js_object_with_string()); + }); + + it('return a JsObject with mixed content key value pairs', function () { + assert.deepEqual({number: 9000, string: 'hello node'}, addon.return_js_object_with_mixed_content()); + }); + + // ArrayBuffers are not yet implemented in the n-api runtime. + it.skip('gets a 16-byte, zeroed ArrayBuffer', function() { + var b = addon.return_array_buffer(); + assert.equal(b.byteLength, 16); + assert.equal((new Uint32Array(b))[0], 0); + assert.equal((new Uint32Array(b))[1], 0); + assert.equal((new Uint32Array(b))[2], 0); + assert.equal((new Uint32Array(b))[3], 0); + }); + + // ArrayBuffers are not yet implemented in the n-api runtime. + it.skip('correctly reads an ArrayBuffer using the lock API', function() { + var b = new ArrayBuffer(16); + var a = new Uint32Array(b); + a[0] = 47; + a[1] = 133; + a[2] = 9; + a[3] = 88888888; + assert.equal(addon.read_array_buffer_with_lock(b, 0), 47); + assert.equal(addon.read_array_buffer_with_lock(b, 1), 133); + assert.equal(addon.read_array_buffer_with_lock(b, 2), 9); + assert.equal(addon.read_array_buffer_with_lock(b, 3), 88888888); + }); + + // ArrayBuffers are not yet implemented in the n-api runtime. + it.skip('correctly reads an ArrayBuffer using the borrow API', function() { + var b = new ArrayBuffer(16); + var a = new Uint32Array(b); + a[0] = 49; + a[1] = 135; + a[2] = 11; + a[3] = 89898989; + assert.equal(addon.read_array_buffer_with_borrow(b, 0), 49); + assert.equal(addon.read_array_buffer_with_borrow(b, 1), 135); + assert.equal(addon.read_array_buffer_with_borrow(b, 2), 11); + assert.equal(addon.read_array_buffer_with_borrow(b, 3), 89898989); + }); + + // ArrayBuffers are not yet implemented in the n-api runtime. + it.skip('correctly writes to an ArrayBuffer using the lock API', function() { + var b = new ArrayBuffer(16); + addon.write_array_buffer_with_lock(b, 0, 999); + assert.equal((new Uint32Array(b))[0], 999); + addon.write_array_buffer_with_lock(b, 1, 111); + assert.equal((new Uint32Array(b))[1], 111); + addon.write_array_buffer_with_lock(b, 2, 121212); + assert.equal((new Uint32Array(b))[2], 121212); + addon.write_array_buffer_with_lock(b, 3, 99991111); + assert.equal((new Uint32Array(b))[3], 99991111); + }); + + // ArrayBuffers are not yet implemented in the n-api runtime. + it.skip('correctly writes to an ArrayBuffer using the borrow_mut API', function() { + var b = new ArrayBuffer(16); + addon.write_array_buffer_with_borrow_mut(b, 0, 434); + assert.equal((new Uint32Array(b))[0], 434); + addon.write_array_buffer_with_borrow_mut(b, 1, 100); + assert.equal((new Uint32Array(b))[1], 100); + addon.write_array_buffer_with_borrow_mut(b, 2, 22); + assert.equal((new Uint32Array(b))[2], 22); + addon.write_array_buffer_with_borrow_mut(b, 3, 400100); + assert.equal((new Uint32Array(b))[3], 400100); + }); + + // ArrayBuffers are not yet implemented in the n-api runtime. + it.skip('correctly reads a Buffer using the lock API', function() { + var b = Buffer.allocUnsafe(16); + b.writeUInt32LE(147, 0); + b.writeUInt32LE(1133, 4); + b.writeUInt32LE(109, 8); + b.writeUInt32LE(189189, 12); + assert.equal(addon.read_buffer_with_lock(b, 0), 147); + assert.equal(addon.read_buffer_with_lock(b, 1), 1133); + assert.equal(addon.read_buffer_with_lock(b, 2), 109); + assert.equal(addon.read_buffer_with_lock(b, 3), 189189); + }); + + // ArrayBuffers are not yet implemented in the n-api runtime. + it.skip('correctly reads a Buffer using the borrow API', function() { + var b = Buffer.allocUnsafe(16); + b.writeUInt32LE(149, 0); + b.writeUInt32LE(2244, 4); + b.writeUInt32LE(707, 8); + b.writeUInt32LE(22914478, 12); + assert.equal(addon.read_buffer_with_borrow(b, 0), 149); + assert.equal(addon.read_buffer_with_borrow(b, 1), 2244); + assert.equal(addon.read_buffer_with_borrow(b, 2), 707); + assert.equal(addon.read_buffer_with_borrow(b, 3), 22914478); + }); + + // ArrayBuffers are not yet implemented in the n-api runtime. + it.skip('correctly writes to a Buffer using the lock API', function() { + var b = Buffer.allocUnsafe(16); + b.fill(0); + addon.write_buffer_with_lock(b, 0, 6); + assert.equal(b.readUInt32LE(0), 6); + addon.write_buffer_with_lock(b, 1, 6000001); + assert.equal(b.readUInt32LE(4), 6000001); + addon.write_buffer_with_lock(b, 2, 4500); + assert.equal(b.readUInt32LE(8), 4500); + addon.write_buffer_with_lock(b, 3, 421600); + assert.equal(b.readUInt32LE(12), 421600); + }); + + // ArrayBuffers are not yet implemented in the n-api runtime. + it.skip('correctly writes to a Buffer using the borrow_mut API', function() { + var b = Buffer.allocUnsafe(16); + b.fill(0); + addon.write_buffer_with_borrow_mut(b, 0, 16); + assert.equal(b.readUInt32LE(0), 16); + addon.write_buffer_with_borrow_mut(b, 1, 16000001); + assert.equal(b.readUInt32LE(4), 16000001); + addon.write_buffer_with_borrow_mut(b, 2, 232); + assert.equal(b.readUInt32LE(8), 232); + addon.write_buffer_with_borrow_mut(b, 3, 66012); + assert.equal(b.readUInt32LE(12), 66012); + }); +}); diff --git a/test/napi/native/src/js/objects.rs b/test/napi/native/src/js/objects.rs new file mode 100644 index 000000000..36332ff47 --- /dev/null +++ b/test/napi/native/src/js/objects.rs @@ -0,0 +1,117 @@ +use neon::prelude::*; + +pub fn return_js_global_object(mut cx: FunctionContext) -> JsResult { + Ok(cx.global()) +} + +pub fn return_js_object(mut cx: FunctionContext) -> JsResult { + Ok(cx.empty_object()) +} + +pub fn return_js_object_with_mixed_content(mut cx: FunctionContext) -> JsResult { + let js_object: Handle = cx.empty_object(); + let n = cx.number(9000.0); + js_object.set(&mut cx, "number", n)?; + let s = cx.string("hello node"); + js_object.set(&mut cx, "string", s)?; + Ok(js_object) +} + +pub fn return_js_object_with_number(mut cx: FunctionContext) -> JsResult { + let js_object: Handle = cx.empty_object(); + let n = cx.number(9000.0); + js_object.set(&mut cx, "number", n)?; + Ok(js_object) +} + +pub fn return_js_object_with_string(mut cx: FunctionContext) -> JsResult { + let js_object: Handle = cx.empty_object(); + let s = cx.string("hello node"); + js_object.set(&mut cx, "string", s)?; + Ok(js_object) +} + +pub fn return_array_buffer(mut cx: FunctionContext) -> JsResult { + let b: Handle = cx.array_buffer(16)?; + Ok(b) +} + +pub fn read_array_buffer_with_lock(mut cx: FunctionContext) -> JsResult { + let b: Handle = cx.argument(0)?; + let i = cx.argument::(1)?.value(&mut cx) as u32 as usize; + let x = { + let guard = cx.lock(); + let data = b.borrow(&guard); + let slice = data.as_slice::(); + slice[i] + }; + Ok(cx.number(x)) +} + +pub fn read_array_buffer_with_borrow(mut cx: FunctionContext) -> JsResult { + let b: Handle = cx.argument(0)?; + let i = cx.argument::(1)?.value(&mut cx) as u32 as usize; + let x = cx.borrow(&b, |data| { data.as_slice::()[i] }); + Ok(cx.number(x)) +} + +pub fn write_array_buffer_with_lock(mut cx: FunctionContext) -> JsResult { + let mut b: Handle = cx.argument(0)?; + let i = cx.argument::(1)?.value(&mut cx) as u32 as usize; + let x = cx.argument::(2)?.value(&mut cx) as u32; + { + let guard = cx.lock(); + let data = b.borrow_mut(&guard); + let slice = data.as_mut_slice::(); + slice[i] = x; + } + Ok(cx.undefined()) +} + +pub fn write_array_buffer_with_borrow_mut(mut cx: FunctionContext) -> JsResult { + let mut b: Handle = cx.argument(0)?; + let i = cx.argument::(1)?.value(&mut cx) as u32 as usize; + let x = cx.argument::(2)?.value(&mut cx) as u32; + cx.borrow_mut(&mut b, |data| { data.as_mut_slice::()[i] = x; }); + Ok(cx.undefined()) +} + +pub fn read_buffer_with_lock(mut cx: FunctionContext) -> JsResult { + let b: Handle = cx.argument(0)?; + let i = cx.argument::(1)?.value(&mut cx) as u32 as usize; + let x = { + let guard = cx.lock(); + let data = b.borrow(&guard); + let slice = data.as_slice::(); + slice[i] + }; + Ok(cx.number(x)) +} + +pub fn read_buffer_with_borrow(mut cx: FunctionContext) -> JsResult { + let b: Handle = cx.argument(0)?; + let i = cx.argument::(1)?.value(&mut cx) as u32 as usize; + let x = cx.borrow(&b, |data| { data.as_slice::()[i] }); + Ok(cx.number(x)) +} + +pub fn write_buffer_with_lock(mut cx: FunctionContext) -> JsResult { + let mut b: Handle = cx.argument(0)?; + let i = cx.argument::(1)?.value(&mut cx) as u32 as usize; + let x = cx.argument::(2)?.value(&mut cx) as u32; + { + let guard = cx.lock(); + let data = b.borrow_mut(&guard); + let slice = data.as_mut_slice::(); + slice[i] = x; + } + Ok(cx.undefined()) +} + +pub fn write_buffer_with_borrow_mut(mut cx: FunctionContext) -> JsResult { + let mut b: Handle = cx.argument(0)?; + let i = cx.argument::(1)?.value(&mut cx) as u32 as usize; + let x = cx.argument::(2)?.value(&mut cx) as u32; + cx.borrow_mut(&mut b, |data| { data.as_mut_slice::()[i] = x; }); + Ok(cx.undefined()) +} diff --git a/test/napi/native/src/lib.rs b/test/napi/native/src/lib.rs index b73387fe9..7c802ceb4 100644 --- a/test/napi/native/src/lib.rs +++ b/test/napi/native/src/lib.rs @@ -2,9 +2,11 @@ use neon::prelude::*; mod js { pub mod functions; + pub mod objects; } use js::functions::*; +use js::objects::*; register_module!(|mut cx| { let greeting = cx.string("Hello, World!"); @@ -93,6 +95,12 @@ register_module!(|mut cx| { cx.export_function("execute_scoped", execute_scoped)?; cx.export_function("compute_scoped", compute_scoped)?; + cx.export_function("return_js_global_object", return_js_global_object)?; + cx.export_function("return_js_object", return_js_object)?; + cx.export_function("return_js_object_with_number", return_js_object_with_number)?; + cx.export_function("return_js_object_with_string", return_js_object_with_string)?; + cx.export_function("return_js_object_with_mixed_content", return_js_object_with_mixed_content)?; + cx.export_function("panic", panic)?; cx.export_function("panic_after_throw", panic_after_throw)?; From 546f2ce76d3bf55265958943baefafc8bb859455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 13 Mar 2020 16:47:54 +0100 Subject: [PATCH 09/23] Implement runtime::fun::call. --- crates/neon-runtime/src/napi/fun.rs | 13 ++++--------- test/napi/lib/functions.js | 2 +- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/crates/neon-runtime/src/napi/fun.rs b/crates/neon-runtime/src/napi/fun.rs index d0621c118..3e5edd4aa 100644 --- a/crates/neon-runtime/src/napi/fun.rs +++ b/crates/neon-runtime/src/napi/fun.rs @@ -31,15 +31,10 @@ pub unsafe extern "C" fn get_dynamic_callback(env: Env, data: *mut c_void) -> *m data } -pub unsafe extern "C" fn call( - _out: &mut Local, - _env: Env, - _fun: Local, - _this: Local, - _argc: i32, - _argv: *mut c_void, -) -> bool { - unimplemented!() +pub unsafe extern "C" fn call(out: &mut Local, env: Env, fun: Local, this: Local, argc: i32, argv: *mut c_void) -> bool { + let status = napi::napi_call_function(env, this, fun, argc as usize, argv as *const _, out as *mut _); + + status == napi::napi_status::napi_ok } pub unsafe extern "C" fn construct( diff --git a/test/napi/lib/functions.js b/test/napi/lib/functions.js index d9709787c..7ef641785 100644 --- a/test/napi/lib/functions.js +++ b/test/napi/lib/functions.js @@ -11,7 +11,7 @@ describe('JsFunction', function() { }); // The n-api runtime cannot yet call JS functions. - it.skip('call a JsFunction built in JS that implements x => x + 1', function () { + it('call a JsFunction built in JS that implements x => x + 1', function () { assert.equal(addon.call_js_function(function(x) { return x + 1 }), 17); }); From 440e5f9149d7f57b6960759de1c96776073b861e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 13 Mar 2020 16:50:31 +0100 Subject: [PATCH 10/23] Implement runtime::fun::construct. --- crates/neon-runtime/src/napi/fun.rs | 12 ++++-------- test/napi/lib/functions.js | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/crates/neon-runtime/src/napi/fun.rs b/crates/neon-runtime/src/napi/fun.rs index 3e5edd4aa..11036d90b 100644 --- a/crates/neon-runtime/src/napi/fun.rs +++ b/crates/neon-runtime/src/napi/fun.rs @@ -37,12 +37,8 @@ pub unsafe extern "C" fn call(out: &mut Local, env: Env, fun: Local, this: Local status == napi::napi_status::napi_ok } -pub unsafe extern "C" fn construct( - _out: &mut Local, - _env: Env, - _fun: Local, - _argc: i32, - _argv: *mut c_void, -) -> bool { - unimplemented!() +pub unsafe extern "C" fn construct(out: &mut Local, env: Env, fun: Local, argc: i32, argv: *mut c_void) -> bool { + let status = napi::napi_new_instance(env, fun, argc as usize, argv as *const _, out as *mut _); + + status == napi::napi_status::napi_ok } diff --git a/test/napi/lib/functions.js b/test/napi/lib/functions.js index 7ef641785..88a06493d 100644 --- a/test/napi/lib/functions.js +++ b/test/napi/lib/functions.js @@ -16,7 +16,7 @@ describe('JsFunction', function() { }); // The n-api runtime cannot yet call JS functions. - it.skip('new a JsFunction', function () { + it('new a JsFunction', function () { assert.equal(addon.construct_js_function(Date), 1970); }); From 1835acf0ddd2c03890335f77ee19831441f5f58b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 13 Mar 2020 16:57:30 +0100 Subject: [PATCH 11/23] Implement `is_object` tag check. --- crates/neon-runtime/src/napi/tag.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/neon-runtime/src/napi/tag.rs b/crates/neon-runtime/src/napi/tag.rs index e09a6b3ec..6f2e7bd9b 100644 --- a/crates/neon-runtime/src/napi/tag.rs +++ b/crates/neon-runtime/src/napi/tag.rs @@ -31,7 +31,9 @@ pub unsafe extern "C" fn is_string(env: Env, val: Local) -> bool { is_type(env, val, napi::napi_valuetype::napi_string) } -pub unsafe extern "C" fn is_object(_env: Env, _val: Local) -> bool { unimplemented!() } +pub unsafe extern "C" fn is_object(env: Env, val: Local) -> bool { + is_type(env, val, napi::napi_valuetype::napi_object) +} pub unsafe extern "C" fn is_array(_env: Env, _val: Local) -> bool { unimplemented!() } From 10495c4ac2c89aafef4d7dcf4d4a2f7d8f315f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 13 Mar 2020 16:57:39 +0100 Subject: [PATCH 12/23] Implement runtime::fun::this(). --- crates/neon-runtime/src/napi/call.rs | 13 +++++++++++-- crates/neon-sys/native/src/neon.cc | 2 +- crates/neon-sys/native/src/neon.h | 2 +- crates/neon-sys/src/lib.rs | 2 +- src/context/mod.rs | 5 +++-- test/napi/lib/functions.js | 2 +- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/crates/neon-runtime/src/napi/call.rs b/crates/neon-runtime/src/napi/call.rs index f3fa44a9b..4dccdcab9 100644 --- a/crates/neon-runtime/src/napi/call.rs +++ b/crates/neon-runtime/src/napi/call.rs @@ -30,7 +30,17 @@ pub unsafe extern "C" fn current_isolate() -> Env { panic!("current_isolate won' pub unsafe extern "C" fn is_construct(_info: FunctionCallbackInfo) -> bool { unimplemented!() } -pub unsafe extern "C" fn this(_info: FunctionCallbackInfo, _out: &mut Local) { unimplemented!() } +pub unsafe extern "C" fn this(env: Env, info: FunctionCallbackInfo, out: &mut Local) { + let status = napi::napi_get_cb_info( + env, + info, + null_mut(), + null_mut(), + out as *mut _, + null_mut(), + ); + assert_eq!(status, napi::napi_status::napi_ok); +} /// Mutates the `out` argument provided to refer to the associated data value of the /// `napi_callback_info`. @@ -45,7 +55,6 @@ pub unsafe extern "C" fn data(env: Env, info: FunctionCallbackInfo, out: &mut *m null_mut(), &mut data as *mut _, ); - println!("data() status = {:?} argc = {}", status, argc); if status == napi::napi_status::napi_ok { *out = data; } diff --git a/crates/neon-sys/native/src/neon.cc b/crates/neon-sys/native/src/neon.cc index ab01c2b49..00020eb27 100644 --- a/crates/neon-sys/native/src/neon.cc +++ b/crates/neon-sys/native/src/neon.cc @@ -26,7 +26,7 @@ extern "C" bool Neon_Call_IsConstruct(v8::FunctionCallbackInfo *info) return info->IsConstructCall(); } -extern "C" void Neon_Call_This(v8::FunctionCallbackInfo *info, v8::Local *out) { +extern "C" void Neon_Call_This(v8::Isolate *isolate, v8::FunctionCallbackInfo *info, v8::Local *out) { *out = info->This(); } diff --git a/crates/neon-sys/native/src/neon.h b/crates/neon-sys/native/src/neon.h index 4686733c5..5f730147b 100644 --- a/crates/neon-sys/native/src/neon.h +++ b/crates/neon-sys/native/src/neon.h @@ -17,7 +17,7 @@ extern "C" { void *Neon_Call_GetIsolate(v8::FunctionCallbackInfo *info); void *Neon_Call_CurrentIsolate(); bool Neon_Call_IsConstruct(v8::FunctionCallbackInfo *info); - void Neon_Call_This(v8::FunctionCallbackInfo *info, v8::Local *out); + void Neon_Call_This(v8::Isolate *isolate, v8::FunctionCallbackInfo *info, v8::Local *out); void Neon_Call_Data(v8::Isolate *isolate, v8::FunctionCallbackInfo *info, void **out); int32_t Neon_Call_Length(v8::Isolate *isolate, v8::FunctionCallbackInfo *info); void Neon_Call_Get(v8::Isolate *isolate, v8::FunctionCallbackInfo *info, int32_t i, v8::Local *out); diff --git a/crates/neon-sys/src/lib.rs b/crates/neon-sys/src/lib.rs index d384a5880..d735f42a7 100644 --- a/crates/neon-sys/src/lib.rs +++ b/crates/neon-sys/src/lib.rs @@ -97,7 +97,7 @@ extern "C" { pub fn Neon_Call_GetIsolate(info: FunctionCallbackInfo) -> Isolate; pub fn Neon_Call_CurrentIsolate() -> Isolate; pub fn Neon_Call_IsConstruct(info: FunctionCallbackInfo) -> bool; - pub fn Neon_Call_This(info: FunctionCallbackInfo, out: &mut Local); + pub fn Neon_Call_This(isolate: Isolate, info: FunctionCallbackInfo, out: &mut Local); pub fn Neon_Call_Data(isolate: Isolate, info: FunctionCallbackInfo, out: &mut *mut c_void); pub fn Neon_Call_Length(isolate: Isolate, info: FunctionCallbackInfo) -> i32; pub fn Neon_Call_Get(isolate: Isolate, info: FunctionCallbackInfo, i: i32, out: &mut Local); diff --git a/src/context/mod.rs b/src/context/mod.rs index 0e936fab9..568dbbaca 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -82,10 +82,11 @@ impl CallbackInfo { } } - pub fn this<'b, V: Context<'b>>(&self, _: &mut V) -> raw::Local { + pub fn this<'b, C: Context<'b>>(&self, cx: &mut C) -> raw::Local { + let env = cx.env(); unsafe { let mut local: raw::Local = std::mem::zeroed(); - neon_runtime::call::this(std::mem::transmute(self.info), &mut local); + neon_runtime::call::this(env.to_raw(), std::mem::transmute(self.info), &mut local); local } } diff --git a/test/napi/lib/functions.js b/test/napi/lib/functions.js index 88a06493d..0875c5b3c 100644 --- a/test/napi/lib/functions.js +++ b/test/napi/lib/functions.js @@ -55,7 +55,7 @@ describe('JsFunction', function() { }); // The n-api runtime cannot yet access `this`. - it.skip('can manipulate an object `this` binding', function() { + it('can manipulate an object `this` binding', function() { var o = { modified: false }; addon.require_object_this.call(o); assert.equal(o.modified, true); From f4adc5b613c92939c6e1524650b978f65eca0eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 13 Mar 2020 16:58:30 +0100 Subject: [PATCH 13/23] Enable other passing `this` tests. --- test/napi/lib/functions.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/napi/lib/functions.js b/test/napi/lib/functions.js index 0875c5b3c..2372ec2fc 100644 --- a/test/napi/lib/functions.js +++ b/test/napi/lib/functions.js @@ -42,8 +42,7 @@ describe('JsFunction', function() { assert.equal(addon.num_arguments('a', 'b', 'c', 'd'), 4); }); - // The n-api runtime cannot yet access `this`. - it.skip('gets the right `this`-value', function() { + it('gets the right `this`-value', function() { var o = { iamobject: 'i am object' }; assert.equal(addon.return_this.call(o), o); @@ -54,7 +53,6 @@ describe('JsFunction', function() { assert.notStrictEqual(addon.return_this.call(n), n); }); - // The n-api runtime cannot yet access `this`. it('can manipulate an object `this` binding', function() { var o = { modified: false }; addon.require_object_this.call(o); @@ -63,8 +61,7 @@ describe('JsFunction', function() { addon.require_object_this.call(42); }); - // The n-api runtime cannot yet access `this`. - it.skip('implicitly gets global', function() { + it('implicitly gets global', function() { var global = (new Function("return this"))(); assert.equal(addon.return_this.call(undefined), global); }); From 07384320cd3ec9e7c9a8913a558a16d215fa877c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 13 Mar 2020 17:01:05 +0100 Subject: [PATCH 14/23] Remove outdated comments. --- test/napi/lib/functions.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/napi/lib/functions.js b/test/napi/lib/functions.js index 2372ec2fc..7b16a6a61 100644 --- a/test/napi/lib/functions.js +++ b/test/napi/lib/functions.js @@ -10,12 +10,10 @@ describe('JsFunction', function() { assert.equal(addon.return_js_function()(41), 42); }); - // The n-api runtime cannot yet call JS functions. it('call a JsFunction built in JS that implements x => x + 1', function () { assert.equal(addon.call_js_function(function(x) { return x + 1 }), 17); }); - // The n-api runtime cannot yet call JS functions. it('new a JsFunction', function () { assert.equal(addon.construct_js_function(Date), 1970); }); From b7e52e134e1d566941852dc3e9cfd2d2b85dac5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Wed, 8 Apr 2020 16:54:01 +0200 Subject: [PATCH 15/23] Add test for get_own_property_names. --- test/napi/lib/objects.js | 14 ++++++++++++++ test/napi/native/src/lib.rs | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/test/napi/lib/objects.js b/test/napi/lib/objects.js index cd829c70a..65c1dd106 100644 --- a/test/napi/lib/objects.js +++ b/test/napi/lib/objects.js @@ -139,4 +139,18 @@ describe('JsObject', function() { addon.write_buffer_with_borrow_mut(b, 3, 66012); assert.equal(b.readUInt32LE(12), 66012); }); + + it('reads the names of own properties', function() { + var superObject = { + a: 1 + }; + + var childObject = Object.create(superObject); + childObject.b = 2; + + assert.deepEqual( + addon.get_own_property_names(childObject), + Object.getOwnPropertyNames(childObject) + ); + }); }); diff --git a/test/napi/native/src/lib.rs b/test/napi/native/src/lib.rs index 7c802ceb4..8127786f0 100644 --- a/test/napi/native/src/lib.rs +++ b/test/napi/native/src/lib.rs @@ -104,5 +104,12 @@ register_module!(|mut cx| { cx.export_function("panic", panic)?; cx.export_function("panic_after_throw", panic_after_throw)?; + fn call_get_own_property_names(mut cx: FunctionContext) -> JsResult { + let object = cx.argument::(0)?; + object.get_own_property_names(&mut cx) + } + + cx.export_function("get_own_property_names", call_get_own_property_names)?; + Ok(()) }); From 3b4de376748bf89c4632e648a9b6bac326c21cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Wed, 8 Apr 2020 17:04:20 +0200 Subject: [PATCH 16/23] Check that `get_own_property_names` does not return Symbols --- test/napi/lib/objects.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/napi/lib/objects.js b/test/napi/lib/objects.js index 65c1dd106..b39af020b 100644 --- a/test/napi/lib/objects.js +++ b/test/napi/lib/objects.js @@ -140,7 +140,7 @@ describe('JsObject', function() { assert.equal(b.readUInt32LE(12), 66012); }); - it('reads the names of own properties', function() { + it('returns only own properties from get_own_property_names', function() { var superObject = { a: 1 }; @@ -153,4 +153,16 @@ describe('JsObject', function() { Object.getOwnPropertyNames(childObject) ); }); + + it('does not return Symbols from get_own_property_names', function() { + var object = {}; + object['this should be a thing'] = 0; + object[Symbol('this should not be a thing')] = 1; + + assert.deepEqual( + addon.get_own_property_names(object), + Object.getOwnPropertyNames(object) + ); + assert.equal(addon.get_own_property_names(object).length, 1); + }); }); From d9e33245abc8fae568e01d371fa289be629d4d8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 24 Apr 2020 13:55:10 +0200 Subject: [PATCH 17/23] Remove possibly-incorrect handling of Rust panics in callback --- src/types/internal.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/types/internal.rs b/src/types/internal.rs index d17876371..875c95349 100644 --- a/src/types/internal.rs +++ b/src/types/internal.rs @@ -62,8 +62,13 @@ impl Callback for FunctionCallback { if let Ok(value) = convert_panics(|| { dynamic_callback(cx) }) { value.to_raw() } else { - // TODO this should probably not be null - std::ptr::null_mut() + // What should we return if the function panicked? + // + // `ptr::null_mut()` may work, but we should have a test to verify that, which + // can be created after [#505][0]. For now, let's not guess! + // + // [0]: https://github.com/neon-bindings/neon/pull/505. + unimplemented!("cannot return from function after a panic") } }) } From d25fe613a0bed808ed3cdf1291733baef3c50c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 24 Apr 2020 14:34:55 +0200 Subject: [PATCH 18/23] Add a lifetime parameter to `CallbackInfo` --- src/context/mod.rs | 15 ++++++++------- src/object/class/internal.rs | 8 ++++---- src/object/class/mod.rs | 4 ++-- src/types/internal.rs | 4 ++-- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/context/mod.rs b/src/context/mod.rs index 568dbbaca..869f61aaf 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -6,6 +6,7 @@ use std; use std::cell::RefCell; use std::convert::Into; use std::marker::PhantomData; +use std::os::raw::c_void; use std::panic::UnwindSafe; use neon_runtime; use neon_runtime::raw; @@ -20,15 +21,15 @@ use object::{Object, This}; use object::class::Class; use result::{NeonResult, JsResult, Throw}; use self::internal::{ContextInternal, Scope, ScopeMetadata}; -use std::os::raw::c_void; #[repr(C)] -pub(crate) struct CallbackInfo { - info: raw::FunctionCallbackInfo +pub(crate) struct CallbackInfo<'a> { + info: raw::FunctionCallbackInfo, + _lifetime: PhantomData<&'a raw::FunctionCallbackInfo>, } -impl CallbackInfo { - pub fn data<'a>(&self, env: Env) -> *mut c_void { +impl CallbackInfo<'_> { + pub fn data(&self, env: Env) -> *mut c_void { unsafe { let mut raw_data: *mut c_void = std::mem::zeroed(); neon_runtime::call::data(env.to_raw(), self.info, &mut raw_data); @@ -448,7 +449,7 @@ impl<'a, 'b> Context<'a> for ComputeContext<'a, 'b> { } /// The type parameter `T` is the type of the `this`-binding. pub struct CallContext<'a, T: This> { scope: Scope<'a, raw::HandleScope>, - info: &'a CallbackInfo, + info: &'a CallbackInfo<'a>, phantom_type: PhantomData } @@ -458,7 +459,7 @@ impl<'a, T: This> CallContext<'a, T> { /// Indicates whether the function was called via the JavaScript `[[Call]]` or `[[Construct]]` semantics. pub fn kind(&self) -> CallKind { self.info.kind() } - pub(crate) fn with FnOnce(CallContext<'b, T>) -> U>(env: Env, info: &'a CallbackInfo, f: F) -> U { + pub(crate) fn with FnOnce(CallContext<'b, T>) -> U>(env: Env, info: &'a CallbackInfo<'a>, f: F) -> U { Scope::with(env, |scope| { f(CallContext { scope, diff --git a/src/object/class/internal.rs b/src/object/class/internal.rs index ccd259038..ab1caeec7 100644 --- a/src/object/class/internal.rs +++ b/src/object/class/internal.rs @@ -15,7 +15,7 @@ use types::error::convert_panics; pub struct MethodCallback(pub fn(CallContext) -> JsResult); impl Callback<()> for MethodCallback { - extern "C" fn invoke(env: Env, info: CallbackInfo) { + extern "C" fn invoke(env: Env, info: CallbackInfo<'_>) { unsafe { info.with_cx::(env, |mut cx| { let data = info.data(cx.env()); @@ -65,7 +65,7 @@ impl ConstructorCallCallback { } impl Callback<()> for ConstructorCallCallback { - extern "C" fn invoke(env: Env, info: CallbackInfo) { + extern "C" fn invoke(env: Env, info: CallbackInfo<'_>) { unsafe { info.with_cx(env, |cx| { let data = info.data(cx.env()); @@ -87,7 +87,7 @@ impl Callback<()> for ConstructorCallCallback { pub struct AllocateCallback(pub fn(CallContext) -> NeonResult); impl Callback<*mut c_void> for AllocateCallback { - extern "C" fn invoke(env: Env, info: CallbackInfo) -> *mut c_void { + extern "C" fn invoke(env: Env, info: CallbackInfo<'_>) -> *mut c_void { unsafe { info.with_cx(env, |cx| { let data = info.data(cx.env()); @@ -112,7 +112,7 @@ impl Callback<*mut c_void> for AllocateCallback { pub struct ConstructCallback(pub fn(CallContext) -> NeonResult>>); impl Callback for ConstructCallback { - extern "C" fn invoke(env: Env, info: CallbackInfo) -> bool { + extern "C" fn invoke(env: Env, info: CallbackInfo<'_>) -> bool { unsafe { info.with_cx(env, |cx| { let data = info.data(cx.env()); diff --git a/src/object/class/mod.rs b/src/object/class/mod.rs index 8bf85fe52..c156ca924 100644 --- a/src/object/class/mod.rs +++ b/src/object/class/mod.rs @@ -269,13 +269,13 @@ pub(crate) trait Callback: Sized { /// Extracts the computed Rust function and invokes it. The Neon runtime /// ensures that the computed function is provided as the extra data field, /// wrapped as a V8 External, in the `CallbackInfo` argument. - extern "C" fn invoke(env: Env, info: CallbackInfo) -> T; + extern "C" fn invoke(env: Env, info: CallbackInfo<'_>) -> T; /// See `invoke`. This is used by the non-n-api implementation, so that every impl for this /// trait doesn't need to provide two versions of `invoke`. #[cfg(feature = "legacy-runtime")] #[doc(hidden)] - extern "C" fn invoke_compat(info: CallbackInfo) -> T { + extern "C" fn invoke_compat(info: CallbackInfo<'_>) -> T { Self::invoke(Env::current(), info) } diff --git a/src/types/internal.rs b/src/types/internal.rs index 875c95349..48bdaeb02 100644 --- a/src/types/internal.rs +++ b/src/types/internal.rs @@ -33,7 +33,7 @@ pub struct FunctionCallback(pub fn(FunctionContext) -> JsResult); #[cfg(feature = "legacy-runtime")] impl Callback<()> for FunctionCallback { - extern "C" fn invoke(env: Env, info: CallbackInfo) { + extern "C" fn invoke(env: Env, info: CallbackInfo<'_>) { unsafe { info.with_cx::(env, |cx| { let data = info.data(env); @@ -53,7 +53,7 @@ impl Callback<()> for FunctionCallback { #[cfg(feature = "napi-runtime")] impl Callback for FunctionCallback { - extern "C" fn invoke(env: Env, info: CallbackInfo) -> raw::Local { + extern "C" fn invoke(env: Env, info: CallbackInfo<'_>) -> raw::Local { unsafe { info.with_cx::(env, |cx| { let data = info.data(env); From a488204d10355090b01217c0859284f790817a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Tue, 9 Jun 2020 14:53:49 +0200 Subject: [PATCH 19/23] kick ci From 528124767ce2976bfb03164adbab1ff6399adf56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Tue, 16 Jun 2020 22:22:15 +0200 Subject: [PATCH 20/23] Remove unnecessary `argc` value from `neon_runtime::call::data()`. --- crates/neon-runtime/src/napi/call.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/neon-runtime/src/napi/call.rs b/crates/neon-runtime/src/napi/call.rs index 4dccdcab9..6119caccb 100644 --- a/crates/neon-runtime/src/napi/call.rs +++ b/crates/neon-runtime/src/napi/call.rs @@ -46,11 +46,10 @@ pub unsafe extern "C" fn this(env: Env, info: FunctionCallbackInfo, out: &mut Lo /// `napi_callback_info`. pub unsafe extern "C" fn data(env: Env, info: FunctionCallbackInfo, out: &mut *mut c_void) { let mut data = null_mut(); - let mut argc = 0usize; let status = napi::napi_get_cb_info( env, info, - &mut argc as *mut _, + null_mut(), null_mut(), null_mut(), &mut data as *mut _, From e31c16cf5bf481673e225f4413d28c457bf2237f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Tue, 16 Jun 2020 22:39:09 +0200 Subject: [PATCH 21/23] try to explain why we unwrap the external --- crates/neon-sys/native/src/neon.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/neon-sys/native/src/neon.cc b/crates/neon-sys/native/src/neon.cc index 00020eb27..2056b16ef 100644 --- a/crates/neon-sys/native/src/neon.cc +++ b/crates/neon-sys/native/src/neon.cc @@ -37,6 +37,8 @@ extern "C" void Neon_Call_Data(v8::Isolate *isolate, v8::FunctionCallbackInfo external = info->Data(); if (external->IsExternal()) { *out = external.As()->Value(); From 7213332d729613f9e1f5133bbf9436e63c78bf1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Tue, 16 Jun 2020 22:40:07 +0200 Subject: [PATCH 22/23] do a capitalisation --- test/napi/lib/functions.js | 10 +++++----- test/napi/lib/objects.js | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/test/napi/lib/functions.js b/test/napi/lib/functions.js index 7b16a6a61..6f7f5b201 100644 --- a/test/napi/lib/functions.js +++ b/test/napi/lib/functions.js @@ -22,12 +22,12 @@ describe('JsFunction', function() { addon.check_string_and_number("string", 42); }); - // The n-api runtime doesn't yet handle panics. + // The N-API runtime doesn't yet handle panics. it.skip('converts a Rust panic to a throw in a function', function() { assert.throws(function() { addon.panic() }, Error, /^internal error in Neon module: zomg$/); }); - // The n-api runtime doesn't yet handle panics. + // The N-API runtime doesn't yet handle panics. it.skip('lets panic override a throw', function() { assert.throws(function() { addon.panic_after_throw() }, Error, /^internal error in Neon module: this should override the RangeError$/); }); @@ -73,19 +73,19 @@ describe('JsFunction', function() { assert.equal(addon.is_argument_zero_some.call(null, ['a', 'b']), true); }); - // The n-api runtime cannot yet throw errors. + // The N-API runtime cannot yet throw errors. it.skip('correctly casts an argument via cx.arguments', function() { assert.equal(addon.require_argument_zero_string('foobar'), 'foobar'); assert.throws(function() { addon.require_argument_zero_string(new Date()) }, TypeError); assert.throws(function() { addon.require_argument_zero_string(17) }, TypeError); }); - // The n-api runtime does not support scoped computation yet. + // The N-API runtime does not support scoped computation yet. it.skip('executes a scoped computation', function() { assert.equal(addon.execute_scoped(), 99); }); - // The n-api runtime does not support scoped computation yet. + // The N-API runtime does not support scoped computation yet. it.skip('computes a value in a scoped computation', function() { assert.equal(addon.compute_scoped(), 99); }); diff --git a/test/napi/lib/objects.js b/test/napi/lib/objects.js index b39af020b..e161a3202 100644 --- a/test/napi/lib/objects.js +++ b/test/napi/lib/objects.js @@ -22,7 +22,7 @@ describe('JsObject', function() { assert.deepEqual({number: 9000, string: 'hello node'}, addon.return_js_object_with_mixed_content()); }); - // ArrayBuffers are not yet implemented in the n-api runtime. + // ArrayBuffers are not yet implemented in the N-API runtime. it.skip('gets a 16-byte, zeroed ArrayBuffer', function() { var b = addon.return_array_buffer(); assert.equal(b.byteLength, 16); @@ -32,7 +32,7 @@ describe('JsObject', function() { assert.equal((new Uint32Array(b))[3], 0); }); - // ArrayBuffers are not yet implemented in the n-api runtime. + // ArrayBuffers are not yet implemented in the N-API runtime. it.skip('correctly reads an ArrayBuffer using the lock API', function() { var b = new ArrayBuffer(16); var a = new Uint32Array(b); @@ -46,7 +46,7 @@ describe('JsObject', function() { assert.equal(addon.read_array_buffer_with_lock(b, 3), 88888888); }); - // ArrayBuffers are not yet implemented in the n-api runtime. + // ArrayBuffers are not yet implemented in the N-API runtime. it.skip('correctly reads an ArrayBuffer using the borrow API', function() { var b = new ArrayBuffer(16); var a = new Uint32Array(b); @@ -60,7 +60,7 @@ describe('JsObject', function() { assert.equal(addon.read_array_buffer_with_borrow(b, 3), 89898989); }); - // ArrayBuffers are not yet implemented in the n-api runtime. + // ArrayBuffers are not yet implemented in the N-API runtime. it.skip('correctly writes to an ArrayBuffer using the lock API', function() { var b = new ArrayBuffer(16); addon.write_array_buffer_with_lock(b, 0, 999); @@ -73,7 +73,7 @@ describe('JsObject', function() { assert.equal((new Uint32Array(b))[3], 99991111); }); - // ArrayBuffers are not yet implemented in the n-api runtime. + // ArrayBuffers are not yet implemented in the N-API runtime. it.skip('correctly writes to an ArrayBuffer using the borrow_mut API', function() { var b = new ArrayBuffer(16); addon.write_array_buffer_with_borrow_mut(b, 0, 434); @@ -86,7 +86,7 @@ describe('JsObject', function() { assert.equal((new Uint32Array(b))[3], 400100); }); - // ArrayBuffers are not yet implemented in the n-api runtime. + // ArrayBuffers are not yet implemented in the N-API runtime. it.skip('correctly reads a Buffer using the lock API', function() { var b = Buffer.allocUnsafe(16); b.writeUInt32LE(147, 0); @@ -99,7 +99,7 @@ describe('JsObject', function() { assert.equal(addon.read_buffer_with_lock(b, 3), 189189); }); - // ArrayBuffers are not yet implemented in the n-api runtime. + // ArrayBuffers are not yet implemented in the N-API runtime. it.skip('correctly reads a Buffer using the borrow API', function() { var b = Buffer.allocUnsafe(16); b.writeUInt32LE(149, 0); @@ -112,7 +112,7 @@ describe('JsObject', function() { assert.equal(addon.read_buffer_with_borrow(b, 3), 22914478); }); - // ArrayBuffers are not yet implemented in the n-api runtime. + // ArrayBuffers are not yet implemented in the N-API runtime. it.skip('correctly writes to a Buffer using the lock API', function() { var b = Buffer.allocUnsafe(16); b.fill(0); @@ -126,7 +126,7 @@ describe('JsObject', function() { assert.equal(b.readUInt32LE(12), 421600); }); - // ArrayBuffers are not yet implemented in the n-api runtime. + // ArrayBuffers are not yet implemented in the N-API runtime. it.skip('correctly writes to a Buffer using the borrow_mut API', function() { var b = Buffer.allocUnsafe(16); b.fill(0); From 0a40f6abb03883e505fe56986523ef342ce55dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 19 Jun 2020 19:01:29 +0200 Subject: [PATCH 23/23] reference TODO issue --- crates/neon-runtime/src/napi/call.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/neon-runtime/src/napi/call.rs b/crates/neon-runtime/src/napi/call.rs index 6119caccb..fd15b9f25 100644 --- a/crates/neon-runtime/src/napi/call.rs +++ b/crates/neon-runtime/src/napi/call.rs @@ -77,7 +77,7 @@ pub unsafe extern "C" fn len(env: Env, info: FunctionCallbackInfo) -> i32 { /// Mutates the `out` argument provided to refer to the `napi_value` of the `i`th argument /// passed to the function. pub unsafe extern "C" fn get(env: Env, info: FunctionCallbackInfo, i: i32, out: &mut Local) { - // TODO make this not allocate + // TODO make this not allocate: https://github.com/neon-bindings/neon/issues/530 // Instead, we can probably get all the arguments at once in `neon` itself? let mut args = vec![null_mut(); (i + 1) as usize]; let mut num_args = args.len();