feat(napi): support return Reference on class instance

This commit is contained in:
LongYinan 2022-04-26 18:06:52 +08:00
parent 8ae06dccc8
commit 878b843f29
No known key found for this signature in database
GPG key ID: C3666B7FC82ADAD7
8 changed files with 171 additions and 44 deletions

View file

@ -216,19 +216,50 @@ impl NapiStruct {
fn gen_to_napi_value_ctor_impl_for_non_default_constructor_struct(&self) -> TokenStream { fn gen_to_napi_value_ctor_impl_for_non_default_constructor_struct(&self) -> TokenStream {
let name = &self.name; 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! { quote! {
impl napi::bindgen_prelude::ToNapiValue for #name { impl napi::bindgen_prelude::ToNapiValue for #name {
unsafe fn to_napi_value( unsafe fn to_napi_value(
env: napi::sys::napi_env, val: #name env: napi::sys::napi_env,
val: #name
) -> napi::Result<napi::bindgen_prelude::sys::napi_value> { ) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) { if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
let mut ctor = std::ptr::null_mut(); let wrapped_value = Box::into_raw(Box::new(val)) as *mut std::ffi::c_void;
#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_raw))
)
}
}
}
impl #name {
pub fn into_reference(val: #name, env: napi::Env) -> napi::Result<napi::bindgen_prelude::Reference<#name>> {
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<napi::bindgen_prelude::sys::napi_value> {
let mut ctor = std::ptr::null_mut();
napi::check_status!( napi::check_status!(
napi::sys::napi_get_reference_value(env, ctor_ref, &mut ctor), napi::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
"Failed to get constructor of class `{}`", "Failed to get constructor of class `{}`",
#js_name_str #js_name_raw
)?; )?;
let mut result = std::ptr::null_mut(); let mut result = std::ptr::null_mut();
@ -236,9 +267,8 @@ impl NapiStruct {
napi::check_status!( napi::check_status!(
napi::sys::napi_new_instance(env, ctor, 0, std::ptr::null_mut(), &mut result), napi::sys::napi_new_instance(env, ctor, 0, std::ptr::null_mut(), &mut result),
"Failed to construct class `{}`", "Failed to construct class `{}`",
#js_name_str #js_name_raw
)?; )?;
let wrapped_value = Box::into_raw(Box::new(val)) as *mut std::ffi::c_void;
let mut object_ref = std::ptr::null_mut(); let mut object_ref = std::ptr::null_mut();
let initial_finalize: Box<dyn FnOnce()> = Box::new(|| {}); let initial_finalize: Box<dyn FnOnce()> = Box::new(|| {});
let finalize_callbacks_ptr = std::rc::Rc::into_raw(std::rc::Rc::new(std::cell::Cell::new(Box::into_raw(initial_finalize)))); let finalize_callbacks_ptr = std::rc::Rc::into_raw(std::rc::Rc::new(std::cell::Cell::new(Box::into_raw(initial_finalize))));
@ -252,16 +282,11 @@ impl NapiStruct {
&mut object_ref, &mut object_ref,
), ),
"Failed to wrap native object of class `{}`", "Failed to wrap native object of class `{}`",
#js_name_str #js_name_raw
)?; )?;
napi::bindgen_prelude::Reference::<#name>::add_ref(wrapped_value, (wrapped_value, object_ref, finalize_callbacks_ptr)); 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)); napi::bindgen_prelude::___CALL_FROM_FACTORY.with(|inner| inner.store(false, std::sync::atomic::Ordering::Relaxed));
Ok(result) Ok(result)
} else {
Err(napi::bindgen_prelude::Error::new(
napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_str))
)
}
} }
} }
} }

View file

@ -278,6 +278,15 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (S
Some(("Promise<unknown>".to_owned(), false)) Some(("Promise<unknown>".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()) { } else if let Some(&known_ty) = KNOWN_TYPES.get(rust_ty.as_str()) {
if known_ty.contains("{}") { if known_ty.contains("{}") {
ts_ty = Some(( ts_ty = Some((

View file

@ -4,7 +4,7 @@ use std::ffi::c_void;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::rc::Rc; 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 = ( type RefInformation = (
*mut c_void, *mut c_void,
@ -20,7 +20,7 @@ thread_local! {
/// ///
/// Create a `reference` from `Class` instance. /// Create a `reference` from `Class` instance.
/// Unref the `Reference` when the `Reference` is dropped. /// Unref the `Reference` when the `Reference` is dropped.
pub struct Reference<T> { pub struct Reference<T: 'static> {
raw: *mut T, raw: *mut T,
napi_ref: crate::sys::napi_ref, napi_ref: crate::sys::napi_ref,
env: *mut c_void, env: *mut c_void,
@ -42,7 +42,7 @@ impl<T> Drop for Reference<T> {
} }
} }
impl<T> Reference<T> { impl<T: 'static> Reference<T> {
#[doc(hidden)] #[doc(hidden)]
pub fn add_ref(t: *mut c_void, value: RefInformation) { pub fn add_ref(t: *mut c_void, value: RefInformation) {
REFERENCE_MAP.with(|map| { REFERENCE_MAP.with(|map| {
@ -78,6 +78,17 @@ impl<T> Reference<T> {
} }
} }
impl<T: 'static> ToNapiValue for Reference<T> {
unsafe fn to_napi_value(env: crate::sys::napi_env, val: Self) -> Result<crate::sys::napi_value> {
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<T: 'static> Reference<T> { impl<T: 'static> Reference<T> {
pub fn clone(&self, env: Env) -> Result<Self> { pub fn clone(&self, env: Env) -> Result<Self> {
let mut ref_count = 0; let mut ref_count = 0;
@ -114,7 +125,7 @@ impl<T: 'static> Reference<T> {
} }
} }
impl<T> Deref for Reference<T> { impl<T: 'static> Deref for Reference<T> {
type Target = T; type Target = T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@ -122,7 +133,7 @@ impl<T> Deref for Reference<T> {
} }
} }
impl<T> DerefMut for Reference<T> { impl<T: 'static> DerefMut for Reference<T> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { Box::leak(Box::from_raw(self.raw)) } unsafe { Box::leak(Box::from_raw(self.raw)) }
} }
@ -131,7 +142,7 @@ impl<T> DerefMut for Reference<T> {
/// ### Experimental feature /// ### Experimental feature
/// ///
/// Create a `SharedReference` from an existed `Reference`. /// Create a `SharedReference` from an existed `Reference`.
pub struct SharedReference<T, S> { pub struct SharedReference<T: 'static, S: 'static> {
raw: *mut S, raw: *mut S,
owner: Reference<T>, owner: Reference<T>,
} }
@ -168,7 +179,18 @@ impl<T: 'static, S: 'static> SharedReference<T, S> {
} }
} }
impl<T, S> Deref for SharedReference<T, S> { impl<T: 'static, S: 'static> ToNapiValue for SharedReference<T, S> {
unsafe fn to_napi_value(env: crate::sys::napi_env, val: Self) -> Result<crate::sys::napi_value> {
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<T: 'static, S: 'static> Deref for SharedReference<T, S> {
type Target = S; type Target = S;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@ -176,7 +198,7 @@ impl<T, S> Deref for SharedReference<T, S> {
} }
} }
impl<T, S> DerefMut for SharedReference<T, S> { impl<T: 'static, S: 'static> DerefMut for SharedReference<T, S> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { Box::leak(Box::from_raw(self.raw)) } unsafe { Box::leak(Box::from_raw(self.raw)) }
} }

View file

@ -271,6 +271,15 @@ Generated by [AVA](https://avajs.dev).
export class JsRemote {␊ export class JsRemote {␊
name(): string␊ name(): string␊
}␊ }␊
export type CSSRuleList = CssRuleList␊
export class CssRuleList {␊
getRules(): Array<string>
}␊
export type CSSStyleSheet = CssStyleSheet␊
export class CssStyleSheet {␊
constructor(rules: Array<string>)␊
get rules(): CssRuleList␊
}␊
export namespace xxh3 {␊ export namespace xxh3 {␊
export const ALIGNMENT: number␊ export const ALIGNMENT: number␊
export function xxh3_64(input: Buffer): bigint␊ export function xxh3_64(input: Buffer): bigint␊

View file

@ -90,6 +90,7 @@ import {
chronoDateAdd1Minute, chronoDateAdd1Minute,
bufferPassThrough, bufferPassThrough,
JsRepo, JsRepo,
CssStyleSheet,
asyncReduceBuffer, asyncReduceBuffer,
callbackReturnPromise, callbackReturnPromise,
} from '../' } from '../'
@ -199,6 +200,13 @@ test('should be able to create object reference and shared reference', (t) => {
t.is(repo.remote().name(), 'origin') 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) => { test('callback', (t) => {
getCwd((cwd) => { getCwd((cwd) => {
t.is(cwd, process.cwd()) t.is(cwd, process.cwd())

View file

@ -261,6 +261,15 @@ export class JsRepo {
export class JsRemote { export class JsRemote {
name(): string name(): string
} }
export type CSSRuleList = CssRuleList
export class CssRuleList {
getRules(): Array<string>
}
export type CSSStyleSheet = CssStyleSheet
export class CssStyleSheet {
constructor(rules: Array<string>)
get rules(): CssRuleList
}
export namespace xxh3 { export namespace xxh3 {
export const ALIGNMENT: number export const ALIGNMENT: number
export function xxh3_64(input: Buffer): bigint export function xxh3_64(input: Buffer): bigint

View file

@ -1,3 +1,5 @@
use std::{cell::RefCell, rc::Rc};
use napi::bindgen_prelude::*; use napi::bindgen_prelude::*;
pub struct Repository { pub struct Repository {
@ -54,3 +56,46 @@ impl JsRemote {
self.inner.name() self.inner.name()
} }
} }
struct OwnedStyleSheet {
rules: Vec<String>,
}
#[napi]
pub struct CSSRuleList {
owned: Rc<RefCell<OwnedStyleSheet>>,
}
#[napi]
impl CSSRuleList {
#[napi]
pub fn get_rules(&self) -> Vec<String> {
self.owned.borrow().rules.to_vec()
}
}
#[napi]
pub struct CSSStyleSheet {
inner: Rc<RefCell<OwnedStyleSheet>>,
rules: Reference<CSSRuleList>,
}
#[napi]
impl CSSStyleSheet {
#[napi(constructor)]
pub fn new(env: Env, rules: Vec<String>) -> Result<Self> {
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<Reference<CSSRuleList>> {
self.rules.clone(env)
}
}