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.
This commit is contained in:
Dj 2023-01-07 19:58:10 -08:00 committed by GitHub
parent 84ef26ac9b
commit ad82918f56
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 568 additions and 80 deletions

View file

@ -48,7 +48,7 @@ impl PtrSymbol {
.clone()
.into_iter()
.map(libffi::middle::Type::from),
def.result.into(),
def.result.clone().into(),
);
Self { cif, ptr }
@ -113,7 +113,7 @@ impl Future for CallbackInfo {
}
}
unsafe extern "C" fn deno_ffi_callback(
_cif: &libffi::low::ffi_cif,
cif: &libffi::low::ffi_cif,
result: &mut c_void,
args: *const *const c_void,
info: &CallbackInfo,
@ -121,15 +121,16 @@ unsafe extern "C" fn deno_ffi_callback(
LOCAL_ISOLATE_POINTER.with(|s| {
if ptr::eq(*s.borrow(), info.isolate) {
// Own isolate thread, okay to call directly
do_ffi_callback(info, result, args);
do_ffi_callback(cif, info, result, args);
} else {
let async_work_sender = &info.async_work_sender;
// SAFETY: Safe as this function blocks until `do_ffi_callback` completes and a response message is received.
let cif: &'static libffi::low::ffi_cif = std::mem::transmute(cif);
let result: &'static mut c_void = std::mem::transmute(result);
let info: &'static CallbackInfo = std::mem::transmute(info);
let (response_sender, response_receiver) = sync_channel::<()>(0);
let fut = Box::new(move || {
do_ffi_callback(info, result, args);
do_ffi_callback(cif, info, result, args);
response_sender.send(()).unwrap();
});
async_work_sender.unbounded_send(fut).unwrap();
@ -143,6 +144,7 @@ unsafe extern "C" fn deno_ffi_callback(
}
unsafe fn do_ffi_callback(
cif: &libffi::low::ffi_cif,
info: &CallbackInfo,
result: &mut c_void,
args: *const *const c_void,
@ -172,9 +174,12 @@ unsafe fn do_ffi_callback(
let result = result as *mut c_void;
let vals: &[*const c_void] =
std::slice::from_raw_parts(args, info.parameters.len());
let arg_types = std::slice::from_raw_parts(cif.arg_types, cif.nargs as usize);
let mut params: Vec<v8::Local<v8::Value>> = vec![];
for (native_type, val) in info.parameters.iter().zip(vals) {
for ((index, native_type), val) in
info.parameters.iter().enumerate().zip(vals)
{
let value: v8::Local<v8::Value> = match native_type {
NativeType::Bool => {
let value = *((*val) as *const bool);
@ -237,6 +242,20 @@ unsafe fn do_ffi_callback(
v8::Number::new(scope, result as f64).into()
}
}
NativeType::Struct(_) => {
let size = arg_types[index].as_ref().unwrap().size;
let ptr = (*val) as *const u8;
let slice = std::slice::from_raw_parts(ptr, size);
let boxed = Box::from(slice);
let store = v8::ArrayBuffer::new_backing_store_from_boxed_slice(boxed);
let ab =
v8::ArrayBuffer::with_backing_store(scope, &store.make_shared());
let local_value: v8::Local<v8::Value> =
v8::Uint8Array::new(scope, ab, 0, ab.byte_length())
.unwrap()
.into();
local_value
}
NativeType::Void => unreachable!(),
};
params.push(value);
@ -440,6 +459,35 @@ unsafe fn do_ffi_callback(
as u64;
}
}
NativeType::Struct(_) => {
let size;
let pointer = if let Ok(value) =
v8::Local::<v8::ArrayBufferView>::try_from(value)
{
let byte_offset = value.byte_offset();
let ab = value
.buffer(scope)
.expect("Unable to deserialize result parameter.");
size = value.byte_length();
ab.data()
.expect("Unable to deserialize result parameter.")
.as_ptr()
.add(byte_offset)
} else if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(value) {
size = value.byte_length();
value
.data()
.expect("Unable to deserialize result parameter.")
.as_ptr()
} else {
panic!("Unable to deserialize result parameter.");
};
std::ptr::copy_nonoverlapping(
pointer as *mut u8,
result as *mut u8,
std::cmp::min(size, (*cif.rtype).size),
);
}
NativeType::Void => {
// nop
}
@ -522,7 +570,7 @@ where
let info: *mut CallbackInfo = Box::leak(Box::new(CallbackInfo {
parameters: args.parameters.clone(),
result: args.result,
result: args.result.clone(),
async_work_sender,
callback,
context,