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 a3b5cf47..70853898 100644 Binary files a/examples/napi/__test__/typegen.spec.ts.snap and b/examples/napi/__test__/typegen.spec.ts.snap differ diff --git a/examples/napi/__test__/values.spec.ts b/examples/napi/__test__/values.spec.ts index 6dad5eec..e421c980 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -202,9 +202,11 @@ test('should be able to create object reference and shared reference', (t) => { 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, )?;