diff options
Diffstat (limited to 'third_party/rust/uniffi_bindgen/src/interface/mod.rs')
-rw-r--r-- | third_party/rust/uniffi_bindgen/src/interface/mod.rs | 342 |
1 files changed, 222 insertions, 120 deletions
diff --git a/third_party/rust/uniffi_bindgen/src/interface/mod.rs b/third_party/rust/uniffi_bindgen/src/interface/mod.rs index 8e4df2149b..90a941637a 100644 --- a/third_party/rust/uniffi_bindgen/src/interface/mod.rs +++ b/third_party/rust/uniffi_bindgen/src/interface/mod.rs @@ -67,7 +67,9 @@ mod record; pub use record::{Field, Record}; pub mod ffi; -pub use ffi::{FfiArgument, FfiFunction, FfiType}; +pub use ffi::{ + FfiArgument, FfiCallbackFunction, FfiDefinition, FfiField, FfiFunction, FfiStruct, FfiType, +}; pub use uniffi_meta::Radix; use uniffi_meta::{ ConstructorMetadata, LiteralMetadata, NamespaceMetadata, ObjectMetadata, TraitMethodMetadata, @@ -139,6 +141,11 @@ impl ComponentInterface { self.types.namespace ); } + + if group.namespace_docstring.is_some() { + self.types.namespace_docstring = group.namespace_docstring.clone(); + } + // Unconditionally add the String type, which is used by the panic handling self.types.add_known_type(&uniffi_meta::Type::String)?; crate::macro_metadata::add_group_to_ci(self, group)?; @@ -153,6 +160,10 @@ impl ComponentInterface { &self.types.namespace.name } + pub fn namespace_docstring(&self) -> Option<&str> { + self.types.namespace_docstring.as_deref() + } + pub fn uniffi_contract_version(&self) -> u32 { // This is set by the scripts in the version-mismatch fixture let force_version = std::env::var("UNIFFI_FORCE_CONTRACT_VERSION"); @@ -204,6 +215,29 @@ impl ComponentInterface { self.objects.iter().find(|o| o.name == name) } + fn callback_interface_callback_definitions( + &self, + ) -> impl IntoIterator<Item = FfiCallbackFunction> + '_ { + self.callback_interfaces + .iter() + .flat_map(|cbi| cbi.ffi_callbacks()) + .chain(self.objects.iter().flat_map(|o| o.ffi_callbacks())) + } + + /// Get the definitions for callback FFI functions + /// + /// These are defined by the foreign code and invoked by Rust. + fn callback_interface_vtable_definitions(&self) -> impl IntoIterator<Item = FfiStruct> + '_ { + self.callback_interface_definitions() + .iter() + .map(|cbi| cbi.vtable_definition()) + .chain( + self.object_definitions() + .iter() + .flat_map(|o| o.vtable_definition()), + ) + } + /// Get the definitions for every Callback Interface type in the interface. pub fn callback_interface_definitions(&self) -> &[CallbackInterface] { &self.callback_interfaces @@ -215,6 +249,17 @@ impl ComponentInterface { self.callback_interfaces.iter().find(|o| o.name == name) } + /// Get the definitions for every Callback Interface type in the interface. + pub fn has_async_callback_interface_definition(&self) -> bool { + self.callback_interfaces + .iter() + .any(|cbi| cbi.has_async_method()) + || self + .objects + .iter() + .any(|o| o.has_callback_interface() && o.has_async_method()) + } + /// Get the definitions for every Method type in the interface. pub fn iter_callables(&self) -> impl Iterator<Item = &dyn Callable> { // Each of the `as &dyn Callable` casts is a trivial cast, but it seems like the clearest @@ -241,13 +286,19 @@ impl ComponentInterface { let fielded = !e.is_flat(); // For flat errors, we should only generate read() methods if we need them to support // callback interface errors - let used_in_callback_interface = self + let used_in_foreign_interface = self .callback_interface_definitions() .iter() .flat_map(|cb| cb.methods()) + .chain( + self.object_definitions() + .iter() + .filter(|o| o.has_callback_interface()) + .flat_map(|o| o.methods()), + ) .any(|m| m.throws_type() == Some(&e.as_type())); - self.is_name_used_as_error(&e.name) && (fielded || used_in_callback_interface) + self.is_name_used_as_error(&e.name) && (fielded || used_in_foreign_interface) } /// Get details about all `Type::External` types. @@ -304,8 +355,17 @@ impl ComponentInterface { /// This is important to know in language bindings that cannot integrate object types /// tightly with the host GC, and hence need to perform manual destruction of objects. pub fn item_contains_object_references(&self, item: &Type) -> bool { - self.iter_types_in_item(item) - .any(|t| matches!(t, Type::Object { .. })) + // this is surely broken for external records with object refs? + self.iter_types_in_item(item).any(|t| { + matches!( + t, + Type::Object { .. } + | Type::External { + kind: ExternalKind::Interface, + .. + } + ) + }) } /// Check whether the given item contains any (possibly nested) unsigned types @@ -335,6 +395,13 @@ impl ComponentInterface { .any(|t| matches!(t, Type::Map { .. })) } + /// Check whether the interface contains any object types + pub fn contains_object_types(&self) -> bool { + self.types + .iter_known_types() + .any(|t| matches!(t, Type::Object { .. })) + } + // The namespace to use in crate-level FFI function definitions. Not used as the ffi // namespace for types - each type has its own `module_path` which is used for them. fn ffi_namespace(&self) -> &str { @@ -364,7 +431,7 @@ impl ComponentInterface { is_async: false, arguments: vec![FfiArgument { name: "size".to_string(), - type_: FfiType::Int32, + type_: FfiType::UInt64, }], return_type: Some(FfiType::RustBuffer(None)), has_rust_call_status_arg: true, @@ -420,7 +487,7 @@ impl ComponentInterface { }, FfiArgument { name: "additional".to_string(), - type_: FfiType::Int32, + type_: FfiType::UInt64, }, ], return_type: Some(FfiType::RustBuffer(None)), @@ -429,24 +496,6 @@ impl ComponentInterface { } } - /// Builtin FFI function to set the Rust Future continuation callback - pub fn ffi_rust_future_continuation_callback_set(&self) -> FfiFunction { - FfiFunction { - name: format!( - "ffi_{}_rust_future_continuation_callback_set", - self.ffi_namespace() - ), - arguments: vec![FfiArgument { - name: "callback".to_owned(), - type_: FfiType::RustFutureContinuationCallback, - }], - return_type: None, - is_async: false, - has_rust_call_status_arg: false, - is_object_free_function: false, - } - } - /// Builtin FFI function to poll a Rust future. pub fn ffi_rust_future_poll(&self, return_ffi_type: Option<FfiType>) -> FfiFunction { FfiFunction { @@ -455,12 +504,15 @@ impl ComponentInterface { arguments: vec![ FfiArgument { name: "handle".to_owned(), - type_: FfiType::RustFutureHandle, + type_: FfiType::Handle, + }, + FfiArgument { + name: "callback".to_owned(), + type_: FfiType::Callback("RustFutureContinuationCallback".to_owned()), }, - // Data to pass to the continuation FfiArgument { - name: "uniffi_callback".to_owned(), - type_: FfiType::RustFutureContinuationData, + name: "callback_data".to_owned(), + type_: FfiType::Handle, }, ], return_type: None, @@ -478,7 +530,7 @@ impl ComponentInterface { is_async: false, arguments: vec![FfiArgument { name: "handle".to_owned(), - type_: FfiType::RustFutureHandle, + type_: FfiType::Handle, }], return_type: return_ffi_type, has_rust_call_status_arg: true, @@ -493,7 +545,7 @@ impl ComponentInterface { is_async: false, arguments: vec![FfiArgument { name: "handle".to_owned(), - type_: FfiType::RustFutureHandle, + type_: FfiType::Handle, }], return_type: None, has_rust_call_status_arg: false, @@ -508,7 +560,7 @@ impl ComponentInterface { is_async: false, arguments: vec![FfiArgument { name: "handle".to_owned(), - type_: FfiType::RustFutureHandle, + type_: FfiType::Handle, }], return_type: None, has_rust_call_status_arg: false, @@ -518,29 +570,17 @@ impl ComponentInterface { fn rust_future_ffi_fn_name(&self, base_name: &str, return_ffi_type: Option<FfiType>) -> String { let namespace = self.ffi_namespace(); - match return_ffi_type { - Some(t) => match t { - FfiType::UInt8 => format!("ffi_{namespace}_{base_name}_u8"), - FfiType::Int8 => format!("ffi_{namespace}_{base_name}_i8"), - FfiType::UInt16 => format!("ffi_{namespace}_{base_name}_u16"), - FfiType::Int16 => format!("ffi_{namespace}_{base_name}_i16"), - FfiType::UInt32 => format!("ffi_{namespace}_{base_name}_u32"), - FfiType::Int32 => format!("ffi_{namespace}_{base_name}_i32"), - FfiType::UInt64 => format!("ffi_{namespace}_{base_name}_u64"), - FfiType::Int64 => format!("ffi_{namespace}_{base_name}_i64"), - FfiType::Float32 => format!("ffi_{namespace}_{base_name}_f32"), - FfiType::Float64 => format!("ffi_{namespace}_{base_name}_f64"), - FfiType::RustArcPtr(_) => format!("ffi_{namespace}_{base_name}_pointer"), - FfiType::RustBuffer(_) => format!("ffi_{namespace}_{base_name}_rust_buffer"), - _ => unimplemented!("FFI return type: {t:?}"), - }, - None => format!("ffi_{namespace}_{base_name}_void"), - } + let return_type_name = FfiType::return_type_name(return_ffi_type.as_ref()); + format!("ffi_{namespace}_{base_name}_{return_type_name}") } /// Does this interface contain async functions? pub fn has_async_fns(&self) -> bool { self.iter_ffi_function_definitions().any(|f| f.is_async()) + || self + .callback_interfaces + .iter() + .any(CallbackInterface::has_async_method) } /// Iterate over `T` parameters of the `FutureCallback<T>` callbacks in this interface @@ -561,6 +601,73 @@ impl ComponentInterface { unique_results.into_iter() } + /// Iterate over all Ffi definitions + pub fn ffi_definitions(&self) -> impl Iterator<Item = FfiDefinition> + '_ { + // Note: for languages like Python it's important to keep things in dependency order. + // For example some FFI function definitions depend on FFI struct definitions, so the + // function definitions come last. + self.builtin_ffi_definitions() + .into_iter() + .chain( + self.callback_interface_callback_definitions() + .into_iter() + .map(Into::into), + ) + .chain( + self.callback_interface_vtable_definitions() + .into_iter() + .map(Into::into), + ) + .chain(self.iter_ffi_function_definitions().map(Into::into)) + } + + fn builtin_ffi_definitions(&self) -> impl IntoIterator<Item = FfiDefinition> + '_ { + [ + FfiCallbackFunction { + name: "RustFutureContinuationCallback".to_owned(), + arguments: vec![ + FfiArgument::new("data", FfiType::UInt64), + FfiArgument::new("poll_result", FfiType::Int8), + ], + return_type: None, + has_rust_call_status_arg: false, + } + .into(), + FfiCallbackFunction { + name: "ForeignFutureFree".to_owned(), + arguments: vec![FfiArgument::new("handle", FfiType::UInt64)], + return_type: None, + has_rust_call_status_arg: false, + } + .into(), + FfiCallbackFunction { + name: "CallbackInterfaceFree".to_owned(), + arguments: vec![FfiArgument::new("handle", FfiType::UInt64)], + return_type: None, + has_rust_call_status_arg: false, + } + .into(), + FfiStruct { + name: "ForeignFuture".to_owned(), + fields: vec![ + FfiField::new("handle", FfiType::UInt64), + FfiField::new("free", FfiType::Callback("ForeignFutureFree".to_owned())), + ], + } + .into(), + ] + .into_iter() + .chain( + self.all_possible_return_ffi_types() + .flat_map(|return_type| { + [ + callbacks::foreign_future_ffi_result_struct(return_type.clone()).into(), + callbacks::ffi_foreign_future_complete(return_type).into(), + ] + }), + ) + } + /// List the definitions of all FFI functions in the interface. /// /// The set of FFI functions is derived automatically from the set of higher-level types @@ -569,9 +676,8 @@ impl ComponentInterface { self.iter_user_ffi_function_definitions() .cloned() .chain(self.iter_rust_buffer_ffi_function_definitions()) - .chain(self.iter_futures_ffi_function_definitons()) + .chain(self.iter_futures_ffi_function_definitions()) .chain(self.iter_checksum_ffi_functions()) - .chain(self.ffi_foreign_executor_callback_set()) .chain([self.ffi_uniffi_contract_version()]) } @@ -618,9 +724,8 @@ impl ComponentInterface { .into_iter() } - /// List all FFI functions definitions for async functionality. - pub fn iter_futures_ffi_function_definitons(&self) -> impl Iterator<Item = FfiFunction> + '_ { - let all_possible_return_ffi_types = [ + fn all_possible_return_ffi_types(&self) -> impl Iterator<Item = Option<FfiType>> { + [ Some(FfiType::UInt8), Some(FfiType::Int8), Some(FfiType::UInt16), @@ -631,46 +736,26 @@ impl ComponentInterface { Some(FfiType::Int64), Some(FfiType::Float32), Some(FfiType::Float64), - // RustBuffer and RustArcPtr have an inner field which doesn't affect the rust future - // complete scaffolding function, so we just use a placeholder value here. + // RustBuffer and RustArcPtr have an inner field which we have to fill in with a + // placeholder value. Some(FfiType::RustArcPtr("".to_owned())), Some(FfiType::RustBuffer(None)), None, - ]; - - iter::once(self.ffi_rust_future_continuation_callback_set()).chain( - all_possible_return_ffi_types - .into_iter() - .flat_map(|return_type| { - [ - self.ffi_rust_future_poll(return_type.clone()), - self.ffi_rust_future_cancel(return_type.clone()), - self.ffi_rust_future_free(return_type.clone()), - self.ffi_rust_future_complete(return_type), - ] - }), - ) + ] + .into_iter() } - /// The ffi_foreign_executor_callback_set FFI function - /// - /// We only include this in the FFI if the `ForeignExecutor` type is actually used - pub fn ffi_foreign_executor_callback_set(&self) -> Option<FfiFunction> { - if self.types.contains(&Type::ForeignExecutor) { - Some(FfiFunction { - name: format!("ffi_{}_foreign_executor_callback_set", self.ffi_namespace()), - arguments: vec![FfiArgument { - name: "callback".into(), - type_: FfiType::ForeignExecutorCallback, - }], - return_type: None, - is_async: false, - has_rust_call_status_arg: false, - is_object_free_function: false, + /// List all FFI functions definitions for async functionality. + pub fn iter_futures_ffi_function_definitions(&self) -> impl Iterator<Item = FfiFunction> + '_ { + self.all_possible_return_ffi_types() + .flat_map(|return_type| { + [ + self.ffi_rust_future_poll(return_type.clone()), + self.ffi_rust_future_cancel(return_type.clone()), + self.ffi_rust_future_free(return_type.clone()), + self.ffi_rust_future_complete(return_type), + ] }) - } else { - None - } } /// List all API checksums to check @@ -778,6 +863,8 @@ impl ComponentInterface { bail!("Conflicting type definition for \"{}\"", defn.name()); } self.types.add_known_types(defn.iter_types())?; + defn.throws_name() + .map(|n| self.errors.insert(n.to_string())); self.functions.push(defn); Ok(()) @@ -789,6 +876,8 @@ impl ComponentInterface { let defn: Constructor = meta.into(); self.types.add_known_types(defn.iter_types())?; + defn.throws_name() + .map(|n| self.errors.insert(n.to_string())); object.constructors.push(defn); Ok(()) @@ -800,6 +889,9 @@ impl ComponentInterface { .ok_or_else(|| anyhow!("add_method_meta: object {} not found", &method.object_name))?; self.types.add_known_types(method.iter_types())?; + method + .throws_name() + .map(|n| self.errors.insert(n.to_string())); method.object_impl = object.imp; object.methods.push(method); Ok(()) @@ -825,10 +917,6 @@ impl ComponentInterface { Ok(()) } - pub(super) fn note_name_used_as_error(&mut self, name: &str) { - self.errors.insert(name.to_string()); - } - pub fn is_name_used_as_error(&self, name: &str) -> bool { self.errors.contains(name) } @@ -856,6 +944,9 @@ impl ComponentInterface { self.callback_interface_throws_types.insert(error.clone()); } self.types.add_known_types(method.iter_types())?; + method + .throws_name() + .map(|n| self.errors.insert(n.to_string())); cbi.methods.push(method); } else { self.add_method_meta(meta)?; @@ -880,31 +971,6 @@ impl ComponentInterface { bail!("Conflicting type definition for \"{}\"", f.name()); } } - - for ty in self.iter_types() { - match ty { - Type::Object { name, .. } => { - ensure!( - self.objects.iter().any(|o| o.name == *name), - "Object `{name}` has no definition" - ); - } - Type::Record { name, .. } => { - ensure!( - self.records.contains_key(name), - "Record `{name}` has no definition", - ); - } - Type::Enum { name, .. } => { - ensure!( - self.enums.contains_key(name), - "Enum `{name}` has no definition", - ); - } - _ => {} - } - } - Ok(()) } @@ -1047,7 +1113,7 @@ fn throws_name(throws: &Option<Type>) -> Option<&str> { // Type has no `name()` method, just `canonical_name()` which isn't what we want. match throws { None => None, - Some(Type::Enum { name, .. }) => Some(name), + Some(Type::Enum { name, .. }) | Some(Type::Object { name, .. }) => Some(name), _ => panic!("unknown throw type: {throws:?}"), } } @@ -1089,35 +1155,50 @@ mod test { let err = ComponentInterface::from_webidl(UDL2, "crate_name").unwrap_err(); assert_eq!( err.to_string(), - "Mismatching definition for enum `Testing`!\nexisting definition: Enum { + "Mismatching definition for enum `Testing`! +existing definition: Enum { name: \"Testing\", module_path: \"crate_name\", + discr_type: None, variants: [ Variant { name: \"one\", + discr: None, fields: [], + docstring: None, }, Variant { name: \"two\", + discr: None, fields: [], + docstring: None, }, ], flat: true, + non_exhaustive: false, + docstring: None, }, new definition: Enum { name: \"Testing\", module_path: \"crate_name\", + discr_type: None, variants: [ Variant { name: \"three\", + discr: None, fields: [], + docstring: None, }, Variant { name: \"four\", + discr: None, fields: [], + docstring: None, }, ], flat: true, + non_exhaustive: false, + docstring: None, }", ); @@ -1231,4 +1312,25 @@ new definition: Enum { imp: ObjectImpl::Struct, })); } + + #[test] + fn test_docstring_namespace() { + const UDL: &str = r#" + /// informative docstring + namespace test{}; + "#; + let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap(); + assert_eq!(ci.namespace_docstring().unwrap(), "informative docstring"); + } + + #[test] + fn test_multiline_docstring() { + const UDL: &str = r#" + /// informative + /// docstring + namespace test{}; + "#; + let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap(); + assert_eq!(ci.namespace_docstring().unwrap(), "informative\ndocstring"); + } } |