From 0ef482c6ca87390267a6fd1d31f02a390da1f5f2 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Wed, 17 Aug 2022 17:49:22 +0800 Subject: [PATCH] feat(napi-derive): support inject This into raw function --- crates/backend/src/codegen/fn.rs | 59 +++++++++++++++++- crates/backend/src/lib.rs | 37 +++++++++++ crates/backend/src/typegen.rs | 35 +---------- crates/backend/src/typegen/fn.rs | 34 +++++++++- .../src/bindgen_runtime/js_values/class.rs | 5 +- .../src/bindgen_runtime/js_values/number.rs | 2 +- examples/napi/__test__/typegen.spec.ts.md | 5 ++ examples/napi/__test__/typegen.spec.ts.snap | Bin 3531 -> 3557 bytes examples/napi/__test__/values.spec.ts | 11 +++- examples/napi/index.d.ts | 5 ++ examples/napi/src/class.rs | 10 +++ 11 files changed, 160 insertions(+), 43 deletions(-) diff --git a/crates/backend/src/codegen/fn.rs b/crates/backend/src/codegen/fn.rs index 4f7c1604..35e2df94 100644 --- a/crates/backend/src/codegen/fn.rs +++ b/crates/backend/src/codegen/fn.rs @@ -156,8 +156,63 @@ impl NapiFn { } } } else if p.ident == "This" { - if !is_in_class { - bail_span!(p, "`This` 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(generic_type)) = + angle_bracketed_args.first() + { + if let syn::Type::Path(syn::TypePath { + path: syn::Path { segments, .. }, + .. + }) = generic_type + { + if let Some(syn::PathSegment { ident, .. }) = segments.first() { + if let Some((primitive_type, _)) = + crate::PRIMITIVE_TYPES.iter().find(|(p, _)| ident == *p) + { + bail_span!( + ident, + "This type must not be {} \nthis in JavaScript function must be `Object` type or `undefined`", + primitive_type + ); + } + args.push( + quote! { + { + <#ident as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.this)? + } + }, + ); + skipped_arg_count += 1; + continue; + } + } else if let syn::Type::Reference(syn::TypeReference { + elem, + mutability, + .. + }) = generic_type + { + if let syn::Type::Path(syn::TypePath { + path: syn::Path { segments, .. }, + .. + }) = elem.as_ref() + { + if let Some(syn::PathSegment { ident, .. }) = segments.first() { + let token = if mutability.is_some() { + quote! { <#ident as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.this)? } + } else { + quote! { <#ident as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.this)? } + }; + args.push(token); + skipped_arg_count += 1; + continue; + } + } + } + } } args.push( quote! { ::from_raw_unchecked(env, cb.this) }, diff --git a/crates/backend/src/lib.rs b/crates/backend/src/lib.rs index 332ab69a..9ea18085 100644 --- a/crates/backend/src/lib.rs +++ b/crates/backend/src/lib.rs @@ -55,3 +55,40 @@ napi_ast_impl! { (Enum, NapiEnum), (Const, NapiConst), } + +pub(crate) static PRIMITIVE_TYPES: &[(&str, &str)] = &[ + ("JsUndefined", "undefined"), + ("()", "undefined"), + ("Undefined", "undefined"), + ("JsNumber", "number"), + ("i8", "number"), + ("i16", "number"), + ("i32", "number"), + ("i64", "number"), + ("f64", "number"), + ("u8", "number"), + ("u16", "number"), + ("u32", "number"), + ("u64", "bigint"), + ("i64n", "bigint"), + ("u128", "bigint"), + ("i128", "bigint"), + ("usize", "bigint"), + ("isize", "bigint"), + ("JsBigInt", "bigint"), + ("BigInt", "bigint"), + ("JsBoolean", "boolean"), + ("bool", "boolean"), + ("JsString", "string"), + ("String", "string"), + ("str", "string"), + ("Latin1String", "string"), + ("Utf16String", "string"), + ("char", "string"), + ("Null", "null"), + ("JsNull", "null"), + ("null", "null"), + ("Symbol", "symbol"), + ("JsSymbol", "symbol"), + ("JsFunction", "(...args: any[]) => any"), +]; diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index d0a2357c..ead99fac 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -120,35 +120,8 @@ pub trait ToTypeDef { static KNOWN_TYPES: Lazy> = Lazy::new(|| { let mut map = HashMap::default(); + map.extend(crate::PRIMITIVE_TYPES.iter().cloned()); map.extend([ - ("JsUndefined", "undefined"), - ("()", "undefined"), - ("Undefined", "undefined"), - ("JsNumber", "number"), - ("i8", "number"), - ("i16", "number"), - ("i32", "number"), - ("i64", "number"), - ("f64", "number"), - ("u8", "number"), - ("u16", "number"), - ("u32", "number"), - ("u64", "bigint"), - ("i64n", "bigint"), - ("u128", "bigint"), - ("i128", "bigint"), - ("usize", "bigint"), - ("isize", "bigint"), - ("JsBigInt", "bigint"), - ("BigInt", "bigint"), - ("JsBoolean", "boolean"), - ("bool", "boolean"), - ("JsString", "string"), - ("String", "string"), - ("str", "string"), - ("Latin1String", "string"), - ("Utf16String", "string"), - ("char", "string"), ("JsObject", "object"), ("Object", "object"), ("Array", "unknown[]"), @@ -201,14 +174,8 @@ static KNOWN_TYPES: Lazy> = Lazy::new(|| { ("Either24", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), ("Either25", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), ("Either26", "{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}"), - ("Null", "null"), - ("JsNull", "null"), - ("null", "null"), - ("Symbol", "symbol"), - ("JsSymbol", "symbol"), ("external", "object"), ("AbortSignal", "AbortSignal"), - ("JsFunction", "(...args: any[]) => any"), ("JsGlobal", "typeof global"), ("External", "ExternalObject<{}>"), ("unknown", "unknown"), diff --git a/crates/backend/src/typegen/fn.rs b/crates/backend/src/typegen/fn.rs index a5541c14..e9122006 100644 --- a/crates/backend/src/typegen/fn.rs +++ b/crates/backend/src/typegen/fn.rs @@ -13,14 +13,18 @@ struct FnArg { } struct FnArgList { + this: Option, args: Vec, last_required: Option, } impl Display for FnArgList { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if let Some(this) = &self.this { + write!(f, "this: {}", this.ts_type)?; + } for (i, arg) in self.args.iter().enumerate() { - if i != 0 { + if i != 0 || self.this.is_some() { write!(f, ", ")?; } let is_optional = arg.is_optional @@ -39,13 +43,22 @@ impl Display for FnArgList { impl FromIterator for FnArgList { fn from_iter>(iter: T) -> Self { - let args = iter.into_iter().collect::>(); + let mut args = Vec::new(); + let mut this = None; + for arg in iter.into_iter() { + if arg.arg != "this" { + args.push(arg); + } else { + this = Some(arg); + } + } let last_required = args .iter() .enumerate() .rfind(|(_, arg)| !arg.is_optional) .map(|(i, _)| i); FnArgList { + this, args, last_required, } @@ -128,12 +141,27 @@ impl NapiFn { if let syn::Type::Path(path) = path.ty.as_ref() { if let Some(PathSegment { ident, - arguments: PathArguments::AngleBracketed(_), + arguments: + PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + args: angle_bracketed_args, + .. + }), }) = path.path.segments.last() { if ident == "Reference" || ident == "WeakReference" { return None; } + if ident == "This" { + if let Some(syn::GenericArgument::Type(ty)) = angle_bracketed_args.first() { + let (ts_type, _) = ty_to_ts_type(&ty, false, false); + return Some(FnArg { + arg: "this".to_owned(), + ts_type, + is_optional: false, + }); + } + return None; + } } } diff --git a/crates/napi/src/bindgen_runtime/js_values/class.rs b/crates/napi/src/bindgen_runtime/js_values/class.rs index 2b700fed..9005ce33 100644 --- a/crates/napi/src/bindgen_runtime/js_values/class.rs +++ b/crates/napi/src/bindgen_runtime/js_values/class.rs @@ -2,9 +2,10 @@ use std::any::type_name; use std::ops::{Deref, DerefMut}; use std::ptr; -use crate::{bindgen_runtime::FromNapiValue, check_status, sys, JsObject, NapiRaw}; +use super::Object; +use crate::{bindgen_runtime::FromNapiValue, check_status, sys, NapiRaw}; -pub type This = JsObject; +pub type This = T; pub struct ClassInstance { pub value: sys::napi_value, diff --git a/crates/napi/src/bindgen_runtime/js_values/number.rs b/crates/napi/src/bindgen_runtime/js_values/number.rs index 97296809..80421706 100644 --- a/crates/napi/src/bindgen_runtime/js_values/number.rs +++ b/crates/napi/src/bindgen_runtime/js_values/number.rs @@ -41,7 +41,7 @@ macro_rules! impl_number_conversions { check_status!( unsafe { sys::$get(env, napi_val, &mut ret) }, "Failed to convert napi value {:?} into rust type `{}`", - type_of!(env, napi_val), + type_of!(env, napi_val)?, $name, )?; diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 00c6ddf5..839b73db 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -48,6 +48,7 @@ Generated by [AVA](https://avajs.dev). }␊ export function createObjectWithClassField(): ObjectFieldClassInstance␊ export function receiveObjectWithClassField(object: ObjectFieldClassInstance): Bird␊ + export function plusOne(this: Width): number␊ export function dateToNumber(input: Date): number␊ export function chronoDateToMillis(input: Date): number␊ export function chronoDateAdd1Minute(input: Date): Date␊ @@ -298,6 +299,10 @@ Generated by [AVA](https://avajs.dev). export class CustomFinalize {␊ constructor(width: number, height: number)␊ }␊ + export class Width {␊ + value: number␊ + constructor(value: number)␊ + }␊ export class ClassWithFactory {␊ name: string␊ static withName(name: string): ClassWithFactory␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 17f9c8a756eae3f6d8fd16a88aa44954a667c46c..4b49767a2c08c17091d3f0614cac63df8cd0dc46 100644 GIT binary patch literal 3557 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 literal 3531 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 F005?W@rVEb diff --git a/examples/napi/__test__/values.spec.ts b/examples/napi/__test__/values.spec.ts index 999a77ac..2b884642 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -108,6 +108,8 @@ import { getNumArr, getNestedNumArr, CustomFinalize, + plusOne, + Width, } from '../' test('export const', (t) => { @@ -161,7 +163,7 @@ test('enum', (t) => { t.is(enumToI32(CustomNumEnum.Eight), 8) }) -test('class', (t) => { +test.only('class', (t) => { const dog = new Animal(Kind.Dog, '旺财') t.is(dog.name, '旺财') @@ -192,6 +194,13 @@ test('class', (t) => { const turtle = NinjaTurtle.newRaph() t.is(turtle.returnThis(), turtle) t.is(NinjaTurtle.isInstanceOf(turtle), true) + // Inject this to function + const width = new Width(1) + t.is(plusOne.call(width), 2) + t.throws(() => { + // @ts-expect-error + plusOne.call('') + }) }) test('class factory', (t) => { diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 0ea1ca8e..970ce77a 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -38,6 +38,7 @@ export interface ObjectFieldClassInstance { } export function createObjectWithClassField(): ObjectFieldClassInstance export function receiveObjectWithClassField(object: ObjectFieldClassInstance): Bird +export function plusOne(this: Width): number export function dateToNumber(input: Date): number export function chronoDateToMillis(input: Date): number export function chronoDateAdd1Minute(input: Date): Date @@ -288,6 +289,10 @@ export class NotWritableClass { export class CustomFinalize { constructor(width: number, height: number) } +export class Width { + value: number + constructor(value: number) +} export class ClassWithFactory { name: string static withName(name: string): ClassWithFactory diff --git a/examples/napi/src/class.rs b/examples/napi/src/class.rs index b0c3a545..72c3a5ff 100644 --- a/examples/napi/src/class.rs +++ b/examples/napi/src/class.rs @@ -395,3 +395,13 @@ impl ObjectFinalize for CustomFinalize { Ok(()) } } + +#[napi(constructor)] +pub struct Width { + pub value: i32, +} + +#[napi] +pub fn plus_one(this: This<&Width>) -> i32 { + this.value + 1 +}