summaryrefslogtreecommitdiff
path: root/ext/ffi/ir.rs
diff options
context:
space:
mode:
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)?);
}