From 27402aee81f8781cedb6c0a0a813a70b5008a72f Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Sat, 7 May 2022 21:48:10 -0400 Subject: [PATCH] feat(napi): add support for weak references --- crates/backend/src/typegen.rs | 2 +- crates/backend/src/typegen/fn.rs | 2 +- .../bindgen_runtime/js_values/value_ref.rs | 57 +++++++++++++++++- crates/napi/src/bindgen_runtime/mod.rs | 6 -- examples/napi/__test__/typegen.spec.ts.md | 4 +- examples/napi/__test__/typegen.spec.ts.snap | Bin 3048 -> 3074 bytes examples/napi/__test__/values.spec.ts | 4 +- examples/napi/index.d.ts | 4 +- examples/napi/src/reference.rs | 32 +++++++++- 9 files changed, 96 insertions(+), 15 deletions(-) diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index 21b80a52..5e76f71a 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -280,7 +280,7 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (S Some(("Promise".to_owned(), false)) } }); - } else if rust_ty == "Reference" { + } else if rust_ty == "Reference" || rust_ty == "WeakReference" { ts_ty = r#struct::TASK_STRUCTS.with(|t| { // Reference => T if let Some(arg) = args.first() { diff --git a/crates/backend/src/typegen/fn.rs b/crates/backend/src/typegen/fn.rs index 3bf6feab..eee33a5b 100644 --- a/crates/backend/src/typegen/fn.rs +++ b/crates/backend/src/typegen/fn.rs @@ -131,7 +131,7 @@ impl NapiFn { arguments: PathArguments::AngleBracketed(_), }) = path.path.segments.last() { - if ident == "Reference" { + if ident == "Reference" || ident == "WeakReference" { return None; } } diff --git a/crates/napi/src/bindgen_runtime/js_values/value_ref.rs b/crates/napi/src/bindgen_runtime/js_values/value_ref.rs index aacb809a..cbfa53e8 100644 --- a/crates/napi/src/bindgen_runtime/js_values/value_ref.rs +++ b/crates/napi/src/bindgen_runtime/js_values/value_ref.rs @@ -1,7 +1,7 @@ use std::cell::Cell; use std::ffi::c_void; use std::ops::{Deref, DerefMut}; -use std::rc::Rc; +use std::rc::{Rc, Weak}; use lazy_static::lazy_static; @@ -121,6 +121,14 @@ impl Reference { }) } + pub fn downgrade(&self) -> WeakReference { + WeakReference { + raw: self.raw, + napi_ref: self.napi_ref, + finalize_callbacks: Rc::downgrade(&self.finalize_callbacks), + } + } + /// Safety to share because caller can provide `Env` pub fn share_with Result>( self, @@ -156,6 +164,53 @@ impl DerefMut for Reference { } } +pub struct WeakReference { + raw: *mut T, + napi_ref: crate::sys::napi_ref, + finalize_callbacks: Weak>, +} + +impl Clone for WeakReference { + fn clone(&self) -> Self { + Self { + raw: self.raw, + napi_ref: self.napi_ref, + finalize_callbacks: self.finalize_callbacks.clone(), + } + } +} + +impl ToNapiValue for WeakReference { + unsafe fn to_napi_value(env: crate::sys::napi_env, val: Self) -> Result { + let mut result = std::ptr::null_mut(); + check_status!( + unsafe { crate::sys::napi_get_reference_value(env, val.napi_ref, &mut result) }, + "Failed to get reference value" + )?; + Ok(result) + } +} + +impl WeakReference { + pub fn upgrade(&self, env: Env) -> Result>> { + if let Some(finalize_callbacks) = self.finalize_callbacks.upgrade() { + let mut ref_count = 0; + check_status!( + unsafe { crate::sys::napi_reference_ref(env.0, self.napi_ref, &mut ref_count) }, + "Failed to ref napi reference" + )?; + Ok(Some(Reference { + raw: self.raw, + napi_ref: self.napi_ref, + env: env.0 as *mut c_void, + finalize_callbacks, + })) + } else { + Ok(None) + } + } +} + /// ### Experimental feature /// /// Create a `SharedReference` from an existed `Reference`. diff --git a/crates/napi/src/bindgen_runtime/mod.rs b/crates/napi/src/bindgen_runtime/mod.rs index 807375a4..f013365f 100644 --- a/crates/napi/src/bindgen_runtime/mod.rs +++ b/crates/napi/src/bindgen_runtime/mod.rs @@ -37,7 +37,6 @@ pub unsafe extern "C" fn raw_finalize_unchecked( #[cfg(debug_assertions)] { let rc_strong_count = Rc::strong_count(&finalize_callbacks_rc); - let rc_weak_count = Rc::weak_count(&finalize_callbacks_rc); // If `Rc` strong count is 2, it means the finalize of referenced `Object` is called before the `fn drop` of the `Reference` // It always happened on exiting process // In general, the `fn drop` would happen first @@ -46,11 +45,6 @@ pub unsafe extern "C" fn raw_finalize_unchecked( "Rc strong count is: {}, it should be 1 or 2", rc_strong_count ); - assert!( - rc_weak_count == 0, - "Rc weak count is: {}, it should be 0", - rc_weak_count - ); } let finalize = unsafe { Box::from_raw(finalize_callbacks_rc.get()) }; finalize(); diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 2c845b38..3816e402 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -278,10 +278,12 @@ Generated by [AVA](https://avajs.dev). export type CSSRuleList = CssRuleList␊ export class CssRuleList {␊ getRules(): Array␊ + get parentStyleSheet(): CSSStyleSheet␊ + get name(): string | null␊ }␊ export type CSSStyleSheet = CssStyleSheet␊ export class CssStyleSheet {␊ - constructor(rules: Array)␊ + constructor(name: string, rules: Array)␊ get rules(): CssRuleList␊ anotherCssStyleSheet(): AnotherCssStyleSheet␊ }␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index a3b5cf475ca1f65f6e538873687af4c4dcf8e365..708538988c9b262ceec7b59ec7eeae2ef28a7804 100644 GIT binary patch literal 3074 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 QnfsOh0ap)4hv+E)0B%Iz#Q*>R literal 3048 zcmVr0h^-OQQ$bf1VJh|4rJNR+KDf)E8PO>~D*4mTK|mG#uJ!&8PyX}- z|NZWdPrmy7cI(A6qGZS;)eg9%gOHJDFIHfa>CNZPChpQiiRYGQKn62%(m6YPe%{jn z>~1PzMf{LTN%rp)iz6DI4nBii+x;DKzd}gNXdt3+M!x>Ktv5{rdp@Qdw#ER|m(P5;#<-iVKy_^GSw&AcY zUcKzdp4QzPa7PkjkGiAl-Ugme!*D?T>o!3PtY_vUZ+6Iy-~kZ8UlnvQ#b5hTBZXB0 ztBtpINqz+XhS=m0#Lkak=rCN8M^DjMHF2pSYZyOL5zSp-BJDS1bCnFisgjsUSY1AqEDqT7&r(EBugF7PaNFRj+GIbDMeNG* z8yt4bK(^>e9;6%S^WNIp8W4tzL8IB_m8Jnt)o@7#Jl8aA?Dls&3vn1t69x1%g+7Rs z!>kfVJr>8gcIJ@5UXUUn{kj?yNmS&4a>M%KMCT5V8{T`c*PQD7aV#RSr}2(?7;^bo zJjg{G$2>|DD`Da-dTf_KBIClhi*!vuVS#@JCs3VcaGld-ia zjf;>WI6PUe^couXJ&yuyRiwI0<7?e65V9vmkZsmi(>L$IwI!4$=sL{%E>-Y;!@loR zsb(o=;$LIh$`8e1w?CNz9P&#WqVGfk@3AW!K-#({pejhsrjRZIGK6r3BZ}B};1EPZGarCIk$qFlum5n4rG_&dJOl&`fHxCqBwuKQJ} zeeu$*tHm_Erw5&(d0LFohan~~4AZ1@0lDk7-dTd5?)og|qHZ+9GD*zCwlT|}=~E*T z)(9_HRG?hwKA$`SBR|c@`-7#CH52~8R{uri$)bKk<@lIRttrnLOqs#9ZT6Oy-~^fo zw5#rc78~`#woQnW^-xP7+G5;ZTEVn`@urn&XNmd85=p~fI)UFXZ)6yHp4AxoX^w76 zp0kL8&%lgz4_RDfb?qVJb9q%kySp31EEIB@^-x*tHN*iVAvo$OYSL;t>bjBEBAKc7 zu`V*9(^K8@dtjTlPR*|L&dxhrH?&mR)mrU&Ah~-rr95gzTEkC|w2S;{$Oj^#zR$gA zF)%c?3WE4On@JC5dOO%xZXoK5&iu;&IK_9N7*K5lybBUTGSVA$po>kRFE^?t%{$3X z7@7mLiAi?~=&{)(1Zz5C+2Pid`m~d)&_eS7UX@3!f4B-beN5-WVW+31>ex1H7kiFe z4p$q97#2W8C#fdj2busp4(4bujLwY?K}rIHyFhJ~u#{9u)SNpe5?__fm|3yWY%ZE7 zokhDqan2C~*a0NFltiO^eTjv+JEqZynNAly>=fqfmO#>xwqN-sv?QpLRHc+_`bXJH~Y*4LM-1cmFz$gvQ1 z*;5#!!Qa>LTEXe8bD%Q=aD?wGr<~3J(7M}e3y5IsOE%1&PmU�!Xt&nHrlWv}{XA$wI^hRD+wohjF6V!t*3CBxxh$rZMSr|y71Q|e; zN(LeKSCNdstF~L959xPA_+$#8C=+qWBivsu@uuE$c3W2~?v<4OoY;x@gofGmn!am; z%1F(&E{fTQ#A|rj)*x9qHMJV+sKdvgIU8P%xmqa8tr8(hpppZ^j7%8h#lTRK;5XdY z$qh+3)X-P8qyUB8i=OCOPWhI0vxX~v26b4Qmii?iDS);3_@UtZlmy!gKT^l7Goncz;*v-FiN z$^_!oyunV=TpD(r^Tm9_lwv!Dd8i{#}UwO(1i-GoW?Yxsz2iI zhV+_k4)6|!;hm~k@0zu4L3KcrL~=T4ok)17OE&02d*mU^rx3U1`&w(}GRKhsm0}XU(#-uf_FvE)Y3jORb zU2NNt1(RWZWx9a$kvwNpQ4t~Fv0E%+DCbH?Cc@)$9Ua`ytl1dG-CplJ3E2lOF~qu3 z+EwY8@)w#Wgcq)B{!*$!zNcm(>x~&p8RY8<3kS4pf*84#c-c6Qtt77a1iB90bQb7p zd29AdVq1`s^tG6!T2Y0t2CJ7dV~ocnr+6iFcQ@YBNQLXm&WFQyC&&9IeW$k6T=Z}K z@akpLEx#!n0JO3BI!DrVqJ|Ox%_sB`Ke&72%=5cAKv4T!l9AT5rK!e!WdUF8qM { test('should be able to into_reference', (t) => { const rules = ['body: { color: red }', 'div: { color: blue }'] - const sheet = new CssStyleSheet(rules) + const sheet = new CssStyleSheet('test.css', rules) t.is(sheet.rules, sheet.rules) t.deepEqual(sheet.rules.getRules(), rules) + t.is(sheet.rules.parentStyleSheet, sheet) + t.is(sheet.rules.name, 'test.css') const anotherStyleSheet = sheet.anotherCssStyleSheet() t.is(anotherStyleSheet.rules, sheet.rules) }) diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 71da4b96..11f42f2a 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -268,10 +268,12 @@ export class JsRemote { export type CSSRuleList = CssRuleList export class CssRuleList { getRules(): Array + get parentStyleSheet(): CSSStyleSheet + get name(): string | null } export type CSSStyleSheet = CssStyleSheet export class CssStyleSheet { - constructor(rules: Array) + constructor(name: string, rules: Array) get rules(): CssRuleList anotherCssStyleSheet(): AnotherCssStyleSheet } diff --git a/examples/napi/src/reference.rs b/examples/napi/src/reference.rs index 9c9f1ee1..c87af198 100644 --- a/examples/napi/src/reference.rs +++ b/examples/napi/src/reference.rs @@ -64,6 +64,7 @@ struct OwnedStyleSheet { #[napi] pub struct CSSRuleList { owned: Rc>, + parent: WeakReference, } #[napi] @@ -72,10 +73,26 @@ impl CSSRuleList { pub fn get_rules(&self) -> Vec { self.owned.borrow().rules.to_vec() } + + #[napi(getter)] + pub fn parent_style_sheet(&self) -> WeakReference { + self.parent.clone() + } + + #[napi(getter)] + pub fn name(&self, env: Env) -> Result> { + Ok( + self + .parent + .upgrade(env)? + .map(|stylesheet| stylesheet.name.clone()), + ) + } } #[napi] pub struct CSSStyleSheet { + name: String, inner: Rc>, rules: Option>, } @@ -97,13 +114,21 @@ impl AnotherCSSStyleSheet { #[napi] impl CSSStyleSheet { #[napi(constructor)] - pub fn new(rules: Vec) -> Result { + pub fn new(name: String, rules: Vec) -> Result { let inner = Rc::new(RefCell::new(OwnedStyleSheet { rules })); - Ok(CSSStyleSheet { inner, rules: None }) + Ok(CSSStyleSheet { + name, + inner, + rules: None, + }) } #[napi(getter)] - pub fn rules(&mut self, env: Env) -> Result> { + pub fn rules( + &mut self, + env: Env, + reference: Reference, + ) -> Result> { if let Some(rules) = &self.rules { return rules.clone(env); } @@ -111,6 +136,7 @@ impl CSSStyleSheet { let rules = CSSRuleList::into_reference( CSSRuleList { owned: self.inner.clone(), + parent: reference.downgrade(), }, env, )?;