diff --git a/crates/backend/src/codegen/struct.rs b/crates/backend/src/codegen/struct.rs index 36046126..9fd954d7 100644 --- a/crates/backend/src/codegen/struct.rs +++ b/crates/backend/src/codegen/struct.rs @@ -216,54 +216,79 @@ impl NapiStruct { fn gen_to_napi_value_ctor_impl_for_non_default_constructor_struct(&self) -> TokenStream { let name = &self.name; - let js_name_str = format!("{}\0", &self.js_name); + let js_name_raw = &self.js_name; + let js_name_str = format!("{}\0", js_name_raw); quote! { impl napi::bindgen_prelude::ToNapiValue for #name { unsafe fn to_napi_value( - env: napi::sys::napi_env, val: #name + env: napi::sys::napi_env, + val: #name ) -> napi::Result { if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) { - let mut ctor = std::ptr::null_mut(); - - napi::check_status!( - napi::sys::napi_get_reference_value(env, ctor_ref, &mut ctor), - "Failed to get constructor of class `{}`", - #js_name_str - )?; - - let mut result = std::ptr::null_mut(); - napi::bindgen_prelude::___CALL_FROM_FACTORY.with(|inner| inner.store(true, std::sync::atomic::Ordering::Relaxed)); - napi::check_status!( - napi::sys::napi_new_instance(env, ctor, 0, std::ptr::null_mut(), &mut result), - "Failed to construct class `{}`", - #js_name_str - )?; let wrapped_value = Box::into_raw(Box::new(val)) as *mut std::ffi::c_void; - let mut object_ref = std::ptr::null_mut(); - let initial_finalize: Box = Box::new(|| {}); - let finalize_callbacks_ptr = std::rc::Rc::into_raw(std::rc::Rc::new(std::cell::Cell::new(Box::into_raw(initial_finalize)))); - napi::check_status!( - napi::sys::napi_wrap( - env, - result, - wrapped_value, - Some(napi::bindgen_prelude::raw_finalize_unchecked::<#name>), - std::ptr::null_mut(), - &mut object_ref, - ), - "Failed to wrap native object of class `{}`", - #js_name_str - )?; - napi::bindgen_prelude::Reference::<#name>::add_ref(wrapped_value, (wrapped_value, object_ref, finalize_callbacks_ptr)); - napi::bindgen_prelude::___CALL_FROM_FACTORY.with(|inner| inner.store(false, std::sync::atomic::Ordering::Relaxed)); - Ok(result) + #name::new_instance(env, wrapped_value, ctor_ref) } else { Err(napi::bindgen_prelude::Error::new( - napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_str)) + napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw)) ) } } } + + impl #name { + pub fn into_reference(val: #name, env: napi::Env) -> napi::Result> { + if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) { + unsafe { + let wrapped_value = Box::into_raw(Box::new(val)) as *mut std::ffi::c_void; + #name::new_instance(env.raw(), wrapped_value, ctor_ref)?; + napi::bindgen_prelude::Reference::<#name>::from_value_ptr(wrapped_value, env.raw()) + } + } else { + Err(napi::bindgen_prelude::Error::new( + napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw)) + ) + } + } + + unsafe fn new_instance( + env: napi::sys::napi_env, + wrapped_value: *mut std::ffi::c_void, + ctor_ref: napi::sys::napi_ref, + ) -> napi::Result { + let mut ctor = std::ptr::null_mut(); + napi::check_status!( + napi::sys::napi_get_reference_value(env, ctor_ref, &mut ctor), + "Failed to get constructor of class `{}`", + #js_name_raw + )?; + + let mut result = std::ptr::null_mut(); + napi::bindgen_prelude::___CALL_FROM_FACTORY.with(|inner| inner.store(true, std::sync::atomic::Ordering::Relaxed)); + napi::check_status!( + napi::sys::napi_new_instance(env, ctor, 0, std::ptr::null_mut(), &mut result), + "Failed to construct class `{}`", + #js_name_raw + )?; + let mut object_ref = std::ptr::null_mut(); + let initial_finalize: Box = Box::new(|| {}); + let finalize_callbacks_ptr = std::rc::Rc::into_raw(std::rc::Rc::new(std::cell::Cell::new(Box::into_raw(initial_finalize)))); + napi::check_status!( + napi::sys::napi_wrap( + env, + result, + wrapped_value, + Some(napi::bindgen_prelude::raw_finalize_unchecked::<#name>), + std::ptr::null_mut(), + &mut object_ref, + ), + "Failed to wrap native object of class `{}`", + #js_name_raw + )?; + napi::bindgen_prelude::Reference::<#name>::add_ref(wrapped_value, (wrapped_value, object_ref, finalize_callbacks_ptr)); + napi::bindgen_prelude::___CALL_FROM_FACTORY.with(|inner| inner.store(false, std::sync::atomic::Ordering::Relaxed)); + Ok(result) + } + } } } diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index 916fbd37..ba54b16d 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -278,6 +278,15 @@ 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" { + ts_ty = r#struct::TASK_STRUCTS.with(|t| { + let (output_type, _) = args.first().unwrap().to_owned(); + if let Some(o) = t.borrow().get(&output_type) { + Some((o.to_owned(), false)) + } else { + Some((output_type, false)) + } + }); } else if let Some(&known_ty) = KNOWN_TYPES.get(rust_ty.as_str()) { if known_ty.contains("{}") { ts_ty = Some(( 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 21647eb4..6443d810 100644 --- a/crates/napi/src/bindgen_runtime/js_values/value_ref.rs +++ b/crates/napi/src/bindgen_runtime/js_values/value_ref.rs @@ -4,7 +4,7 @@ use std::ffi::c_void; use std::ops::{Deref, DerefMut}; use std::rc::Rc; -use crate::{check_status, Env, Error, Result, Status}; +use crate::{bindgen_runtime::ToNapiValue, check_status, Env, Error, Result, Status}; type RefInformation = ( *mut c_void, @@ -20,7 +20,7 @@ thread_local! { /// /// Create a `reference` from `Class` instance. /// Unref the `Reference` when the `Reference` is dropped. -pub struct Reference { +pub struct Reference { raw: *mut T, napi_ref: crate::sys::napi_ref, env: *mut c_void, @@ -42,7 +42,7 @@ impl Drop for Reference { } } -impl Reference { +impl Reference { #[doc(hidden)] pub fn add_ref(t: *mut c_void, value: RefInformation) { REFERENCE_MAP.with(|map| { @@ -78,6 +78,17 @@ impl Reference { } } +impl ToNapiValue for Reference { + 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 Reference { pub fn clone(&self, env: Env) -> Result { let mut ref_count = 0; @@ -114,7 +125,7 @@ impl Reference { } } -impl Deref for Reference { +impl Deref for Reference { type Target = T; fn deref(&self) -> &Self::Target { @@ -122,7 +133,7 @@ impl Deref for Reference { } } -impl DerefMut for Reference { +impl DerefMut for Reference { fn deref_mut(&mut self) -> &mut Self::Target { unsafe { Box::leak(Box::from_raw(self.raw)) } } @@ -131,7 +142,7 @@ impl DerefMut for Reference { /// ### Experimental feature /// /// Create a `SharedReference` from an existed `Reference`. -pub struct SharedReference { +pub struct SharedReference { raw: *mut S, owner: Reference, } @@ -168,7 +179,18 @@ impl SharedReference { } } -impl Deref for SharedReference { +impl ToNapiValue for SharedReference { + 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.owner.napi_ref, &mut result) }, + "Failed to get reference value" + )?; + Ok(result) + } +} + +impl Deref for SharedReference { type Target = S; fn deref(&self) -> &Self::Target { @@ -176,7 +198,7 @@ impl Deref for SharedReference { } } -impl DerefMut for SharedReference { +impl DerefMut for SharedReference { fn deref_mut(&mut self) -> &mut Self::Target { unsafe { Box::leak(Box::from_raw(self.raw)) } } diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index a1ef7ede..0eeafa58 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -271,6 +271,15 @@ Generated by [AVA](https://avajs.dev). export class JsRemote {␊ name(): string␊ }␊ + export type CSSRuleList = CssRuleList␊ + export class CssRuleList {␊ + getRules(): Array␊ + }␊ + export type CSSStyleSheet = CssStyleSheet␊ + export class CssStyleSheet {␊ + constructor(rules: Array)␊ + get rules(): CssRuleList␊ + }␊ export namespace xxh3 {␊ export const ALIGNMENT: number␊ export function xxh3_64(input: Buffer): bigint␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 2fedede7..4c495ff7 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 cac38f37..fcab599d 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -90,6 +90,7 @@ import { chronoDateAdd1Minute, bufferPassThrough, JsRepo, + CssStyleSheet, asyncReduceBuffer, callbackReturnPromise, } from '../' @@ -199,6 +200,13 @@ test('should be able to create object reference and shared reference', (t) => { t.is(repo.remote().name(), 'origin') }) +test('should be able to into_reference', (t) => { + const rules = ['body: { color: red }', 'div: { color: blue }'] + const sheet = new CssStyleSheet(rules) + t.is(sheet.rules, sheet.rules) + t.deepEqual(sheet.rules.getRules(), rules) +}) + test('callback', (t) => { getCwd((cwd) => { t.is(cwd, process.cwd()) diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 12fdd1a9..3269086e 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -261,6 +261,15 @@ export class JsRepo { export class JsRemote { name(): string } +export type CSSRuleList = CssRuleList +export class CssRuleList { + getRules(): Array +} +export type CSSStyleSheet = CssStyleSheet +export class CssStyleSheet { + constructor(rules: Array) + get rules(): CssRuleList +} export namespace xxh3 { export const ALIGNMENT: number export function xxh3_64(input: Buffer): bigint diff --git a/examples/napi/src/reference.rs b/examples/napi/src/reference.rs index 887ecbce..ace4f229 100644 --- a/examples/napi/src/reference.rs +++ b/examples/napi/src/reference.rs @@ -1,3 +1,5 @@ +use std::{cell::RefCell, rc::Rc}; + use napi::bindgen_prelude::*; pub struct Repository { @@ -54,3 +56,46 @@ impl JsRemote { self.inner.name() } } + +struct OwnedStyleSheet { + rules: Vec, +} + +#[napi] +pub struct CSSRuleList { + owned: Rc>, +} + +#[napi] +impl CSSRuleList { + #[napi] + pub fn get_rules(&self) -> Vec { + self.owned.borrow().rules.to_vec() + } +} + +#[napi] +pub struct CSSStyleSheet { + inner: Rc>, + rules: Reference, +} + +#[napi] +impl CSSStyleSheet { + #[napi(constructor)] + pub fn new(env: Env, rules: Vec) -> Result { + let inner = Rc::new(RefCell::new(OwnedStyleSheet { rules })); + let rules = CSSRuleList::into_reference( + CSSRuleList { + owned: inner.clone(), + }, + env, + )?; + Ok(CSSStyleSheet { inner, rules }) + } + + #[napi(getter)] + pub fn rules(&self, env: Env) -> Result> { + self.rules.clone(env) + } +}