summaryrefslogtreecommitdiff
path: root/ext/ffi/call.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/call.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/call.rs')
-rw-r--r--ext/ffi/call.rs79
1 files changed, 72 insertions, 7 deletions
diff --git a/ext/ffi/call.rs b/ext/ffi/call.rs
index 2479c3092..2cfd5cef0 100644
--- a/ext/ffi/call.rs
+++ b/ext/ffi/call.rs
@@ -22,11 +22,28 @@ use std::ffi::c_void;
use std::future::Future;
use std::rc::Rc;
+// SAFETY: Makes an FFI call
+unsafe fn ffi_call_rtype_struct(
+ cif: &libffi::middle::Cif,
+ fn_ptr: &libffi::middle::CodePtr,
+ call_args: Vec<Arg>,
+ out_buffer: *mut u8,
+) -> NativeValue {
+ libffi::raw::ffi_call(
+ cif.as_raw_ptr(),
+ Some(*fn_ptr.as_safe_fun()),
+ out_buffer as *mut c_void,
+ call_args.as_ptr() as *mut *mut c_void,
+ );
+ NativeValue { void_value: () }
+}
+
// A one-off synchronous FFI call.
pub(crate) fn ffi_call_sync<'scope>(
scope: &mut v8::HandleScope<'scope>,
args: v8::FunctionCallbackArguments,
symbol: &Symbol,
+ out_buffer: Option<OutBuffer>,
) -> Result<NativeValue, AnyError>
where
'scope: 'scope,
@@ -86,6 +103,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)?);
}
@@ -97,7 +117,12 @@ where
}
}
}
- let call_args: Vec<Arg> = ffi_args.iter().map(Arg::new).collect();
+ let call_args: Vec<Arg> = ffi_args
+ .iter()
+ .enumerate()
+ // SAFETY: Creating a `Arg` from a `NativeValue` is pretty safe.
+ .map(|(i, v)| unsafe { v.as_arg(parameter_types.get(i).unwrap()) })
+ .collect();
// SAFETY: types in the `Cif` match the actual calling convention and
// types of symbol.
unsafe {
@@ -149,6 +174,12 @@ where
pointer: cif.call::<*mut c_void>(*fun_ptr, &call_args),
}
}
+ NativeType::Struct(_) => ffi_call_rtype_struct(
+ &symbol.cif,
+ &symbol.ptr,
+ call_args,
+ out_buffer.unwrap().0,
+ ),
})
}
}
@@ -159,13 +190,14 @@ fn ffi_call(
fun_ptr: libffi::middle::CodePtr,
parameter_types: &[NativeType],
result_type: NativeType,
+ out_buffer: Option<OutBuffer>,
) -> Result<NativeValue, AnyError> {
let call_args: Vec<Arg> = call_args
.iter()
.enumerate()
.map(|(index, ffi_arg)| {
// SAFETY: the union field is initialized
- unsafe { ffi_arg.as_arg(*parameter_types.get(index).unwrap()) }
+ unsafe { ffi_arg.as_arg(parameter_types.get(index).unwrap()) }
})
.collect();
@@ -220,6 +252,9 @@ fn ffi_call(
pointer: cif.call::<*mut c_void>(fun_ptr, &call_args),
}
}
+ NativeType::Struct(_) => {
+ ffi_call_rtype_struct(cif, &fun_ptr, call_args, out_buffer.unwrap().0)
+ }
})
}
}
@@ -231,6 +266,7 @@ pub fn op_ffi_call_ptr_nonblocking<'scope, FP>(
pointer: usize,
def: ForeignFunction,
parameters: serde_v8::Value<'scope>,
+ out_buffer: Option<serde_v8::Value<'scope>>,
) -> Result<impl Future<Output = Result<Value, AnyError>>, AnyError>
where
FP: FfiPermissions + 'static,
@@ -244,10 +280,22 @@ where
let symbol = PtrSymbol::new(pointer, &def);
let call_args = ffi_parse_args(scope, parameters, &def.parameters)?;
+ let def_result = def.result.clone();
+
+ let out_buffer = out_buffer
+ .map(|v| v8::Local::<v8::TypedArray>::try_from(v.v8_value).unwrap());
+ let out_buffer_ptr = out_buffer_as_ptr(scope, out_buffer);
let join_handle = tokio::task::spawn_blocking(move || {
let PtrSymbol { cif, ptr } = symbol.clone();
- ffi_call(call_args, &cif, ptr, &def.parameters, def.result)
+ ffi_call(
+ call_args,
+ &cif,
+ ptr,
+ &def.parameters,
+ def.result,
+ out_buffer_ptr,
+ )
});
Ok(async move {
@@ -255,7 +303,7 @@ where
.await
.map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??;
// SAFETY: Same return type declared to libffi; trust user to have it right beyond that.
- Ok(unsafe { result.to_value(def.result) })
+ Ok(unsafe { result.to_value(def_result) })
})
}
@@ -267,6 +315,7 @@ pub fn op_ffi_call_nonblocking<'scope>(
rid: ResourceId,
symbol: String,
parameters: serde_v8::Value<'scope>,
+ out_buffer: Option<serde_v8::Value<'scope>>,
) -> Result<impl Future<Output = Result<Value, AnyError>> + 'static, AnyError> {
let symbol = {
let state = state.borrow();
@@ -279,8 +328,11 @@ pub fn op_ffi_call_nonblocking<'scope>(
};
let call_args = ffi_parse_args(scope, parameters, &symbol.parameter_types)?;
+ let out_buffer = out_buffer
+ .map(|v| v8::Local::<v8::TypedArray>::try_from(v.v8_value).unwrap());
+ let out_buffer_ptr = out_buffer_as_ptr(scope, out_buffer);
- let result_type = symbol.result_type;
+ let result_type = symbol.result_type.clone();
let join_handle = tokio::task::spawn_blocking(move || {
let Symbol {
cif,
@@ -289,7 +341,14 @@ pub fn op_ffi_call_nonblocking<'scope>(
result_type,
..
} = symbol.clone();
- ffi_call(call_args, &cif, ptr, &parameter_types, result_type)
+ ffi_call(
+ call_args,
+ &cif,
+ ptr,
+ &parameter_types,
+ result_type,
+ out_buffer_ptr,
+ )
});
Ok(async move {
@@ -308,6 +367,7 @@ pub fn op_ffi_call_ptr<FP, 'scope>(
pointer: usize,
def: ForeignFunction,
parameters: serde_v8::Value<'scope>,
+ out_buffer: Option<serde_v8::Value<'scope>>,
) -> Result<serde_v8::Value<'scope>, AnyError>
where
FP: FfiPermissions + 'static,
@@ -322,12 +382,17 @@ where
let symbol = PtrSymbol::new(pointer, &def);
let call_args = ffi_parse_args(scope, parameters, &def.parameters)?;
+ let out_buffer = out_buffer
+ .map(|v| v8::Local::<v8::TypedArray>::try_from(v.v8_value).unwrap());
+ let out_buffer_ptr = out_buffer_as_ptr(scope, out_buffer);
+
let result = ffi_call(
call_args,
&symbol.cif,
symbol.ptr,
&def.parameters,
- def.result,
+ def.result.clone(),
+ out_buffer_ptr,
)?;
// SAFETY: Same return type declared to libffi; trust user to have it right beyond that.
let result = unsafe { result.to_v8(scope, def.result) };