chore(example): remove JsFunction from example (#2123)

* chore(example): remove jsfunction

* feat(napi): implement ToNapiValue for Ref as required by TSFN

* fix(test): tsfn test build failed

* chore: lint code

* fix(test): fix tsfn unit test
This commit is contained in:
Ranger 2024-05-26 21:07:01 +08:00 committed by GitHub
parent a3b01870a3
commit 80d9d87ef9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 101 additions and 148 deletions

View File

@ -1,6 +1,6 @@
use napi::{
CallContext, ContextlessResult, Env, JsBoolean, JsFunction, JsObject, JsString, JsUndefined,
JsUnknown, Result,
bindgen_prelude::Function, CallContext, ContextlessResult, Env, JsBoolean, JsObject, JsString,
JsUndefined, JsUnknown, Result,
};
#[js_function(2)]
@ -48,8 +48,8 @@ pub fn throw_syntax_error(ctx: CallContext) -> Result<JsUndefined> {
let syntax_error = ctx
.env
.get_global()?
.get_named_property::<JsFunction>("SyntaxError")?;
ctx.env.throw(syntax_error.new_instance(&[message])?)?;
.get_named_property::<Function<JsString>>("SyntaxError")?;
ctx.env.throw(syntax_error.new_instance(message)?)?;
ctx.env.get_undefined()
}

View File

@ -1,25 +1,25 @@
use napi::{
bindgen_prelude::Function, CallContext, JsError, JsFunction, JsNull, JsObject, JsUnknown, Result,
bindgen_prelude::Function, CallContext, JsError, JsNull, JsObject, JsString, JsUnknown, Result,
};
#[js_function(1)]
pub fn call_function(ctx: CallContext) -> Result<JsNull> {
let js_func = ctx.get::<JsFunction>(0)?;
let js_func = ctx.get::<Function<(JsUnknown, JsUnknown)>>(0)?;
let js_string_hello = ctx.env.create_string("hello".as_ref())?.into_unknown();
let js_string_world = ctx.env.create_string("world".as_ref())?.into_unknown();
js_func.call(None, &[js_string_hello, js_string_world])?;
js_func.call((js_string_hello, js_string_world))?;
ctx.env.get_null()
}
#[js_function(1)]
pub fn call_function_with_ref_arguments(ctx: CallContext) -> Result<JsNull> {
let js_func = ctx.get::<JsFunction>(0)?;
let js_func = ctx.get::<Function<(JsString, JsString)>>(0)?;
let js_string_hello = ctx.env.create_string("hello".as_ref())?;
let js_string_world = ctx.env.create_string("world".as_ref())?;
js_func.call(None, &[&js_string_hello, &js_string_world])?;
js_func.call((js_string_hello, js_string_world))?;
ctx.env.get_null()
}
@ -27,21 +27,21 @@ pub fn call_function_with_ref_arguments(ctx: CallContext) -> Result<JsNull> {
#[js_function(1)]
pub fn call_function_with_this(ctx: CallContext) -> Result<JsNull> {
let js_this: JsObject = ctx.this_unchecked();
let js_func = ctx.get::<JsFunction>(0)?;
let js_func = ctx.get::<Function<()>>(0)?;
js_func.call_without_args(Some(&js_this))?;
js_func.apply(&js_this, ())?;
ctx.env.get_null()
}
#[js_function(2)]
pub fn call_function_error(ctx: CallContext) -> Result<JsUnknown> {
let js_func = ctx.get::<JsFunction>(0)?;
let error_func = ctx.get::<JsFunction>(1)?;
let js_func = ctx.get::<Function<()>>(0)?;
let error_func = ctx.get::<Function>(1)?;
match js_func.call_without_args(None) {
match js_func.call(()) {
Ok(v) => Ok(v),
Err(e) => error_func.call(None, &[JsError::from(e).into_unknown(*ctx.env)]),
Err(e) => error_func.call(JsError::from(e).into_unknown(*ctx.env)),
}
}

View File

@ -2,25 +2,18 @@ use std::path::Path;
use std::thread;
use napi::{
threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunctionCallMode},
CallContext, Error, JsBoolean, JsFunction, JsNumber, JsObject, JsString, JsUndefined, Ref,
Result, Status,
bindgen_prelude::Function, threadsafe_function::ThreadsafeFunctionCallMode, CallContext, Error,
JsBoolean, JsNumber, JsObject, JsString, JsUndefined, Ref, Result, Status,
};
#[js_function(1)]
pub fn test_threadsafe_function(ctx: CallContext) -> Result<JsUndefined> {
let func = ctx.get::<JsFunction>(0)?;
let func = ctx.get::<Function<Vec<u32>>>(0)?;
let tsfn =
ctx
.env
.create_threadsafe_function(&func, 0, |ctx: ThreadSafeCallContext<Vec<u32>>| {
ctx
.value
.iter()
.map(|v| ctx.env.create_uint32(*v))
.collect::<Result<Vec<JsNumber>>>()
})?;
let tsfn = func
.build_threadsafe_function()
.callee_handled::<true>()
.build()?;
let tsfn_cloned = tsfn.clone();
@ -41,18 +34,9 @@ pub fn test_threadsafe_function(ctx: CallContext) -> Result<JsUndefined> {
#[js_function(1)]
pub fn test_abort_threadsafe_function(ctx: CallContext) -> Result<JsBoolean> {
let func = ctx.get::<JsFunction>(0)?;
let func = ctx.get::<Function<Vec<JsNumber>>>(0)?;
let tsfn =
ctx
.env
.create_threadsafe_function(&func, 0, |ctx: ThreadSafeCallContext<Vec<u32>>| {
ctx
.value
.iter()
.map(|v| ctx.env.create_uint32(*v))
.collect::<Result<Vec<JsNumber>>>()
})?;
let tsfn = func.build_threadsafe_function().build()?;
let tsfn_cloned = tsfn.clone();
@ -62,20 +46,11 @@ pub fn test_abort_threadsafe_function(ctx: CallContext) -> Result<JsBoolean> {
#[js_function(1)]
pub fn test_abort_independent_threadsafe_function(ctx: CallContext) -> Result<JsBoolean> {
let func = ctx.get::<JsFunction>(0)?;
let func = ctx.get::<Function>(0)?;
let tsfn = ctx
.env
.create_threadsafe_function(&func, 0, |ctx: ThreadSafeCallContext<u32>| {
ctx.env.create_uint32(ctx.value).map(|v| vec![v])
})?;
let tsfn = func.build_threadsafe_function().build()?;
let tsfn_other =
ctx
.env
.create_threadsafe_function(&func, 0, |ctx: ThreadSafeCallContext<u32>| {
ctx.env.create_uint32(ctx.value).map(|v| vec![v])
})?;
let tsfn_other = func.build_threadsafe_function().build()?;
tsfn_other.abort()?;
ctx.env.get_boolean(tsfn.aborted())
@ -83,30 +58,25 @@ pub fn test_abort_independent_threadsafe_function(ctx: CallContext) -> Result<Js
#[js_function(1)]
pub fn test_call_aborted_threadsafe_function(ctx: CallContext) -> Result<JsUndefined> {
let func = ctx.get::<JsFunction>(0)?;
let func = ctx.get::<Function<u32>>(0)?;
let tsfn = ctx
.env
.create_threadsafe_function(&func, 0, |ctx: ThreadSafeCallContext<u32>| {
ctx.env.create_uint32(ctx.value).map(|v| vec![v])
})?;
let tsfn = func.build_threadsafe_function().build()?;
let tsfn_clone = tsfn.clone();
tsfn_clone.abort()?;
let call_status = tsfn.call(Ok(1), ThreadsafeFunctionCallMode::NonBlocking);
let call_status = tsfn.call(1, ThreadsafeFunctionCallMode::NonBlocking);
assert!(call_status != Status::Ok);
ctx.env.get_undefined()
}
#[js_function(1)]
pub fn test_tsfn_error(ctx: CallContext) -> Result<JsUndefined> {
let func = ctx.get::<JsFunction>(0)?;
let tsfn = ctx
.env
.create_threadsafe_function(&func, 0, |ctx: ThreadSafeCallContext<()>| {
ctx.env.get_undefined().map(|v| vec![v])
})?;
let func = ctx.get::<Function<Option<Error>>>(0)?;
let tsfn = func
.build_threadsafe_function()
.callee_handled::<true>()
.build()?;
thread::spawn(move || {
tsfn.call(
Err(Error::new(Status::GenericFailure, "invalid".to_owned())),
@ -126,18 +96,18 @@ async fn read_file_content(filepath: &Path) -> Result<Vec<u8>> {
#[js_function(2)]
pub fn test_tokio_readfile(ctx: CallContext) -> Result<JsUndefined> {
let js_filepath = ctx.get::<JsString>(0)?;
let js_func = ctx.get::<JsFunction>(1)?;
let js_func = ctx.get::<Function<Vec<u8>>>(1)?;
let path_str = js_filepath.into_utf8()?.into_owned()?;
let tsfn =
ctx
.env
.create_threadsafe_function(&js_func, 0, |ctx: ThreadSafeCallContext<Vec<u8>>| {
ctx
.env
.create_buffer_with_data(ctx.value)
.map(|v| v.into_raw())
})?;
let tsfn = js_func
.build_threadsafe_function()
.callee_handled::<true>()
.build_callback(move |ctx| {
ctx
.env
.create_buffer_with_data(ctx.value)
.map(|v| v.into_raw())
})?;
let rt = tokio::runtime::Runtime::new()
.map_err(|e| Error::from_reason(format!("Create tokio runtime failed {}", e)))?;
@ -149,24 +119,23 @@ pub fn test_tokio_readfile(ctx: CallContext) -> Result<JsUndefined> {
ctx.env.get_undefined()
}
#[js_function(2)]
#[js_function(3)]
pub fn test_tsfn_with_ref(ctx: CallContext) -> Result<JsUndefined> {
let callback = ctx.get::<JsFunction>(0)?;
let callback: Function<Ref<()>, napi::JsUnknown> = ctx.get::<Function<Ref<()>>>(0)?;
let options = ctx.get::<JsObject>(1)?;
let options_ref = ctx.env.create_reference(options)?;
let tsfn = ctx.env.create_threadsafe_function(
&callback,
0,
|mut ctx: ThreadSafeCallContext<Ref<()>>| {
let option_ref = ctx.env.create_reference(options);
let tsfn = callback
.build_threadsafe_function()
.callee_handled::<true>()
.build_callback(move |mut ctx| {
ctx
.env
.get_reference_value_unchecked::<JsObject>(&ctx.value)
.and_then(|obj| ctx.value.unref(ctx.env).map(|_| obj))
},
)?;
})?;
thread::spawn(move || {
tsfn.call(Ok(options_ref), ThreadsafeFunctionCallMode::Blocking);
tsfn.call(option_ref, ThreadsafeFunctionCallMode::Blocking);
});
ctx.env.get_undefined()

View File

@ -1,24 +1,20 @@
use napi::{
threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction},
CallContext, JsFunction, JsObject, JsUndefined,
bindgen_prelude::Function, threadsafe_function::ThreadsafeFunction, CallContext, JsObject,
JsUndefined,
};
use napi_derive::js_function;
#[derive(Clone)]
pub struct A {
pub cb: ThreadsafeFunction<String>,
pub cb: ThreadsafeFunction<String, napi::JsUnknown, String, false>,
}
#[js_function(1)]
pub fn constructor(ctx: CallContext) -> napi::Result<JsUndefined> {
let callback = ctx.get::<JsFunction>(0)?;
let callback = ctx.get::<Function<String>>(0)?;
let cb =
ctx
.env
.create_threadsafe_function(&callback, 0, |ctx: ThreadSafeCallContext<String>| {
Ok(ctx.value)
})?;
let cb: ThreadsafeFunction<String, napi::JsUnknown, String, false> =
callback.build_threadsafe_function().build()?;
let mut this: JsObject = ctx.this_unchecked();
let obj = A { cb };
@ -32,7 +28,7 @@ pub fn call(ctx: CallContext) -> napi::Result<JsUndefined> {
let this = ctx.this_unchecked();
let obj = ctx.env.unwrap::<A>(&this)?;
obj.cb.call(
Ok("ThreadsafeFunction NonBlocking Call".to_owned()),
"ThreadsafeFunction NonBlocking Call".to_owned(),
napi::threadsafe_function::ThreadsafeFunctionCallMode::NonBlocking,
);
ctx.env.get_undefined()

View File

@ -281,9 +281,9 @@ Generated by [AVA](https://avajs.dev).
export declare function appendBuffer(buf: Buffer): Buffer␊
export declare function apply0(ctx: Animal, callback: (...args: any[]) => any): void␊
export declare function apply0(ctx: Animal, callback: () => void): void␊
export declare function apply1(ctx: Animal, callback: (...args: any[]) => any, name: string): void␊
export declare function apply1(ctx: Animal, callback: (arg: string) => void, name: string): void␊
export declare function arrayBufferPassThrough(buf: Uint8Array): Promise<Uint8Array>
@ -323,11 +323,11 @@ Generated by [AVA](https://avajs.dev).
baz: number␊
}␊
export declare function call0(callback: (...args: any[]) => any): number␊
export declare function call0(callback: () => number): number␊
export declare function call1(callback: (...args: any[]) => any, arg: number): number␊
export declare function call1(callback: (arg: number) => number, arg: number): number␊
export declare function call2(callback: (...args: any[]) => any, arg1: number, arg2: number): number␊
export declare function call2(callback: (arg0: number, arg1: number) => number, arg1: number, arg2: number): number␊
export declare function callbackReturnPromise<T>(functionInput: () => T | Promise<T>, callback: (err: Error | null, result: T) => void): T | Promise<T>
@ -444,7 +444,7 @@ Generated by [AVA](https://avajs.dev).
export declare function either4(input: string | number | boolean | Obj): number␊
export declare function eitherBoolOrFunction(input: boolean | ((...args: any[]) => any)): void␊
export declare function eitherBoolOrFunction(input: boolean | (any)): void␊
export declare function eitherBoolOrTuple(input: boolean | [boolean, string]): void␊

View File

@ -271,9 +271,9 @@ export interface AllOptionalObject {
export declare function appendBuffer(buf: Buffer): Buffer
export declare function apply0(ctx: Animal, callback: (...args: any[]) => any): void
export declare function apply0(ctx: Animal, callback: () => void): void
export declare function apply1(ctx: Animal, callback: (...args: any[]) => any, name: string): void
export declare function apply1(ctx: Animal, callback: (arg: string) => void, name: string): void
export declare function arrayBufferPassThrough(buf: Uint8Array): Promise<Uint8Array>
@ -313,11 +313,11 @@ export interface C {
baz: number
}
export declare function call0(callback: (...args: any[]) => any): number
export declare function call0(callback: () => number): number
export declare function call1(callback: (...args: any[]) => any, arg: number): number
export declare function call1(callback: (arg: number) => number, arg: number): number
export declare function call2(callback: (...args: any[]) => any, arg1: number, arg2: number): number
export declare function call2(callback: (arg0: number, arg1: number) => number, arg1: number, arg2: number): number
export declare function callbackReturnPromise<T>(functionInput: () => T | Promise<T>, callback: (err: Error | null, result: T) => void): T | Promise<T>
@ -434,7 +434,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 | ((...args: any[]) => any)): void
export declare function eitherBoolOrFunction(input: boolean | (any)): void
export declare function eitherBoolOrTuple(input: boolean | [boolean, string]): void

View File

@ -1,10 +1,6 @@
use std::{env, format};
use napi::{
bindgen_prelude::*,
threadsafe_function::{ThreadsafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode},
JsUnknown,
};
use napi::{bindgen_prelude::*, threadsafe_function::ThreadsafeFunctionCallMode, JsUnknown};
#[napi]
fn get_cwd<T: Fn(String) -> Result<()>>(callback: T) {
@ -54,13 +50,15 @@ fn read_file_content() -> Result<String> {
fn callback_return_promise<T: Fn() -> Result<JsUnknown>>(
env: Env,
fn_in: T,
fn_out: JsFunction,
fn_out: Function<String, ()>,
) -> Result<JsUnknown> {
let ret = fn_in()?;
if ret.is_promise()? {
let p = Promise::<String>::from_unknown(ret)?;
let fn_out_tsfn: ThreadsafeFunction<String> =
fn_out.create_threadsafe_function(|ctx: ThreadsafeCallContext<String>| Ok(ctx.value))?;
let fn_out_tsfn = fn_out
.build_threadsafe_function()
.callee_handled::<true>()
.build()?;
env
.execute_tokio_future(
async move {

View File

@ -131,7 +131,7 @@ pub fn either_from_objects(input: Either3<A, B, C>) -> String {
}
#[napi]
pub fn either_bool_or_function(_input: Either<bool, JsFunction>) {}
pub fn either_bool_or_function(_input: Either<bool, Function>) {}
#[napi]
pub async fn promise_in_either(input: Either<u32, Promise<u32>>) -> Result<bool> {

View File

@ -59,13 +59,8 @@ fn validate_external(e: &External<u32>) -> u32 {
}
#[napi(strict, ts_args_type = "cb: () => number")]
fn validate_function(cb: JsFunction) -> Result<u32> {
Ok(
cb.call::<JsUnknown>(None, &[])?
.coerce_to_number()?
.get_uint32()?
+ 3,
)
fn validate_function(cb: Function<(), JsUnknown>) -> Result<u32> {
Ok(cb.call(())?.coerce_to_number()?.get_uint32()? + 3)
}
#[napi(strict)]

View File

@ -1,5 +1,4 @@
use napi::bindgen_prelude::{Object, Result};
use napi::JsFunction;
use napi::bindgen_prelude::{Function, Object, Result};
#[napi(ts_args_type = "a: { foo: number }", ts_return_type = "string[]")]
fn ts_rename(a: Object) -> Result<Object> {
@ -9,20 +8,12 @@ fn ts_rename(a: Object) -> Result<Object> {
#[napi]
fn override_individual_arg_on_function(
not_overridden: String,
#[napi(ts_arg_type = "() => string")] f: JsFunction,
#[napi(ts_arg_type = "() => string")] f: Function<(), String>,
not_overridden2: u32,
) -> String {
let u = f.call_without_args(None).unwrap();
let s = u
.coerce_to_string()
.unwrap()
.into_utf8()
.unwrap()
.as_str()
.unwrap()
.to_string();
let u = f.call(()).unwrap();
format!("oia: {}-{}-{}", not_overridden, not_overridden2, s)
format!("oia: {}-{}-{}", not_overridden, not_overridden2, u)
}
#[napi]

View File

@ -3,34 +3,38 @@
use napi::{
bindgen_prelude::{ClassInstance, Function, FunctionRef},
threadsafe_function::ThreadsafeFunctionCallMode,
Env, Error, JsFunction, JsObject, Result, Status,
Env, Error, JsObject, Result, Status,
};
use crate::class::Animal;
#[napi]
pub fn call0(callback: JsFunction) -> Result<u32> {
callback.call0()
pub fn call0(callback: Function<(), u32>) -> Result<u32> {
callback.call(())
}
#[napi]
pub fn call1(callback: JsFunction, arg: u32) -> Result<u32> {
callback.call1(arg)
pub fn call1(callback: Function<u32, u32>, arg: u32) -> Result<u32> {
callback.call(arg)
}
#[napi]
pub fn call2(callback: JsFunction, arg1: u32, arg2: u32) -> Result<u32> {
callback.call2(arg1, arg2)
pub fn call2(callback: Function<(u32, u32), u32>, arg1: u32, arg2: u32) -> Result<u32> {
callback.call((arg1, arg2))
}
#[napi]
pub fn apply0(ctx: ClassInstance<Animal>, callback: JsFunction) -> Result<()> {
callback.apply0(ctx)
pub fn apply0(ctx: ClassInstance<Animal>, callback: Function<(), ()>) -> Result<()> {
callback.apply(ctx, ())
}
#[napi]
pub fn apply1(ctx: ClassInstance<Animal>, callback: JsFunction, name: String) -> Result<()> {
callback.apply1(ctx, name)
pub fn apply1(
ctx: ClassInstance<Animal>,
callback: Function<String, ()>,
name: String,
) -> Result<()> {
callback.apply(ctx, name)
}
#[napi]