diff options
Diffstat (limited to 'ext/ffi/ir.rs')
-rw-r--r-- | ext/ffi/ir.rs | 79 |
1 files changed, 78 insertions, 1 deletions
diff --git a/ext/ffi/ir.rs b/ext/ffi/ir.rs index df13f0611..80f727cd2 100644 --- a/ext/ffi/ir.rs +++ b/ext/ffi/ir.rs @@ -12,6 +12,29 @@ use libffi::middle::Arg; use std::ffi::c_void; use std::ptr; +pub struct OutBuffer(pub *mut u8, pub usize); + +// SAFETY: OutBuffer is allocated by us in 00_ffi.js and is guaranteed to be +// only used for the purpose of writing return value of structs. +unsafe impl Send for OutBuffer {} +// SAFETY: See above +unsafe impl Sync for OutBuffer {} + +pub fn out_buffer_as_ptr( + scope: &mut v8::HandleScope, + out_buffer: Option<v8::Local<v8::TypedArray>>, +) -> Option<OutBuffer> { + match out_buffer { + Some(out_buffer) => { + let ab = out_buffer.buffer(scope).unwrap(); + let len = ab.byte_length(); + ab.data() + .map(|non_null| OutBuffer(non_null.as_ptr() as *mut u8, len)) + } + None => None, + } +} + /// Intermediate format for easy translation from NativeType + V8 value /// to libffi argument types. #[repr(C)] @@ -34,7 +57,7 @@ pub union NativeValue { } impl NativeValue { - pub unsafe fn as_arg(&self, native_type: NativeType) -> Arg { + pub unsafe fn as_arg(&self, native_type: &NativeType) -> Arg { match native_type { NativeType::Void => unreachable!(), NativeType::Bool => Arg::new(&self.bool_value), @@ -53,6 +76,7 @@ impl NativeValue { NativeType::Pointer | NativeType::Buffer | NativeType::Function => { Arg::new(&self.pointer) } + NativeType::Struct(_) => Arg::new(&*self.pointer), } } @@ -76,6 +100,10 @@ impl NativeValue { NativeType::Pointer | NativeType::Function | NativeType::Buffer => { Value::from(self.pointer as usize) } + NativeType::Struct(_) => { + // Return value is written to out_buffer + Value::Null + } } } @@ -187,6 +215,10 @@ impl NativeValue { }; local_value.into() } + NativeType::Struct(_) => { + let local_value: v8::Local<v8::Value> = v8::null(scope).into(); + local_value.into() + } } } } @@ -427,6 +459,48 @@ pub fn ffi_parse_buffer_arg( } #[inline] +pub fn ffi_parse_struct_arg( + scope: &mut v8::HandleScope, + arg: v8::Local<v8::Value>, +) -> Result<NativeValue, AnyError> { + // Order of checking: + // 1. ArrayBuffer: Fairly common and not supported by Fast API, optimise this case. + // 2. ArrayBufferView: Common and supported by Fast API + + let pointer = if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(arg) { + if let Some(non_null) = value.data() { + non_null.as_ptr() + } else { + return Err(type_error( + "Invalid FFI ArrayBuffer, expected data in buffer", + )); + } + } else if let Ok(value) = v8::Local::<v8::ArrayBufferView>::try_from(arg) { + let byte_offset = value.byte_offset(); + let pointer = value + .buffer(scope) + .ok_or_else(|| { + type_error("Invalid FFI ArrayBufferView, expected data in the buffer") + })? + .data(); + if let Some(non_null) = pointer { + // SAFETY: Pointer is non-null, and V8 guarantees that the byte_offset + // is within the buffer backing store. + unsafe { non_null.as_ptr().add(byte_offset) } + } else { + return Err(type_error( + "Invalid FFI ArrayBufferView, expected data in buffer", + )); + } + } else { + return Err(type_error( + "Invalid FFI struct type, expected ArrayBuffer, or ArrayBufferView", + )); + }; + Ok(NativeValue { pointer }) +} + +#[inline] pub fn ffi_parse_function_arg( scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>, @@ -511,6 +585,9 @@ where NativeType::Buffer => { ffi_args.push(ffi_parse_buffer_arg(scope, value)?); } + NativeType::Struct(_) => { + ffi_args.push(ffi_parse_struct_arg(scope, value)?); + } NativeType::Pointer => { ffi_args.push(ffi_parse_pointer_arg(scope, value)?); } |