From c1e07b3c122b14ee44959a757c9e6ac0e8d66bba Mon Sep 17 00:00:00 2001 From: LongYinan Date: Sat, 4 Jun 2022 01:07:39 +0800 Subject: [PATCH] feat(napi): support into_instance in class struct --- crates/backend/src/codegen/struct.rs | 15 ++++ crates/backend/src/typegen.rs | 1 + crates/napi/src/bindgen_runtime/js_values.rs | 2 + .../src/bindgen_runtime/js_values/class.rs | 65 ++++++++++++++++++ examples/napi/__test__/typegen.spec.ts.md | 5 ++ examples/napi/__test__/typegen.spec.ts.snap | Bin 3242 -> 3285 bytes examples/napi/__test__/values.spec.ts | 8 +++ examples/napi/index.d.ts | 5 ++ examples/napi/src/class.rs | 27 +++++++- 9 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 crates/napi/src/bindgen_runtime/js_values/class.rs diff --git a/crates/backend/src/codegen/struct.rs b/crates/backend/src/codegen/struct.rs index 54659e59..63bcb09e 100644 --- a/crates/backend/src/codegen/struct.rs +++ b/crates/backend/src/codegen/struct.rs @@ -263,6 +263,21 @@ impl NapiStruct { } } + pub fn into_instance(self, env: napi::Env) -> napi::Result> { + if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) { + unsafe { + let wrapped_value = Box::leak(Box::new(self)); + let instance_value = #name::new_instance(env.raw(), wrapped_value as *mut _ as *mut std::ffi::c_void, ctor_ref)?; + + Ok(napi::bindgen_prelude::ClassInstance::<#name>::new(instance_value, wrapped_value)) + } + } else { + Err(napi::bindgen_prelude::Error::new( + napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw)) + ) + } + } + unsafe fn new_instance( env: napi::sys::napi_env, wrapped_value: *mut std::ffi::c_void, diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index 5e76f71a..3f835ef1 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -175,6 +175,7 @@ static KNOWN_TYPES: Lazy> = Lazy::new(|| { ("Buffer", "Buffer"), ("Vec", "Array<{}>"), ("Result", "Error | {}"), + ("ClassInstance", "{}"), ("Either", "{} | {}"), ("Either3", "{} | {} | {}"), ("Either4", "{} | {} | {} | {}"), diff --git a/crates/napi/src/bindgen_runtime/js_values.rs b/crates/napi/src/bindgen_runtime/js_values.rs index d8669bf8..cb1af7f8 100644 --- a/crates/napi/src/bindgen_runtime/js_values.rs +++ b/crates/napi/src/bindgen_runtime/js_values.rs @@ -8,6 +8,7 @@ mod arraybuffer; mod bigint; mod boolean; mod buffer; +mod class; #[cfg(all(feature = "chrono_date", feature = "napi5"))] mod date; mod either; @@ -34,6 +35,7 @@ pub use arraybuffer::*; #[cfg(feature = "napi6")] pub use bigint::*; pub use buffer::*; +pub use class::*; pub use either::*; pub use external::*; #[cfg(feature = "napi4")] diff --git a/crates/napi/src/bindgen_runtime/js_values/class.rs b/crates/napi/src/bindgen_runtime/js_values/class.rs new file mode 100644 index 00000000..c2dab55c --- /dev/null +++ b/crates/napi/src/bindgen_runtime/js_values/class.rs @@ -0,0 +1,65 @@ +use std::any::type_name; +use std::ops::{Deref, DerefMut}; +use std::ptr; + +use crate::{bindgen_runtime::FromNapiValue, check_status, sys, NapiRaw}; + +pub struct ClassInstance { + pub value: sys::napi_value, + inner: &'static mut T, +} + +impl ClassInstance { + #[doc(hidden)] + pub fn new(value: sys::napi_value, inner: &'static mut T) -> Self { + Self { value, inner } + } +} + +impl NapiRaw for ClassInstance { + unsafe fn raw(&self) -> sys::napi_value { + self.value + } +} + +impl FromNapiValue for ClassInstance { + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result { + let mut value = ptr::null_mut(); + check_status!( + unsafe { sys::napi_unwrap(env, napi_val, &mut value) }, + "Unwrap value [{}] from class failed", + type_name::(), + )?; + let value = unsafe { Box::from_raw(value as *mut T) }; + Ok(Self { + value: napi_val, + inner: Box::leak(value), + }) + } +} + +impl Deref for ClassInstance { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.inner + } +} + +impl DerefMut for ClassInstance { + fn deref_mut(&mut self) -> &mut T { + self.inner + } +} + +impl AsRef for ClassInstance { + fn as_ref(&self) -> &T { + self.inner + } +} + +impl AsMut for ClassInstance { + fn as_mut(&mut self) -> &mut T { + self.inner + } +} diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index f4698cc0..cfc7453c 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -41,6 +41,11 @@ Generated by [AVA](https://avajs.dev). export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void␊ export function returnJsFunction(): (...args: any[]) => any␊ export function callbackReturnPromise(functionInput: () => T | Promise, callback: (err: Error | null, result: T) => void): T | Promise␊ + export interface ObjectFieldClassInstance {␊ + bird: Bird␊ + }␊ + export function createObjectWithClassField(): ObjectFieldClassInstance␊ + export function receiveObjectWithClassField(object: ObjectFieldClassInstance): Bird␊ export function dateToNumber(input: Date): number␊ export function chronoDateToMillis(input: Date): number␊ export function chronoDateAdd1Minute(input: Date): Date␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 40c7c246f46b866e6a7b11712d3cc88c713f51b9..a7eaeb365f12c122dc3abcea82e16c073fc52729 100644 GIT binary patch literal 3285 zcmV;`3@YXuyFs?R|01&aPIP!xAh*hlCLx!mP`N%CESBwBK3hBL$YB=@KBD3;-c z`tfI`xsKWOU({5laYQCeu!L$xLYX8i)I=}lj6`fq#yng0PK1$6HUUHDn<6LHA@7Ik4GPYujj*;m%wQAl^c>Dz`nsZxd7k!0Tdm_Yp!vsX`o}w#m7jbJcaS zmn1UrdHDpJoiN}{yjJ_!1loM?@Zm!s3=xBh#o4)`0Z(;z$rwB_G@S1aUj`21K+IDO z^bCa|uvEZwi6zzAU?dX)|8DrcY` zme|mFoRsCu?K#g3Ei7B}-7};da*k7>(!#-Z%cAfwk}_sg)Rr$(8kJ)_2inf#Crv?*UB27OsslV(mPD`CnQ$4G`TPp z86mrJ0s;MTH+%C2{7jh`lf55M4evMI`yo|&kzuCc1!jiALcHbnW^;hU^|G4t8=1m; zEWAe$qb>+Y3tY1~MC6E!A!35P2zHeVmMSam!)9>?snQaQ!aW?}I>&-JMxh`xDGMK` z3B&nq9PG<9f#)aqe9Euk@hLv;@yQg0e1?M~Zs`2gATvyTn~$4(0G1ufgYB(=^~%gF zDM%qMU77R{w52f{zg+_mDE@cm*;=JBXsa&EM6#j|^~!|y3fu}cSG_k>dO1HVduU^& zUG`b3eZf`{>Y^?D4d3U&H|h&agkxecEdi7l$=cn4!?fjn0L8a`T8%M;J~6TjX2IY+ z1kz__W{vesVeK$i75q7tji>=u=6NvBo?4MGVFl5uZ0SP}`RooD#ctl+9vqFlUJFOA zz*?0n9qN}8|UVLU#mHe#@femB#`K zyRuJdWY6QSWgM{6TLw}%U0Wic_AJG&XU`dbP_T<%#BxN967UdGj>*JKG=Z*Gg*jELtF$Qg`*CbH(1mMLKb1@y z#&p8+&21=vvSPbN3)KU7^)(vp!xzA-?)q;J`lp7gzAH|5uo5fy@TCEdVG+c+G?N7E zz!1P@d5HvLabjhNG7=c_O4Pc9Wuz*s7OP`;4RuA0xfWa1=A#A58}v#PmuxYBnO4G* zXytFHuv{5TshBWZ|D%OvVTt87S)O(~_bPwC^%rb1;(3&&clk>oPkS-kTdnF7Kf==yfi?UyQ|@v42?z}*QiTx z*ra&x^%3Uuc`+{Gw&z#Pls?6S!4nC-4lwfouTISQTc_82_6BVU5K?r9b!r?7{9cbYpRN!7mO@9mxek2dfTpU{~OoGS&SN%nm@$iHu|Lzm_z)s-Q$_>yYXa#V4kW4i{LO>JKJ%^V?A=tF_^fn zHywLbW(x)$0N`oPO48sh5ajgx?X6mOoK3B~=qn#xc%62qM5pC=q28JA$C7%BH)<_n z2^$wJJQ{T|GKOu2gP6`7`S!##GOBq;l|pMvC)0|K*Sb|aVNn{g#yx6CYxl?l^1&2O zShL9FLv|HpGTR3``vLO7_bJEQ8}KKneOVkSq9qwYmP$u454%W4;u+)>(1-9lA$&Fm zP~?etCnV0KGsxltZUT;5P0ni+ItqVBemd-`qa>ws-%2E`sg#j*GK)yr_|rhBu~D zH;1kR&l23t6KQMo56#90LMAf|E=92+^EMr4Z}KATwo|=nvzCpA<_%?_Z84x zc5S<`8C{In5o367=L*NNdOKxdc7eZ)$-HRG2qHOaIGuW9=SMoU%3XV3~xhBORuRj#*n%GBEj+Eb!t(AUM$m{!b7*{`GSy76^8ALg!Ny(j#mQ z?~tmCfsCz>o2}u$+BwXg_5FoZa*dTvxra9eyrk1Ed6?z6PX7U#9ANpJlM+L@wkogFB~d@n=V4eM=Nfm2A} zLVICLKyYk{Vin+S3AXbKxvy=@37gB>W<)&k(`Ev-l4ka<{lq*s(ZTuLu37`y;Pmt) zjoDkSFxU)~a-)jD7B37<2sAiu_)^jap*cleo$5u*PN$5S3=c~c6O9#Ub1W?3StdO? zRW?1)sv66AC_(-!+`AZ>IN5xK5GWE&H&I%}XrN)fB7_5}$kK>e$-{LBw*}37<_n{| zYK~`p*Vj{P@uhR={@a5ujt=*ZhMvVY+~90~_w-5I^^Hx*0JORFtUxk3xPcM?EhqFf z@@eJvY1o;soe=fBXR&QvW-;?Bt-78m)9`)7Cr~p6J`1-ky4&(~=L@uj=d~*?X_|HV T^~#&E+k5{92EUjt=q&&M%ppk> literal 3242 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 diff --git a/examples/napi/__test__/values.spec.ts b/examples/napi/__test__/values.spec.ts index 2a6012e1..412e053e 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -97,6 +97,8 @@ import { eitherFromOption, overrideIndividualArgOnFunction, overrideIndividualArgOnFunctionWithCbArg, + createObjectWithClassField, + receiveObjectWithClassField, } from '../' test('export const', (t) => { @@ -206,6 +208,12 @@ test('class Factory return Result', (t) => { t.is(c.method(), 'not empty') }) +test('class in object field', (t) => { + const obj = createObjectWithClassField() + t.is(obj.bird.name, 'Carolyn') + t.is(receiveObjectWithClassField(obj), obj.bird) +}) + test('should be able to create object reference and shared reference', (t) => { const repo = new JsRepo('.') t.is(repo.remote().name(), 'origin') diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index b59573d7..0531adfe 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -31,6 +31,11 @@ export function optionOnly(callback: (arg0?: string | undefined | null) => void) export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void export function returnJsFunction(): (...args: any[]) => any export function callbackReturnPromise(functionInput: () => T | Promise, callback: (err: Error | null, result: T) => void): T | Promise +export interface ObjectFieldClassInstance { + bird: Bird +} +export function createObjectWithClassField(): ObjectFieldClassInstance +export function receiveObjectWithClassField(object: ObjectFieldClassInstance): Bird export function dateToNumber(input: Date): number export function chronoDateToMillis(input: Date): number export function chronoDateAdd1Minute(input: Date): Date diff --git a/examples/napi/src/class.rs b/examples/napi/src/class.rs index 2f4bec89..9d3c937e 100644 --- a/examples/napi/src/class.rs +++ b/examples/napi/src/class.rs @@ -1,4 +1,7 @@ -use napi::{bindgen_prelude::Buffer, Result}; +use napi::{ + bindgen_prelude::{Buffer, ClassInstance}, + Env, Result, +}; use crate::r#enum::Kind; @@ -305,3 +308,25 @@ impl Optional { } } } + +#[napi(object)] +pub struct ObjectFieldClassInstance { + pub bird: ClassInstance, +} + +#[napi] +pub fn create_object_with_class_field(env: Env) -> Result { + Ok(ObjectFieldClassInstance { + bird: Bird { + name: "Carolyn".to_owned(), + } + .into_instance(env)?, + }) +} + +#[napi] +pub fn receive_object_with_class_field( + object: ObjectFieldClassInstance, +) -> Result> { + Ok(object.bird) +}