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 b0d4d6c71..fd15b9f25 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,78 @@ 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(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); +} -pub unsafe extern "C" fn data(_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 status = napi::napi_get_cb_info( + env, + info, + null_mut(), + null_mut(), + null_mut(), + &mut data as *mut _, + ); + if status == napi::napi_status::napi_ok { + *out = data; + } +} + +/// 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 +} -pub unsafe extern "C" fn len(_info: &FunctionCallbackInfo) -> i32 { unimplemented!() } +/// 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: 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(); -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/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 10963b6fd..11036d90b 100644 --- a/crates/neon-runtime/src/napi/fun.rs +++ b/crates/neon-runtime/src/napi/fun.rs @@ -1,13 +1,44 @@ -use std::os::raw::c_void; +//! Facilities for working with JS functions. + use call::CCallback; use raw::{Env, Local}; +use std::os::raw::c_void; +use std::mem::MaybeUninit; +use std::ptr::{null, null_mut}; + +use nodejs_sys as napi; + +/// 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, + ); + + status == napi::napi_status::napi_ok +} + +pub unsafe extern "C" fn new_template(_out: &mut Local, _env: Env, _callback: CCallback) -> bool { + unimplemented!() +} -pub unsafe extern "C" fn new(_out: &mut Local, _env: Env, _callback: CCallback) -> bool { unimplemented!() } +pub unsafe extern "C" fn get_dynamic_callback(env: Env, data: *mut c_void) -> *mut c_void { + data +} -pub unsafe extern "C" fn new_template(_out: &mut Local, _env: Env, _callback: CCallback) -> 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 _); -pub unsafe extern "C" fn get_dynamic_callback(_obj: Local) -> *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 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 _); -pub unsafe extern "C" fn construct(_out: &mut Local, _env: Env, _fun: Local, _argc: i32, _argv: *mut c_void) -> bool { unimplemented!() } + status == napi::napi_status::napi_ok +} 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-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/crates/neon-runtime/src/napi/tag.rs b/crates/neon-runtime/src/napi/tag.rs index 1d51f7a97..6f2e7bd9b 100644 --- a/crates/neon-runtime/src/napi/tag.rs +++ b/crates/neon-runtime/src/napi/tag.rs @@ -31,11 +31,15 @@ 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!() } -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/crates/neon-sys/native/src/neon.cc b/crates/neon-sys/native/src/neon.cc index edbc4b3ab..2056b16ef 100644 --- a/crates/neon-sys/native/src/neon.cc +++ b/crates/neon-sys/native/src/neon.cc @@ -26,25 +26,30 @@ 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(); } -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(); + + // Call data is stored wrapped in a v8::External by Neon_Fun_New et al, so we need to unwrap it before handing it back to Rust. + v8::Local external = info->Data(); + if (external->IsExternal()) { + *out = external.As()->Value(); + } } -extern "C" int32_t Neon_Call_Length(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]; } @@ -330,18 +335,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 +438,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..5f730147b 100644 --- a/crates/neon-sys/native/src/neon.h +++ b/crates/neon-sys/native/src/neon.h @@ -17,10 +17,10 @@ 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_Data(v8::FunctionCallbackInfo *info, v8::Local *out); - int32_t Neon_Call_Length(v8::FunctionCallbackInfo *info); - void Neon_Call_Get(v8::FunctionCallbackInfo *info, int32_t i, 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); void Neon_Primitive_Number(v8::Local *out, v8::Isolate *isolate, double value); void Neon_Primitive_Undefined(v8::Local *out, v8::Isolate *isolate); @@ -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 136a1b89e..d735f42a7 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(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); 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); @@ -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 2febcdaf8..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; @@ -22,26 +23,27 @@ use result::{NeonResult, JsResult, Throw}; use self::internal::{ContextInternal, Scope, ScopeMetadata}; #[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) -> Handle<'a, JsValue> { +impl CallbackInfo<'_> { + pub fn data(&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 } } - 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>) { unsafe { - neon_runtime::call::set_return(&self.info, value.to_raw()) + neon_runtime::call::set_return(self.info, value.to_raw()) } } @@ -53,38 +55,39 @@ 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))) } } - 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 } } @@ -446,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 } @@ -456,8 +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>(info: &'a CallbackInfo, f: F) -> U { - let env = Env::current(); + 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, @@ -468,7 +470,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/src/object/class/internal.rs b/src/object/class/internal.rs index 6c564cd90..ab1caeec7 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,10 +15,10 @@ 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| { - let data = info.data(); + info.with_cx::(env, |mut cx| { + let data = info.data(cx.env()); let this: Handle = Handle::new_internal(JsValue::from_raw(info.this(&mut cx))); #[cfg(feature = "legacy-runtime")] @@ -33,13 +33,13 @@ 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); } }) } - } + } fn as_ptr(self) -> *mut c_void { self.0 as *mut c_void @@ -65,12 +65,12 @@ 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| { - let data = info.data(); + info.with_cx(env, |cx| { + 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); } @@ -87,12 +87,12 @@ 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| { - let data = info.data(); + info.with_cx(env, |cx| { + 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) @@ -112,12 +112,12 @@ 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| { - let data = info.data(); + info.with_cx(env, |cx| { + 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/object/class/mod.rs b/src/object/class/mod.rs index dd04e196b..c156ca924 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..48bdaeb02 100644 --- a/src/types/internal.rs +++ b/src/types/internal.rs @@ -31,13 +31,14 @@ 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(info: &CallbackInfo) { + extern "C" fn invoke(env: Env, info: CallbackInfo<'_>) { unsafe { - info.with_cx::(|cx| { - let data = info.data(); + info.with_cx::(env, |cx| { + 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); } @@ -49,3 +50,31 @@ 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 { + // 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") + } + }) + } + } + + fn as_ptr(self) -> *mut c_void { + unsafe { mem::transmute(self.0) } + } +} diff --git a/test/napi/lib/functions.js b/test/napi/lib/functions.js new file mode 100644 index 000000000..6f7f5b201 --- /dev/null +++ b/test/napi/lib/functions.js @@ -0,0 +1,92 @@ +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); + }); + + 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); + }); + + it('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); + }); + + it('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); + }); + + it('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); + }); + + it('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/lib/hello.js b/test/napi/lib/hello.js index becef4295..773c06822 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.add1(2), 3.0); + }) }); diff --git a/test/napi/lib/objects.js b/test/napi/lib/objects.js new file mode 100644 index 000000000..e161a3202 --- /dev/null +++ b/test/napi/lib/objects.js @@ -0,0 +1,168 @@ +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); + }); + + it('returns only own properties from get_own_property_names', function() { + var superObject = { + a: 1 + }; + + var childObject = Object.create(superObject); + childObject.b = 2; + + assert.deepEqual( + addon.get_own_property_names(childObject), + 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); + }); +}); 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/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 f79e1692c..8127786f0 100644 --- a/test/napi/native/src/lib.rs +++ b/test/napi/native/src/lib.rs @@ -1,5 +1,13 @@ 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!"); let greeting_copy = greeting.value(&mut cx); @@ -68,5 +76,40 @@ 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)) + } + + 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("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)?; + + 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(()) });