From 1a7cff167e1185f154517c8584419cad76974e75 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Wed, 6 Jul 2022 19:08:34 +0800 Subject: [PATCH] feat(napi): clone reference for TypedArray/Buffer --- crates/backend/src/codegen/struct.rs | 42 ++++- .../bindgen_runtime/js_values/arraybuffer.rs | 166 ++++++++++++++++-- .../src/bindgen_runtime/js_values/buffer.rs | 51 ++++-- examples/napi/__test__/typegen.spec.ts.md | 3 + examples/napi/__test__/typegen.spec.ts.snap | Bin 3376 -> 3409 bytes examples/napi/__test__/values.spec.ts | 15 ++ examples/napi/index.d.ts | 3 + examples/napi/src/class.rs | 14 +- examples/napi/src/typed_array.rs | 5 + 9 files changed, 267 insertions(+), 32 deletions(-) diff --git a/crates/backend/src/codegen/struct.rs b/crates/backend/src/codegen/struct.rs index bde440fd..9a63ca3d 100644 --- a/crates/backend/src/codegen/struct.rs +++ b/crates/backend/src/codegen/struct.rs @@ -10,6 +10,19 @@ use crate::{ }; static NAPI_IMPL_ID: AtomicU32 = AtomicU32::new(0); +const TYPED_ARRAY_TYPE: &[&str] = &[ + "Int8Array", + "Uint8Array", + "Uint8ClampedArray", + "Int16Array", + "Uint16Array", + "Int32Array", + "Uint32Array", + "Float32Array", + "Float64Array", + "BigInt64Array", + "BigUint64Array", +]; // Generate trait implementations for given Struct. fn gen_napi_value_map_impl(name: &Ident, to_napi_val_impl: TokenStream) -> TokenStream { @@ -584,6 +597,30 @@ impl NapiStruct { let setter_name = Ident::new(&format!("set_{}", field_name), Span::call_site()); if field.getter { + let default_to_napi_value_convert = quote! { + let val = obj.#field_ident.to_owned(); + unsafe { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, val) } + }; + let to_napi_value_convert = if let syn::Type::Path(syn::TypePath { + path: syn::Path { segments, .. }, + .. + }) = ty + { + if let Some(syn::PathSegment { ident, .. }) = segments.last() { + if TYPED_ARRAY_TYPE.iter().any(|name| ident == name) || ident == "Buffer" { + quote! { + let val = &mut obj.#field_ident; + unsafe { <&mut #ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, val) } + } + } else { + default_to_napi_value_convert + } + } else { + default_to_napi_value_convert + } + } else { + default_to_napi_value_convert + }; getters_setters.push(( field.js_name.clone(), quote! { @@ -592,10 +629,9 @@ impl NapiStruct { cb: napi::bindgen_prelude::sys::napi_callback_info ) -> napi::bindgen_prelude::sys::napi_value { napi::bindgen_prelude::CallbackInfo::<0>::new(env, cb, Some(0)) - .and_then(|mut cb| unsafe { cb.unwrap_borrow::<#struct_name>() }) + .and_then(|mut cb| unsafe { cb.unwrap_borrow_mut::<#struct_name>() }) .and_then(|obj| { - let val = obj.#field_ident.to_owned(); - unsafe { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, val) } + #to_napi_value_convert }) .unwrap_or_else(|e| { unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) }; diff --git a/crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs b/crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs index 9ce890c7..22f0be4c 100644 --- a/crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs +++ b/crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs @@ -2,31 +2,87 @@ use std::ffi::c_void; use std::mem; use std::ops::{Deref, DerefMut}; use std::ptr; +use std::sync::Arc; pub use crate::js_values::TypedArrayType; use crate::{check_status, sys, Error, Result, Status}; use super::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue}; +trait Finalizer { + type RustType; + + fn finalizer(&mut self) -> Box; + + fn data_managed_type(&self) -> &DataManagedType; + + fn len(&self) -> &usize; + + fn ref_count(&self) -> usize; +} + macro_rules! impl_typed_array { ($name:ident, $rust_type:ident, $typed_array_type:expr) => { pub struct $name { + /// Used for `clone_reference` Owned | External TypedArray + data_reference: Option>, data: *mut $rust_type, length: usize, data_managed_type: DataManagedType, byte_offset: usize, + raw: Option<(crate::sys::napi_ref, crate::sys::napi_env)>, finalizer_notify: Box, } + unsafe impl Send for $name {} + + impl Finalizer for $name { + type RustType = $rust_type; + + fn finalizer(&mut self) -> Box { + std::mem::replace(&mut self.finalizer_notify, Box::new($name::noop_finalize)) + } + + fn data_managed_type(&self) -> &DataManagedType { + &self.data_managed_type + } + + fn len(&self) -> &usize { + &self.length + } + + fn ref_count(&self) -> usize { + if let Some(inner) = &self.data_reference { + Arc::strong_count(inner) + } else { + 0 + } + } + } + + impl Drop for $name { + fn drop(&mut self) { + if let Some((ref_, env)) = self.raw { + crate::check_status_or_throw!( + env, + unsafe { sys::napi_reference_unref(env, ref_, &mut 0) }, + "Failed to delete Buffer reference in drop" + ); + } + } + } + impl $name { fn noop_finalize(_data: *mut $rust_type, _length: usize) {} pub fn new(mut data: Vec<$rust_type>) -> Self { let ret = $name { + data_reference: Some(Arc::new(())), data: data.as_mut_ptr(), length: data.len(), data_managed_type: DataManagedType::Owned, byte_offset: 0, + raw: None, finalizer_notify: Box::new(Self::noop_finalize), }; mem::forget(data); @@ -39,10 +95,12 @@ macro_rules! impl_typed_array { { let mut data_copied = data.as_ref().to_vec(); let ret = $name { + data_reference: Some(Arc::new(())), data: data_copied.as_mut_ptr(), length: data.as_ref().len(), data_managed_type: DataManagedType::Owned, finalizer_notify: Box::new(Self::noop_finalize), + raw: None, byte_offset: 0, }; mem::forget(data_copied); @@ -57,13 +115,40 @@ macro_rules! impl_typed_array { F: 'static + FnOnce(*mut $rust_type, usize), { $name { + data_reference: Some(Arc::new(())), data, length, data_managed_type: DataManagedType::External, finalizer_notify: Box::new(notify), + raw: None, byte_offset: 0, } } + + /// Clone reference, the inner data is not copied nor moved + pub fn clone(&mut self, env: crate::Env) -> crate::Result<$name> { + let raw = if let Some((ref_, _)) = self.raw { + crate::check_status!( + unsafe { sys::napi_reference_ref(env.0, ref_, &mut 0) }, + "Failed to ref Buffer reference in TypedArray::clone" + )?; + Some((ref_, env.0)) + } else { + None + }; + Ok(Self { + data_reference: match self.data_managed_type { + DataManagedType::Owned | DataManagedType::External => self.data_reference.clone(), + _ => None, + }, + data: self.data, + length: self.length, + data_managed_type: self.data_managed_type, + finalizer_notify: Box::new(Self::noop_finalize), + raw, + byte_offset: self.byte_offset, + }) + } } impl Deref for $name { @@ -110,7 +195,7 @@ macro_rules! impl_typed_array { unsafe fn validate( env: sys::napi_env, napi_val: sys::napi_value, - ) -> Result<$crate::sys::napi_value> { + ) -> Result { let mut is_typed_array = false; check_status!( unsafe { sys::napi_is_typedarray(env, napi_val, &mut is_typed_array) }, @@ -133,6 +218,11 @@ macro_rules! impl_typed_array { let mut data = ptr::null_mut(); let mut array_buffer = ptr::null_mut(); let mut byte_offset = 0; + let mut ref_ = ptr::null_mut(); + check_status!( + unsafe { sys::napi_create_reference(env, napi_val, 1, &mut ref_) }, + "Failed to create reference from Buffer" + )?; check_status!( unsafe { sys::napi_get_typedarray_info( @@ -154,9 +244,11 @@ macro_rules! impl_typed_array { )); } Ok($name { + data_reference: None, data: data as *mut $rust_type, length, byte_offset, + raw: Some((ref_, env)), data_managed_type: DataManagedType::Vm, finalizer_notify: Box::new(Self::noop_finalize), }) @@ -164,15 +256,22 @@ macro_rules! impl_typed_array { } impl ToNapiValue for $name { - unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result { + unsafe fn to_napi_value(env: sys::napi_env, mut val: Self) -> Result { + if let Some((ref_, _)) = val.raw { + let mut napi_value = std::ptr::null_mut(); + check_status!( + unsafe { sys::napi_get_reference_value(env, ref_, &mut napi_value) }, + "Failed to delete reference from Buffer" + )?; + val.raw = None; + return Ok(napi_value); + } let mut arraybuffer_value = ptr::null_mut(); let ratio = mem::size_of::<$rust_type>() / mem::size_of::(); let length = val.length * ratio; - let hint_ptr = Box::into_raw(Box::new(( - val.data_managed_type, - val.length, - val.finalizer_notify, - ))); + let val_data = val.data; + let val_length = val.length; + let hint_ptr = Box::into_raw(Box::new(val)); check_status!( if length == 0 { // Rust uses 0x1 as the data pointer for empty buffers, @@ -185,9 +284,9 @@ macro_rules! impl_typed_array { unsafe { sys::napi_create_external_arraybuffer( env, - val.data as *mut c_void, + val_data as *mut c_void, length, - Some(finalizer::<$rust_type>), + Some(finalizer::<$rust_type, $name>), hint_ptr as *mut c_void, &mut arraybuffer_value, ) @@ -201,7 +300,7 @@ macro_rules! impl_typed_array { sys::napi_create_typedarray( env, $typed_array_type as i32, - val.length, + val_length, arraybuffer_value, 0, &mut napi_val, @@ -212,31 +311,64 @@ macro_rules! impl_typed_array { Ok(napi_val) } } + + impl ToNapiValue for &mut $name { + unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result { + if let Some((ref_, _)) = val.raw { + let mut napi_value = std::ptr::null_mut(); + check_status!( + unsafe { sys::napi_get_reference_value(env, ref_, &mut napi_value) }, + "Failed to delete reference from Buffer" + )?; + Ok(napi_value) + } else { + let cloned_value = $name { + data_reference: match val.data_managed_type { + DataManagedType::Owned | DataManagedType::External => val.data_reference.clone(), + _ => None, + }, + data: val.data, + length: val.length, + data_managed_type: val.data_managed_type, + finalizer_notify: Box::new($name::noop_finalize), + raw: None, + byte_offset: val.byte_offset, + }; + unsafe { ToNapiValue::to_napi_value(env, cloned_value) } + } + } + } }; } -unsafe extern "C" fn finalizer( +unsafe extern "C" fn finalizer>( _env: sys::napi_env, finalize_data: *mut c_void, finalize_hint: *mut c_void, ) { - let (data_managed_type, length, finalizer_notify) = unsafe { - *Box::from_raw(finalize_hint as *mut (DataManagedType, usize, Box)) - }; + let mut data = unsafe { *Box::from_raw(finalize_hint as *mut T) }; + let data_managed_type = *data.data_managed_type(); + let length = *data.len(); + let finalizer_notify = data.finalizer(); match data_managed_type { DataManagedType::Vm => { // do nothing } DataManagedType::Owned => { - let length = length; - unsafe { Vec::from_raw_parts(finalize_data as *mut T, length, length) }; + if data.ref_count() == 0 { + let length = length; + unsafe { Vec::from_raw_parts(finalize_data as *mut Data, length, length) }; + } } DataManagedType::External => { - (finalizer_notify)(finalize_data as *mut T, length); + if data.ref_count() == 0 { + (finalizer_notify)(finalize_data as *mut Data, length); + } } } } +#[derive(PartialEq, Eq, Clone, Copy)] enum DataManagedType { /// Vm managed data, passed in from JavaScript Vm, diff --git a/crates/napi/src/bindgen_runtime/js_values/buffer.rs b/crates/napi/src/bindgen_runtime/js_values/buffer.rs index 15c2c916..d72e6d07 100644 --- a/crates/napi/src/bindgen_runtime/js_values/buffer.rs +++ b/crates/napi/src/bindgen_runtime/js_values/buffer.rs @@ -4,6 +4,7 @@ use std::mem; use std::ops::{Deref, DerefMut}; use std::ptr; use std::slice; +use std::sync::Arc; #[cfg(debug_assertions)] use std::sync::Mutex; @@ -19,6 +20,7 @@ thread_local! { /// So it is safe to use it in `async fn`, the `&[u8]` under the hood will not be dropped until the `drop` called. /// Clone will create a new `Reference` to the same underlying `JavaScript Buffer`. pub struct Buffer { + data_reference: Option>, inner: &'static mut [u8], capacity: usize, raw: Option<(sys::napi_ref, sys::napi_env)>, @@ -40,22 +42,21 @@ unsafe impl Send for Buffer {} impl Buffer { pub fn clone(&mut self, env: &Env) -> Result { - if let Some((ref_, _)) = self.raw { + let raw = if let Some((ref_, _)) = self.raw { check_status!( unsafe { sys::napi_reference_ref(env.0, ref_, &mut 0) }, "Failed to ref Buffer reference in Buffer::clone" )?; - Ok(Self { - inner: unsafe { slice::from_raw_parts_mut(self.inner.as_mut_ptr(), self.inner.len()) }, - capacity: self.capacity, - raw: Some((ref_, env.0)), - }) + Some((ref_, env.0)) } else { - Err(Error::new( - Status::InvalidArg, - "clone only available when the buffer is created from a JavaScript Buffer".to_owned(), - )) - } + None + }; + Ok(Self { + data_reference: self.data_reference.clone(), + inner: unsafe { slice::from_raw_parts_mut(self.inner.as_mut_ptr(), self.inner.len()) }, + capacity: self.capacity, + raw, + }) } } @@ -76,6 +77,7 @@ impl From> for Buffer { let capacity = data.capacity(); mem::forget(data); Buffer { + data_reference: Some(Arc::new(())), inner: unsafe { slice::from_raw_parts_mut(inner_ptr, len) }, capacity, raw: None, @@ -146,6 +148,7 @@ impl FromNapiValue for Buffer { )?; Ok(Self { + data_reference: None, inner: unsafe { slice::from_raw_parts_mut(buf as *mut _, len) }, capacity: len, raw: Some((ref_, env)), @@ -196,6 +199,32 @@ impl ToNapiValue for Buffer { } } +impl ToNapiValue for &mut Buffer { + unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result { + // From Node.js value, not from `Vec` + if let Some((ref_, _)) = val.raw { + let mut buf = ptr::null_mut(); + check_status!( + unsafe { sys::napi_get_reference_value(env, ref_, &mut buf) }, + "Failed to get Buffer value from reference" + )?; + check_status!( + unsafe { sys::napi_delete_reference(env, ref_) }, + "Failed to delete Buffer reference" + )?; + Ok(buf) + } else { + let buf = Buffer { + data_reference: val.data_reference.clone(), + inner: unsafe { slice::from_raw_parts_mut(val.inner.as_mut_ptr(), val.capacity) }, + capacity: val.capacity, + raw: None, + }; + unsafe { ToNapiValue::to_napi_value(env, buf) } + } + } +} + impl ValidateNapiValue for Buffer { unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result { let mut is_buffer = false; diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index c75e36cf..e80fb308 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -187,6 +187,7 @@ Generated by [AVA](https://avajs.dev). export function mutateTypedArray(input: Float32Array): void␊ export function derefUint8Array(a: Uint8Array, b: Uint8ClampedArray): number␊ export function bufferPassThrough(buf: Buffer): Promise␊ + export function arrayBufferPassThrough(buf: Uint8Array): Promise␊ export function asyncReduceBuffer(buf: Buffer): Promise␊ /**␊ * \`constructor\` option for \`struct\` requires all fields to be public,␊ @@ -240,8 +241,10 @@ Generated by [AVA](https://avajs.dev). export class Blake2BKey { }␊ export class Context {␊ maybeNeed?: boolean␊ + buffer: Uint8Array␊ constructor()␊ static withData(data: string): Context␊ + static withBuffer(buf: Uint8Array): Context␊ method(): string␊ }␊ export class AnimalWithDefaultConstructor {␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index ac458c4c9c95dad995d819688ce7c710894c1be0..f317e4292c04469106b9d59e82237c021e94ebd9 100644 GIT binary patch literal 3409 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 literal 3376 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 { t.is(c.method(), 'not empty') }) +test('class default field is TypedArray', (t) => { + const c = new Context() + t.deepEqual(c.buffer, new Uint8Array([0, 1, 2, 3])) + const fixture = new Uint8Array([0, 1, 2, 3, 4, 5, 6]) + const c2 = Context.withBuffer(fixture) + t.is(c2.buffer, fixture) +}) + test('class Factory return Result', (t) => { const c = Context.withData('not empty') t.is(c.method(), 'not empty') @@ -479,6 +488,12 @@ test('buffer passthrough', async (t) => { t.deepEqual(ret, fixture) }) +test('arraybuffer passthrough', async (t) => { + const fixture = new Uint8Array([1, 2, 3, 4, 5]) + const ret = await arrayBufferPassThrough(fixture) + t.deepEqual(ret, fixture) +}) + test('async reduce buffer', async (t) => { const input = [1, 2, 3, 4, 5, 6] const fixture = Buffer.from(input) diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 49f05a7d..ada4c0fc 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -177,6 +177,7 @@ export function createExternalTypedArray(): Uint32Array export function mutateTypedArray(input: Float32Array): void export function derefUint8Array(a: Uint8Array, b: Uint8ClampedArray): number export function bufferPassThrough(buf: Buffer): Promise +export function arrayBufferPassThrough(buf: Uint8Array): Promise export function asyncReduceBuffer(buf: Buffer): Promise /** * `constructor` option for `struct` requires all fields to be public, @@ -230,8 +231,10 @@ export type Blake2bKey = Blake2BKey export class Blake2BKey { } export class Context { maybeNeed?: boolean + buffer: Uint8Array constructor() static withData(data: string): Context + static withBuffer(buf: Uint8Array): Context method(): string } export class AnimalWithDefaultConstructor { diff --git a/examples/napi/src/class.rs b/examples/napi/src/class.rs index 3cdf682f..e01fc00b 100644 --- a/examples/napi/src/class.rs +++ b/examples/napi/src/class.rs @@ -1,5 +1,5 @@ use napi::{ - bindgen_prelude::{Buffer, ClassInstance, This}, + bindgen_prelude::{Buffer, ClassInstance, This, Uint8Array}, Env, Result, }; @@ -159,6 +159,7 @@ impl Blake2bKey { pub struct Context { data: String, pub maybe_need: Option, + pub buffer: Uint8Array, } // Test for return `napi::Result` and `Result` @@ -169,6 +170,7 @@ impl Context { Ok(Self { data: "not empty".into(), maybe_need: None, + buffer: Uint8Array::new(vec![0, 1, 2, 3]), }) } @@ -177,9 +179,19 @@ impl Context { Ok(Self { data, maybe_need: Some(true), + buffer: Uint8Array::new(vec![0, 1, 2, 3]), }) } + #[napi(factory)] + pub fn with_buffer(buf: Uint8Array) -> Self { + Self { + data: "not empty".into(), + maybe_need: None, + buffer: buf, + } + } + #[napi] pub fn method(&self) -> String { self.data.clone() diff --git a/examples/napi/src/typed_array.rs b/examples/napi/src/typed_array.rs index 8f24e494..4b9fdd0c 100644 --- a/examples/napi/src/typed_array.rs +++ b/examples/napi/src/typed_array.rs @@ -44,6 +44,11 @@ async fn buffer_pass_through(buf: Buffer) -> Result { Ok(buf) } +#[napi] +async fn array_buffer_pass_through(buf: Uint8Array) -> Result { + Ok(buf) +} + struct AsyncBuffer { buf: Buffer, }