feat(napi): allow Reference as a class method param (#1966)
As of before this commit, there was a lock in the codegen preventing Reference from being used as a function argument outside of a Reference<Self>. This changes it, allowing Reference of any class to be added as a class method argument anywhere. It has the same limitations as reference, as in it requires the class to have been created with a factory or constructor. This change implements FromNapiValue on Reference, which will unwrap the class and call the existing from_value_ptr method. It also updated typegen so that we only emit the reference type if we're in an impl block that doesn't match the Reference we're getting. This ensures that typegen works as expected with the previous behaviour.
This commit is contained in:
parent
17f509f96e
commit
43415251b8
9 changed files with 49 additions and 9 deletions
|
@ -272,11 +272,6 @@ impl NapiFn {
|
|||
});
|
||||
skipped_arg_count += 1;
|
||||
continue;
|
||||
} else {
|
||||
bail_span!(
|
||||
p,
|
||||
"The `T` of Reference<T> type must be the same as the class type"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,8 +141,25 @@ impl NapiFn {
|
|||
if let syn::Type::Path(path) = path.ty.as_ref() {
|
||||
if let Some(PathSegment { ident, arguments }) = path.path.segments.last() {
|
||||
if ident == "Reference" || ident == "WeakReference" {
|
||||
if let Some(parent) = &self.parent {
|
||||
if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
|
||||
args: angle_bracketed_args,
|
||||
..
|
||||
}) = arguments
|
||||
{
|
||||
if let Some(syn::GenericArgument::Type(ty)) = angle_bracketed_args.first() {
|
||||
if let syn::Type::Path(syn::TypePath { path, .. }) = ty {
|
||||
if let Some(segment) = path.segments.first() {
|
||||
if segment.ident.to_string() == parent.to_string() {
|
||||
// If we have a Reference<A> in an impl A block, it shouldn't be an arg
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ident == "This" || ident == "this" {
|
||||
if self.kind != FnKind::Normal {
|
||||
return None;
|
||||
|
|
|
@ -2,8 +2,10 @@ use std::cell::{Cell, RefCell};
|
|||
use std::collections::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ptr;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use crate::bindgen_prelude::FromNapiValue;
|
||||
use crate::{bindgen_runtime::ToNapiValue, check_status, Env, Error, Result, Status};
|
||||
|
||||
type RefInformation = (
|
||||
|
@ -94,7 +96,7 @@ impl<T: 'static> 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();
|
||||
let mut result = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe { crate::sys::napi_get_reference_value(env, val.napi_ref, &mut result) },
|
||||
"Failed to get reference value"
|
||||
|
@ -103,6 +105,21 @@ impl<T: 'static> ToNapiValue for Reference<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromNapiValue for Reference<T> {
|
||||
unsafe fn from_napi_value(
|
||||
env: crate::sys::napi_env,
|
||||
napi_val: crate::sys::napi_value,
|
||||
) -> Result<Self> {
|
||||
let mut value = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe { crate::sys::napi_unwrap(env, napi_val, &mut value) },
|
||||
"Unwrap value [{}] from class Reference failed",
|
||||
std::any::type_name::<T>(),
|
||||
)?;
|
||||
unsafe { Reference::from_value_ptr(value.cast(), env) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Reference<T> {
|
||||
pub fn clone(&self, env: Env) -> Result<Self> {
|
||||
let mut ref_count = 0;
|
||||
|
@ -188,7 +205,7 @@ impl<T: 'static> ToNapiValue for WeakReference<T> {
|
|||
),
|
||||
));
|
||||
};
|
||||
let mut result = std::ptr::null_mut();
|
||||
let mut result = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe { crate::sys::napi_get_reference_value(env, val.napi_ref, &mut result) },
|
||||
"Failed to get reference value"
|
||||
|
@ -276,7 +293,7 @@ impl<T: 'static, S: 'static> 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();
|
||||
let mut result = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe { crate::sys::napi_get_reference_value(env, val.owner.napi_ref, &mut result) },
|
||||
"Failed to get reference value"
|
||||
|
|
|
@ -174,6 +174,7 @@ Generated by [AVA](https://avajs.dev).
|
|||
}␊
|
||||
␊
|
||||
export class JsRemote {␊
|
||||
constructor(repo: JsRepo)␊
|
||||
name(): string␊
|
||||
}␊
|
||||
␊
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -134,6 +134,7 @@ const {
|
|||
bufferPassThrough,
|
||||
arrayBufferPassThrough,
|
||||
JsRepo,
|
||||
JsRemote,
|
||||
CssStyleSheet,
|
||||
CatchOnConstructor,
|
||||
CatchOnConstructor2,
|
||||
|
@ -409,6 +410,7 @@ test('custom finalize class', (t) => {
|
|||
test('should be able to create object reference and shared reference', (t) => {
|
||||
const repo = new JsRepo('.')
|
||||
t.is(repo.remote().name(), 'origin')
|
||||
t.is(new JsRemote(repo).name(), 'origin')
|
||||
})
|
||||
|
||||
test('should be able to into_reference', (t) => {
|
||||
|
|
1
examples/napi/index.d.ts
vendored
1
examples/napi/index.d.ts
vendored
|
@ -164,6 +164,7 @@ export class JsClassForEither {
|
|||
}
|
||||
|
||||
export class JsRemote {
|
||||
constructor(repo: JsRepo)
|
||||
name(): string
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,13 @@ pub struct JsRemote {
|
|||
|
||||
#[napi]
|
||||
impl JsRemote {
|
||||
#[napi(constructor)]
|
||||
pub fn new(repo: Reference<JsRepo>, env: Env) -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: repo.share_with(env, |repo| Ok(repo.inner.remote()))?,
|
||||
})
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn name(&self) -> String {
|
||||
self.inner.name()
|
||||
|
|
Loading…
Reference in a new issue