mirror of
https://github.com/napi-rs/napi-rs.git
synced 2025-12-08 19:56:07 +00:00
feat!(napi): create function from #[napi] fn (#2757)
This commit is contained in:
parent
6222b39f5a
commit
7e34e30b66
@ -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)]
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
|
||||
@ -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<napi::bindgen_prelude::sys::napi_value> {
|
||||
@ -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<napi::bindgen_prelude::sys::napi_value> {
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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::<Vec<_>>()
|
||||
};
|
||||
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))
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ impl FromIterator<FnArg> for FnArgList {
|
||||
|
||||
impl ToTypeDef for NapiFn {
|
||||
fn to_type_def(&self) -> Option<TypeDef> {
|
||||
if self.skip_typescript || self.module_exports {
|
||||
if self.skip_typescript || self.module_exports || self.no_export {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
@ -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)),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<ModuleRegisterCallback> = 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<RegisteredClasses> = LazyCell::new(Default::default);
|
||||
static FN_REGISTER_MAP: LazyCell<FnRegisterMap> = LazyCell::new(Default::default);
|
||||
}
|
||||
#[cfg(all(feature = "napi4", not(feature = "noop")))]
|
||||
pub(crate) static CUSTOM_GC_TSFN: std::sync::atomic::AtomicPtr<sys::napi_threadsafe_function__> =
|
||||
@ -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<sys::napi_ref> {
|
||||
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<JsObject> {
|
||||
/// 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<crate::Callback> {
|
||||
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(
|
||||
|
||||
@ -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␊
|
||||
␊
|
||||
|
||||
Binary file not shown.
@ -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))
|
||||
})
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -459,6 +459,8 @@ export declare function createExternalString(content: string): ExternalObject<st
|
||||
|
||||
export declare function createExternalTypedArray(): Uint32Array
|
||||
|
||||
export declare function createFunction(): (arg: number) => 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
|
||||
|
||||
|
||||
@ -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<Function<u32, u32>> {
|
||||
env.create_function("customFunction", no_export_function_c_callback)
|
||||
}
|
||||
|
||||
#[napi(no_export)]
|
||||
pub fn no_export_function(input: u32) -> u32 {
|
||||
input + 200
|
||||
}
|
||||
|
||||
@ -96,7 +96,7 @@ pub fn create_obj_with_property(env: &Env) -> Result<Object<'_>> {
|
||||
.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)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user