diff options
author | Dj <43033058+DjDeveloperr@users.noreply.github.com> | 2023-01-07 19:58:10 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-08 09:28:10 +0530 |
commit | ad82918f56b215a428ebe7c533ee825e1152d1b4 (patch) | |
tree | 2669f9d8d419a88e9d58b12b98579e884aec6988 /ext/ffi/call.rs | |
parent | 84ef26ac9b5f0e1199d77837cd97cb203baa8729 (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.rs | 79 |
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, ¶meter_types, result_type) + ffi_call( + call_args, + &cif, + ptr, + ¶meter_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) }; |