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:
Louis 2024-02-22 15:37:50 +01:00 committed by GitHub
parent 17f509f96e
commit 43415251b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 49 additions and 9 deletions

View file

@ -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"
)
}
}
}

View file

@ -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;

View file

@ -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"

View file

@ -174,6 +174,7 @@ Generated by [AVA](https://avajs.dev).
}␊
export class JsRemote {␊
constructor(repo: JsRepo)␊
name(): string␊
}␊

View file

@ -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) => {

View file

@ -164,6 +164,7 @@ export class JsClassForEither {
}
export class JsRemote {
constructor(repo: JsRepo)
name(): string
}

View file

@ -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()