diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
commit | d8bbc7858622b6d9c278469aab701ca0b609cddf (patch) | |
tree | eff41dc61d9f714852212739e6b3738b82a2af87 /third_party/rust/uniffi_bindgen/src/interface/callbacks.rs | |
parent | Releasing progress-linux version 125.0.3-1~progress7.99u1. (diff) | |
download | firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip |
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | third_party/rust/uniffi_bindgen/src/interface/callbacks.rs | 207 |
1 files changed, 190 insertions, 17 deletions
diff --git a/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs b/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs index e3bca4f966..f176a7a684 100644 --- a/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs +++ b/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs @@ -33,9 +33,12 @@ //! # Ok::<(), anyhow::Error>(()) //! ``` +use std::iter; + +use heck::ToUpperCamelCase; use uniffi_meta::Checksum; -use super::ffi::{FfiArgument, FfiFunction, FfiType}; +use super::ffi::{FfiArgument, FfiCallbackFunction, FfiField, FfiFunction, FfiStruct, FfiType}; use super::object::Method; use super::{AsType, Type, TypeIterator}; @@ -52,18 +55,11 @@ pub struct CallbackInterface { // avoids a weird circular dependency in the calculation. #[checksum_ignore] pub(super) ffi_init_callback: FfiFunction, + #[checksum_ignore] + pub(super) docstring: Option<String>, } impl CallbackInterface { - pub fn new(name: String, module_path: String) -> CallbackInterface { - CallbackInterface { - name, - module_path, - methods: Default::default(), - ffi_init_callback: Default::default(), - } - } - pub fn name(&self) -> &str { &self.name } @@ -77,18 +73,45 @@ impl CallbackInterface { } pub(super) fn derive_ffi_funcs(&mut self) { - self.ffi_init_callback.name = - uniffi_meta::init_callback_fn_symbol_name(&self.module_path, &self.name); - self.ffi_init_callback.arguments = vec![FfiArgument { - name: "callback_stub".to_string(), - type_: FfiType::ForeignCallback, - }]; - self.ffi_init_callback.return_type = None; + self.ffi_init_callback = + FfiFunction::callback_init(&self.module_path, &self.name, vtable_name(&self.name)); + } + + /// FfiCallbacks to define for our methods. + pub fn ffi_callbacks(&self) -> Vec<FfiCallbackFunction> { + ffi_callbacks(&self.name, &self.methods) + } + + /// The VTable FFI type + pub fn vtable(&self) -> FfiType { + FfiType::Struct(vtable_name(&self.name)) + } + + /// the VTable struct to define. + pub fn vtable_definition(&self) -> FfiStruct { + vtable_struct(&self.name, &self.methods) + } + + /// Vec of (ffi_callback, method) pairs + pub fn vtable_methods(&self) -> Vec<(FfiCallbackFunction, &Method)> { + self.methods + .iter() + .enumerate() + .map(|(i, method)| (method_ffi_callback(&self.name, method, i), method)) + .collect() } pub fn iter_types(&self) -> TypeIterator<'_> { Box::new(self.methods.iter().flat_map(Method::iter_types)) } + + pub fn docstring(&self) -> Option<&str> { + self.docstring.as_deref() + } + + pub fn has_async_method(&self) -> bool { + self.methods.iter().any(Method::is_async) + } } impl AsType for CallbackInterface { @@ -100,6 +123,139 @@ impl AsType for CallbackInterface { } } +impl TryFrom<uniffi_meta::CallbackInterfaceMetadata> for CallbackInterface { + type Error = anyhow::Error; + + fn try_from(meta: uniffi_meta::CallbackInterfaceMetadata) -> anyhow::Result<Self> { + Ok(Self { + name: meta.name, + module_path: meta.module_path, + methods: Default::default(), + ffi_init_callback: Default::default(), + docstring: meta.docstring.clone(), + }) + } +} + +/// [FfiCallbackFunction] functions for the methods of a callback/trait interface +pub fn ffi_callbacks(trait_name: &str, methods: &[Method]) -> Vec<FfiCallbackFunction> { + methods + .iter() + .enumerate() + .map(|(i, method)| method_ffi_callback(trait_name, method, i)) + .collect() +} + +pub fn method_ffi_callback(trait_name: &str, method: &Method, index: usize) -> FfiCallbackFunction { + if !method.is_async() { + FfiCallbackFunction { + name: method_ffi_callback_name(trait_name, index), + arguments: iter::once(FfiArgument::new("uniffi_handle", FfiType::UInt64)) + .chain(method.arguments().into_iter().map(Into::into)) + .chain(iter::once(match method.return_type() { + Some(t) => FfiArgument::new("uniffi_out_return", FfiType::from(t).reference()), + None => FfiArgument::new("uniffi_out_return", FfiType::VoidPointer), + })) + .collect(), + has_rust_call_status_arg: true, + return_type: None, + } + } else { + let completion_callback = + ffi_foreign_future_complete(method.return_type().map(FfiType::from)); + FfiCallbackFunction { + name: method_ffi_callback_name(trait_name, index), + arguments: iter::once(FfiArgument::new("uniffi_handle", FfiType::UInt64)) + .chain(method.arguments().into_iter().map(Into::into)) + .chain([ + FfiArgument::new( + "uniffi_future_callback", + FfiType::Callback(completion_callback.name), + ), + FfiArgument::new("uniffi_callback_data", FfiType::UInt64), + FfiArgument::new( + "uniffi_out_return", + FfiType::Struct("ForeignFuture".to_owned()).reference(), + ), + ]) + .collect(), + has_rust_call_status_arg: false, + return_type: None, + } + } +} + +/// Result struct to pass to the completion callback for async methods +pub fn foreign_future_ffi_result_struct(return_ffi_type: Option<FfiType>) -> FfiStruct { + let return_type_name = + FfiType::return_type_name(return_ffi_type.as_ref()).to_upper_camel_case(); + FfiStruct { + name: format!("ForeignFutureStruct{return_type_name}"), + fields: match return_ffi_type { + Some(return_ffi_type) => vec![ + FfiField::new("return_value", return_ffi_type), + FfiField::new("call_status", FfiType::RustCallStatus), + ], + None => vec![ + // In Rust, `return_value` is `()` -- a ZST. + // ZSTs are not valid in `C`, but they also take up 0 space. + // Skip the `return_value` field to make the layout correct. + FfiField::new("call_status", FfiType::RustCallStatus), + ], + }, + } +} + +/// Definition for callback functions to complete an async callback interface method +pub fn ffi_foreign_future_complete(return_ffi_type: Option<FfiType>) -> FfiCallbackFunction { + let return_type_name = + FfiType::return_type_name(return_ffi_type.as_ref()).to_upper_camel_case(); + FfiCallbackFunction { + name: format!("ForeignFutureComplete{return_type_name}"), + arguments: vec![ + FfiArgument::new("callback_data", FfiType::UInt64), + FfiArgument::new( + "result", + FfiType::Struct(format!("ForeignFutureStruct{return_type_name}")), + ), + ], + return_type: None, + has_rust_call_status_arg: false, + } +} + +/// [FfiStruct] for a callback/trait interface VTable +/// +/// This struct has a FfiCallbackFunction field for each method, plus extra fields for special +/// methods +pub fn vtable_struct(trait_name: &str, methods: &[Method]) -> FfiStruct { + FfiStruct { + name: vtable_name(trait_name), + fields: methods + .iter() + .enumerate() + .map(|(i, method)| { + FfiField::new( + method.name(), + FfiType::Callback(format!("CallbackInterface{trait_name}Method{i}")), + ) + }) + .chain([FfiField::new( + "uniffi_free", + FfiType::Callback("CallbackInterfaceFree".to_owned()), + )]) + .collect(), + } +} + +pub fn method_ffi_callback_name(trait_name: &str, index: usize) -> String { + format!("CallbackInterface{trait_name}Method{index}") +} + +pub fn vtable_name(trait_name: &str) -> String { + format!("VTableCallbackInterface{trait_name}") +} + #[cfg(test)] mod test { use super::super::ComponentInterface; @@ -146,4 +302,21 @@ mod test { assert_eq!(callbacks_two.methods()[0].name(), "two"); assert_eq!(callbacks_two.methods()[1].name(), "too"); } + + #[test] + fn test_docstring_callback_interface() { + const UDL: &str = r#" + namespace test{}; + /// informative docstring + callback interface Testing { }; + "#; + let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap(); + assert_eq!( + ci.get_callback_interface_definition("Testing") + .unwrap() + .docstring() + .unwrap(), + "informative docstring" + ); + } } |