Merge pull request #1150 from napi-rs/into-reference
feat(napi): support return Reference on class instance
This commit is contained in:
commit
2c775082cd
8 changed files with 171 additions and 44 deletions
|
@ -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<napi::bindgen_prelude::sys::napi_value> {
|
||||
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<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))));
|
||||
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<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::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<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))));
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
});
|
||||
} 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((
|
||||
|
|
|
@ -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<T> {
|
||||
pub struct Reference<T: 'static> {
|
||||
raw: *mut T,
|
||||
napi_ref: crate::sys::napi_ref,
|
||||
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)]
|
||||
pub fn add_ref(t: *mut c_void, value: RefInformation) {
|
||||
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> {
|
||||
pub fn clone(&self, env: Env) -> Result<Self> {
|
||||
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;
|
||||
|
||||
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 {
|
||||
unsafe { Box::leak(Box::from_raw(self.raw)) }
|
||||
}
|
||||
|
@ -131,7 +142,7 @@ impl<T> DerefMut for Reference<T> {
|
|||
/// ### Experimental feature
|
||||
///
|
||||
/// Create a `SharedReference` from an existed `Reference`.
|
||||
pub struct SharedReference<T, S> {
|
||||
pub struct SharedReference<T: 'static, S: 'static> {
|
||||
raw: *mut S,
|
||||
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;
|
||||
|
||||
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 {
|
||||
unsafe { Box::leak(Box::from_raw(self.raw)) }
|
||||
}
|
||||
|
|
|
@ -271,6 +271,15 @@ Generated by [AVA](https://avajs.dev).
|
|||
export class JsRemote {␊
|
||||
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 const ALIGNMENT: number␊
|
||||
export function xxh3_64(input: Buffer): bigint␊
|
||||
|
|
Binary file not shown.
|
@ -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())
|
||||
|
|
9
examples/napi/index.d.ts
vendored
9
examples/napi/index.d.ts
vendored
|
@ -261,6 +261,15 @@ export class JsRepo {
|
|||
export class JsRemote {
|
||||
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 const ALIGNMENT: number
|
||||
export function xxh3_64(input: Buffer): bigint
|
||||
|
|
|
@ -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<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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue