summaryrefslogtreecommitdiff
path: root/ext/ffi/ir.rs
diff options
context:
space:
mode:
authorDj <43033058+DjDeveloperr@users.noreply.github.com>2023-01-07 19:58:10 -0800
committerGitHub <noreply@github.com>2023-01-08 09:28:10 +0530
commitad82918f56b215a428ebe7c533ee825e1152d1b4 (patch)
tree2669f9d8d419a88e9d58b12b98579e884aec6988 /ext/ffi/ir.rs
parent84ef26ac9b5f0e1199d77837cd97cb203baa8729 (diff)
feat(ext/ffi): structs by value (#15060)
Adds support for passing and returning structs as buffers to FFI. This does not implement fastapi support for structs. Needed for certain system APIs such as AppKit on macOS.
Diffstat (limited to 'ext/ffi/ir.rs')
-rw-r--r--ext/ffi/ir.rs79
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)?);
}