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,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))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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((
|
||||||
|
|
|
@ -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)) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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␊
|
||||||
|
|
Binary file not shown.
|
@ -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())
|
||||||
|
|
9
examples/napi/index.d.ts
vendored
9
examples/napi/index.d.ts
vendored
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue