From 87fd74cbb62c437c09c1dfc298002e01b262425c Mon Sep 17 00:00:00 2001 From: LongYinan Date: Tue, 5 Jul 2022 23:09:40 +0800 Subject: [PATCH] feat(napi-derive): allow injecting this in class method --- crates/backend/src/codegen/fn.rs | 58 +++++++++++------- crates/backend/src/typegen.rs | 1 + .../src/bindgen_runtime/js_values/class.rs | 4 +- examples/napi/__test__/typegen.spec.ts.md | 1 + examples/napi/__test__/typegen.spec.ts.snap | Bin 3346 -> 3357 bytes examples/napi/__test__/values.spec.ts | 3 + examples/napi/index.d.ts | 1 + examples/napi/src/class.rs | 7 ++- 8 files changed, 51 insertions(+), 24 deletions(-) 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 3a445810b61f6f906c7d990151d8718719f337ba..f47907357b3740f2a741f87d9c616383348a272b 100644 GIT binary patch literal 3357 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 literal 3346 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 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")]