From 0ef482c6ca87390267a6fd1d31f02a390da1f5f2 Mon Sep 17 00:00:00 2001
From: LongYinan <lynweklm@gmail.com>
Date: Wed, 17 Aug 2022 17:49:22 +0800
Subject: [PATCH 1/2] feat(napi-derive): support inject This<Value> 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! { <napi::bindgen_prelude::This as napi::NapiValue>::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<HashMap<&'static str, &'static str>> = 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<HashMap<&'static str, &'static str>> = 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<FnArg>,
   args: Vec<FnArg>,
   last_required: Option<usize>,
 }
 
 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<FnArg> for FnArgList {
   fn from_iter<T: IntoIterator<Item = FnArg>>(iter: T) -> Self {
-    let args = iter.into_iter().collect::<Vec<_>>();
+    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 = Object> = T;
 
 pub struct ClassInstance<T: 'static> {
   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
+}

From 528b1d21cb2a4055dbde2992375ff8396876d3c9 Mon Sep 17 00:00:00 2001
From: LongYinan <lynweklm@gmail.com>
Date: Wed, 17 Aug 2022 17:53:44 +0800
Subject: [PATCH 2/2] style: clippy fix

---
 crates/backend/src/ast.rs        | 4 ++--
 crates/backend/src/typegen/fn.rs | 2 +-
 examples/napi/src/lib.rs         | 1 +
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs
index 64677f82..101dfc54 100644
--- a/crates/backend/src/ast.rs
+++ b/crates/backend/src/ast.rs
@@ -54,7 +54,7 @@ pub enum NapiFnArgKind {
   Callback(Box<CallbackArg>),
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub enum FnKind {
   Normal,
   Constructor,
@@ -84,7 +84,7 @@ pub struct NapiStruct {
   pub use_custom_finalize: bool,
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub enum NapiStructKind {
   None,
   Constructor,
diff --git a/crates/backend/src/typegen/fn.rs b/crates/backend/src/typegen/fn.rs
index e9122006..b6ae0fd0 100644
--- a/crates/backend/src/typegen/fn.rs
+++ b/crates/backend/src/typegen/fn.rs
@@ -153,7 +153,7 @@ impl NapiFn {
                 }
                 if ident == "This" {
                   if let Some(syn::GenericArgument::Type(ty)) = angle_bracketed_args.first() {
-                    let (ts_type, _) = ty_to_ts_type(&ty, false, false);
+                    let (ts_type, _) = ty_to_ts_type(ty, false, false);
                     return Some(FnArg {
                       arg: "this".to_owned(),
                       ts_type,
diff --git a/examples/napi/src/lib.rs b/examples/napi/src/lib.rs
index 189f1eef..1825406c 100644
--- a/examples/napi/src/lib.rs
+++ b/examples/napi/src/lib.rs
@@ -1,5 +1,6 @@
 #![allow(dead_code)]
 #![allow(unreachable_code)]
+#![allow(clippy::blacklisted_name)]
 
 #[macro_use]
 extern crate napi_derive;