From 7e34e30b66b2c35de31a3f4386d166c2154e4e8c Mon Sep 17 00:00:00 2001 From: LongYinan Date: Wed, 2 Jul 2025 01:54:31 -0700 Subject: [PATCH] feat!(napi): create function from #[napi] fn (#2757) --- crates/backend/src/ast.rs | 1 + crates/backend/src/codegen.rs | 2 +- crates/backend/src/codegen/fn.rs | 49 ++++++++++------ crates/backend/src/typegen.rs | 5 +- crates/backend/src/typegen/fn.rs | 2 +- crates/macro/src/parser/attrs.rs | 1 + crates/macro/src/parser/mod.rs | 38 ++++++++++++ .../src/bindgen_runtime/module_register.rs | 55 ------------------ .../__tests__/__snapshots__/values.spec.ts.md | 6 +- .../__snapshots__/values.spec.ts.snap | Bin 7137 -> 7153 bytes examples/napi/__tests__/values.spec.ts | 8 +++ examples/napi/example.wasi-browser.js | 1 + examples/napi/example.wasi.cjs | 1 + examples/napi/index.cjs | 1 + examples/napi/index.d.cts | 6 +- examples/napi/src/function.rs | 12 +++- examples/napi/src/object.rs | 2 +- 17 files changed, 108 insertions(+), 82 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 13eb3414..c6893f58 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -32,6 +32,7 @@ pub struct NapiFn { pub catch_unwind: bool, pub unsafe_: bool, pub register_name: Ident, + pub no_export: bool, } #[derive(Debug, Clone)] diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index b574000c..257eef66 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -27,7 +27,7 @@ pub trait TryToTokens { } fn get_intermediate_ident(name: &str) -> Ident { - let new_name = format!("_napi_internal_register_{name}"); + let new_name = format!("{name}_c_callback"); Ident::new(&new_name, Span::call_site()) } diff --git a/crates/backend/src/codegen/fn.rs b/crates/backend/src/codegen/fn.rs index ad11d615..7ab73df6 100644 --- a/crates/backend/src/codegen/fn.rs +++ b/crates/backend/src/codegen/fn.rs @@ -726,10 +726,14 @@ impl NapiFn { let module_register_name = &self.register_name; let intermediate_ident = get_intermediate_ident(&name_str); let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref()); - let cb_name = Ident::new(&format!("{name_str}_js_function"), Span::call_site()); + let cb_name = Ident::new( + &format!("_napi_rs_internal_register_{name_str}"), + Span::call_site(), + ); if self.module_exports { return quote! { + #[doc(hidden)] #[allow(non_snake_case)] #[allow(clippy::all)] unsafe fn #cb_name(env: napi::bindgen_prelude::sys::napi_env, exports: napi::bindgen_prelude::sys::napi_value) -> napi::bindgen_prelude::Result { @@ -737,6 +741,7 @@ impl NapiFn { Ok(exports) } + #[doc(hidden)] #[allow(clippy::all)] #[allow(non_snake_case)] #[cfg(all(not(test), not(target_family = "wasm")))] @@ -755,7 +760,32 @@ impl NapiFn { }; } + let register_module_export_tokens = if self.no_export { + quote! {} + } else { + quote! { + #[doc(hidden)] + #[allow(clippy::all)] + #[allow(non_snake_case)] + #[cfg(all(not(test), not(target_family = "wasm")))] + #[napi::ctor::ctor(crate_path=::napi::ctor)] + fn #module_register_name() { + napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name); + } + + #[doc(hidden)] + #[allow(clippy::all)] + #[allow(non_snake_case)] + #[cfg(all(not(test), target_family = "wasm"))] + #[no_mangle] + extern "C" fn #module_register_name() { + napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name); + } + } + }; + quote! { + #[doc(hidden)] #[allow(non_snake_case)] #[allow(clippy::all)] unsafe fn #cb_name(env: napi::bindgen_prelude::sys::napi_env) -> napi::bindgen_prelude::Result { @@ -773,25 +803,10 @@ impl NapiFn { "Failed to register function `{}`", #name_str, )?; - napi::bindgen_prelude::register_js_function(#js_name, #cb_name, Some(#intermediate_ident)); Ok(fn_ptr) } - #[allow(clippy::all)] - #[allow(non_snake_case)] - #[cfg(all(not(test), not(target_family = "wasm")))] - #[napi::ctor::ctor(crate_path=::napi::ctor)] - fn #module_register_name() { - napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name); - } - - #[allow(clippy::all)] - #[allow(non_snake_case)] - #[cfg(all(not(test), target_family = "wasm"))] - #[no_mangle] - extern "C" fn #module_register_name() { - napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name); - } + #register_module_export_tokens } } } diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index 29df6a34..fab5926c 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -487,7 +487,7 @@ pub fn ty_to_ts_type( Some((fill_ty(known_ty, union_args), false)) } } else { - let filtered_args = + let mut filtered_args = if let Some(arg_indices) = KNOWN_TYPES_IGNORE_ARG.get(rust_ty.as_str()) { args .enumerate() @@ -497,6 +497,9 @@ pub fn ty_to_ts_type( } else { args.collect::>() }; + if rust_ty.starts_with("Function") && filtered_args.is_empty() { + filtered_args = vec!["arg?: unknown".to_owned(), "unknown".to_owned()]; + } Some((fill_ty(known_ty, filtered_args), false)) } diff --git a/crates/backend/src/typegen/fn.rs b/crates/backend/src/typegen/fn.rs index be988212..3cd64ef5 100644 --- a/crates/backend/src/typegen/fn.rs +++ b/crates/backend/src/typegen/fn.rs @@ -67,7 +67,7 @@ impl FromIterator for FnArgList { impl ToTypeDef for NapiFn { fn to_type_def(&self) -> Option { - if self.skip_typescript || self.module_exports { + if self.skip_typescript || self.module_exports || self.no_export { return None; } diff --git a/crates/macro/src/parser/attrs.rs b/crates/macro/src/parser/attrs.rs index 84a07ea9..2243735c 100644 --- a/crates/macro/src/parser/attrs.rs +++ b/crates/macro/src/parser/attrs.rs @@ -80,6 +80,7 @@ macro_rules! attrgen { (discriminant, Discriminant(Span, String, Span)), (transparent, Transparent(Span)), (array, Array(Span)), + (no_export, NoExport(Span)), // impl later // (inspectable, Inspectable(Span)), diff --git a/crates/macro/src/parser/mod.rs b/crates/macro/src/parser/mod.rs index cb601950..46011757 100644 --- a/crates/macro/src/parser/mod.rs +++ b/crates/macro/src/parser/mod.rs @@ -728,6 +728,13 @@ fn napi_fn_from_decl( bail_span!(sig.ident, "module_exports fn can't have generic parameters"); } + if opts.no_export().is_some() { + bail_span!( + sig.ident, + "#[napi(no_export)] can not be used with module_exports attribute" + ); + } + for arg in args.iter() { match &arg.kind { NapiFnArgKind::Callback(_) => { @@ -897,6 +904,7 @@ fn napi_fn_from_decl( catch_unwind: opts.catch_unwind().is_some(), unsafe_: sig.unsafety.is_some(), register_name: get_register_ident(ident.to_string().as_str()), + no_export: opts.no_export().is_some(), }) }) } @@ -964,6 +972,12 @@ impl ParseNapi for syn::ItemStruct { "#[napi(catch_unwind)] can only be applied to a function or method." ); } + if opts.no_export().is_some() { + bail_span!( + self, + "#[napi(no_export)] can only be applied to a function." + ); + } if opts.object().is_some() && opts.custom_finalize().is_some() { bail_span!(self, "Custom finalize is not supported for #[napi(object)]"); } @@ -999,6 +1013,12 @@ impl ParseNapi for syn::ItemImpl { "#[napi(catch_unwind)] can only be applied to a function or method." ); } + if opts.no_export().is_some() { + bail_span!( + self, + "#[napi(no_export)] can only be applied to a function." + ); + } // #[napi] macro will be remove from impl items after converted to ast let napi = self.convert_to_ast(opts); self.to_tokens(tokens); @@ -1031,6 +1051,12 @@ impl ParseNapi for syn::ItemEnum { "#[napi(catch_unwind)] can only be applied to a function or method." ); } + if opts.no_export().is_some() { + bail_span!( + self, + "#[napi(no_export)] can only be applied to a function." + ); + } let napi = self.convert_to_ast(opts); self.to_tokens(tokens); @@ -1061,6 +1087,12 @@ impl ParseNapi for syn::ItemConst { "#[napi(catch_unwind)] can only be applied to a function or method." ); } + if opts.no_export().is_some() { + bail_span!( + self, + "#[napi(no_export)] can only be applied to a function." + ); + } let napi = self.convert_to_ast(opts); self.to_tokens(tokens); napi @@ -1090,6 +1122,12 @@ impl ParseNapi for syn::ItemType { "#[napi(catch_unwind)] can only be applied to a function or method." ); } + if opts.no_export().is_some() { + bail_span!( + self, + "#[napi(no_export)] can only be applied to a function." + ); + } let napi = self.convert_to_ast(opts); self.to_tokens(tokens); napi diff --git a/crates/napi/src/bindgen_runtime/module_register.rs b/crates/napi/src/bindgen_runtime/module_register.rs index f29e0bf1..c946a785 100644 --- a/crates/napi/src/bindgen_runtime/module_register.rs +++ b/crates/napi/src/bindgen_runtime/module_register.rs @@ -94,12 +94,6 @@ impl ModuleClassProperty { } } -type FnRegisterMap = PersistedPerInstanceHashMap< - ExportRegisterCallback, - (sys::napi_callback, &'static str), - FxBuildHasher, ->; - #[cfg(not(feature = "noop"))] static MODULE_REGISTER_CALLBACK: LazyLock = LazyLock::new(Default::default); #[cfg(not(feature = "noop"))] @@ -113,7 +107,6 @@ static MODULE_COUNT: AtomicUsize = AtomicUsize::new(0); static FIRST_MODULE_REGISTERED: AtomicBool = AtomicBool::new(false); thread_local! { static REGISTERED_CLASSES: LazyCell = LazyCell::new(Default::default); - static FN_REGISTER_MAP: LazyCell = LazyCell::new(Default::default); } #[cfg(all(feature = "napi4", not(feature = "noop")))] pub(crate) static CUSTOM_GC_TSFN: std::sync::atomic::AtomicPtr = @@ -194,19 +187,6 @@ pub fn register_module_export_hook(cb: ExportRegisterHookCallback) { #[doc(hidden)] pub fn register_module_export_hook(_cb: ExportRegisterHookCallback) {} -#[doc(hidden)] -pub fn register_js_function( - name: &'static str, - cb: ExportRegisterCallback, - c_fn: sys::napi_callback, -) { - FN_REGISTER_MAP.with(|cell| { - cell.borrow_mut(|inner| { - inner.insert(cb, (c_fn, name)); - }) - }); -} - #[doc(hidden)] pub fn get_class_constructor(js_name: &'static str) -> Option { REGISTERED_CLASSES.with(|cell| cell.borrow_mut(|map| map.get(js_name).copied())) @@ -239,41 +219,6 @@ pub fn register_class( ) { } -/// Get `C Callback` from defined Rust `fn` -/// ```rust -/// #[napi] -/// fn some_fn() -> u32 { -/// 1 -/// } -/// -/// #[napi] -/// fn create_obj(env: Env) -> Result { -/// let mut obj = env.create_object()?; -/// obj.define_property(&[Property::new("getter")?.with_getter(get_c_callback(some_fn_js_function)?)])?; -/// Ok(obj) -/// } -/// ``` -/// -/// ```js -/// console.log(createObj().getter) // 1 -/// ``` -/// -pub fn get_c_callback(raw_fn: ExportRegisterCallback) -> Result { - FN_REGISTER_MAP.with(|cell| { - cell.borrow_mut(|inner| { - inner - .get(&raw_fn) - .and_then(|(cb, _name)| *cb) - .ok_or_else(|| { - crate::Error::new( - crate::Status::InvalidArg, - "JavaScript function does not exist".to_owned(), - ) - }) - }) - }) -} - #[cfg(all(target_family = "wasm", not(feature = "noop")))] #[no_mangle] unsafe extern "C" fn napi_register_wasm_v1( diff --git a/examples/napi/__tests__/__snapshots__/values.spec.ts.md b/examples/napi/__tests__/__snapshots__/values.spec.ts.md index 8e9edc89..dad9010b 100644 --- a/examples/napi/__tests__/__snapshots__/values.spec.ts.md +++ b/examples/napi/__tests__/__snapshots__/values.spec.ts.md @@ -497,6 +497,8 @@ Generated by [AVA](https://avajs.dev). ␊ export declare function createExternalTypedArray(): Uint32Array␊ ␊ + export declare function createFunction(): (arg: number) => number␊ + ␊ export declare function createObj(): object␊ ␊ export declare function createObjectRef(): object␊ @@ -573,7 +575,7 @@ Generated by [AVA](https://avajs.dev). ␊ export declare function either4(input: string | number | boolean | Obj): number␊ ␊ - export declare function eitherBoolOrFunction(input: boolean | (any)): void␊ + export declare function eitherBoolOrFunction(input: boolean | ((arg?: unknown) => unknown)): void␊ ␊ export declare function eitherBoolOrTuple(input: boolean | [boolean, string]): void␊ ␊ @@ -934,7 +936,7 @@ Generated by [AVA](https://avajs.dev). ␊ export declare function testSerdeRoundtrip(data: any): any␊ ␊ - export declare function threadsafeFunctionBuildThrowErrorWithStatus(cb: any): void␊ + export declare function threadsafeFunctionBuildThrowErrorWithStatus(cb: (arg?: unknown) => unknown): void␊ ␊ export declare function threadsafeFunctionClosureCapture(defaultValue: Animal, func: (arg: Animal) => void): void␊ ␊ diff --git a/examples/napi/__tests__/__snapshots__/values.spec.ts.snap b/examples/napi/__tests__/__snapshots__/values.spec.ts.snap index 6ee0daf1645a6179ac31903b0876bf14f3b577b2..e213c691c240d45c2bdfcb8c4383ae1e1c5b25a9 100644 GIT binary patch literal 7153 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 literal 7137 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 diff --git a/examples/napi/__tests__/values.spec.ts b/examples/napi/__tests__/values.spec.ts index f3bdfa90..84a359cd 100644 --- a/examples/napi/__tests__/values.spec.ts +++ b/examples/napi/__tests__/values.spec.ts @@ -196,6 +196,7 @@ import { type AliasedStruct, returnObjectOnlyToJs, buildThreadsafeFunctionFromFunction, + buildThreadsafeFunctionFromFunctionCalleeHandle, createOptionalExternal, getOptionalExternal, mutateOptionalExternal, @@ -255,6 +256,7 @@ import { uint8ArrayFromExternal, Thing, ThingList, + createFunction, } from '../index.cjs' // import other stuff in `#[napi(module_exports)]` import nativeAddon from '../index.cjs' @@ -424,6 +426,8 @@ test('function call', async (t) => { referenceAsCallback((a, b) => a + b, 42, 10), 52, ) + const fn = createFunction() + t.is(fn(42), 242) }) test('class', (t) => { @@ -1667,6 +1671,10 @@ Napi4Test('build ThreadsafeFunction from Function', (t) => { buildThreadsafeFunctionFromFunction(fn) + t.notThrows(() => { + buildThreadsafeFunctionFromFunctionCalleeHandle(() => {}) + }) + return subject.pipe(take(3)) }) diff --git a/examples/napi/example.wasi-browser.js b/examples/napi/example.wasi-browser.js index 13f20b00..b6a9f2a6 100644 --- a/examples/napi/example.wasi-browser.js +++ b/examples/napi/example.wasi-browser.js @@ -185,6 +185,7 @@ export const createExternalBufferSlice = __napiModule.exports.createExternalBuff export const createExternalRef = __napiModule.exports.createExternalRef export const createExternalString = __napiModule.exports.createExternalString export const createExternalTypedArray = __napiModule.exports.createExternalTypedArray +export const createFunction = __napiModule.exports.createFunction export const createObj = __napiModule.exports.createObj export const createObjectRef = __napiModule.exports.createObjectRef export const createObjectWithClassField = __napiModule.exports.createObjectWithClassField diff --git a/examples/napi/example.wasi.cjs b/examples/napi/example.wasi.cjs index 8d76caeb..785c7b24 100644 --- a/examples/napi/example.wasi.cjs +++ b/examples/napi/example.wasi.cjs @@ -209,6 +209,7 @@ module.exports.createExternalBufferSlice = __napiModule.exports.createExternalBu module.exports.createExternalRef = __napiModule.exports.createExternalRef module.exports.createExternalString = __napiModule.exports.createExternalString module.exports.createExternalTypedArray = __napiModule.exports.createExternalTypedArray +module.exports.createFunction = __napiModule.exports.createFunction module.exports.createObj = __napiModule.exports.createObj module.exports.createObjectRef = __napiModule.exports.createObjectRef module.exports.createObjectWithClassField = __napiModule.exports.createObjectWithClassField diff --git a/examples/napi/index.cjs b/examples/napi/index.cjs index daf8348d..85a4090e 100644 --- a/examples/napi/index.cjs +++ b/examples/napi/index.cjs @@ -500,6 +500,7 @@ module.exports.createExternalBufferSlice = nativeBinding.createExternalBufferSli module.exports.createExternalRef = nativeBinding.createExternalRef module.exports.createExternalString = nativeBinding.createExternalString module.exports.createExternalTypedArray = nativeBinding.createExternalTypedArray +module.exports.createFunction = nativeBinding.createFunction module.exports.createObj = nativeBinding.createObj module.exports.createObjectRef = nativeBinding.createObjectRef module.exports.createObjectWithClassField = nativeBinding.createObjectWithClassField diff --git a/examples/napi/index.d.cts b/examples/napi/index.d.cts index 33f53a80..4038594a 100644 --- a/examples/napi/index.d.cts +++ b/examples/napi/index.d.cts @@ -459,6 +459,8 @@ export declare function createExternalString(content: string): ExternalObject number + export declare function createObj(): object export declare function createObjectRef(): object @@ -535,7 +537,7 @@ export declare function either3(input: string | number | boolean): number export declare function either4(input: string | number | boolean | Obj): number -export declare function eitherBoolOrFunction(input: boolean | (any)): void +export declare function eitherBoolOrFunction(input: boolean | ((arg?: unknown) => unknown)): void export declare function eitherBoolOrTuple(input: boolean | [boolean, string]): void @@ -896,7 +898,7 @@ export declare function testSerdeBufferBytes(obj: object): bigint export declare function testSerdeRoundtrip(data: any): any -export declare function threadsafeFunctionBuildThrowErrorWithStatus(cb: any): void +export declare function threadsafeFunctionBuildThrowErrorWithStatus(cb: (arg?: unknown) => unknown): void export declare function threadsafeFunctionClosureCapture(defaultValue: Animal, func: (arg: Animal) => void): void diff --git a/examples/napi/src/function.rs b/examples/napi/src/function.rs index 2d7c89ee..e413d9dd 100644 --- a/examples/napi/src/function.rs +++ b/examples/napi/src/function.rs @@ -1,5 +1,3 @@ -#![allow(deprecated)] - use napi::{ bindgen_prelude::{ClassInstance, FnArgs, Function, FunctionRef, PromiseRaw}, threadsafe_function::ThreadsafeFunctionCallMode, @@ -140,3 +138,13 @@ pub fn build_threadsafe_function_from_function_callee_handle( Ok(()) } + +#[napi] +pub fn create_function(env: &Env) -> Result> { + env.create_function("customFunction", no_export_function_c_callback) +} + +#[napi(no_export)] +pub fn no_export_function(input: u32) -> u32 { + input + 200 +} diff --git a/examples/napi/src/object.rs b/examples/napi/src/object.rs index 96ff3012..9e931abc 100644 --- a/examples/napi/src/object.rs +++ b/examples/napi/src/object.rs @@ -96,7 +96,7 @@ pub fn create_obj_with_property(env: &Env) -> Result> { .with_value(&arraybuffer), Property::new() .with_utf8_name("getter")? - .with_getter(get_c_callback(getter_from_obj_js_function)?), + .with_getter(getter_from_obj_c_callback), ])?; Ok(obj) }