diff --git a/crates/backend/src/codegen/fn.rs b/crates/backend/src/codegen/fn.rs index e66efee3..d1580709 100644 --- a/crates/backend/src/codegen/fn.rs +++ b/crates/backend/src/codegen/fn.rs @@ -12,7 +12,7 @@ impl TryToTokens for NapiFn { let intermediate_ident = get_intermediate_ident(&name_str); let args_len = self.args.len(); - let (arg_conversions, arg_names) = self.gen_arg_conversions(); + let (arg_conversions, arg_names) = self.gen_arg_conversions()?; let receiver = self.gen_fn_receiver(); let receiver_ret_name = Ident::new("_ret", Span::call_site()); let ret = self.gen_fn_return(&receiver_ret_name); @@ -93,7 +93,7 @@ impl TryToTokens for NapiFn { } impl NapiFn { - fn gen_arg_conversions(&self) -> (Vec, Vec) { + fn gen_arg_conversions(&self) -> BindgenResult<(Vec, Vec)> { let mut arg_conversions = vec![]; let mut args = vec![]; @@ -117,7 +117,7 @@ impl NapiFn { } let mut skipped_arg_count = 0; - self.args.iter().enumerate().for_each(|(i, arg)| { + for (i, arg) in self.args.iter().enumerate() { let i = i - skipped_arg_count; let ident = Ident::new(&format!("arg{}", i), Span::call_site()); @@ -127,27 +127,41 @@ impl NapiFn { args.push(quote! { napi::bindgen_prelude::Env::from(env) }); skipped_arg_count += 1; } else { - if self.parent.is_some() { - if let syn::Type::Path(path) = path.ty.as_ref() { - if let Some(p) = path.path.segments.last() { - if p.ident == "Reference" { - if let syn::PathArguments::AngleBracketed( - syn::AngleBracketedGenericArguments { args: angle_bracketed_args, .. }, - ) = &p.arguments + let is_in_class = self.parent.is_some(); + if let syn::Type::Path(path) = path.ty.as_ref() { + if let Some(p) = path.path.segments.last() { + if p.ident == "Reference" { + if !is_in_class { + bail_span!(p, "`Reference` is only allowed in class methods"); + } + if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + args: angle_bracketed_args, + .. + }) = &p.arguments + { + if let Some(syn::GenericArgument::Type(syn::Type::Path(path))) = + angle_bracketed_args.first() { - if let Some(syn::GenericArgument::Type(syn::Type::Path(path))) = angle_bracketed_args.first() { - if let Some(p) = path.path.segments.first() { - if p.ident == *self.parent.as_ref().unwrap() { - args.push( - quote! { napi::bindgen_prelude::Reference::from_value_ptr(this_ptr as *mut std::ffi::c_void, env)? }, - ); - skipped_arg_count += 1; - return; - } - } + if let Some(p) = path.path.segments.first() { + if p.ident == *self.parent.as_ref().unwrap() { + args.push( + quote! { napi::bindgen_prelude::Reference::from_value_ptr(this_ptr as *mut std::ffi::c_void, env)? }, + ); + skipped_arg_count += 1; + continue; + } } } } + } else if p.ident == "This" { + if !is_in_class { + bail_span!(p, "`This` is only allowed in class methods"); + } + args.push( + quote! { ::from_raw_unchecked(env, cb.this) }, + ); + skipped_arg_count += 1; + continue; } } } @@ -160,9 +174,9 @@ impl NapiFn { args.push(quote! { #ident }); } } - }); + } - (arg_conversions, args) + Ok((arg_conversions, args)) } fn gen_ty_arg_conversion( diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index 81bd9108..344351d7 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -214,6 +214,7 @@ static KNOWN_TYPES: Lazy> = Lazy::new(|| { ("unknown", "unknown"), ("Unknown", "unknown"), ("JsUnknown", "unknown"), + ("This", "this") ]); map diff --git a/crates/napi/src/bindgen_runtime/js_values/class.rs b/crates/napi/src/bindgen_runtime/js_values/class.rs index c2dab55c..2b700fed 100644 --- a/crates/napi/src/bindgen_runtime/js_values/class.rs +++ b/crates/napi/src/bindgen_runtime/js_values/class.rs @@ -2,7 +2,9 @@ use std::any::type_name; use std::ops::{Deref, DerefMut}; use std::ptr; -use crate::{bindgen_runtime::FromNapiValue, check_status, sys, NapiRaw}; +use crate::{bindgen_runtime::FromNapiValue, check_status, sys, JsObject, NapiRaw}; + +pub type This = JsObject; pub struct ClassInstance { pub value: sys::napi_value, diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 5702f86f..91b27604 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -254,6 +254,7 @@ Generated by [AVA](https://avajs.dev). static newRaph(): NinjaTurtle␊ getMaskColor(): string␊ getName(): string␊ + returnThis(this: this): this␊ }␊ export type JsAssets = Assets␊ export class Assets {␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 3a445810..f4790735 100644 Binary files a/examples/napi/__test__/typegen.spec.ts.snap and b/examples/napi/__test__/typegen.spec.ts.snap differ diff --git a/examples/napi/__test__/values.spec.ts b/examples/napi/__test__/values.spec.ts index e04a1d76..0661928b 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -21,6 +21,7 @@ import { getCwd, Animal, Kind, + NinjaTurtle, ClassWithFactory, CustomNumEnum, Context, @@ -173,6 +174,8 @@ test('class', (t) => { t.is(dog.type, Kind.Cat) const assets = new Assets() t.is(assets.get(1)?.filePath, 1) + const turtle = NinjaTurtle.newRaph() + t.is(turtle.returnThis(), turtle) }) test('class factory', (t) => { diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 8c4bf1b7..c6a3ae9d 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -244,6 +244,7 @@ export class NinjaTurtle { static newRaph(): NinjaTurtle getMaskColor(): string getName(): string + returnThis(this: this): this } export type JsAssets = Assets export class Assets { diff --git a/examples/napi/src/class.rs b/examples/napi/src/class.rs index 9d3c937e..3cdf682f 100644 --- a/examples/napi/src/class.rs +++ b/examples/napi/src/class.rs @@ -1,5 +1,5 @@ use napi::{ - bindgen_prelude::{Buffer, ClassInstance}, + bindgen_prelude::{Buffer, ClassInstance, This}, Env, Result, }; @@ -229,6 +229,11 @@ impl NinjaTurtle { pub fn get_name(&self) -> &str { self.name.as_str() } + + #[napi] + pub fn return_this(&self, this: This) -> This { + this + } } #[napi(js_name = "Assets")]