diff --git a/crates/neon-runtime/src/napi/convert.rs b/crates/neon-runtime/src/napi/convert.rs index 45834ec66..2f41e3ba5 100644 --- a/crates/neon-runtime/src/napi/convert.rs +++ b/crates/neon-runtime/src/napi/convert.rs @@ -1,5 +1,11 @@ -use raw::Local; +use nodejs_sys as napi; + +use raw::{Env, Local}; pub unsafe extern "C" fn to_object(_out: &mut Local, _value: &Local) -> bool { unimplemented!() } -pub unsafe extern "C" fn to_string(_out: &mut Local, _value: Local) -> bool { unimplemented!() } +pub unsafe extern "C" fn to_string(out: &mut Local, env: Env, value: Local) -> bool { + let status = napi::napi_coerce_to_string(env, value, out as *mut _); + + status == napi::napi_status::napi_ok +} diff --git a/crates/neon-runtime/src/napi/object.rs b/crates/neon-runtime/src/napi/object.rs index 001992387..f3d2b2451 100644 --- a/crates/neon-runtime/src/napi/object.rs +++ b/crates/neon-runtime/src/napi/object.rs @@ -2,6 +2,9 @@ use std::mem::MaybeUninit; use nodejs_sys as napi; +use array; +use convert; +use tag; use raw::{Env, Local}; /// Mutates the `out` argument to refer to a `napi_value` containing a newly created JavaScript Object. @@ -14,7 +17,36 @@ pub unsafe extern "C" fn new(out: &mut Local, env: Env) { pub unsafe extern "C" fn get_own_property_names(out: &mut Local, env: Env, object: Local) -> bool { let status = napi::napi_get_property_names(env, object, out as *mut _); - status == napi::napi_status::napi_ok + if status != napi::napi_status::napi_ok { + return false; + } + + // Before https://github.com/nodejs/node/pull/27524, `napi_get_property_names` would return + // numbers for numeric indices instead of strings. + let len = array::len(env, *out); + for index in 0..len { + let mut element: Local = std::mem::zeroed(); + // In general, getters may cause arbitrary JS code to be run, but this is a newly created + // Array from an official internal API so it doesn't do anything strange. + if !get_index(&mut element, env, *out, index) { + continue; + } + if tag::is_string(env, element) { + continue; + } + let mut stringified: Local = std::mem::zeroed(); + // If we can't convert to a string, something went wrong. + if !convert::to_string(&mut stringified, env, element) { + return false; + } + let mut dummy = false; + // If we can't convert assign to this array, something went wrong. + if !set_index(&mut dummy, env, *out, index, stringified) { + return false; + } + } + + true } // Unused. diff --git a/crates/neon-sys/native/src/neon.cc b/crates/neon-sys/native/src/neon.cc index 5dc50a80d..edbc4b3ab 100644 --- a/crates/neon-sys/native/src/neon.cc +++ b/crates/neon-sys/native/src/neon.cc @@ -173,7 +173,7 @@ extern "C" size_t Neon_String_Data(char *out, size_t len, v8::Local s return Nan::DecodeWrite(out, len, str, Nan::UTF8); } -extern "C" bool Neon_Convert_ToString(v8::Local *out, v8::Local value) { +extern "C" bool Neon_Convert_ToString(v8::Local *out, v8::Isolate *isolate, v8::Local value) { Nan::MaybeLocal maybe = Nan::To(value); return maybe.ToLocal(out); } diff --git a/crates/neon-sys/native/src/neon.h b/crates/neon-sys/native/src/neon.h index cf7c42208..2b5cf2aad 100644 --- a/crates/neon-sys/native/src/neon.h +++ b/crates/neon-sys/native/src/neon.h @@ -46,7 +46,7 @@ extern "C" { int32_t Neon_String_Utf8Length(v8::Local str); size_t Neon_String_Data(char *out, size_t len, v8::Local str); - bool Neon_Convert_ToString(v8::Local *out, v8::Local value); + bool Neon_Convert_ToString(v8::Local *out, v8::Isolate *isolate, v8::Local value); bool Neon_Convert_ToObject(v8::Local *out, v8::Local *value); bool Neon_Buffer_New(v8::Local *out, uint32_t size); diff --git a/crates/neon-sys/src/lib.rs b/crates/neon-sys/src/lib.rs index 15b9002c6..136a1b89e 100644 --- a/crates/neon-sys/src/lib.rs +++ b/crates/neon-sys/src/lib.rs @@ -123,7 +123,7 @@ extern "C" { pub fn Neon_Class_GetInstanceInternals(obj: Local) -> *mut c_void; pub fn Neon_Convert_ToObject(out: &mut Local, value: &Local) -> bool; - pub fn Neon_Convert_ToString(out: &mut Local, value: Local) -> bool; + pub fn Neon_Convert_ToString(out: &mut Local, isolate: Isolate, value: Local) -> bool; pub fn Neon_Error_Throw(val: Local); pub fn Neon_Error_NewError(out: &mut Local, msg: Local); diff --git a/src/types/mod.rs b/src/types/mod.rs index cc34d7bea..217534e53 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -50,8 +50,11 @@ impl SuperType for JsObject { /// The trait shared by all JavaScript values. pub trait Value: ValueInternal { - fn to_string<'a, C: Context<'a>>(self, _: &mut C) -> JsResult<'a, JsString> { - build(|out| { unsafe { neon_runtime::convert::to_string(out, self.to_raw()) } }) + fn to_string<'a, C: Context<'a>>(self, cx: &mut C) -> JsResult<'a, JsString> { + let env = cx.env(); + build(|out| { + unsafe { neon_runtime::convert::to_string(out, env.to_raw(), self.to_raw()) } + }) } fn as_value<'a, C: Context<'a>>(self, _: &mut C) -> Handle<'a, JsValue> { diff --git a/test/napi/native/src/lib.rs b/test/napi/native/src/lib.rs index 6fc51942c..f79e1692c 100644 --- a/test/napi/native/src/lib.rs +++ b/test/napi/native/src/lib.rs @@ -59,8 +59,11 @@ register_module!(|mut cx| { let property_names = rust_created.get_own_property_names(&mut cx)? .to_vec(&mut cx)? .into_iter() - .map(|value| value.downcast::(&mut cx).unwrap().value(&mut cx)) - .collect::>(); + .map(|value| { + let string: Handle = value.downcast_or_throw(&mut cx)?; + Ok(string.value(&mut cx)) + }) + .collect::, _>>()?; assert_eq!(property_names, &["0", "a", "whatever"]); cx.export_value("rustCreated", rust_created)?;