From 618d0f804651ed9f08ee2336671bb0473c8e7bec Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Mon, 21 Nov 2022 09:17:19 -0700 Subject: [PATCH] fix(napi-derive): unsound behavior while using reference and async together --- crates/backend/src/ast.rs | 3 +- crates/backend/src/codegen/fn.rs | 171 ++++++++++++++++---- crates/macro/src/parser/mod.rs | 1 + examples/napi/__test__/typegen.spec.ts.md | 1 + examples/napi/__test__/typegen.spec.ts.snap | Bin 3603 -> 3614 bytes examples/napi/__test__/values.spec.ts | 5 + examples/napi/index.d.ts | 1 + examples/napi/src/class.rs | 6 + 8 files changed, 160 insertions(+), 28 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 544c95888c..2070fc7f38 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -27,6 +27,7 @@ pub struct NapiFn { pub enumerable: bool, pub configurable: bool, pub catch_unwind: bool, + pub unsafe_: bool, } #[derive(Debug, Clone)] @@ -64,7 +65,7 @@ pub enum FnKind { Setter, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum FnSelf { Value, Ref, diff --git a/crates/backend/src/codegen/fn.rs b/crates/backend/src/codegen/fn.rs index 43d7e274e0..00f268f0e5 100644 --- a/crates/backend/src/codegen/fn.rs +++ b/crates/backend/src/codegen/fn.rs @@ -1,9 +1,10 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::ToTokens; +use syn::spanned::Spanned; use crate::{ codegen::{get_intermediate_ident, get_register_ident, js_mod_to_token_stream}, - BindgenResult, CallbackArg, FnKind, FnSelf, NapiFn, NapiFnArgKind, TryToTokens, + BindgenResult, CallbackArg, Diagnostic, FnKind, FnSelf, NapiFn, NapiFnArgKind, TryToTokens, }; impl TryToTokens for NapiFn { @@ -12,13 +13,78 @@ impl TryToTokens for NapiFn { let intermediate_ident = get_intermediate_ident(&name_str); let args_len = self.args.len(); - let (arg_conversions, arg_names) = self.gen_arg_conversions()?; + let ArgConversions { + arg_conversions, + args: arg_names, + refs, + mut_ref_spans, + unsafe_, + } = self.gen_arg_conversions()?; + // The JS engine can't properly track mutability in an async context, so refuse to compile + // code that tries to use async and mutability together without `unsafe` mark. + if self.is_async && !mut_ref_spans.is_empty() && !unsafe_ { + return Diagnostic::from_vec( + mut_ref_spans + .into_iter() + .map(|s| Diagnostic::span_error(s, "mutable reference is unsafe with async")) + .collect(), + ); + } + if Some(FnSelf::MutRef) == self.fn_self && self.is_async { + return Err(Diagnostic::span_error( + self.name.span(), + "&mut self is incompatible with async napi methods", + )); + } + let arg_ref_count = refs.len(); let receiver = self.gen_fn_receiver(); let receiver_ret_name = Ident::new("_ret", Span::call_site()); let ret = self.gen_fn_return(&receiver_ret_name); let register = self.gen_fn_register(); let attrs = &self.attrs; + let build_ref_container = if self.is_async { + quote! { + struct NapiRefContainer([napi::sys::napi_ref; #arg_ref_count]); + impl NapiRefContainer { + fn drop(self, env: napi::sys::napi_env) { + for r in self.0.into_iter() { + assert_eq!( + unsafe { napi::sys::napi_delete_reference(env, r) }, + napi::sys::Status::napi_ok, + "failed to delete napi ref" + ); + } + } + } + unsafe impl Send for NapiRefContainer {} + unsafe impl Sync for NapiRefContainer {} + let _make_ref = |a: ::std::ptr::NonNull| { + let mut node_ref = ::std::mem::MaybeUninit::uninit(); + assert_eq!(unsafe { + napi::bindgen_prelude::sys::napi_create_reference(env, a.as_ptr(), 1, node_ref.as_mut_ptr()) + }, + napi::bindgen_prelude::sys::Status::napi_ok, + "failed to create napi ref" + ); + unsafe { node_ref.assume_init() } + }; + let mut _args_array = [::std::ptr::null_mut::(); #arg_ref_count]; + let mut _arg_write_index = 0; + + #(#refs)* + + #[cfg(debug_assert)] + { + for a in &_args_array { + assert!(!a.is_null(), "failed to initialize napi ref"); + } + } + let _args_ref = NapiRefContainer(_args_array); + } + } else { + quote! {} + }; let native_call = if !self.is_async { quote! { napi::bindgen_prelude::within_runtime_if_available(move || { @@ -35,16 +101,26 @@ impl TryToTokens for NapiFn { quote! { Ok(#receiver(#(#arg_names),*).await) } }; quote! { - napi::bindgen_prelude::execute_tokio_future(env, async move { #call }, |env, #receiver_ret_name| { + napi::bindgen_prelude::execute_tokio_future(env, async move { #call }, move |env, #receiver_ret_name| { + _args_ref.drop(env); #ret }) } }; + let function_call_inner = quote! { + napi::bindgen_prelude::CallbackInfo::<#args_len>::new(env, cb, None).and_then(|mut cb| { + #build_ref_container + #(#arg_conversions)* + #native_call + }) + }; + let function_call = if args_len == 0 && self.fn_self.is_none() && self.kind != FnKind::Constructor && self.kind != FnKind::Factory + && !self.is_async { quote! { #native_call } } else if self.kind == FnKind::Constructor { @@ -55,18 +131,10 @@ impl TryToTokens for NapiFn { if inner.load(std::sync::atomic::Ordering::Relaxed) { return std::ptr::null_mut(); } - napi::bindgen_prelude::CallbackInfo::<#args_len>::new(env, cb, None).and_then(|mut cb| { - #(#arg_conversions)* - #native_call - }) + #function_call_inner } } else { - quote! { - napi::bindgen_prelude::CallbackInfo::<#args_len>::new(env, cb, None).and_then(|mut cb| { - #(#arg_conversions)* - #native_call - }) - } + function_call_inner }; let function_call = if self.catch_unwind { @@ -109,20 +177,30 @@ impl TryToTokens for NapiFn { } impl NapiFn { - fn gen_arg_conversions(&self) -> BindgenResult<(Vec, Vec)> { + fn gen_arg_conversions(&self) -> BindgenResult { let mut arg_conversions = vec![]; let mut args = vec![]; + let mut refs = vec![]; + let mut mut_ref_spans = vec![]; + let make_ref = |input| { + quote! { + _args_array[_arg_write_index] = _make_ref(::std::ptr::NonNull::new(#input).expect("ref ptr was null")); + _arg_write_index += 1; + } + }; // fetch this if let Some(parent) = &self.parent { match self.fn_self { Some(FnSelf::Ref) => { + refs.push(make_ref(quote! { cb.this })); arg_conversions.push(quote! { let this_ptr = unsafe { cb.unwrap_raw::<#parent>()? }; let this: &#parent = Box::leak(Box::from_raw(this_ptr)); }); } Some(FnSelf::MutRef) => { + refs.push(make_ref(quote! { cb.this })); arg_conversions.push(quote! { let this_ptr = unsafe { cb.unwrap_raw::<#parent>()? }; let this: &mut #parent = Box::leak(Box::from_raw(this_ptr)); @@ -215,7 +293,9 @@ impl NapiFn { }) = elem.as_ref() { if let Some(syn::PathSegment { ident, .. }) = segments.first() { + refs.push(make_ref(quote! { cb.this })); let token = if mutability.is_some() { + mut_ref_spans.push(generic_type.span()); quote! { <#ident as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.this)? } } else { quote! { <#ident as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.this)? } @@ -228,15 +308,21 @@ impl NapiFn { } } } - args.push( - quote! { ::from_raw_unchecked(env, cb.this) }, - ); + refs.push(make_ref(quote! { cb.this })); + args.push(quote! { ::from_raw_unchecked(env, cb.this) }); skipped_arg_count += 1; continue; } } } - arg_conversions.push(self.gen_ty_arg_conversion(&ident, i, path)); + let (arg_conversion, arg_type) = self.gen_ty_arg_conversion(&ident, i, path); + if NapiArgType::MutRef == arg_type { + mut_ref_spans.push(path.ty.span()); + } + if arg_type.is_ref() { + refs.push(make_ref(quote! { cb.get_arg(#i) })); + } + arg_conversions.push(arg_conversion); args.push(quote! { #ident }); } } @@ -247,17 +333,24 @@ impl NapiFn { } } - Ok((arg_conversions, args)) + Ok(ArgConversions { + arg_conversions, + args, + refs, + mut_ref_spans, + unsafe_: self.unsafe_, + }) } + /// Returns a type conversion, and a boolean indicating whether this value needs to have a reference created to extend the lifetime + /// for async functions. fn gen_ty_arg_conversion( &self, arg_name: &Ident, index: usize, path: &syn::PatType, - ) -> TokenStream { + ) -> (TokenStream, NapiArgType) { let ty = &*path.ty; - let type_check = if self.return_if_invalid { quote! { if let Ok(maybe_promise) = <#ty as napi::bindgen_prelude::ValidateNapiValue>::validate(env, cb.get_arg(#index)) { @@ -285,28 +378,31 @@ impl NapiFn { elem, .. }) => { - quote! { + let q = quote! { let #arg_name = { #type_check <#elem as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.get_arg(#index))? }; - } + }; + (q, NapiArgType::MutRef) } syn::Type::Reference(syn::TypeReference { elem, .. }) => { - quote! { + let q = quote! { let #arg_name = { #type_check <#elem as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.get_arg(#index))? }; - } + }; + (q, NapiArgType::Ref) } _ => { - quote! { + let q = quote! { let #arg_name = { #type_check <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#index))? }; - } + }; + (q, NapiArgType::Value) } } } @@ -482,3 +578,24 @@ impl NapiFn { } } } + +struct ArgConversions { + pub args: Vec, + pub arg_conversions: Vec, + pub refs: Vec, + pub mut_ref_spans: Vec, + pub unsafe_: bool, +} + +#[derive(Debug, PartialEq, Eq)] +enum NapiArgType { + Ref, + MutRef, + Value, +} + +impl NapiArgType { + fn is_ref(&self) -> bool { + matches!(self, NapiArgType::Ref | NapiArgType::MutRef) + } +} diff --git a/crates/macro/src/parser/mod.rs b/crates/macro/src/parser/mod.rs index 2c068d1b6f..cb659923d2 100644 --- a/crates/macro/src/parser/mod.rs +++ b/crates/macro/src/parser/mod.rs @@ -696,6 +696,7 @@ fn napi_fn_from_decl( enumerable: opts.enumerable(), configurable: opts.configurable(), catch_unwind: opts.catch_unwind().is_some(), + unsafe_: sig.unsafety.is_some(), } }) } diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index ce2521b9ea..71a27146a4 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -247,6 +247,7 @@ Generated by [AVA](https://avajs.dev). name: string␊ constructor(name: string)␊ getCount(): number␊ + getNameAsync(): Promise␊ }␊ export type Blake2bHasher = Blake2BHasher␊ /** Smoking test for type generation */␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 88f7a84c7d53fcdb2b078003448de159b0fd5d1a..2bcb9a7bd40ef7b07e088045ff27030340b63285 100644 GIT binary patch literal 3614 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 k9kSc<{9 literal 3603 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 Zu;SvdTc+Qvysf>t^FJS`^WBm&002c8-@E_- diff --git a/examples/napi/__test__/values.spec.ts b/examples/napi/__test__/values.spec.ts index 6dcbedf3bf..4465ad986f 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -206,6 +206,11 @@ test('class', (t) => { }) }) +test('async self in class', async (t) => { + const b = new Bird('foo') + t.is(await b.getNameAsync(), 'foo') +}) + test('class factory', (t) => { const duck = ClassWithFactory.withName('Default') t.is(duck.name, 'Default') diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 67d8b6c70b..dacaa6b9b2 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -237,6 +237,7 @@ export class Bird { name: string constructor(name: string) getCount(): number + getNameAsync(): Promise } export type Blake2bHasher = Blake2BHasher /** Smoking test for type generation */ diff --git a/examples/napi/src/class.rs b/examples/napi/src/class.rs index 72c3a5ff3f..29a2b4b37f 100644 --- a/examples/napi/src/class.rs +++ b/examples/napi/src/class.rs @@ -123,6 +123,12 @@ impl Bird { pub fn get_count(&self) -> u32 { 1234 } + + #[napi] + pub async fn get_name_async(&self) -> &str { + tokio::time::sleep(std::time::Duration::new(1, 0)).await; + self.name.as_str() + } } /// Smoking test for type generation