From a6676a0523e850d868921071667a6cc8b92c7194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 10 Jan 2023 15:35:46 +0100 Subject: [PATCH] fix(napi): handle static properties in classes (#17320) Adds support for static properties when using "napi_define_class". --- cli/napi/js_native_api.rs | 40 ++++++++++++++++++++++++++++++++--- ext/napi/lib.rs | 33 +++++++++++++++++++++++++++++ test_napi/object_wrap_test.js | 1 + test_napi/src/object_wrap.rs | 19 +++++++++++++++++ 4 files changed, 90 insertions(+), 3 deletions(-) diff --git a/cli/napi/js_native_api.rs b/cli/napi/js_native_api.rs index 0c8bc31d78573..c72da9b029ec5 100644 --- a/cli/napi/js_native_api.rs +++ b/cli/napi/js_native_api.rs @@ -296,7 +296,6 @@ fn napi_create_error( let msg = transmute::>(msg); let msg = msg.to_string(&mut env.scope()).unwrap(); - let error = v8::Exception::error(&mut env.scope(), msg); *result = error.into(); @@ -1134,8 +1133,15 @@ fn napi_define_class( let scope = &mut env.scope(); let napi_properties: &[napi_property_descriptor] = std::slice::from_raw_parts(properties, property_count); + let mut static_property_count = 0; for p in napi_properties { + if p.attributes & napi_static != 0 { + // Will be handled below + static_property_count += 1; + continue; + } + let name = if !p.utf8name.is_null() { let name_str = CStr::from_ptr(p.utf8name).to_str().unwrap(); v8::String::new(scope, name_str).unwrap() @@ -1197,9 +1203,37 @@ fn napi_define_class( let value: v8::Local = tpl.get_function(scope).unwrap().into(); *result = value.into(); + + if static_property_count > 0 { + let mut static_descriptors = Vec::with_capacity(static_property_count); + + for p in napi_properties { + if p.attributes & napi_static != 0 { + static_descriptors.push(*p); + } + } + + let res = napi_define_properties( + env_ptr, + *result, + static_descriptors.len(), + static_descriptors.as_ptr() as *const napi_property_descriptor, + ); + + napi_status_to_result(res)?; + } + Ok(()) } +fn napi_status_to_result(status: napi_status) -> Result { + if status == napi_ok { + Ok(()) + } else { + Err(status.into()) + } +} + #[napi_sym::napi_sym] fn napi_define_properties( env_ptr: *mut Env, @@ -1211,10 +1245,10 @@ fn napi_define_properties( let scope = &mut env.scope(); let object = transmute::>(obj); let properties = std::slice::from_raw_parts(properties, property_count); - for property in properties { let name = if !property.utf8name.is_null() { - let name_str = CStr::from_ptr(property.utf8name).to_str().unwrap(); + let name_str = CStr::from_ptr(property.utf8name); + let name_str = name_str.to_str().unwrap(); v8::String::new(scope, name_str).unwrap() } else { transmute::>(property.name) diff --git a/ext/napi/lib.rs b/ext/napi/lib.rs index e76a08ed96d20..5775457741bb0 100644 --- a/ext/napi/lib.rs +++ b/ext/napi/lib.rs @@ -123,6 +123,38 @@ pub enum Error { WouldDeadlock, } +#[allow(clippy::from_over_into)] +impl Into for napi_status { + fn into(self) -> Error { + match self { + napi_invalid_arg => Error::InvalidArg, + napi_object_expected => Error::ObjectExpected, + napi_string_expected => Error::StringExpected, + napi_name_expected => Error::NameExpected, + napi_function_expected => Error::FunctionExpected, + napi_number_expected => Error::NumberExpected, + napi_boolean_expected => Error::BooleanExpected, + napi_array_expected => Error::ArrayExpected, + napi_generic_failure => Error::GenericFailure, + napi_pending_exception => Error::PendingException, + napi_cancelled => Error::Cancelled, + napi_escape_called_twice => Error::EscapeCalledTwice, + napi_handle_scope_mismatch => Error::HandleScopeMismatch, + napi_callback_scope_mismatch => Error::CallbackScopeMismatch, + napi_queue_full => Error::QueueFull, + napi_closing => Error::Closing, + napi_bigint_expected => Error::BigIntExpected, + napi_date_expected => Error::DateExpected, + napi_arraybuffer_expected => Error::ArrayBufferExpected, + napi_detachable_arraybuffer_expected => { + Error::DetachableArraybufferExpected + } + napi_would_deadlock => Error::WouldDeadlock, + _ => unreachable!(), + } + } +} + pub type Result = std::result::Result<(), Error>; impl From for napi_status { @@ -603,6 +635,7 @@ where ), ) }; + Ok(serde_v8::Value { v8_value: exports.into(), }) diff --git a/test_napi/object_wrap_test.js b/test_napi/object_wrap_test.js index 2522a8e6cc41e..ae64821ead3fa 100644 --- a/test_napi/object_wrap_test.js +++ b/test_napi/object_wrap_test.js @@ -14,4 +14,5 @@ Deno.test("napi object wrap new", function () { obj.increment(); obj.set_value(10); assertEquals(obj.get_value(), 10); + assertEquals(objectWrap.NapiObject.factory(), 64); }); diff --git a/test_napi/src/object_wrap.rs b/test_napi/src/object_wrap.rs index bedb6d8525007..5b15a16d7d355 100644 --- a/test_napi/src/object_wrap.rs +++ b/test_napi/src/object_wrap.rs @@ -1,5 +1,6 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +use napi_sys::PropertyAttributes; use napi_sys::Status::napi_ok; use napi_sys::ValueType::napi_number; use napi_sys::*; @@ -116,13 +117,31 @@ impl NapiObject { ptr::null_mut() } + + pub extern "C" fn factory( + env: napi_env, + info: napi_callback_info, + ) -> napi_value { + let (_args, argc, _this) = crate::get_callback_info!(env, info, 0); + assert_eq!(argc, 0); + + let int64 = 64; + let mut value: napi_value = ptr::null_mut(); + assert!(unsafe { napi_create_int64(env, int64, &mut value) } == napi_ok); + value + } } pub fn init(env: napi_env, exports: napi_value) { + let mut static_prop = + crate::new_property!(env, "factory\0", NapiObject::factory); + static_prop.attributes = PropertyAttributes::static_; + let properties = &[ crate::new_property!(env, "set_value\0", NapiObject::set_value), crate::new_property!(env, "get_value\0", NapiObject::get_value), crate::new_property!(env, "increment\0", NapiObject::increment), + static_prop, ]; let mut cons: napi_value = ptr::null_mut();