Skip to content

Commit 5ac1533

Browse files
authored
feat(napi): accept slice as TypedArray (#1951)
1 parent d7dc4dc commit 5ac1533

8 files changed

Lines changed: 244 additions & 12 deletions

File tree

crates/backend/src/codegen/fn.rs

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ impl NapiFn {
268268
if let Some(p) = path.path.segments.first() {
269269
if p.ident == *self.parent.as_ref().unwrap() {
270270
args.push(
271-
quote! { napi::bindgen_prelude::Reference::from_value_ptr(this_ptr as *mut std::ffi::c_void, env)? },
271+
quote! { napi::bindgen_prelude::Reference::from_value_ptr(this_ptr.cast(), env)? },
272272
);
273273
skipped_arg_count += 1;
274274
continue;
@@ -344,7 +344,7 @@ impl NapiFn {
344344
}
345345
}
346346
}
347-
let (arg_conversion, arg_type) = self.gen_ty_arg_conversion(&ident, i, path);
347+
let (arg_conversion, arg_type) = self.gen_ty_arg_conversion(&ident, i, path)?;
348348
if NapiArgType::MutRef == arg_type {
349349
mut_ref_spans.push(path.ty.span());
350350
}
@@ -378,7 +378,7 @@ impl NapiFn {
378378
arg_name: &Ident,
379379
index: usize,
380380
path: &syn::PatType,
381-
) -> (TokenStream, NapiArgType) {
381+
) -> BindgenResult<(TokenStream, NapiArgType)> {
382382
let ty = &*path.ty;
383383
let type_check = if self.return_if_invalid {
384384
quote! {
@@ -402,6 +402,15 @@ impl NapiFn {
402402
};
403403

404404
match ty {
405+
syn::Type::Reference(syn::TypeReference {
406+
lifetime: Some(lifetime),
407+
..
408+
}) => {
409+
return Err(Diagnostic::span_error(
410+
lifetime.span(),
411+
"lifetime is not allowed in napi function arguments",
412+
));
413+
}
405414
syn::Type::Reference(syn::TypeReference {
406415
mutability: Some(_),
407416
elem,
@@ -413,16 +422,52 @@ impl NapiFn {
413422
<#elem as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.get_arg(#index))?
414423
};
415424
};
416-
(q, NapiArgType::MutRef)
425+
Ok((q, NapiArgType::MutRef))
417426
}
418-
syn::Type::Reference(syn::TypeReference { elem, .. }) => {
419-
let q = quote! {
420-
let #arg_name = {
421-
#type_check
422-
<#elem as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.get_arg(#index))?
423-
};
427+
syn::Type::Reference(syn::TypeReference {
428+
mutability, elem, ..
429+
}) => {
430+
if let syn::Type::Slice(slice) = &**elem {
431+
if let syn::Type::Path(ele) = &*slice.elem {
432+
if let Some(syn::PathSegment { ident, .. }) = ele.path.segments.first() {
433+
static TYPEDARRAY_SLICE_TYPES: &[&str] = &[
434+
"u8", "i8", "u16", "i16", "u32", "i32", "f32", "f64", "u64", "i64",
435+
];
436+
if TYPEDARRAY_SLICE_TYPES.contains(&&*ident.to_string()) {
437+
let q = quote! {
438+
let #arg_name = {
439+
#type_check
440+
<&mut #elem as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#index))?
441+
};
442+
};
443+
return Ok((q, NapiArgType::Ref));
444+
}
445+
}
446+
}
447+
}
448+
let q = if mutability.is_some() {
449+
quote! {
450+
let #arg_name = {
451+
#type_check
452+
<#elem as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.get_arg(#index))?
453+
}
454+
}
455+
} else {
456+
quote! {
457+
let #arg_name = {
458+
#type_check
459+
<#elem as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.get_arg(#index))?
460+
};
461+
}
424462
};
425-
(q, NapiArgType::Ref)
463+
Ok((
464+
q,
465+
if mutability.is_some() {
466+
NapiArgType::MutRef
467+
} else {
468+
NapiArgType::Ref
469+
},
470+
))
426471
}
427472
_ => {
428473
let q = quote! {
@@ -431,7 +476,7 @@ impl NapiFn {
431476
<#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#index))?
432477
};
433478
};
434-
(q, NapiArgType::Value)
479+
Ok((q, NapiArgType::Value))
435480
}
436481
}
437482
}

crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,46 @@ macro_rules! impl_typed_array {
457457
};
458458
}
459459

460+
macro_rules! impl_from_slice {
461+
($name:ident, $rust_type:ident, $typed_array_type:expr) => {
462+
impl FromNapiValue for &mut [$rust_type] {
463+
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
464+
let mut typed_array_type = 0;
465+
let mut length = 0;
466+
let mut data = ptr::null_mut();
467+
let mut array_buffer = ptr::null_mut();
468+
let mut byte_offset = 0;
469+
let mut ref_ = ptr::null_mut();
470+
check_status!(
471+
unsafe { sys::napi_create_reference(env, napi_val, 1, &mut ref_) },
472+
"Failed to create reference from Buffer"
473+
)?;
474+
check_status!(
475+
unsafe {
476+
sys::napi_get_typedarray_info(
477+
env,
478+
napi_val,
479+
&mut typed_array_type,
480+
&mut length,
481+
&mut data,
482+
&mut array_buffer,
483+
&mut byte_offset,
484+
)
485+
},
486+
"Get TypedArray info failed"
487+
)?;
488+
if typed_array_type != $typed_array_type as i32 {
489+
return Err(Error::new(
490+
Status::InvalidArg,
491+
format!("Expected $name, got {}", typed_array_type),
492+
));
493+
}
494+
Ok(unsafe { core::slice::from_raw_parts_mut(data as *mut $rust_type, length) })
495+
}
496+
}
497+
};
498+
}
499+
460500
unsafe extern "C" fn finalizer<Data, T: Finalizer<RustType = Data>>(
461501
_env: sys::napi_env,
462502
finalize_data: *mut c_void,
@@ -494,18 +534,30 @@ enum DataManagedType {
494534
}
495535

496536
impl_typed_array!(Int8Array, i8, TypedArrayType::Int8);
537+
impl_from_slice!(Int8Array, i8, TypedArrayType::Int8);
497538
impl_typed_array!(Uint8Array, u8, TypedArrayType::Uint8);
539+
impl_from_slice!(Uint8Array, u8, TypedArrayType::Uint8);
498540
impl_typed_array!(Uint8ClampedArray, u8, TypedArrayType::Uint8Clamped);
499541
impl_typed_array!(Int16Array, i16, TypedArrayType::Int16);
542+
impl_from_slice!(Int16Array, i16, TypedArrayType::Int16);
500543
impl_typed_array!(Uint16Array, u16, TypedArrayType::Uint16);
544+
impl_from_slice!(Uint16Array, u16, TypedArrayType::Uint16);
501545
impl_typed_array!(Int32Array, i32, TypedArrayType::Int32);
546+
impl_from_slice!(Int32Array, i32, TypedArrayType::Int32);
502547
impl_typed_array!(Uint32Array, u32, TypedArrayType::Uint32);
548+
impl_from_slice!(Uint32Array, u32, TypedArrayType::Uint32);
503549
impl_typed_array!(Float32Array, f32, TypedArrayType::Float32);
550+
impl_from_slice!(Float32Array, f32, TypedArrayType::Float32);
504551
impl_typed_array!(Float64Array, f64, TypedArrayType::Float64);
552+
impl_from_slice!(Float64Array, f64, TypedArrayType::Float64);
505553
#[cfg(feature = "napi6")]
506554
impl_typed_array!(BigInt64Array, i64, TypedArrayType::BigInt64);
507555
#[cfg(feature = "napi6")]
556+
impl_from_slice!(BigInt64Array, i64, TypedArrayType::BigInt64);
557+
#[cfg(feature = "napi6")]
508558
impl_typed_array!(BigUint64Array, u64, TypedArrayType::BigUint64);
559+
#[cfg(feature = "napi6")]
560+
impl_from_slice!(BigUint64Array, u64, TypedArrayType::BigUint64);
509561

510562
impl<T: Into<Vec<u8>>> From<T> for Uint8Array {
511563
fn from(data: T) -> Self {

examples/napi/__tests__/__snapshots__/typegen.spec.ts.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ Generated by [AVA](https://avajs.dev).
8585
constructor(name: string)␊
8686
getCount(): number␊
8787
getNameAsync(): Promise<string>␊
88+
acceptSliceMethod(slice: any): number␊
8889
}␊
8990
9091
/** Smoking test for type generation */␊
@@ -221,6 +222,8 @@ Generated by [AVA](https://avajs.dev).
221222
foo: number␊
222223
}␊
223224
225+
export function acceptSlice(fixture: any): bigint␊
226+
224227
export function acceptThreadsafeFunction(func: (err: Error | null, arg: number) => any): void␊
225228
226229
export function acceptThreadsafeFunctionFatal(func: (arg: number) => any): void␊
@@ -388,6 +391,10 @@ Generated by [AVA](https://avajs.dev).
388391
389392
export function enumToI32(e: CustomNumEnum): number␊
390393
394+
export function f32ArrayToArray(input: any): Array<number>␊
395+
396+
export function f64ArrayToArray(input: any): Array<number>␊
397+
391398
export function fibonacci(n: number): number␊
392399
393400
export function fnReceivedAliased(s: AliasedStruct, e: ALIAS): void␊
@@ -429,6 +436,14 @@ Generated by [AVA](https://avajs.dev).
429436
430437
export function getWords(): Array<string>␊
431438
439+
export function i16ArrayToArray(input: any): Array<number>␊
440+
441+
export function i32ArrayToArray(input: any): Array<number>␊
442+
443+
export function i64ArrayToArray(input: any): Array<number>␊
444+
445+
export function i8ArrayToArray(input: any): Array<number>␊
446+
432447
export function indexmapPassthrough(fixture: IndexMap): IndexMap␊
433448
434449
/** default enum values are continuos i32s start from 0 */␊
@@ -595,6 +610,14 @@ Generated by [AVA](https://avajs.dev).
595610
typeOverrideOptional?: object␊
596611
}␊
597612
613+
export function u16ArrayToArray(input: any): Array<number>␊
614+
615+
export function u32ArrayToArray(input: any): Array<number>␊
616+
617+
export function u64ArrayToArray(input: any): Array<bigint>␊
618+
619+
export function u8ArrayToArray(input: any): Array<number>␊
620+
598621
export function validateArray(arr: Array<number>): number␊
599622
600623
export function validateBigint(input: bigint): bigint␊
79 Bytes
Binary file not shown.

examples/napi/__tests__/values.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,17 @@ const {
9494
xxh3,
9595
xxh64Alias,
9696
tsRename,
97+
acceptSlice,
98+
u8ArrayToArray,
99+
i8ArrayToArray,
100+
u16ArrayToArray,
101+
i16ArrayToArray,
102+
u32ArrayToArray,
103+
i32ArrayToArray,
104+
u64ArrayToArray,
105+
i64ArrayToArray,
106+
f32ArrayToArray,
107+
f64ArrayToArray,
97108
convertU32Array,
98109
createExternalTypedArray,
99110
mutateTypedArray,
@@ -665,6 +676,24 @@ test('buffer', (t) => {
665676
t.true(Array.isArray(asyncBufferToArray(Buffer.from([1, 2, 3]).buffer)))
666677
})
667678

679+
test('TypedArray', (t) => {
680+
t.is(acceptSlice(new Uint8Array([1, 2, 3])), 3n)
681+
t.deepEqual(u8ArrayToArray(new Uint8Array([1, 2, 3])), [1, 2, 3])
682+
t.deepEqual(i8ArrayToArray(new Int8Array([1, 2, 3])), [1, 2, 3])
683+
t.deepEqual(u16ArrayToArray(new Uint16Array([1, 2, 3])), [1, 2, 3])
684+
t.deepEqual(i16ArrayToArray(new Int16Array([1, 2, 3])), [1, 2, 3])
685+
t.deepEqual(u32ArrayToArray(new Uint32Array([1, 2, 3])), [1, 2, 3])
686+
t.deepEqual(i32ArrayToArray(new Int32Array([1, 2, 3])), [1, 2, 3])
687+
t.deepEqual(u64ArrayToArray(new BigUint64Array([1n, 2n, 3n])), [1n, 2n, 3n])
688+
t.deepEqual(i64ArrayToArray(new BigInt64Array([1n, 2n, 3n])), [1, 2, 3])
689+
t.deepEqual(f32ArrayToArray(new Float32Array([1, 2, 3])), [1, 2, 3])
690+
t.deepEqual(f64ArrayToArray(new Float64Array([1, 2, 3])), [1, 2, 3])
691+
692+
const bird = new Bird('Carolyn')
693+
694+
t.is(bird.acceptSliceMethod(new Uint8Array([1, 2, 3])), 3)
695+
})
696+
668697
test('reset empty buffer', (t) => {
669698
const empty = getEmptyBuffer()
670699

examples/napi/index.d.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export class Bird {
7575
constructor(name: string)
7676
getCount(): number
7777
getNameAsync(): Promise<string>
78+
acceptSliceMethod(slice: any): number
7879
}
7980

8081
/** Smoking test for type generation */
@@ -211,6 +212,8 @@ export interface A {
211212
foo: number
212213
}
213214

215+
export function acceptSlice(fixture: any): bigint
216+
214217
export function acceptThreadsafeFunction(func: (err: Error | null, arg: number) => any): void
215218

216219
export function acceptThreadsafeFunctionFatal(func: (arg: number) => any): void
@@ -378,6 +381,10 @@ export const enum Empty {
378381

379382
export function enumToI32(e: CustomNumEnum): number
380383

384+
export function f32ArrayToArray(input: any): Array<number>
385+
386+
export function f64ArrayToArray(input: any): Array<number>
387+
381388
export function fibonacci(n: number): number
382389

383390
export function fnReceivedAliased(s: AliasedStruct, e: ALIAS): void
@@ -419,6 +426,14 @@ export function getUndefined(): void
419426

420427
export function getWords(): Array<string>
421428

429+
export function i16ArrayToArray(input: any): Array<number>
430+
431+
export function i32ArrayToArray(input: any): Array<number>
432+
433+
export function i64ArrayToArray(input: any): Array<number>
434+
435+
export function i8ArrayToArray(input: any): Array<number>
436+
422437
export function indexmapPassthrough(fixture: IndexMap): IndexMap
423438

424439
/** default enum values are continuos i32s start from 0 */
@@ -585,6 +600,14 @@ export interface TsTypeChanged {
585600
typeOverrideOptional?: object
586601
}
587602

603+
export function u16ArrayToArray(input: any): Array<number>
604+
605+
export function u32ArrayToArray(input: any): Array<number>
606+
607+
export function u64ArrayToArray(input: any): Array<bigint>
608+
609+
export function u8ArrayToArray(input: any): Array<number>
610+
588611
export function validateArray(arr: Array<number>): number
589612

590613
export function validateBigint(input: bigint): bigint

examples/napi/src/class.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ impl Bird {
130130
tokio::time::sleep(std::time::Duration::new(1, 0)).await;
131131
self.name.as_str()
132132
}
133+
134+
#[napi]
135+
pub fn accept_slice_method(&self, slice: &[u8]) -> u32 {
136+
slice.len() as u32
137+
}
133138
}
134139

135140
/// Smoking test for type generation

0 commit comments

Comments
 (0)