From 546b108a5b9b2d6623ab2fb1940ad3f6a8fca128 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Mon, 6 Nov 2023 10:58:23 +0800 Subject: [PATCH] feat(napi): support async class factory (#1779) - Close https://github.com/napi-rs/napi-rs/issues/1777 --- crates/backend/src/codegen/fn.rs | 11 ++++- crates/backend/src/codegen/struct.rs | 6 +-- crates/backend/src/typegen/fn.rs | 9 +++- .../napi/src/bindgen_runtime/callback_info.rs | 35 +++++++++++-- crates/napi/src/env.rs | 2 +- crates/napi/src/tokio_runtime.rs | 46 ++++++++++++++++-- .../__snapshots__/typegen.spec.ts.md | 2 + .../__snapshots__/typegen.spec.ts.snap | Bin 4077 -> 4091 bytes examples/napi/__tests__/values.spec.ts | 7 +++ examples/napi/index.d.ts | 2 + examples/napi/src/class_factory.rs | 20 ++++++++ 11 files changed, 126 insertions(+), 14 deletions(-) diff --git a/crates/backend/src/codegen/fn.rs b/crates/backend/src/codegen/fn.rs index 9bf200b4..0dba1c26 100644 --- a/crates/backend/src/codegen/fn.rs +++ b/crates/backend/src/codegen/fn.rs @@ -112,8 +112,15 @@ impl TryToTokens for NapiFn { } }; + // async factory only + let use_after_async = if self.is_async && self.parent.is_some() && self.fn_self.is_none() { + quote! { true } + } else { + quote! { false } + }; + let function_call_inner = quote! { - napi::bindgen_prelude::CallbackInfo::<#args_len>::new(env, cb, None).and_then(|mut cb| { + napi::bindgen_prelude::CallbackInfo::<#args_len>::new(env, cb, None, #use_after_async).and_then(|mut cb| { #build_ref_container #(#arg_conversions)* #native_call @@ -511,6 +518,8 @@ impl NapiFn { if self.is_ret_result { if self.parent_is_generator { quote! { cb.generator_factory(#js_name, #ret?) } + } else if self.is_async { + quote! { cb.factory(#js_name, #ret) } } else { quote! { cb.factory(#js_name, #ret?) } } diff --git a/crates/backend/src/codegen/struct.rs b/crates/backend/src/codegen/struct.rs index bdbd9304..b39103a0 100644 --- a/crates/backend/src/codegen/struct.rs +++ b/crates/backend/src/codegen/struct.rs @@ -250,7 +250,7 @@ impl NapiStruct { env: napi::bindgen_prelude::sys::napi_env, cb: napi::bindgen_prelude::sys::napi_callback_info ) -> napi::bindgen_prelude::sys::napi_value { - napi::bindgen_prelude::CallbackInfo::<#fields_len>::new(env, cb, None) + napi::bindgen_prelude::CallbackInfo::<#fields_len>::new(env, cb, None, false) .and_then(|cb| #constructor) .unwrap_or_else(|e| { unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) }; @@ -669,7 +669,7 @@ impl NapiStruct { env: napi::bindgen_prelude::sys::napi_env, cb: napi::bindgen_prelude::sys::napi_callback_info ) -> napi::bindgen_prelude::sys::napi_value { - napi::bindgen_prelude::CallbackInfo::<0>::new(env, cb, Some(0)) + napi::bindgen_prelude::CallbackInfo::<0>::new(env, cb, Some(0), false) .and_then(|mut cb| unsafe { cb.unwrap_borrow_mut::<#struct_name>() }) .and_then(|obj| { #to_napi_value_convert @@ -691,7 +691,7 @@ impl NapiStruct { env: napi::bindgen_prelude::sys::napi_env, cb: napi::bindgen_prelude::sys::napi_callback_info ) -> napi::bindgen_prelude::sys::napi_value { - napi::bindgen_prelude::CallbackInfo::<1>::new(env, cb, Some(1)) + napi::bindgen_prelude::CallbackInfo::<1>::new(env, cb, Some(1), false) .and_then(|mut cb_info| unsafe { cb_info.unwrap_borrow_mut::<#struct_name>() .and_then(|obj| { diff --git a/crates/backend/src/typegen/fn.rs b/crates/backend/src/typegen/fn.rs index 1d261c0c..a7fa7bff 100644 --- a/crates/backend/src/typegen/fn.rs +++ b/crates/backend/src/typegen/fn.rs @@ -226,7 +226,14 @@ impl NapiFn { FnKind::Factory => self .parent .clone() - .map(|i| format!(": {}", i.to_string().to_case(Case::Pascal))) + .map(|i| { + let parent = i.to_string().to_case(Case::Pascal); + if self.is_async { + format!(": Promise<{}>", parent) + } else { + format!(": {}", parent) + } + }) .unwrap_or_else(|| "".to_owned()), _ => { let ret = if let Some(ret) = &self.ret { diff --git a/crates/napi/src/bindgen_runtime/callback_info.rs b/crates/napi/src/bindgen_runtime/callback_info.rs index b41ce559..54e03968 100644 --- a/crates/napi/src/bindgen_runtime/callback_info.rs +++ b/crates/napi/src/bindgen_runtime/callback_info.rs @@ -16,6 +16,7 @@ pub struct CallbackInfo { env: sys::napi_env, pub this: sys::napi_value, pub args: [sys::napi_value; N], + this_reference: sys::napi_ref, } impl CallbackInfo { @@ -24,6 +25,9 @@ impl CallbackInfo { env: sys::napi_env, callback_info: sys::napi_callback_info, required_argc: Option, + // for async class factory, the `this` will be used after the async call + // so we must create reference for it and use it after async resolved + use_after_async: bool, ) -> Result { let mut this = ptr::null_mut(); let mut args = [ptr::null_mut(); N]; @@ -55,7 +59,21 @@ impl CallbackInfo { } } - Ok(Self { env, this, args }) + let mut this_reference = ptr::null_mut(); + + if use_after_async { + check_status!( + unsafe { sys::napi_create_reference(env, this, 1, &mut this_reference) }, + "Failed to create reference for `this` in async class factory" + )?; + } + + Ok(Self { + env, + this, + args, + this_reference, + }) } pub fn get_arg(&self, index: usize) -> sys::napi_value { @@ -141,8 +159,18 @@ impl CallbackInfo { js_name: &str, obj: T, ) -> Result<(sys::napi_value, *mut T)> { - let this = self.this(); + let mut this = self.this(); let mut instance = ptr::null_mut(); + if !self.this_reference.is_null() { + check_status!( + unsafe { sys::napi_get_reference_value(self.env, self.this_reference, &mut this) }, + "Failed to get reference value for `this` in async class factory" + )?; + check_status!( + unsafe { sys::napi_delete_reference(self.env, self.this_reference) }, + "Failed to delete reference for `this` in async class factory" + )?; + } ___CALL_FROM_FACTORY.with(|s| s.store(true, Ordering::Relaxed)); let status = unsafe { sys::napi_new_instance(self.env, this, 0, ptr::null_mut(), &mut instance) }; @@ -154,6 +182,7 @@ impl CallbackInfo { unsafe { sys::napi_throw(self.env, exception) }; return Ok((ptr::null_mut(), ptr::null_mut())); } + check_status!(status, "Failed to create instance of class `{}`", js_name)?; let obj = Box::new(obj); let initial_finalize: Box = Box::new(|| {}); let finalize_callbacks_ptr = Rc::into_raw(Rc::new(Cell::new(Box::into_raw(initial_finalize)))); @@ -164,7 +193,7 @@ impl CallbackInfo { sys::napi_wrap( self.env, instance, - value_ref as *mut c_void, + value_ref.cast(), Some(raw_finalize_unchecked::), ptr::null_mut(), &mut object_ref, diff --git a/crates/napi/src/env.rs b/crates/napi/src/env.rs index b0b43374..1b5c59dd 100644 --- a/crates/napi/src/env.rs +++ b/crates/napi/src/env.rs @@ -1084,7 +1084,7 @@ impl Env { T: 'static + Send, V: 'static + ToNapiValue, F: 'static + Send + Future>, - R: 'static + Send + Sync + FnOnce(&mut Env, T) -> Result, + R: 'static + Send + FnOnce(&mut Env, T) -> Result, >( &self, fut: F, diff --git a/crates/napi/src/tokio_runtime.rs b/crates/napi/src/tokio_runtime.rs index c56c1a69..c89fa99b 100644 --- a/crates/napi/src/tokio_runtime.rs +++ b/crates/napi/src/tokio_runtime.rs @@ -1,4 +1,4 @@ -use std::{future::Future, sync::RwLock}; +use std::{future::Future, marker::PhantomData, sync::RwLock}; use once_cell::sync::Lazy; use tokio::runtime::Runtime; @@ -83,11 +83,43 @@ pub fn within_runtime_if_available T, T>(f: F) -> T { f() } +struct SendableResolver< + Data: 'static + Send, + R: 'static + FnOnce(sys::napi_env, Data) -> Result, +> { + inner: R, + _data: PhantomData, +} + +// the `SendableResolver` will be only called in the `threadsafe_function_call_js` callback +// which means it will be always called in the Node.js JavaScript thread +// so the inner function is not required to be `Send` +// but the `Send` bound is required by the `execute_tokio_future` function +unsafe impl Result> + Send for SendableResolver +{ +} + +impl Result> + SendableResolver +{ + fn new(inner: R) -> Self { + Self { + inner, + _data: PhantomData, + } + } + + fn resolve(self, env: sys::napi_env, data: Data) -> Result { + (self.inner)(env, data) + } +} + #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn execute_tokio_future< Data: 'static + Send, Fut: 'static + Send + Future>, - Resolver: 'static + Send + Sync + FnOnce(sys::napi_env, Data) -> Result, + Resolver: 'static + FnOnce(sys::napi_env, Data) -> Result, >( env: sys::napi_env, fut: Fut, @@ -95,10 +127,14 @@ pub fn execute_tokio_future< ) -> Result { let (deferred, promise) = JsDeferred::new(env)?; - let inner = async move { + let sendable_resolver = SendableResolver::new(resolver); + + let inner = async { match fut.await { - Ok(v) => deferred.resolve(|env| { - resolver(env.raw(), v).map(|v| unsafe { JsUnknown::from_raw_unchecked(env.raw(), v) }) + Ok(v) => deferred.resolve(move |env| { + sendable_resolver + .resolve(env.raw(), v) + .map(|v| unsafe { JsUnknown::from_raw_unchecked(env.raw(), v) }) }), Err(e) => deferred.reject(e), } diff --git a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md index ad5833cf..7951d2ec 100644 --- a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md +++ b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md @@ -102,6 +102,8 @@ Generated by [AVA](https://avajs.dev). export class ClassWithFactory {␊ name: string␊ static withName(name: string): ClassWithFactory␊ + static with4Name(name: string): Promise␊ + static with4NameResult(name: string): Promise␊ setName(name: string): this␊ }␊ ␊ diff --git a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap index 213862635e39e293f65892867ef085c771625b0d..92bf03121d9090d6e4567587ca57537a00318de9 100644 GIT binary patch literal 4091 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 diff --git a/examples/napi/__tests__/values.spec.ts b/examples/napi/__tests__/values.spec.ts index b562a1d8..2463e568 100644 --- a/examples/napi/__tests__/values.spec.ts +++ b/examples/napi/__tests__/values.spec.ts @@ -265,6 +265,13 @@ test('class factory', (t) => { ) }) +test('async class factory', async (t) => { + const instance = await ClassWithFactory.with4Name('foo') + t.is(instance.name, 'foo-4') + const instance2 = await ClassWithFactory.with4NameResult('foo') + t.is(instance2.name, 'foo-4') +}) + test('class constructor return Result', (t) => { const c = new Context() t.is(c.method(), 'not empty') diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index c3a0e08d..eb7075d9 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -92,6 +92,8 @@ export type Blake2bKey = Blake2BKey export class ClassWithFactory { name: string static withName(name: string): ClassWithFactory + static with4Name(name: string): Promise + static with4NameResult(name: string): Promise setName(name: string): this } diff --git a/examples/napi/src/class_factory.rs b/examples/napi/src/class_factory.rs index 50daac52..39116d7c 100644 --- a/examples/napi/src/class_factory.rs +++ b/examples/napi/src/class_factory.rs @@ -1,3 +1,9 @@ +use napi::Result; + +async fn always_4() -> i32 { + 4 +} + #[napi] pub struct ClassWithFactory { pub name: String, @@ -10,6 +16,20 @@ impl ClassWithFactory { Self { name } } + #[napi(factory)] + pub async fn with_4_name(name: String) -> Self { + Self { + name: format!("{name}-{}", always_4().await), + } + } + + #[napi(factory)] + pub async fn with_4_name_result(name: String) -> Result { + Ok(Self { + name: format!("{name}-{}", always_4().await), + }) + } + #[napi] pub fn set_name(&mut self, name: String) -> &Self { self.name = name;