summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen/src/interface
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/uniffi_bindgen/src/interface')
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/callbacks.rs149
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/enum_.rs567
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/ffi.rs227
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/function.rs367
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/mod.rs1234
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/object.rs773
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/record.rs230
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/universe.rs136
8 files changed, 3683 insertions, 0 deletions
diff --git a/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs b/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs
new file mode 100644
index 0000000000..e3bca4f966
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs
@@ -0,0 +1,149 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Callback Interface definitions for a `ComponentInterface`.
+//!
+//! This module converts callback interface definitions from UDL into structures that
+//! can be added to a `ComponentInterface`. A declaration in the UDL like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! callback interface Example {
+//! string hello();
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in a [`CallbackInterface`] member being added to the resulting
+//! [`crate::ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # callback interface Example {
+//! # string hello();
+//! # };
+//! # "##, "crate_name")?;
+//! let callback = ci.get_callback_interface_definition("Example").unwrap();
+//! assert_eq!(callback.name(), "Example");
+//! assert_eq!(callback.methods()[0].name(), "hello");
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+
+use uniffi_meta::Checksum;
+
+use super::ffi::{FfiArgument, FfiFunction, FfiType};
+use super::object::Method;
+use super::{AsType, Type, TypeIterator};
+
+#[derive(Debug, Clone, Checksum)]
+pub struct CallbackInterface {
+ pub(super) name: String,
+ pub(super) module_path: String,
+ pub(super) methods: Vec<Method>,
+ // We don't include the FFIFunc in the hash calculation, because:
+ // - it is entirely determined by the other fields,
+ // so excluding it is safe.
+ // - its `name` property includes a checksum derived from the very
+ // hash value we're trying to calculate here, so excluding it
+ // avoids a weird circular dependency in the calculation.
+ #[checksum_ignore]
+ pub(super) ffi_init_callback: FfiFunction,
+}
+
+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
+ }
+
+ pub fn methods(&self) -> Vec<&Method> {
+ self.methods.iter().collect()
+ }
+
+ pub fn ffi_init_callback(&self) -> &FfiFunction {
+ &self.ffi_init_callback
+ }
+
+ 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;
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(self.methods.iter().flat_map(Method::iter_types))
+ }
+}
+
+impl AsType for CallbackInterface {
+ fn as_type(&self) -> Type {
+ Type::CallbackInterface {
+ name: self.name.clone(),
+ module_path: self.module_path.clone(),
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::super::ComponentInterface;
+
+ #[test]
+ fn test_empty_interface() {
+ const UDL: &str = r#"
+ namespace test{};
+ // Weird, but allowed.
+ callback interface Testing {};
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.callback_interface_definitions().len(), 1);
+ assert_eq!(
+ ci.get_callback_interface_definition("Testing")
+ .unwrap()
+ .methods()
+ .len(),
+ 0
+ );
+ }
+
+ #[test]
+ fn test_multiple_interfaces() {
+ const UDL: &str = r#"
+ namespace test{};
+ callback interface One {
+ void one();
+ };
+ callback interface Two {
+ u32 two();
+ u64 too();
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.callback_interface_definitions().len(), 2);
+
+ let callbacks_one = ci.get_callback_interface_definition("One").unwrap();
+ assert_eq!(callbacks_one.methods().len(), 1);
+ assert_eq!(callbacks_one.methods()[0].name(), "one");
+
+ let callbacks_two = ci.get_callback_interface_definition("Two").unwrap();
+ assert_eq!(callbacks_two.methods().len(), 2);
+ assert_eq!(callbacks_two.methods()[0].name(), "two");
+ assert_eq!(callbacks_two.methods()[1].name(), "too");
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/enum_.rs b/third_party/rust/uniffi_bindgen/src/interface/enum_.rs
new file mode 100644
index 0000000000..82baf1dd50
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/enum_.rs
@@ -0,0 +1,567 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Enum definitions for a `ComponentInterface`.
+//!
+//! This module converts enum definition from UDL into structures that can be
+//! added to a `ComponentInterface`. A declaration in the UDL like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! enum Example {
+//! "one",
+//! "two"
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in a [`Enum`] member being added to the resulting [`crate::ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # enum Example {
+//! # "one",
+//! # "two"
+//! # };
+//! # "##, "crate_name")?;
+//! let e = ci.get_enum_definition("Example").unwrap();
+//! assert_eq!(e.name(), "Example");
+//! assert_eq!(e.variants().len(), 2);
+//! assert_eq!(e.variants()[0].name(), "one");
+//! assert_eq!(e.variants()[1].name(), "two");
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Like in Rust, UniFFI enums can contain associated data, but this needs to be
+//! declared with a different syntax in order to work within the restrictions of
+//! WebIDL. A declaration like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! [Enum]
+//! interface Example {
+//! Zero();
+//! One(u32 first);
+//! Two(u32 first, string second);
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in an [`Enum`] member whose variants have associated fields:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # [Enum]
+//! # interface ExampleWithData {
+//! # Zero();
+//! # One(u32 first);
+//! # Two(u32 first, string second);
+//! # };
+//! # "##, "crate_name")?;
+//! let e = ci.get_enum_definition("ExampleWithData").unwrap();
+//! assert_eq!(e.name(), "ExampleWithData");
+//! assert_eq!(e.variants().len(), 3);
+//! assert_eq!(e.variants()[0].name(), "Zero");
+//! assert_eq!(e.variants()[0].fields().len(), 0);
+//! assert_eq!(e.variants()[1].name(), "One");
+//! assert_eq!(e.variants()[1].fields().len(), 1);
+//! assert_eq!(e.variants()[1].fields()[0].name(), "first");
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! # Enums are also used to represent error definitions for a `ComponentInterface`.
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! [Error]
+//! enum Example {
+//! "one",
+//! "two"
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in an [`Enum`] member with fieldless variants being added to the resulting [`crate::ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # [Error]
+//! # enum Example {
+//! # "one",
+//! # "two"
+//! # };
+//! # "##, "crate_name")?;
+//! let err = ci.get_enum_definition("Example").unwrap();
+//! assert_eq!(err.name(), "Example");
+//! assert_eq!(err.variants().len(), 2);
+//! assert_eq!(err.variants()[0].name(), "one");
+//! assert_eq!(err.variants()[1].name(), "two");
+//! assert_eq!(err.is_flat(), true);
+//! assert!(ci.is_name_used_as_error(&err.name()));
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! A declaration in the UDL like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! [Error]
+//! interface Example {
+//! one(i16 code);
+//! two(string reason);
+//! three(i32 x, i32 y);
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in an [`Enum`] member with variants that have fields being added to the resulting [`crate::ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # [Error]
+//! # interface Example {
+//! # one();
+//! # two(string reason);
+//! # three(i32 x, i32 y);
+//! # };
+//! # "##, "crate_name")?;
+//! let err = ci.get_enum_definition("Example").unwrap();
+//! assert_eq!(err.name(), "Example");
+//! assert_eq!(err.variants().len(), 3);
+//! assert_eq!(err.variants()[0].name(), "one");
+//! assert_eq!(err.variants()[1].name(), "two");
+//! assert_eq!(err.variants()[2].name(), "three");
+//! assert_eq!(err.variants()[0].fields().len(), 0);
+//! assert_eq!(err.variants()[1].fields().len(), 1);
+//! assert_eq!(err.variants()[1].fields()[0].name(), "reason");
+//! assert_eq!(err.variants()[2].fields().len(), 2);
+//! assert_eq!(err.variants()[2].fields()[0].name(), "x");
+//! assert_eq!(err.variants()[2].fields()[1].name(), "y");
+//! assert_eq!(err.is_flat(), false);
+//! assert!(ci.is_name_used_as_error(err.name()));
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+
+use anyhow::Result;
+use uniffi_meta::Checksum;
+
+use super::record::Field;
+use super::{AsType, Type, TypeIterator};
+
+/// Represents an enum with named variants, each of which may have named
+/// and typed fields.
+///
+/// Enums are passed across the FFI by serializing to a bytebuffer, with a
+/// i32 indicating the variant followed by the serialization of each field.
+#[derive(Debug, Clone, PartialEq, Eq, Checksum)]
+pub struct Enum {
+ pub(super) name: String,
+ pub(super) module_path: String,
+ pub(super) variants: Vec<Variant>,
+ // NOTE: `flat` is a misleading name and to make matters worse, has 2 different
+ // meanings depending on the context :(
+ // * When used as part of Rust scaffolding generation, it means "is this enum
+ // used with an Error, and that error should we lowered to foreign bindings
+ // by converting each variant to a string and lowering the variant with that
+ // string?". In that context, it should probably be called `lowered_as_string` or
+ // similar.
+ // * When used as part of bindings generation, it means "does this enum have only
+ // variants with no associated data"? The foreign binding generators are likely
+ // to generate significantly different versions of the enum based on that value.
+ //
+ // The reason it is described as "has 2 different meanings" by way of example:
+ // * For an Enum described as being a flat error, but the enum itself has variants with data,
+ // `flat` will be `true` for the Enum when generating scaffolding and `false` when
+ // generating bindings.
+ // * For an Enum not used as an error but which has no variants with data, `flat` will be
+ // false when generating the scaffolding but `true` when generating bindings.
+ pub(super) flat: bool,
+}
+
+impl Enum {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn variants(&self) -> &[Variant] {
+ &self.variants
+ }
+
+ pub fn is_flat(&self) -> bool {
+ self.flat
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(self.variants.iter().flat_map(Variant::iter_types))
+ }
+
+ // Sadly can't use TryFrom due to the 'is_flat' complication.
+ pub fn try_from_meta(meta: uniffi_meta::EnumMetadata, flat: bool) -> Result<Self> {
+ // This is messy - error enums are considered "flat" if the user
+ // opted in via a special attribute, regardless of whether the enum
+ // is actually flat.
+ // Real enums are considered flat iff they are actually flat.
+ // We don't have that context here, so this is handled by our caller.
+ Ok(Self {
+ name: meta.name,
+ module_path: meta.module_path,
+ variants: meta
+ .variants
+ .into_iter()
+ .map(TryInto::try_into)
+ .collect::<Result<_>>()?,
+ flat,
+ })
+ }
+}
+
+impl AsType for Enum {
+ fn as_type(&self) -> Type {
+ Type::Enum {
+ name: self.name.clone(),
+ module_path: self.module_path.clone(),
+ }
+ }
+}
+
+/// Represents an individual variant in an Enum.
+///
+/// Each variant has a name and zero or more fields.
+#[derive(Debug, Clone, Default, PartialEq, Eq, Checksum)]
+pub struct Variant {
+ pub(super) name: String,
+ pub(super) fields: Vec<Field>,
+}
+
+impl Variant {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn fields(&self) -> &[Field] {
+ &self.fields
+ }
+
+ pub fn has_fields(&self) -> bool {
+ !self.fields.is_empty()
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(self.fields.iter().flat_map(Field::iter_types))
+ }
+}
+
+impl TryFrom<uniffi_meta::VariantMetadata> for Variant {
+ type Error = anyhow::Error;
+
+ fn try_from(meta: uniffi_meta::VariantMetadata) -> Result<Self> {
+ Ok(Self {
+ name: meta.name,
+ fields: meta
+ .fields
+ .into_iter()
+ .map(TryInto::try_into)
+ .collect::<Result<_>>()?,
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::super::{ComponentInterface, FfiType};
+ use super::*;
+
+ #[test]
+ fn test_duplicate_variants() {
+ const UDL: &str = r#"
+ namespace test{};
+ // Weird, but currently allowed!
+ // We should probably disallow this...
+ enum Testing { "one", "two", "one" };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.enum_definitions().count(), 1);
+ assert_eq!(
+ ci.get_enum_definition("Testing").unwrap().variants().len(),
+ 3
+ );
+ }
+
+ #[test]
+ fn test_associated_data() {
+ const UDL: &str = r#"
+ namespace test {
+ void takes_an_enum(TestEnum e);
+ void takes_an_enum_with_data(TestEnumWithData ed);
+ TestEnum returns_an_enum();
+ TestEnumWithData returns_an_enum_with_data();
+ };
+
+ enum TestEnum { "one", "two" };
+
+ [Enum]
+ interface TestEnumWithData {
+ Zero();
+ One(u32 first);
+ Two(u32 first, string second);
+ };
+
+ [Enum]
+ interface TestEnumWithoutData {
+ One();
+ Two();
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.enum_definitions().count(), 3);
+ assert_eq!(ci.function_definitions().len(), 4);
+
+ // The "flat" enum with no associated data.
+ let e = ci.get_enum_definition("TestEnum").unwrap();
+ assert!(e.is_flat());
+ assert_eq!(e.variants().len(), 2);
+ assert_eq!(
+ e.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
+ vec!["one", "two"]
+ );
+ assert_eq!(e.variants()[0].fields().len(), 0);
+ assert_eq!(e.variants()[1].fields().len(), 0);
+
+ // The enum with associated data.
+ let ed = ci.get_enum_definition("TestEnumWithData").unwrap();
+ assert!(!ed.is_flat());
+ assert_eq!(ed.variants().len(), 3);
+ assert_eq!(
+ ed.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
+ vec!["Zero", "One", "Two"]
+ );
+ assert_eq!(ed.variants()[0].fields().len(), 0);
+ assert_eq!(
+ ed.variants()[1]
+ .fields()
+ .iter()
+ .map(|f| f.name())
+ .collect::<Vec<_>>(),
+ vec!["first"]
+ );
+ assert_eq!(
+ ed.variants()[1]
+ .fields()
+ .iter()
+ .map(|f| f.as_type())
+ .collect::<Vec<_>>(),
+ vec![Type::UInt32]
+ );
+ assert_eq!(
+ ed.variants()[2]
+ .fields()
+ .iter()
+ .map(|f| f.name())
+ .collect::<Vec<_>>(),
+ vec!["first", "second"]
+ );
+ assert_eq!(
+ ed.variants()[2]
+ .fields()
+ .iter()
+ .map(|f| f.as_type())
+ .collect::<Vec<_>>(),
+ vec![Type::UInt32, Type::String]
+ );
+
+ // The enum declared via interface, but with no associated data.
+ let ewd = ci.get_enum_definition("TestEnumWithoutData").unwrap();
+ assert_eq!(ewd.variants().len(), 2);
+ assert_eq!(
+ ewd.variants().iter().map(|v| v.name()).collect::<Vec<_>>(),
+ vec!["One", "Two"]
+ );
+ assert_eq!(ewd.variants()[0].fields().len(), 0);
+ assert_eq!(ewd.variants()[1].fields().len(), 0);
+
+ // Flat enums pass over the FFI as bytebuffers.
+ // (It might be nice to optimize these to pass as plain integers, but that's
+ // difficult atop the current factoring of `ComponentInterface` and friends).
+ let farg = ci.get_function_definition("takes_an_enum").unwrap();
+ assert_eq!(
+ farg.arguments()[0].as_type(),
+ Type::Enum {
+ name: "TestEnum".into(),
+ module_path: "crate_name".into()
+ }
+ );
+ assert_eq!(
+ farg.ffi_func().arguments()[0].type_(),
+ FfiType::RustBuffer(None)
+ );
+ let fret = ci.get_function_definition("returns_an_enum").unwrap();
+ assert!(
+ matches!(fret.return_type(), Some(Type::Enum { name, .. }) if name == "TestEnum" && !ci.is_name_used_as_error(name))
+ );
+ assert!(matches!(
+ fret.ffi_func().return_type(),
+ Some(FfiType::RustBuffer(None))
+ ));
+
+ // Enums with associated data pass over the FFI as bytebuffers.
+ let farg = ci
+ .get_function_definition("takes_an_enum_with_data")
+ .unwrap();
+ assert_eq!(
+ farg.arguments()[0].as_type(),
+ Type::Enum {
+ name: "TestEnumWithData".into(),
+ module_path: "crate_name".into()
+ }
+ );
+ assert_eq!(
+ farg.ffi_func().arguments()[0].type_(),
+ FfiType::RustBuffer(None)
+ );
+ let fret = ci
+ .get_function_definition("returns_an_enum_with_data")
+ .unwrap();
+ assert!(
+ matches!(fret.return_type(), Some(Type::Enum { name, .. }) if name == "TestEnumWithData" && !ci.is_name_used_as_error(name))
+ );
+ assert!(matches!(
+ fret.ffi_func().return_type(),
+ Some(FfiType::RustBuffer(None))
+ ));
+ }
+
+ // Tests for [Error], which are represented as `Enum`
+ #[test]
+ fn test_variants() {
+ const UDL: &str = r#"
+ namespace test{};
+ [Error]
+ enum Testing { "one", "two", "three" };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.enum_definitions().count(), 1);
+ let error = ci.get_enum_definition("Testing").unwrap();
+ assert_eq!(
+ error
+ .variants()
+ .iter()
+ .map(|v| v.name())
+ .collect::<Vec<&str>>(),
+ vec!("one", "two", "three")
+ );
+ assert!(error.is_flat());
+ assert!(ci.is_name_used_as_error(&error.name));
+ }
+
+ #[test]
+ fn test_duplicate_error_variants() {
+ const UDL: &str = r#"
+ namespace test{};
+ // Weird, but currently allowed!
+ // We should probably disallow this...
+ [Error]
+ enum Testing { "one", "two", "one" };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.enum_definitions().count(), 1);
+ assert_eq!(
+ ci.get_enum_definition("Testing").unwrap().variants().len(),
+ 3
+ );
+ }
+
+ #[test]
+ fn test_variant_data() {
+ const UDL: &str = r#"
+ namespace test{};
+
+ [Error]
+ interface Testing {
+ One(string reason);
+ Two(u8 code);
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.enum_definitions().count(), 1);
+ let error: &Enum = ci.get_enum_definition("Testing").unwrap();
+ assert_eq!(
+ error
+ .variants()
+ .iter()
+ .map(|v| v.name())
+ .collect::<Vec<&str>>(),
+ vec!("One", "Two")
+ );
+ assert!(!error.is_flat());
+ assert!(ci.is_name_used_as_error(&error.name));
+ }
+
+ #[test]
+ fn test_enum_variant_named_error() {
+ const UDL: &str = r#"
+ namespace test{};
+
+ [Enum]
+ interface Testing {
+ Normal(string first);
+ Error(string first);
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.enum_definitions().count(), 1);
+ let testing: &Enum = ci.get_enum_definition("Testing").unwrap();
+ assert_eq!(
+ testing.variants()[0]
+ .fields()
+ .iter()
+ .map(|f| f.name())
+ .collect::<Vec<_>>(),
+ vec!["first"]
+ );
+ assert_eq!(
+ testing.variants()[0]
+ .fields()
+ .iter()
+ .map(|f| f.as_type())
+ .collect::<Vec<_>>(),
+ vec![Type::String]
+ );
+ assert_eq!(
+ testing.variants()[1]
+ .fields()
+ .iter()
+ .map(|f| f.name())
+ .collect::<Vec<_>>(),
+ vec!["first"]
+ );
+ assert_eq!(
+ testing.variants()[1]
+ .fields()
+ .iter()
+ .map(|f| f.as_type())
+ .collect::<Vec<_>>(),
+ vec![Type::String]
+ );
+ assert_eq!(
+ testing
+ .variants()
+ .iter()
+ .map(|v| v.name())
+ .collect::<Vec<_>>(),
+ vec!["Normal", "Error"]
+ );
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/ffi.rs b/third_party/rust/uniffi_bindgen/src/interface/ffi.rs
new file mode 100644
index 0000000000..d18aaf8262
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/ffi.rs
@@ -0,0 +1,227 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Low-level typesystem for the FFI layer of a component interface.
+//!
+//! This module provides the "FFI-level" typesystem of a UniFFI Rust Component, that is,
+//! the C-style functions and structs and primitive datatypes that are used to interface
+//! between the Rust component code and the foreign-language bindings.
+//!
+//! These types are purely an implementation detail of UniFFI, so consumers shouldn't
+//! need to know about them. But as a developer working on UniFFI itself, you're likely
+//! to spend a lot of time thinking about how these low-level types are used to represent
+//! the higher-level "interface types" from the [`Type`] enum.
+/// Represents the restricted set of low-level types that can be used to construct
+/// the C-style FFI layer between a rust component and its foreign language bindings.
+///
+/// For the types that involve memory allocation, we make a distinction between
+/// "owned" types (the recipient must free it, or pass it to someone else) and
+/// "borrowed" types (the sender must keep it alive for the duration of the call).
+use uniffi_meta::{ExternalKind, Type};
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub enum FfiType {
+ // N.B. there are no booleans at this layer, since they cause problems for JNA.
+ UInt8,
+ Int8,
+ UInt16,
+ Int16,
+ UInt32,
+ Int32,
+ UInt64,
+ Int64,
+ Float32,
+ Float64,
+ /// A `*const c_void` pointer to a rust-owned `Arc<T>`.
+ /// If you've got one of these, you must call the appropriate rust function to free it.
+ /// The templates will generate a unique `free` function for each T.
+ /// The inner string references the name of the `T` type.
+ RustArcPtr(String),
+ /// A byte buffer allocated by rust, and owned by whoever currently holds it.
+ /// If you've got one of these, you must either call the appropriate rust function to free it
+ /// or pass it to someone that will.
+ /// If the inner option is Some, it is the name of the external type. The bindings may need
+ /// to use this name to import the correct RustBuffer for that type.
+ RustBuffer(Option<String>),
+ /// A borrowed reference to some raw bytes owned by foreign language code.
+ /// The provider of this reference must keep it alive for the duration of the receiving call.
+ ForeignBytes,
+ /// Pointer to a callback function that handles all callbacks on the foreign language side.
+ ForeignCallback,
+ /// Pointer-sized opaque handle that represents a foreign executor. Foreign bindings can
+ /// either use an actual pointer or a usized integer.
+ ForeignExecutorHandle,
+ /// Pointer to the callback function that's invoked to schedule calls with a ForeignExecutor
+ ForeignExecutorCallback,
+ /// Pointer to a Rust future
+ RustFutureHandle,
+ /// Continuation function for a Rust future
+ RustFutureContinuationCallback,
+ RustFutureContinuationData,
+ // TODO: you can imagine a richer structural typesystem here, e.g. `Ref<String>` or something.
+ // We don't need that yet and it's possible we never will, so it isn't here for now.
+}
+
+/// When passing data across the FFI, each `Type` value will be lowered into a corresponding
+/// `FfiType` value. This conversion tells you which one.
+///
+/// Note that the conversion is one-way - given an FfiType, it is not in general possible to
+/// tell what the corresponding Type is that it's being used to represent.
+impl From<&Type> for FfiType {
+ fn from(t: &Type) -> FfiType {
+ match t {
+ // Types that are the same map to themselves, naturally.
+ Type::UInt8 => FfiType::UInt8,
+ Type::Int8 => FfiType::Int8,
+ Type::UInt16 => FfiType::UInt16,
+ Type::Int16 => FfiType::Int16,
+ Type::UInt32 => FfiType::UInt32,
+ Type::Int32 => FfiType::Int32,
+ Type::UInt64 => FfiType::UInt64,
+ Type::Int64 => FfiType::Int64,
+ Type::Float32 => FfiType::Float32,
+ Type::Float64 => FfiType::Float64,
+ // Booleans lower into an Int8, to work around a bug in JNA.
+ Type::Boolean => FfiType::Int8,
+ // Strings are always owned rust values.
+ // We might add a separate type for borrowed strings in future.
+ Type::String => FfiType::RustBuffer(None),
+ // Byte strings are also always owned rust values.
+ // We might add a separate type for borrowed byte strings in future as well.
+ Type::Bytes => FfiType::RustBuffer(None),
+ // Objects are pointers to an Arc<>
+ Type::Object { name, .. } => FfiType::RustArcPtr(name.to_owned()),
+ // Callback interfaces are passed as opaque integer handles.
+ Type::CallbackInterface { .. } => FfiType::UInt64,
+ Type::ForeignExecutor => FfiType::ForeignExecutorHandle,
+ // Other types are serialized into a bytebuffer and deserialized on the other side.
+ Type::Enum { .. }
+ | Type::Record { .. }
+ | Type::Optional { .. }
+ | Type::Sequence { .. }
+ | Type::Map { .. }
+ | Type::Timestamp
+ | Type::Duration => FfiType::RustBuffer(None),
+ Type::External {
+ name,
+ kind: ExternalKind::Interface,
+ ..
+ } => FfiType::RustArcPtr(name.clone()),
+ Type::External {
+ name,
+ kind: ExternalKind::DataClass,
+ ..
+ } => FfiType::RustBuffer(Some(name.clone())),
+ Type::Custom { builtin, .. } => FfiType::from(builtin.as_ref()),
+ }
+ }
+}
+
+// Needed for rust scaffolding askama template
+impl From<Type> for FfiType {
+ fn from(ty: Type) -> Self {
+ (&ty).into()
+ }
+}
+
+impl From<&&Type> for FfiType {
+ fn from(ty: &&Type) -> Self {
+ (*ty).into()
+ }
+}
+
+/// Represents an "extern C"-style function that will be part of the FFI.
+///
+/// These can't be declared explicitly in the UDL, but rather, are derived automatically
+/// from the high-level interface. Each callable thing in the component API will have a
+/// corresponding `FfiFunction` through which it can be invoked, and UniFFI also provides
+/// some built-in `FfiFunction` helpers for use in the foreign language bindings.
+#[derive(Debug, Clone)]
+pub struct FfiFunction {
+ pub(super) name: String,
+ pub(super) is_async: bool,
+ pub(super) arguments: Vec<FfiArgument>,
+ pub(super) return_type: Option<FfiType>,
+ pub(super) has_rust_call_status_arg: bool,
+ /// Used by C# generator to differentiate the free function and call it with void*
+ /// instead of C# `SafeHandle` type. See <https://github.com/mozilla/uniffi-rs/pull/1488>.
+ pub(super) is_object_free_function: bool,
+}
+
+impl FfiFunction {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn is_async(&self) -> bool {
+ self.is_async
+ }
+
+ pub fn arguments(&self) -> Vec<&FfiArgument> {
+ self.arguments.iter().collect()
+ }
+
+ pub fn return_type(&self) -> Option<&FfiType> {
+ self.return_type.as_ref()
+ }
+
+ pub fn has_rust_call_status_arg(&self) -> bool {
+ self.has_rust_call_status_arg
+ }
+
+ pub fn is_object_free_function(&self) -> bool {
+ self.is_object_free_function
+ }
+
+ pub fn init(
+ &mut self,
+ return_type: Option<FfiType>,
+ args: impl IntoIterator<Item = FfiArgument>,
+ ) {
+ self.arguments = args.into_iter().collect();
+ if self.is_async() {
+ self.return_type = Some(FfiType::RustFutureHandle);
+ self.has_rust_call_status_arg = false;
+ } else {
+ self.return_type = return_type;
+ }
+ }
+}
+
+impl Default for FfiFunction {
+ fn default() -> Self {
+ Self {
+ name: "".into(),
+ is_async: false,
+ arguments: Vec::new(),
+ return_type: None,
+ has_rust_call_status_arg: true,
+ is_object_free_function: false,
+ }
+ }
+}
+
+/// Represents an argument to an FFI function.
+///
+/// Each argument has a name and a type.
+#[derive(Debug, Clone)]
+pub struct FfiArgument {
+ pub(super) name: String,
+ pub(super) type_: FfiType,
+}
+
+impl FfiArgument {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+ pub fn type_(&self) -> FfiType {
+ self.type_.clone()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ // There's not really much to test here to be honest,
+ // it's mostly type declarations.
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/function.rs b/third_party/rust/uniffi_bindgen/src/interface/function.rs
new file mode 100644
index 0000000000..2d18288c1c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/function.rs
@@ -0,0 +1,367 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Function definitions for a `ComponentInterface`.
+//!
+//! This module converts function definitions from UDL into structures that
+//! can be added to a `ComponentInterface`. A declaration in the UDL like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! namespace example {
+//! string hello();
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in a [`Function`] member being added to the resulting [`crate::ComponentInterface`]:
+//!
+//! ```
+//! # use uniffi_bindgen::interface::Type;
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {
+//! # string hello();
+//! # };
+//! # "##, "crate_name")?;
+//! let func = ci.get_function_definition("hello").unwrap();
+//! assert_eq!(func.name(), "hello");
+//! assert!(matches!(func.return_type(), Some(Type::String)));
+//! assert_eq!(func.arguments().len(), 0);
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+
+use anyhow::Result;
+
+use super::ffi::{FfiArgument, FfiFunction, FfiType};
+use super::{AsType, ComponentInterface, Literal, ObjectImpl, Type, TypeIterator};
+use uniffi_meta::Checksum;
+
+/// Represents a standalone function.
+///
+/// Each `Function` corresponds to a standalone function in the rust module,
+/// and has a corresponding standalone function in the foreign language bindings.
+///
+/// In the FFI, this will be a standalone function with appropriately lowered types.
+#[derive(Debug, Clone, Checksum)]
+pub struct Function {
+ pub(super) name: String,
+ pub(super) module_path: String,
+ pub(super) is_async: bool,
+ pub(super) arguments: Vec<Argument>,
+ pub(super) return_type: Option<Type>,
+ // We don't include the FFIFunc in the hash calculation, because:
+ // - it is entirely determined by the other fields,
+ // so excluding it is safe.
+ // - its `name` property includes a checksum derived from the very
+ // hash value we're trying to calculate here, so excluding it
+ // avoids a weird circular dependency in the calculation.
+ #[checksum_ignore]
+ pub(super) ffi_func: FfiFunction,
+ pub(super) throws: Option<Type>,
+ pub(super) checksum_fn_name: String,
+ // Force a checksum value, or we'll fallback to the trait.
+ #[checksum_ignore]
+ pub(super) checksum: Option<u16>,
+}
+
+impl Function {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn is_async(&self) -> bool {
+ self.is_async
+ }
+
+ pub fn arguments(&self) -> Vec<&Argument> {
+ self.arguments.iter().collect()
+ }
+
+ pub fn full_arguments(&self) -> Vec<Argument> {
+ self.arguments.to_vec()
+ }
+
+ pub fn return_type(&self) -> Option<&Type> {
+ self.return_type.as_ref()
+ }
+
+ pub fn ffi_func(&self) -> &FfiFunction {
+ &self.ffi_func
+ }
+
+ pub fn checksum_fn_name(&self) -> &str {
+ &self.checksum_fn_name
+ }
+
+ pub fn checksum(&self) -> u16 {
+ self.checksum.unwrap_or_else(|| uniffi_meta::checksum(self))
+ }
+
+ pub fn throws(&self) -> bool {
+ self.throws.is_some()
+ }
+
+ pub fn throws_name(&self) -> Option<&str> {
+ super::throws_name(&self.throws)
+ }
+
+ pub fn throws_type(&self) -> Option<&Type> {
+ self.throws.as_ref()
+ }
+
+ pub fn derive_ffi_func(&mut self) -> Result<()> {
+ assert!(!self.ffi_func.name.is_empty());
+ self.ffi_func.init(
+ self.return_type.as_ref().map(Into::into),
+ self.arguments.iter().map(Into::into),
+ );
+ Ok(())
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(
+ self.arguments
+ .iter()
+ .flat_map(Argument::iter_types)
+ .chain(self.return_type.iter().flat_map(Type::iter_types)),
+ )
+ }
+}
+
+impl From<uniffi_meta::FnParamMetadata> for Argument {
+ fn from(meta: uniffi_meta::FnParamMetadata) -> Self {
+ Argument {
+ name: meta.name,
+ type_: meta.ty,
+ by_ref: meta.by_ref,
+ optional: meta.optional,
+ default: meta.default,
+ }
+ }
+}
+
+impl From<uniffi_meta::FnMetadata> for Function {
+ fn from(meta: uniffi_meta::FnMetadata) -> Self {
+ let ffi_name = meta.ffi_symbol_name();
+ let checksum_fn_name = meta.checksum_symbol_name();
+ let is_async = meta.is_async;
+ let return_type = meta.return_type.map(Into::into);
+ let arguments = meta.inputs.into_iter().map(Into::into).collect();
+
+ let ffi_func = FfiFunction {
+ name: ffi_name,
+ is_async,
+ ..FfiFunction::default()
+ };
+
+ Self {
+ name: meta.name,
+ module_path: meta.module_path,
+ is_async,
+ arguments,
+ return_type,
+ ffi_func,
+ throws: meta.throws,
+ checksum_fn_name,
+ checksum: meta.checksum,
+ }
+ }
+}
+
+/// Represents an argument to a function/constructor/method call.
+///
+/// Each argument has a name and a type, along with some optional metadata.
+#[derive(Debug, Clone, Checksum)]
+pub struct Argument {
+ pub(super) name: String,
+ pub(super) type_: Type,
+ pub(super) by_ref: bool,
+ pub(super) optional: bool,
+ pub(super) default: Option<Literal>,
+}
+
+impl Argument {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn by_ref(&self) -> bool {
+ self.by_ref
+ }
+
+ pub fn is_trait_ref(&self) -> bool {
+ matches!(&self.type_, Type::Object { imp, .. } if *imp == ObjectImpl::Trait)
+ }
+
+ pub fn default_value(&self) -> Option<&Literal> {
+ self.default.as_ref()
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ self.type_.iter_types()
+ }
+}
+
+impl AsType for Argument {
+ fn as_type(&self) -> Type {
+ self.type_.clone()
+ }
+}
+
+impl From<&Argument> for FfiArgument {
+ fn from(a: &Argument) -> FfiArgument {
+ FfiArgument {
+ name: a.name.clone(),
+ type_: (&a.type_).into(),
+ }
+ }
+}
+
+/// Combines the return and throws type of a function/method
+#[derive(Debug, PartialOrd, Ord, PartialEq, Eq)]
+pub struct ResultType {
+ pub return_type: Option<Type>,
+ pub throws_type: Option<Type>,
+}
+
+impl ResultType {
+ /// Get the `T` parameters for the `FutureCallback<T>` for this ResultType
+ pub fn future_callback_param(&self) -> FfiType {
+ match &self.return_type {
+ Some(t) => t.into(),
+ None => FfiType::UInt8,
+ }
+ }
+}
+
+/// Implemented by function-like types (Function, Method, Constructor)
+pub trait Callable {
+ fn arguments(&self) -> Vec<&Argument>;
+ fn return_type(&self) -> Option<Type>;
+ fn throws_type(&self) -> Option<Type>;
+ fn is_async(&self) -> bool;
+ fn result_type(&self) -> ResultType {
+ ResultType {
+ return_type: self.return_type(),
+ throws_type: self.throws_type(),
+ }
+ }
+
+ // Quick way to get the rust future scaffolding function that corresponds to our return type.
+
+ fn ffi_rust_future_poll(&self, ci: &ComponentInterface) -> String {
+ ci.ffi_rust_future_poll(self.return_type().map(Into::into))
+ .name()
+ .to_owned()
+ }
+
+ fn ffi_rust_future_cancel(&self, ci: &ComponentInterface) -> String {
+ ci.ffi_rust_future_cancel(self.return_type().map(Into::into))
+ .name()
+ .to_owned()
+ }
+
+ fn ffi_rust_future_complete(&self, ci: &ComponentInterface) -> String {
+ ci.ffi_rust_future_complete(self.return_type().map(Into::into))
+ .name()
+ .to_owned()
+ }
+
+ fn ffi_rust_future_free(&self, ci: &ComponentInterface) -> String {
+ ci.ffi_rust_future_free(self.return_type().map(Into::into))
+ .name()
+ .to_owned()
+ }
+}
+
+impl Callable for Function {
+ fn arguments(&self) -> Vec<&Argument> {
+ self.arguments()
+ }
+
+ fn return_type(&self) -> Option<Type> {
+ self.return_type().cloned()
+ }
+
+ fn throws_type(&self) -> Option<Type> {
+ self.throws_type().cloned()
+ }
+
+ fn is_async(&self) -> bool {
+ self.is_async
+ }
+}
+
+// Needed because Askama likes to add extra refs to variables
+impl<T: Callable> Callable for &T {
+ fn arguments(&self) -> Vec<&Argument> {
+ (*self).arguments()
+ }
+
+ fn return_type(&self) -> Option<Type> {
+ (*self).return_type()
+ }
+
+ fn throws_type(&self) -> Option<Type> {
+ (*self).throws_type()
+ }
+
+ fn is_async(&self) -> bool {
+ (*self).is_async()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::super::ComponentInterface;
+ use super::*;
+
+ #[test]
+ fn test_minimal_and_rich_function() -> Result<()> {
+ let ci = ComponentInterface::from_webidl(
+ r#"
+ namespace test {
+ void minimal();
+ [Throws=TestError]
+ sequence<string?> rich(u32 arg1, TestDict arg2);
+ };
+ [Error]
+ enum TestError { "err" };
+ dictionary TestDict {
+ u32 field;
+ };
+ "#,
+ "crate_name",
+ )?;
+
+ let func1 = ci.get_function_definition("minimal").unwrap();
+ assert_eq!(func1.name(), "minimal");
+ assert!(func1.return_type().is_none());
+ assert!(func1.throws_type().is_none());
+ assert_eq!(func1.arguments().len(), 0);
+
+ let func2 = ci.get_function_definition("rich").unwrap();
+ assert_eq!(func2.name(), "rich");
+ assert_eq!(
+ func2.return_type().unwrap(),
+ &Type::Sequence {
+ inner_type: Box::new(Type::Optional {
+ inner_type: Box::new(Type::String)
+ })
+ }
+ );
+ assert!(
+ matches!(func2.throws_type(), Some(Type::Enum { name, .. }) if name == "TestError" && ci.is_name_used_as_error(name))
+ );
+ assert_eq!(func2.arguments().len(), 2);
+ assert_eq!(func2.arguments()[0].name(), "arg1");
+ assert_eq!(func2.arguments()[0].as_type(), Type::UInt32);
+ assert_eq!(func2.arguments()[1].name(), "arg2");
+ assert!(
+ matches!(func2.arguments()[1].as_type(), Type::Record { name, .. } if name == "TestDict")
+ );
+ Ok(())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/mod.rs b/third_party/rust/uniffi_bindgen/src/interface/mod.rs
new file mode 100644
index 0000000000..8e4df2149b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/mod.rs
@@ -0,0 +1,1234 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Component Interface Definition.
+//!
+//! This module provides an abstract representation of the interface provided by a UniFFI Rust Component,
+//! in high-level terms suitable for translation into target consumer languages such as Kotlin
+//! and Swift. It also provides facilities for parsing a WebIDL interface definition file into such a
+//! representation.
+//!
+//! The entrypoint to this crate is the `ComponentInterface` struct, which holds a complete definition
+//! of the interface provided by a component, in two parts:
+//!
+//! * The high-level consumer API, in terms of objects and records and methods and so-on
+//! * The low-level FFI contract through which the foreign language code can call into Rust.
+//!
+//! That's really the key concept of this crate so it's worth repeating: a `ComponentInterface` completely
+//! defines the shape and semantics of an interface between the Rust-based implementation of a component
+//! and its foreign language consumers, including details like:
+//!
+//! * The names of all symbols in the compiled object file
+//! * The type and arity of all exported functions
+//! * The layout and conventions used for all arguments and return types
+//!
+//! If you have a dynamic library compiled from a Rust Component using this crate, and a foreign
+//! language binding generated from the same `ComponentInterface` using the same version of this
+//! module, then there should be no opportunities for them to disagree on how the two sides should
+//! interact.
+//!
+//! General and incomplete TODO list for this thing:
+//!
+//! * It should prevent user error and the possibility of generating bad code by doing (at least)
+//! the following checks:
+//! * No duplicate names (types, methods, args, etc)
+//! * No shadowing of builtin names, or names we use in code generation
+//! We expect that if the user actually does one of these things, then they *should* get a compile
+//! error when trying to build the component, because the codegen will be invalid. But we can't
+//! guarantee that there's not some edge-case where it produces valid-but-incorrect code.
+//!
+//! * There is a *lot* of cloning going on, in the spirit of "first make it work". There's probably
+//! a good opportunity here for e.g. interned strings, but we're nowhere near the point were we need
+//! that kind of optimization just yet.
+//!
+//! * Error messages and general developer experience leave a lot to be desired.
+
+use std::{
+ collections::{btree_map::Entry, BTreeMap, BTreeSet, HashSet},
+ iter,
+};
+
+use anyhow::{anyhow, bail, ensure, Result};
+
+pub mod universe;
+pub use uniffi_meta::{AsType, ExternalKind, ObjectImpl, Type};
+use universe::{TypeIterator, TypeUniverse};
+
+mod callbacks;
+pub use callbacks::CallbackInterface;
+mod enum_;
+pub use enum_::{Enum, Variant};
+mod function;
+pub use function::{Argument, Callable, Function, ResultType};
+mod object;
+pub use object::{Constructor, Method, Object, UniffiTrait};
+mod record;
+pub use record::{Field, Record};
+
+pub mod ffi;
+pub use ffi::{FfiArgument, FfiFunction, FfiType};
+pub use uniffi_meta::Radix;
+use uniffi_meta::{
+ ConstructorMetadata, LiteralMetadata, NamespaceMetadata, ObjectMetadata, TraitMethodMetadata,
+ UniffiTraitMetadata, UNIFFI_CONTRACT_VERSION,
+};
+pub type Literal = LiteralMetadata;
+
+/// The main public interface for this module, representing the complete details of an interface exposed
+/// by a rust component and the details of consuming it via an extern-C FFI layer.
+#[derive(Debug, Default)]
+pub struct ComponentInterface {
+ /// All of the types used in the interface.
+ // We can't checksum `self.types`, but its contents are implied by the other fields
+ // anyway, so it's safe to ignore it.
+ pub(super) types: TypeUniverse,
+ /// The high-level API provided by the component.
+ enums: BTreeMap<String, Enum>,
+ records: BTreeMap<String, Record>,
+ functions: Vec<Function>,
+ objects: Vec<Object>,
+ callback_interfaces: Vec<CallbackInterface>,
+ // Type names which were seen used as an error.
+ errors: HashSet<String>,
+ // Types which were seen used as callback interface error.
+ callback_interface_throws_types: BTreeSet<Type>,
+}
+
+impl ComponentInterface {
+ pub fn new(crate_name: &str) -> Self {
+ assert!(!crate_name.is_empty());
+ Self {
+ types: TypeUniverse::new(NamespaceMetadata {
+ crate_name: crate_name.to_string(),
+ ..Default::default()
+ }),
+ ..Default::default()
+ }
+ }
+
+ /// Parse a `ComponentInterface` from a string containing a WebIDL definition.
+ pub fn from_webidl(idl: &str, module_path: &str) -> Result<Self> {
+ ensure!(
+ !module_path.is_empty(),
+ "you must specify a valid crate name"
+ );
+ let group = uniffi_udl::parse_udl(idl, module_path)?;
+ Self::from_metadata(group)
+ }
+
+ /// Create a `ComponentInterface` from a `MetadataGroup`
+ /// Public so that external binding generators can use it.
+ pub fn from_metadata(group: uniffi_meta::MetadataGroup) -> Result<Self> {
+ let mut ci = Self {
+ types: TypeUniverse::new(group.namespace.clone()),
+ ..Default::default()
+ };
+ ci.add_metadata(group)?;
+ Ok(ci)
+ }
+
+ /// Add a metadata group to a `ComponentInterface`.
+ pub fn add_metadata(&mut self, group: uniffi_meta::MetadataGroup) -> Result<()> {
+ if self.types.namespace.name.is_empty() {
+ self.types.namespace = group.namespace.clone();
+ } else if self.types.namespace != group.namespace {
+ bail!(
+ "Namespace mismatch: {:?} - {:?}",
+ group.namespace,
+ self.types.namespace
+ );
+ }
+ // 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)?;
+ Ok(())
+ }
+
+ /// The string namespace within which this API should be presented to the caller.
+ ///
+ /// This string would typically be used to prefix function names in the FFI, to build
+ /// a package or module name for the foreign language, etc.
+ pub fn namespace(&self) -> &str {
+ &self.types.namespace.name
+ }
+
+ 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");
+ match force_version {
+ Ok(v) if !v.is_empty() => v.parse().unwrap(),
+ _ => UNIFFI_CONTRACT_VERSION,
+ }
+ }
+
+ /// Get the definitions for every Enum type in the interface.
+ pub fn enum_definitions(&self) -> impl Iterator<Item = &Enum> {
+ self.enums.values()
+ }
+
+ /// Get an Enum definition by name, or None if no such Enum is defined.
+ pub fn get_enum_definition(&self, name: &str) -> Option<&Enum> {
+ self.enums.get(name)
+ }
+
+ /// Get the definitions for every Record type in the interface.
+ pub fn record_definitions(&self) -> impl Iterator<Item = &Record> {
+ self.records.values()
+ }
+
+ /// Get a Record definition by name, or None if no such Record is defined.
+ pub fn get_record_definition(&self, name: &str) -> Option<&Record> {
+ self.records.get(name)
+ }
+
+ /// Get the definitions for every Function in the interface.
+ pub fn function_definitions(&self) -> &[Function] {
+ &self.functions
+ }
+
+ /// Get a Function definition by name, or None if no such Function is defined.
+ pub fn get_function_definition(&self, name: &str) -> Option<&Function> {
+ // TODO: probably we could store these internally in a HashMap to make this easier?
+ self.functions.iter().find(|f| f.name == name)
+ }
+
+ /// Get the definitions for every Object type in the interface.
+ pub fn object_definitions(&self) -> &[Object] {
+ &self.objects
+ }
+
+ /// Get an Object definition by name, or None if no such Object is defined.
+ pub fn get_object_definition(&self, name: &str) -> Option<&Object> {
+ // TODO: probably we could store these internally in a HashMap to make this easier?
+ self.objects.iter().find(|o| o.name == name)
+ }
+
+ /// Get the definitions for every Callback Interface type in the interface.
+ pub fn callback_interface_definitions(&self) -> &[CallbackInterface] {
+ &self.callback_interfaces
+ }
+
+ /// Get a Callback interface definition by name, or None if no such interface is defined.
+ pub fn get_callback_interface_definition(&self, name: &str) -> Option<&CallbackInterface> {
+ // TODO: probably we could store these internally in a HashMap to make this easier?
+ self.callback_interfaces.iter().find(|o| o.name == name)
+ }
+
+ /// 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
+ // way to express the logic in the current Rust
+ #[allow(trivial_casts)]
+ self.function_definitions()
+ .iter()
+ .map(|f| f as &dyn Callable)
+ .chain(self.objects.iter().flat_map(|o| {
+ o.constructors()
+ .into_iter()
+ .map(|c| c as &dyn Callable)
+ .chain(o.methods().into_iter().map(|m| m as &dyn Callable))
+ }))
+ }
+
+ /// Should we generate read (and lift) functions for errors?
+ ///
+ /// This is a workaround for the fact that lower/write can't be generated for some errors,
+ /// specifically errors that are defined as flat in the UDL, but actually have fields in the
+ /// Rust source.
+ pub fn should_generate_error_read(&self, e: &Enum) -> bool {
+ // We can and should always generate read() methods for fielded errors
+ 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
+ .callback_interface_definitions()
+ .iter()
+ .flat_map(|cb| cb.methods())
+ .any(|m| m.throws_type() == Some(&e.as_type()));
+
+ self.is_name_used_as_error(&e.name) && (fielded || used_in_callback_interface)
+ }
+
+ /// Get details about all `Type::External` types.
+ /// Returns an iterator of (name, crate_name, kind)
+ pub fn iter_external_types(
+ &self,
+ ) -> impl Iterator<Item = (&String, String, ExternalKind, bool)> {
+ self.types.iter_known_types().filter_map(|t| match t {
+ Type::External {
+ name,
+ module_path,
+ kind,
+ tagged,
+ ..
+ } => Some((
+ name,
+ module_path.split("::").next().unwrap().to_string(),
+ *kind,
+ *tagged,
+ )),
+ _ => None,
+ })
+ }
+
+ /// Get details about all `Type::Custom` types
+ pub fn iter_custom_types(&self) -> impl Iterator<Item = (&String, &Type)> {
+ self.types.iter_known_types().filter_map(|t| match t {
+ Type::Custom { name, builtin, .. } => Some((name, &**builtin)),
+ _ => None,
+ })
+ }
+
+ /// Iterate over all known types in the interface.
+ pub fn iter_types(&self) -> impl Iterator<Item = &Type> {
+ self.types.iter_known_types()
+ }
+
+ /// Get a specific type
+ pub fn get_type(&self, name: &str) -> Option<Type> {
+ self.types.get_type_definition(name)
+ }
+
+ /// Iterate over all types contained in the given item.
+ ///
+ /// This method uses `iter_types` to iterate over the types contained within the given type,
+ /// but additionally recurses into the definition of user-defined types like records and enums
+ /// to yield the types that *they* contain.
+ fn iter_types_in_item<'a>(&'a self, item: &'a Type) -> impl Iterator<Item = &'a Type> + 'a {
+ RecursiveTypeIterator::new(self, item)
+ }
+
+ /// Check whether the given item contains any (possibly nested) Type::Object references.
+ ///
+ /// 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 { .. }))
+ }
+
+ /// Check whether the given item contains any (possibly nested) unsigned types
+ pub fn item_contains_unsigned_types(&self, item: &Type) -> bool {
+ self.iter_types_in_item(item)
+ .any(|t| matches!(t, Type::UInt8 | Type::UInt16 | Type::UInt32 | Type::UInt64))
+ }
+
+ /// Check whether the interface contains any optional types
+ pub fn contains_optional_types(&self) -> bool {
+ self.types
+ .iter_known_types()
+ .any(|t| matches!(t, Type::Optional { .. }))
+ }
+
+ /// Check whether the interface contains any sequence types
+ pub fn contains_sequence_types(&self) -> bool {
+ self.types
+ .iter_known_types()
+ .any(|t| matches!(t, Type::Sequence { .. }))
+ }
+
+ /// Check whether the interface contains any map types
+ pub fn contains_map_types(&self) -> bool {
+ self.types
+ .iter_known_types()
+ .any(|t| matches!(t, Type::Map { .. }))
+ }
+
+ // 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 {
+ &self.types.namespace.crate_name
+ }
+
+ /// Builtin FFI function to get the current contract version
+ /// This is needed so that the foreign language bindings can check that they are using the same
+ /// ABI as the scaffolding
+ pub fn ffi_uniffi_contract_version(&self) -> FfiFunction {
+ FfiFunction {
+ name: format!("ffi_{}_uniffi_contract_version", self.ffi_namespace()),
+ is_async: false,
+ arguments: vec![],
+ return_type: Some(FfiType::UInt32),
+ has_rust_call_status_arg: false,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function for allocating a new `RustBuffer`.
+ /// This is needed so that the foreign language bindings can create buffers in which to pass
+ /// complex data types across the FFI.
+ pub fn ffi_rustbuffer_alloc(&self) -> FfiFunction {
+ FfiFunction {
+ name: format!("ffi_{}_rustbuffer_alloc", self.ffi_namespace()),
+ is_async: false,
+ arguments: vec![FfiArgument {
+ name: "size".to_string(),
+ type_: FfiType::Int32,
+ }],
+ return_type: Some(FfiType::RustBuffer(None)),
+ has_rust_call_status_arg: true,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function for copying foreign-owned bytes
+ /// This is needed so that the foreign language bindings can create buffers in which to pass
+ /// complex data types across the FFI.
+ pub fn ffi_rustbuffer_from_bytes(&self) -> FfiFunction {
+ FfiFunction {
+ name: format!("ffi_{}_rustbuffer_from_bytes", self.ffi_namespace()),
+ is_async: false,
+ arguments: vec![FfiArgument {
+ name: "bytes".to_string(),
+ type_: FfiType::ForeignBytes,
+ }],
+ return_type: Some(FfiType::RustBuffer(None)),
+ has_rust_call_status_arg: true,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function for freeing a `RustBuffer`.
+ /// This is needed so that the foreign language bindings can free buffers in which they received
+ /// complex data types returned across the FFI.
+ pub fn ffi_rustbuffer_free(&self) -> FfiFunction {
+ FfiFunction {
+ name: format!("ffi_{}_rustbuffer_free", self.ffi_namespace()),
+ is_async: false,
+ arguments: vec![FfiArgument {
+ name: "buf".to_string(),
+ type_: FfiType::RustBuffer(None),
+ }],
+ return_type: None,
+ has_rust_call_status_arg: true,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function for reserving extra space in a `RustBuffer`.
+ /// This is needed so that the foreign language bindings can grow buffers used for passing
+ /// complex data types across the FFI.
+ pub fn ffi_rustbuffer_reserve(&self) -> FfiFunction {
+ FfiFunction {
+ name: format!("ffi_{}_rustbuffer_reserve", self.ffi_namespace()),
+ is_async: false,
+ arguments: vec![
+ FfiArgument {
+ name: "buf".to_string(),
+ type_: FfiType::RustBuffer(None),
+ },
+ FfiArgument {
+ name: "additional".to_string(),
+ type_: FfiType::Int32,
+ },
+ ],
+ return_type: Some(FfiType::RustBuffer(None)),
+ has_rust_call_status_arg: true,
+ is_object_free_function: false,
+ }
+ }
+
+ /// 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 {
+ name: self.rust_future_ffi_fn_name("rust_future_poll", return_ffi_type),
+ is_async: false,
+ arguments: vec![
+ FfiArgument {
+ name: "handle".to_owned(),
+ type_: FfiType::RustFutureHandle,
+ },
+ // Data to pass to the continuation
+ FfiArgument {
+ name: "uniffi_callback".to_owned(),
+ type_: FfiType::RustFutureContinuationData,
+ },
+ ],
+ return_type: None,
+ has_rust_call_status_arg: false,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function to complete a Rust future and get it's result.
+ ///
+ /// We generate one of these for each FFI return type.
+ pub fn ffi_rust_future_complete(&self, return_ffi_type: Option<FfiType>) -> FfiFunction {
+ FfiFunction {
+ name: self.rust_future_ffi_fn_name("rust_future_complete", return_ffi_type.clone()),
+ is_async: false,
+ arguments: vec![FfiArgument {
+ name: "handle".to_owned(),
+ type_: FfiType::RustFutureHandle,
+ }],
+ return_type: return_ffi_type,
+ has_rust_call_status_arg: true,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function for cancelling a Rust Future
+ pub fn ffi_rust_future_cancel(&self, return_ffi_type: Option<FfiType>) -> FfiFunction {
+ FfiFunction {
+ name: self.rust_future_ffi_fn_name("rust_future_cancel", return_ffi_type),
+ is_async: false,
+ arguments: vec![FfiArgument {
+ name: "handle".to_owned(),
+ type_: FfiType::RustFutureHandle,
+ }],
+ return_type: None,
+ has_rust_call_status_arg: false,
+ is_object_free_function: false,
+ }
+ }
+
+ /// Builtin FFI function for freeing a Rust Future
+ pub fn ffi_rust_future_free(&self, return_ffi_type: Option<FfiType>) -> FfiFunction {
+ FfiFunction {
+ name: self.rust_future_ffi_fn_name("rust_future_free", return_ffi_type),
+ is_async: false,
+ arguments: vec![FfiArgument {
+ name: "handle".to_owned(),
+ type_: FfiType::RustFutureHandle,
+ }],
+ return_type: None,
+ has_rust_call_status_arg: false,
+ is_object_free_function: false,
+ }
+ }
+
+ 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"),
+ }
+ }
+
+ /// Does this interface contain async functions?
+ pub fn has_async_fns(&self) -> bool {
+ self.iter_ffi_function_definitions().any(|f| f.is_async())
+ }
+
+ /// Iterate over `T` parameters of the `FutureCallback<T>` callbacks in this interface
+ pub fn iter_future_callback_params(&self) -> impl Iterator<Item = FfiType> {
+ let unique_results = self
+ .iter_callables()
+ .map(|c| c.result_type().future_callback_param())
+ .collect::<BTreeSet<_>>();
+ unique_results.into_iter()
+ }
+
+ /// Iterate over return/throws types for async functions
+ pub fn iter_async_result_types(&self) -> impl Iterator<Item = ResultType> {
+ let unique_results = self
+ .iter_callables()
+ .map(|c| c.result_type())
+ .collect::<BTreeSet<_>>();
+ unique_results.into_iter()
+ }
+
+ /// 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
+ /// along with the builtin FFI helper functions.
+ pub fn iter_ffi_function_definitions(&self) -> impl Iterator<Item = FfiFunction> + '_ {
+ 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_checksum_ffi_functions())
+ .chain(self.ffi_foreign_executor_callback_set())
+ .chain([self.ffi_uniffi_contract_version()])
+ }
+
+ /// Alternate version of iter_ffi_function_definitions for languages that don't support async
+ pub fn iter_ffi_function_definitions_non_async(
+ &self,
+ ) -> impl Iterator<Item = FfiFunction> + '_ {
+ self.iter_user_ffi_function_definitions()
+ .cloned()
+ .chain(self.iter_rust_buffer_ffi_function_definitions())
+ .chain(self.iter_checksum_ffi_functions())
+ .chain([self.ffi_uniffi_contract_version()])
+ }
+
+ /// List all FFI functions definitions for user-defined interfaces
+ ///
+ /// This includes FFI functions for:
+ /// - Top-level functions
+ /// - Object methods
+ /// - Callback interfaces
+ pub fn iter_user_ffi_function_definitions(&self) -> impl Iterator<Item = &FfiFunction> + '_ {
+ iter::empty()
+ .chain(
+ self.objects
+ .iter()
+ .flat_map(|obj| obj.iter_ffi_function_definitions()),
+ )
+ .chain(
+ self.callback_interfaces
+ .iter()
+ .map(|cb| cb.ffi_init_callback()),
+ )
+ .chain(self.functions.iter().map(|f| &f.ffi_func))
+ }
+
+ /// List all FFI functions definitions for RustBuffer functionality.
+ pub fn iter_rust_buffer_ffi_function_definitions(&self) -> impl Iterator<Item = FfiFunction> {
+ [
+ self.ffi_rustbuffer_alloc(),
+ self.ffi_rustbuffer_from_bytes(),
+ self.ffi_rustbuffer_free(),
+ self.ffi_rustbuffer_reserve(),
+ ]
+ .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 = [
+ Some(FfiType::UInt8),
+ Some(FfiType::Int8),
+ Some(FfiType::UInt16),
+ Some(FfiType::Int16),
+ Some(FfiType::UInt32),
+ Some(FfiType::Int32),
+ Some(FfiType::UInt64),
+ 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.
+ 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),
+ ]
+ }),
+ )
+ }
+
+ /// 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,
+ })
+ } else {
+ None
+ }
+ }
+
+ /// List all API checksums to check
+ ///
+ /// Returns a list of (export_symbol_name, checksum) items
+ pub fn iter_checksums(&self) -> impl Iterator<Item = (String, u16)> + '_ {
+ let func_checksums = self
+ .functions
+ .iter()
+ .map(|f| (f.checksum_fn_name(), f.checksum()));
+ let method_checksums = self.objects.iter().flat_map(|o| {
+ o.methods()
+ .into_iter()
+ .map(|m| (m.checksum_fn_name(), m.checksum()))
+ });
+ let constructor_checksums = self.objects.iter().flat_map(|o| {
+ o.constructors()
+ .into_iter()
+ .map(|c| (c.checksum_fn_name(), c.checksum()))
+ });
+ let callback_method_checksums = self.callback_interfaces.iter().flat_map(|cbi| {
+ cbi.methods().into_iter().filter_map(|m| {
+ if m.checksum_fn_name().is_empty() {
+ // UDL-based callbacks don't have checksum functions, skip these
+ None
+ } else {
+ Some((m.checksum_fn_name(), m.checksum()))
+ }
+ })
+ });
+ func_checksums
+ .chain(method_checksums)
+ .chain(constructor_checksums)
+ .chain(callback_method_checksums)
+ .map(|(fn_name, checksum)| (fn_name.to_string(), checksum))
+ }
+
+ pub fn iter_checksum_ffi_functions(&self) -> impl Iterator<Item = FfiFunction> + '_ {
+ self.iter_checksums().map(|(name, _)| FfiFunction {
+ name,
+ is_async: false,
+ arguments: vec![],
+ return_type: Some(FfiType::UInt16),
+ has_rust_call_status_arg: false,
+ is_object_free_function: false,
+ })
+ }
+
+ // Private methods for building a ComponentInterface.
+ //
+ /// Called by `APIBuilder` impls to add a newly-parsed enum definition to the `ComponentInterface`.
+ pub(super) fn add_enum_definition(&mut self, defn: Enum) -> Result<()> {
+ match self.enums.entry(defn.name().to_owned()) {
+ Entry::Vacant(v) => {
+ self.types.add_known_types(defn.iter_types())?;
+ v.insert(defn);
+ }
+ Entry::Occupied(o) => {
+ let existing_def = o.get();
+ if defn != *existing_def {
+ bail!(
+ "Mismatching definition for enum `{}`!\n\
+ existing definition: {existing_def:#?},\n\
+ new definition: {defn:#?}",
+ defn.name(),
+ );
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Adds a newly-parsed record definition to the `ComponentInterface`.
+ pub(super) fn add_record_definition(&mut self, defn: Record) -> Result<()> {
+ match self.records.entry(defn.name().to_owned()) {
+ Entry::Vacant(v) => {
+ self.types.add_known_types(defn.iter_types())?;
+ v.insert(defn);
+ }
+ Entry::Occupied(o) => {
+ let existing_def = o.get();
+ if defn != *existing_def {
+ bail!(
+ "Mismatching definition for record `{}`!\n\
+ existing definition: {existing_def:#?},\n\
+ new definition: {defn:#?}",
+ defn.name(),
+ );
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Called by `APIBuilder` impls to add a newly-parsed function definition to the `ComponentInterface`.
+ pub(super) fn add_function_definition(&mut self, defn: Function) -> Result<()> {
+ // Since functions are not a first-class type, we have to check for duplicates here
+ // rather than relying on the type-finding pass to catch them.
+ if self.functions.iter().any(|f| f.name == defn.name) {
+ bail!("duplicate function definition: \"{}\"", defn.name);
+ }
+ if self.types.get_type_definition(defn.name()).is_some() {
+ bail!("Conflicting type definition for \"{}\"", defn.name());
+ }
+ self.types.add_known_types(defn.iter_types())?;
+ self.functions.push(defn);
+
+ Ok(())
+ }
+
+ pub(super) fn add_constructor_meta(&mut self, meta: ConstructorMetadata) -> Result<()> {
+ let object = get_object(&mut self.objects, &meta.self_name)
+ .ok_or_else(|| anyhow!("add_constructor_meta: object {} not found", &meta.self_name))?;
+ let defn: Constructor = meta.into();
+
+ self.types.add_known_types(defn.iter_types())?;
+ object.constructors.push(defn);
+
+ Ok(())
+ }
+
+ pub(super) fn add_method_meta(&mut self, meta: impl Into<Method>) -> Result<()> {
+ let mut method: Method = meta.into();
+ let object = get_object(&mut self.objects, &method.object_name)
+ .ok_or_else(|| anyhow!("add_method_meta: object {} not found", &method.object_name))?;
+
+ self.types.add_known_types(method.iter_types())?;
+ method.object_impl = object.imp;
+ object.methods.push(method);
+ Ok(())
+ }
+
+ pub(super) fn add_uniffitrait_meta(&mut self, meta: UniffiTraitMetadata) -> Result<()> {
+ let object = get_object(&mut self.objects, meta.self_name())
+ .ok_or_else(|| anyhow!("add_uniffitrait_meta: object not found"))?;
+ let ut: UniffiTrait = meta.into();
+ self.types.add_known_types(ut.iter_types())?;
+ object.uniffi_traits.push(ut);
+ Ok(())
+ }
+
+ pub(super) fn add_object_meta(&mut self, meta: ObjectMetadata) -> Result<()> {
+ self.add_object_definition(meta.into())
+ }
+
+ /// Called by `APIBuilder` impls to add a newly-parsed object definition to the `ComponentInterface`.
+ fn add_object_definition(&mut self, defn: Object) -> Result<()> {
+ self.types.add_known_types(defn.iter_types())?;
+ self.objects.push(defn);
+ 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)
+ }
+
+ /// Called by `APIBuilder` impls to add a newly-parsed callback interface definition to the `ComponentInterface`.
+ pub(super) fn add_callback_interface_definition(&mut self, defn: CallbackInterface) {
+ self.callback_interfaces.push(defn);
+ }
+
+ pub(super) fn add_trait_method_meta(&mut self, meta: TraitMethodMetadata) -> Result<()> {
+ if let Some(cbi) = get_callback_interface(&mut self.callback_interfaces, &meta.trait_name) {
+ // uniffi_meta should ensure that we process callback interface methods in order, double
+ // check that here
+ if cbi.methods.len() != meta.index as usize {
+ bail!(
+ "UniFFI internal error: callback interface method index mismatch for {}::{} (expected {}, saw {})",
+ meta.trait_name,
+ meta.name,
+ cbi.methods.len(),
+ meta.index,
+ );
+ }
+ let method: Method = meta.into();
+ if let Some(error) = method.throws_type() {
+ self.callback_interface_throws_types.insert(error.clone());
+ }
+ self.types.add_known_types(method.iter_types())?;
+ cbi.methods.push(method);
+ } else {
+ self.add_method_meta(meta)?;
+ }
+ Ok(())
+ }
+
+ /// Perform global consistency checks on the declared interface.
+ ///
+ /// This method checks for consistency problems in the declared interface
+ /// as a whole, and which can only be detected after we've finished defining
+ /// the entire interface.
+ pub fn check_consistency(&self) -> Result<()> {
+ if self.namespace().is_empty() {
+ bail!("missing namespace definition");
+ }
+
+ // Because functions aren't first class types, we need to check here that
+ // a function name hasn't already been used as a type name.
+ for f in self.functions.iter() {
+ if self.types.get_type_definition(f.name()).is_some() {
+ 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(())
+ }
+
+ /// Automatically derive the low-level FFI functions from the high-level types in the interface.
+ ///
+ /// This should only be called after the high-level types have been completed defined, otherwise
+ /// the resulting set will be missing some entries.
+ pub fn derive_ffi_funcs(&mut self) -> Result<()> {
+ for func in self.functions.iter_mut() {
+ func.derive_ffi_func()?;
+ }
+ for obj in self.objects.iter_mut() {
+ obj.derive_ffi_funcs()?;
+ }
+ for callback in self.callback_interfaces.iter_mut() {
+ callback.derive_ffi_funcs();
+ }
+ Ok(())
+ }
+}
+
+fn get_object<'a>(objects: &'a mut [Object], name: &str) -> Option<&'a mut Object> {
+ objects.iter_mut().find(|o| o.name == name)
+}
+
+fn get_callback_interface<'a>(
+ callback_interfaces: &'a mut [CallbackInterface],
+ name: &str,
+) -> Option<&'a mut CallbackInterface> {
+ callback_interfaces.iter_mut().find(|o| o.name == name)
+}
+
+/// Stateful iterator for yielding all types contained in a given type.
+///
+/// This struct is the implementation of [`ComponentInterface::iter_types_in_item`] and should be
+/// considered an opaque implementation detail. It's a separate struct because I couldn't
+/// figure out a way to implement it using iterators and closures that would make the lifetimes
+/// work out correctly.
+///
+/// The idea here is that we want to yield all the types from `iter_types` on a given type, and
+/// additionally we want to recurse into the definition of any user-provided types like records,
+/// enums, etc so we can also yield the types contained therein.
+///
+/// To guard against infinite recursion, we maintain a list of previously-seen user-defined
+/// types, ensuring that we recurse into the definition of those types only once. To simplify
+/// the implementation, we maintain a queue of pending user-defined types that we have seen
+/// but not yet recursed into. (Ironically, the use of an explicit queue means our implementation
+/// is not actually recursive...)
+struct RecursiveTypeIterator<'a> {
+ /// The [`ComponentInterface`] from which this iterator was created.
+ ci: &'a ComponentInterface,
+ /// The currently-active iterator from which we're yielding.
+ current: TypeIterator<'a>,
+ /// A set of names of user-defined types that we have already seen.
+ seen: HashSet<&'a str>,
+ /// A queue of user-defined types that we need to recurse into.
+ pending: Vec<&'a Type>,
+}
+
+impl<'a> RecursiveTypeIterator<'a> {
+ /// Allocate a new `RecursiveTypeIterator` over the given item.
+ fn new(ci: &'a ComponentInterface, item: &'a Type) -> RecursiveTypeIterator<'a> {
+ RecursiveTypeIterator {
+ ci,
+ // We begin by iterating over the types from the item itself.
+ current: item.iter_types(),
+ seen: Default::default(),
+ pending: Default::default(),
+ }
+ }
+
+ /// Add a new type to the queue of pending types, if not previously seen.
+ fn add_pending_type(&mut self, type_: &'a Type) {
+ match type_ {
+ Type::Record { name, .. }
+ | Type::Enum { name, .. }
+ | Type::Object { name, .. }
+ | Type::CallbackInterface { name, .. } => {
+ if !self.seen.contains(name.as_str()) {
+ self.pending.push(type_);
+ self.seen.insert(name.as_str());
+ }
+ }
+ _ => (),
+ }
+ }
+
+ /// Advance the iterator to recurse into the next pending type, if any.
+ ///
+ /// This method is called when the current iterator is empty, and it will select
+ /// the next pending type from the queue and start iterating over its contained types.
+ /// The return value will be the first item from the new iterator.
+ fn advance_to_next_type(&mut self) -> Option<&'a Type> {
+ if let Some(next_type) = self.pending.pop() {
+ // This is a little awkward because the various definition lookup methods return an `Option<T>`.
+ // In the unlikely event that one of them returns `None` then, rather than trying to advance
+ // to a non-existent type, we just leave the existing iterator in place and allow the recursive
+ // call to `next()` to try again with the next pending type.
+ let next_iter = match next_type {
+ Type::Record { name, .. } => {
+ self.ci.get_record_definition(name).map(Record::iter_types)
+ }
+ Type::Enum { name, .. } => self.ci.get_enum_definition(name).map(Enum::iter_types),
+ Type::Object { name, .. } => {
+ self.ci.get_object_definition(name).map(Object::iter_types)
+ }
+ Type::CallbackInterface { name, .. } => self
+ .ci
+ .get_callback_interface_definition(name)
+ .map(CallbackInterface::iter_types),
+ _ => None,
+ };
+ if let Some(next_iter) = next_iter {
+ self.current = next_iter;
+ }
+ // Advance the new iterator to its first item. If the new iterator happens to be empty,
+ // this will recurse back in to `advance_to_next_type` until we find one that isn't.
+ self.next()
+ } else {
+ // We've completely finished the iteration over all pending types.
+ None
+ }
+ }
+}
+
+impl<'a> Iterator for RecursiveTypeIterator<'a> {
+ type Item = &'a Type;
+ fn next(&mut self) -> Option<Self::Item> {
+ if let Some(type_) = self.current.next() {
+ self.add_pending_type(type_);
+ Some(type_)
+ } else {
+ self.advance_to_next_type()
+ }
+ }
+}
+
+// Helpers for functions/methods/constructors which all have the same "throws" semantics.
+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),
+ _ => panic!("unknown throw type: {throws:?}"),
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ // Note that much of the functionality of `ComponentInterface` is tested via its interactions
+ // with specific member types, in the sub-modules defining those member types.
+
+ #[test]
+ fn test_duplicate_type_names_are_an_error() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ constructor();
+ };
+ dictionary Testing {
+ u32 field;
+ };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL, "crate_name").unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Conflicting type definition for `Testing`! \
+ existing definition: Object { module_path: \"crate_name\", name: \"Testing\", imp: Struct }, \
+ new definition: Record { module_path: \"crate_name\", name: \"Testing\" }"
+ );
+
+ const UDL2: &str = r#"
+ namespace test{};
+ enum Testing {
+ "one", "two"
+ };
+ [Error]
+ enum Testing { "three", "four" };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL2, "crate_name").unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Mismatching definition for enum `Testing`!\nexisting definition: Enum {
+ name: \"Testing\",
+ module_path: \"crate_name\",
+ variants: [
+ Variant {
+ name: \"one\",
+ fields: [],
+ },
+ Variant {
+ name: \"two\",
+ fields: [],
+ },
+ ],
+ flat: true,
+},
+new definition: Enum {
+ name: \"Testing\",
+ module_path: \"crate_name\",
+ variants: [
+ Variant {
+ name: \"three\",
+ fields: [],
+ },
+ Variant {
+ name: \"four\",
+ fields: [],
+ },
+ ],
+ flat: true,
+}",
+ );
+
+ const UDL3: &str = r#"
+ namespace test{
+ u32 Testing();
+ };
+ enum Testing {
+ "one", "two"
+ };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL3, "crate_name").unwrap_err();
+ assert!(format!("{err:#}").contains("Conflicting type definition for \"Testing\""));
+ }
+
+ #[test]
+ fn test_contains_optional_types() {
+ let mut ci = ComponentInterface {
+ ..Default::default()
+ };
+
+ // check that `contains_optional_types` returns false when there is no Optional type in the interface
+ assert!(!ci.contains_optional_types());
+
+ // check that `contains_optional_types` returns true when there is an Optional type in the interface
+ assert!(ci
+ .types
+ .add_known_type(&Type::Optional {
+ inner_type: Box::new(Type::String)
+ })
+ .is_ok());
+ assert!(ci.contains_optional_types());
+ }
+
+ #[test]
+ fn test_contains_sequence_types() {
+ let mut ci = ComponentInterface {
+ ..Default::default()
+ };
+
+ // check that `contains_sequence_types` returns false when there is no Sequence type in the interface
+ assert!(!ci.contains_sequence_types());
+
+ // check that `contains_sequence_types` returns true when there is a Sequence type in the interface
+ assert!(ci
+ .types
+ .add_known_type(&Type::Sequence {
+ inner_type: Box::new(Type::UInt64)
+ })
+ .is_ok());
+ assert!(ci.contains_sequence_types());
+ assert!(ci.types.contains(&Type::UInt64));
+ }
+
+ #[test]
+ fn test_contains_map_types() {
+ let mut ci = ComponentInterface {
+ ..Default::default()
+ };
+
+ // check that `contains_map_types` returns false when there is no Map type in the interface
+ assert!(!ci.contains_map_types());
+
+ // check that `contains_map_types` returns true when there is a Map type in the interface
+ assert!(ci
+ .types
+ .add_known_type(&Type::Map {
+ key_type: Box::new(Type::String),
+ value_type: Box::new(Type::Boolean)
+ })
+ .is_ok());
+ assert!(ci.contains_map_types());
+ assert!(ci.types.contains(&Type::String));
+ assert!(ci.types.contains(&Type::Boolean));
+ }
+
+ #[test]
+ fn test_no_infinite_recursion_when_walking_types() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ void tester(Testing foo);
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert!(!ci.item_contains_unsigned_types(&Type::Object {
+ name: "Testing".into(),
+ module_path: "".into(),
+ imp: ObjectImpl::Struct,
+ }));
+ }
+
+ #[test]
+ fn test_correct_recursion_when_walking_types() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface TestObj {
+ void tester(TestRecord foo);
+ };
+ dictionary TestRecord {
+ NestedRecord bar;
+ };
+ dictionary NestedRecord {
+ u64 baz;
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert!(ci.item_contains_unsigned_types(&Type::Object {
+ name: "TestObj".into(),
+ module_path: "".into(),
+ imp: ObjectImpl::Struct,
+ }));
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/object.rs b/third_party/rust/uniffi_bindgen/src/interface/object.rs
new file mode 100644
index 0000000000..942032b3c6
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/object.rs
@@ -0,0 +1,773 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Object definitions for a `ComponentInterface`.
+//!
+//! This module converts "interface" definitions from UDL into [`Object`] structures
+//! that can be added to a `ComponentInterface`, which are the main way we define stateful
+//! objects with behaviour for a UniFFI Rust Component. An [`Object`] is an opaque handle
+//! to some state on which methods can be invoked.
+//!
+//! (The terminology mismatch between "interface" and "object" is a historical artifact of
+//! this tool prior to committing to WebIDL syntax).
+//!
+//! A declaration in the UDL like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! interface Example {
+//! constructor(string? name);
+//! string my_name();
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in an [`Object`] member with one [`Constructor`] and one [`Method`] being added
+//! to the resulting [`crate::ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # interface Example {
+//! # constructor(string? name);
+//! # string my_name();
+//! # };
+//! # "##, "crate_name")?;
+//! let obj = ci.get_object_definition("Example").unwrap();
+//! assert_eq!(obj.name(), "Example");
+//! assert_eq!(obj.constructors().len(), 1);
+//! assert_eq!(obj.constructors()[0].arguments()[0].name(), "name");
+//! assert_eq!(obj.methods().len(),1 );
+//! assert_eq!(obj.methods()[0].name(), "my_name");
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! It's not necessary for all interfaces to have constructors.
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # interface Example {};
+//! # "##, "crate_name")?;
+//! let obj = ci.get_object_definition("Example").unwrap();
+//! assert_eq!(obj.name(), "Example");
+//! assert_eq!(obj.constructors().len(), 0);
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+
+use std::iter;
+
+use anyhow::Result;
+use uniffi_meta::Checksum;
+
+use super::ffi::{FfiArgument, FfiFunction, FfiType};
+use super::function::{Argument, Callable};
+use super::{AsType, ObjectImpl, Type, TypeIterator};
+
+/// An "object" is an opaque type that is passed around by reference, can
+/// have methods called on it, and so on - basically your classic Object Oriented Programming
+/// type of deal, except without elaborate inheritance hierarchies. Some can be instantiated.
+///
+/// In UDL these correspond to the `interface` keyword.
+///
+/// At the FFI layer, objects are represented by an opaque integer handle and a set of functions
+/// a common prefix. The object's constructors are functions that return new objects by handle,
+/// and its methods are functions that take a handle as first argument. The foreign language
+/// binding code is expected to stitch these functions back together into an appropriate class
+/// definition (or that language's equivalent thereof).
+///
+/// TODO:
+/// - maybe "Class" would be a better name than "Object" here?
+#[derive(Debug, Clone, Checksum)]
+pub struct Object {
+ pub(super) name: String,
+ /// How this object is implemented in Rust
+ pub(super) imp: ObjectImpl,
+ pub(super) module_path: String,
+ pub(super) constructors: Vec<Constructor>,
+ pub(super) methods: Vec<Method>,
+ // The "trait" methods - they have a (presumably "well known") name, and
+ // a regular method (albeit with a generated name)
+ // XXX - this should really be a HashSet, but not enough transient types support hash to make it worthwhile now.
+ pub(super) uniffi_traits: Vec<UniffiTrait>,
+ // We don't include the FfiFunc in the hash calculation, because:
+ // - it is entirely determined by the other fields,
+ // so excluding it is safe.
+ // - its `name` property includes a checksum derived from the very
+ // hash value we're trying to calculate here, so excluding it
+ // avoids a weird circular dependency in the calculation.
+ #[checksum_ignore]
+ pub(super) ffi_func_free: FfiFunction,
+}
+
+impl Object {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ /// Returns the fully qualified name that should be used by Rust code for this object.
+ /// Includes `r#`, traits get a leading `dyn`. If we ever supported associated types, then
+ /// this would also include them.
+ pub fn rust_name(&self) -> String {
+ self.imp.rust_name_for(&self.name)
+ }
+
+ pub fn imp(&self) -> &ObjectImpl {
+ &self.imp
+ }
+
+ pub fn constructors(&self) -> Vec<&Constructor> {
+ self.constructors.iter().collect()
+ }
+
+ pub fn primary_constructor(&self) -> Option<&Constructor> {
+ self.constructors
+ .iter()
+ .find(|cons| cons.is_primary_constructor())
+ }
+
+ pub fn alternate_constructors(&self) -> Vec<&Constructor> {
+ self.constructors
+ .iter()
+ .filter(|cons| !cons.is_primary_constructor())
+ .collect()
+ }
+
+ pub fn methods(&self) -> Vec<&Method> {
+ self.methods.iter().collect()
+ }
+
+ pub fn get_method(&self, name: &str) -> Method {
+ let matches: Vec<_> = self.methods.iter().filter(|m| m.name() == name).collect();
+ match matches.len() {
+ 1 => matches[0].clone(),
+ n => panic!("{n} methods named {name}"),
+ }
+ }
+
+ pub fn uniffi_traits(&self) -> Vec<&UniffiTrait> {
+ self.uniffi_traits.iter().collect()
+ }
+
+ pub fn ffi_object_free(&self) -> &FfiFunction {
+ &self.ffi_func_free
+ }
+
+ pub fn iter_ffi_function_definitions(&self) -> impl Iterator<Item = &FfiFunction> {
+ iter::once(&self.ffi_func_free)
+ .chain(self.constructors.iter().map(|f| &f.ffi_func))
+ .chain(self.methods.iter().map(|f| &f.ffi_func))
+ .chain(
+ self.uniffi_traits
+ .iter()
+ .flat_map(|ut| match ut {
+ UniffiTrait::Display { fmt: m }
+ | UniffiTrait::Debug { fmt: m }
+ | UniffiTrait::Hash { hash: m } => vec![m],
+ UniffiTrait::Eq { eq, ne } => vec![eq, ne],
+ })
+ .map(|m| &m.ffi_func),
+ )
+ }
+
+ pub fn derive_ffi_funcs(&mut self) -> Result<()> {
+ assert!(!self.ffi_func_free.name().is_empty());
+ self.ffi_func_free.arguments = vec![FfiArgument {
+ name: "ptr".to_string(),
+ type_: FfiType::RustArcPtr(self.name.to_string()),
+ }];
+ self.ffi_func_free.return_type = None;
+ self.ffi_func_free.is_object_free_function = true;
+
+ for cons in self.constructors.iter_mut() {
+ cons.derive_ffi_func();
+ }
+ for meth in self.methods.iter_mut() {
+ meth.derive_ffi_func()?;
+ }
+ for ut in self.uniffi_traits.iter_mut() {
+ ut.derive_ffi_func()?;
+ }
+
+ Ok(())
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(
+ self.methods
+ .iter()
+ .map(Method::iter_types)
+ .chain(self.uniffi_traits.iter().map(UniffiTrait::iter_types))
+ .chain(self.constructors.iter().map(Constructor::iter_types))
+ .flatten(),
+ )
+ }
+}
+
+impl AsType for Object {
+ fn as_type(&self) -> Type {
+ Type::Object {
+ name: self.name.clone(),
+ module_path: self.module_path.clone(),
+ imp: self.imp,
+ }
+ }
+}
+
+impl From<uniffi_meta::ObjectMetadata> for Object {
+ fn from(meta: uniffi_meta::ObjectMetadata) -> Self {
+ let ffi_free_name = meta.free_ffi_symbol_name();
+ Object {
+ module_path: meta.module_path,
+ name: meta.name,
+ imp: meta.imp,
+ constructors: Default::default(),
+ methods: Default::default(),
+ uniffi_traits: Default::default(),
+ ffi_func_free: FfiFunction {
+ name: ffi_free_name,
+ ..Default::default()
+ },
+ }
+ }
+}
+
+impl From<uniffi_meta::UniffiTraitMetadata> for UniffiTrait {
+ fn from(meta: uniffi_meta::UniffiTraitMetadata) -> Self {
+ match meta {
+ uniffi_meta::UniffiTraitMetadata::Debug { fmt } => {
+ UniffiTrait::Debug { fmt: fmt.into() }
+ }
+ uniffi_meta::UniffiTraitMetadata::Display { fmt } => {
+ UniffiTrait::Display { fmt: fmt.into() }
+ }
+ uniffi_meta::UniffiTraitMetadata::Eq { eq, ne } => UniffiTrait::Eq {
+ eq: eq.into(),
+ ne: ne.into(),
+ },
+ uniffi_meta::UniffiTraitMetadata::Hash { hash } => {
+ UniffiTrait::Hash { hash: hash.into() }
+ }
+ }
+ }
+}
+
+// Represents a constructor for an object type.
+//
+// In the FFI, this will be a function that returns a pointer to an instance
+// of the corresponding object type.
+#[derive(Debug, Clone, Checksum)]
+pub struct Constructor {
+ pub(super) name: String,
+ pub(super) object_name: String,
+ pub(super) object_module_path: String,
+ pub(super) arguments: Vec<Argument>,
+ // We don't include the FFIFunc in the hash calculation, because:
+ // - it is entirely determined by the other fields,
+ // so excluding it is safe.
+ // - its `name` property includes a checksum derived from the very
+ // hash value we're trying to calculate here, so excluding it
+ // avoids a weird circular dependency in the calculation.
+ #[checksum_ignore]
+ pub(super) ffi_func: FfiFunction,
+ pub(super) throws: Option<Type>,
+ pub(super) checksum_fn_name: String,
+ // Force a checksum value, or we'll fallback to the trait.
+ #[checksum_ignore]
+ pub(super) checksum: Option<u16>,
+}
+
+impl Constructor {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn arguments(&self) -> Vec<&Argument> {
+ self.arguments.iter().collect()
+ }
+
+ pub fn full_arguments(&self) -> Vec<Argument> {
+ self.arguments.to_vec()
+ }
+
+ pub fn ffi_func(&self) -> &FfiFunction {
+ &self.ffi_func
+ }
+
+ pub fn checksum_fn_name(&self) -> &str {
+ &self.checksum_fn_name
+ }
+
+ pub fn checksum(&self) -> u16 {
+ self.checksum.unwrap_or_else(|| uniffi_meta::checksum(self))
+ }
+
+ pub fn throws(&self) -> bool {
+ self.throws.is_some()
+ }
+
+ pub fn throws_name(&self) -> Option<&str> {
+ super::throws_name(&self.throws)
+ }
+
+ pub fn throws_type(&self) -> Option<&Type> {
+ self.throws.as_ref()
+ }
+
+ pub fn is_primary_constructor(&self) -> bool {
+ self.name == "new"
+ }
+
+ fn derive_ffi_func(&mut self) {
+ assert!(!self.ffi_func.name().is_empty());
+ self.ffi_func.arguments = self.arguments.iter().map(Into::into).collect();
+ self.ffi_func.return_type = Some(FfiType::RustArcPtr(self.object_name.clone()));
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(self.arguments.iter().flat_map(Argument::iter_types))
+ }
+}
+
+impl From<uniffi_meta::ConstructorMetadata> for Constructor {
+ fn from(meta: uniffi_meta::ConstructorMetadata) -> Self {
+ let ffi_name = meta.ffi_symbol_name();
+ let checksum_fn_name = meta.checksum_symbol_name();
+ let arguments = meta.inputs.into_iter().map(Into::into).collect();
+
+ let ffi_func = FfiFunction {
+ name: ffi_name,
+ ..FfiFunction::default()
+ };
+ Self {
+ name: meta.name,
+ object_name: meta.self_name,
+ object_module_path: meta.module_path,
+ arguments,
+ ffi_func,
+ throws: meta.throws.map(Into::into),
+ checksum_fn_name,
+ checksum: meta.checksum,
+ }
+ }
+}
+
+// Represents an instance method for an object type.
+//
+// The FFI will represent this as a function whose first/self argument is a
+// `FfiType::RustArcPtr` to the instance.
+#[derive(Debug, Clone, Checksum)]
+pub struct Method {
+ pub(super) name: String,
+ pub(super) object_name: String,
+ pub(super) object_module_path: String,
+ pub(super) is_async: bool,
+ pub(super) object_impl: ObjectImpl,
+ pub(super) arguments: Vec<Argument>,
+ pub(super) return_type: Option<Type>,
+ // We don't include the FFIFunc in the hash calculation, because:
+ // - it is entirely determined by the other fields,
+ // so excluding it is safe.
+ // - its `name` property includes a checksum derived from the very
+ // hash value we're trying to calculate here, so excluding it
+ // avoids a weird circular dependency in the calculation.
+ #[checksum_ignore]
+ pub(super) ffi_func: FfiFunction,
+ pub(super) throws: Option<Type>,
+ pub(super) takes_self_by_arc: bool,
+ pub(super) checksum_fn_name: String,
+ // Force a checksum value, or we'll fallback to the trait.
+ #[checksum_ignore]
+ pub(super) checksum: Option<u16>,
+}
+
+impl Method {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn is_async(&self) -> bool {
+ self.is_async
+ }
+
+ pub fn arguments(&self) -> Vec<&Argument> {
+ self.arguments.iter().collect()
+ }
+
+ // Methods have a special implicit first argument for the object instance,
+ // hence `arguments` and `full_arguments` are different.
+ pub fn full_arguments(&self) -> Vec<Argument> {
+ vec![Argument {
+ name: "ptr".to_string(),
+ // TODO: ideally we'd get this via `ci.resolve_type_expression` so that it
+ // is contained in the proper `TypeUniverse`, but this works for now.
+ type_: Type::Object {
+ name: self.object_name.clone(),
+ module_path: self.object_module_path.clone(),
+ imp: self.object_impl,
+ },
+ by_ref: !self.takes_self_by_arc,
+ optional: false,
+ default: None,
+ }]
+ .into_iter()
+ .chain(self.arguments.iter().cloned())
+ .collect()
+ }
+
+ pub fn return_type(&self) -> Option<&Type> {
+ self.return_type.as_ref()
+ }
+
+ pub fn ffi_func(&self) -> &FfiFunction {
+ &self.ffi_func
+ }
+
+ pub fn checksum_fn_name(&self) -> &str {
+ &self.checksum_fn_name
+ }
+
+ pub fn checksum(&self) -> u16 {
+ self.checksum.unwrap_or_else(|| uniffi_meta::checksum(self))
+ }
+
+ pub fn throws(&self) -> bool {
+ self.throws.is_some()
+ }
+
+ pub fn throws_name(&self) -> Option<&str> {
+ super::throws_name(&self.throws)
+ }
+
+ pub fn throws_type(&self) -> Option<&Type> {
+ self.throws.as_ref()
+ }
+
+ pub fn takes_self_by_arc(&self) -> bool {
+ self.takes_self_by_arc
+ }
+
+ pub fn derive_ffi_func(&mut self) -> Result<()> {
+ assert!(!self.ffi_func.name().is_empty());
+ self.ffi_func.init(
+ self.return_type.as_ref().map(Into::into),
+ self.full_arguments().iter().map(Into::into),
+ );
+ Ok(())
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(
+ self.arguments
+ .iter()
+ .flat_map(Argument::iter_types)
+ .chain(self.return_type.iter().flat_map(Type::iter_types)),
+ )
+ }
+}
+
+impl From<uniffi_meta::MethodMetadata> for Method {
+ fn from(meta: uniffi_meta::MethodMetadata) -> Self {
+ let ffi_name = meta.ffi_symbol_name();
+ let checksum_fn_name = meta.checksum_symbol_name();
+ let is_async = meta.is_async;
+ let return_type = meta.return_type.map(Into::into);
+ let arguments = meta.inputs.into_iter().map(Into::into).collect();
+
+ let ffi_func = FfiFunction {
+ name: ffi_name,
+ is_async,
+ ..FfiFunction::default()
+ };
+
+ Self {
+ name: meta.name,
+ object_name: meta.self_name,
+ object_module_path: meta.module_path,
+ is_async,
+ object_impl: ObjectImpl::Struct, // will be filled in later
+ arguments,
+ return_type,
+ ffi_func,
+ throws: meta.throws.map(Into::into),
+ takes_self_by_arc: meta.takes_self_by_arc,
+ checksum_fn_name,
+ checksum: meta.checksum,
+ }
+ }
+}
+
+impl From<uniffi_meta::TraitMethodMetadata> for Method {
+ fn from(meta: uniffi_meta::TraitMethodMetadata) -> Self {
+ let ffi_name = meta.ffi_symbol_name();
+ let checksum_fn_name = meta.checksum_symbol_name();
+ let return_type = meta.return_type.map(Into::into);
+ let arguments = meta.inputs.into_iter().map(Into::into).collect();
+ let ffi_func = FfiFunction {
+ name: ffi_name,
+ ..FfiFunction::default()
+ };
+ Self {
+ name: meta.name,
+ object_name: meta.trait_name,
+ object_module_path: meta.module_path,
+ is_async: false,
+ arguments,
+ return_type,
+ throws: meta.throws.map(Into::into),
+ takes_self_by_arc: meta.takes_self_by_arc,
+ checksum_fn_name,
+ checksum: meta.checksum,
+ ffi_func,
+ object_impl: ObjectImpl::Struct,
+ }
+ }
+}
+
+/// The list of traits we support generating helper methods for.
+#[derive(Clone, Debug, Checksum)]
+pub enum UniffiTrait {
+ Debug { fmt: Method },
+ Display { fmt: Method },
+ Eq { eq: Method, ne: Method },
+ Hash { hash: Method },
+}
+
+impl UniffiTrait {
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(
+ match self {
+ UniffiTrait::Display { fmt: m }
+ | UniffiTrait::Debug { fmt: m }
+ | UniffiTrait::Hash { hash: m } => vec![m.iter_types()],
+ UniffiTrait::Eq { eq, ne } => vec![eq.iter_types(), ne.iter_types()],
+ }
+ .into_iter()
+ .flatten(),
+ )
+ }
+
+ pub fn derive_ffi_func(&mut self) -> Result<()> {
+ match self {
+ UniffiTrait::Display { fmt: m }
+ | UniffiTrait::Debug { fmt: m }
+ | UniffiTrait::Hash { hash: m } => {
+ m.derive_ffi_func()?;
+ }
+ UniffiTrait::Eq { eq, ne } => {
+ eq.derive_ffi_func()?;
+ ne.derive_ffi_func()?;
+ }
+ }
+ Ok(())
+ }
+}
+
+impl Callable for Constructor {
+ fn arguments(&self) -> Vec<&Argument> {
+ self.arguments()
+ }
+
+ fn return_type(&self) -> Option<Type> {
+ Some(Type::Object {
+ name: self.object_name.clone(),
+ module_path: self.object_module_path.clone(),
+ imp: ObjectImpl::Struct,
+ })
+ }
+
+ fn throws_type(&self) -> Option<Type> {
+ self.throws_type().cloned()
+ }
+
+ fn is_async(&self) -> bool {
+ false
+ }
+}
+
+impl Callable for Method {
+ fn arguments(&self) -> Vec<&Argument> {
+ self.arguments()
+ }
+
+ fn return_type(&self) -> Option<Type> {
+ self.return_type().cloned()
+ }
+
+ fn throws_type(&self) -> Option<Type> {
+ self.throws_type().cloned()
+ }
+
+ fn is_async(&self) -> bool {
+ self.is_async
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::super::ComponentInterface;
+ use super::*;
+
+ #[test]
+ fn test_that_all_argument_and_return_types_become_known() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ constructor(string? name, u16 age);
+ sequence<u32> code_points_of_name();
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.object_definitions().len(), 1);
+ ci.get_object_definition("Testing").unwrap();
+
+ assert_eq!(ci.iter_types().count(), 6);
+ assert!(ci.iter_types().any(|t| t == &Type::UInt16));
+ assert!(ci.iter_types().any(|t| t == &Type::UInt32));
+ assert!(ci.iter_types().any(|t| t
+ == &Type::Sequence {
+ inner_type: Box::new(Type::UInt32)
+ }));
+ assert!(ci.iter_types().any(|t| t == &Type::String));
+ assert!(ci.iter_types().any(|t| t
+ == &Type::Optional {
+ inner_type: Box::new(Type::String)
+ }));
+ assert!(ci
+ .iter_types()
+ .any(|t| matches!(t, Type::Object { name, ..} if name == "Testing")));
+ }
+
+ #[test]
+ fn test_alternate_constructors() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ constructor();
+ [Name=new_with_u32]
+ constructor(u32 v);
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.object_definitions().len(), 1);
+
+ let obj = ci.get_object_definition("Testing").unwrap();
+ assert!(obj.primary_constructor().is_some());
+ assert_eq!(obj.alternate_constructors().len(), 1);
+ assert_eq!(obj.methods().len(), 0);
+
+ let cons = obj.primary_constructor().unwrap();
+ assert_eq!(cons.name(), "new");
+ assert_eq!(cons.arguments.len(), 0);
+ assert_eq!(cons.ffi_func.arguments.len(), 0);
+
+ let cons = obj.alternate_constructors()[0];
+ assert_eq!(cons.name(), "new_with_u32");
+ assert_eq!(cons.arguments.len(), 1);
+ assert_eq!(cons.ffi_func.arguments.len(), 1);
+ }
+
+ #[test]
+ fn test_the_name_new_identifies_the_primary_constructor() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ [Name=newish]
+ constructor();
+ [Name=new]
+ constructor(u32 v);
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.object_definitions().len(), 1);
+
+ let obj = ci.get_object_definition("Testing").unwrap();
+ assert!(obj.primary_constructor().is_some());
+ assert_eq!(obj.alternate_constructors().len(), 1);
+ assert_eq!(obj.methods().len(), 0);
+
+ let cons = obj.primary_constructor().unwrap();
+ assert_eq!(cons.name(), "new");
+ assert_eq!(cons.arguments.len(), 1);
+
+ let cons = obj.alternate_constructors()[0];
+ assert_eq!(cons.name(), "newish");
+ assert_eq!(cons.arguments.len(), 0);
+ assert_eq!(cons.ffi_func.arguments.len(), 0);
+ }
+
+ #[test]
+ fn test_the_name_new_is_reserved_for_constructors() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ constructor();
+ void new(u32 v);
+ };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL, "crate_name").unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "the method name \"new\" is reserved for the default constructor"
+ );
+ }
+
+ #[test]
+ fn test_duplicate_primary_constructors_not_allowed() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ constructor();
+ constructor(u32 v);
+ };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL, "crate_name").unwrap_err();
+ assert_eq!(err.to_string(), "Duplicate interface member name: \"new\"");
+
+ const UDL2: &str = r#"
+ namespace test{};
+ interface Testing {
+ constructor();
+ [Name=new]
+ constructor(u32 v);
+ };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL2, "crate_name").unwrap_err();
+ assert_eq!(err.to_string(), "Duplicate interface member name: \"new\"");
+ }
+
+ #[test]
+ fn test_trait_attribute() {
+ const UDL: &str = r#"
+ namespace test{};
+ interface NotATrait {
+ };
+ [Trait]
+ interface ATrait {
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ let obj = ci.get_object_definition("NotATrait").unwrap();
+ assert_eq!(obj.imp.rust_name_for(&obj.name), "r#NotATrait");
+ let obj = ci.get_object_definition("ATrait").unwrap();
+ assert_eq!(obj.imp.rust_name_for(&obj.name), "dyn r#ATrait");
+ }
+
+ #[test]
+ fn test_trait_constructors_not_allowed() {
+ const UDL: &str = r#"
+ namespace test{};
+ [Trait]
+ interface Testing {
+ constructor();
+ };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL, "crate_name").unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Trait interfaces can not have constructors: \"new\""
+ );
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/record.rs b/third_party/rust/uniffi_bindgen/src/interface/record.rs
new file mode 100644
index 0000000000..17d3774a49
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/record.rs
@@ -0,0 +1,230 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! # Record definitions for a `ComponentInterface`.
+//!
+//! This module converts "dictionary" definitions from UDL into [`Record`] structures
+//! that can be added to a `ComponentInterface`, which are the main way we define structured
+//! data types for a UniFFI Rust Component. A [`Record`] has a fixed set of named fields,
+//! each of a specific type.
+//!
+//! (The terminology mismatch between "dictionary" and "record" is a historical artifact
+//! due to this tool being loosely inspired by WebAssembly Interface Types, which used
+//! the term "record" for this sort of data).
+//!
+//! A declaration in the UDL like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! dictionary Example {
+//! string name;
+//! u32 value;
+//! };
+//! # "##, "crate_name")?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in a [`Record`] member with two [`Field`]s being added to the resulting
+//! [`crate::ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # dictionary Example {
+//! # string name;
+//! # u32 value;
+//! # };
+//! # "##, "crate_name")?;
+//! let record = ci.get_record_definition("Example").unwrap();
+//! assert_eq!(record.name(), "Example");
+//! assert_eq!(record.fields()[0].name(), "name");
+//! assert_eq!(record.fields()[1].name(), "value");
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+
+use anyhow::Result;
+use uniffi_meta::Checksum;
+
+use super::Literal;
+use super::{AsType, Type, TypeIterator};
+
+/// Represents a "data class" style object, for passing around complex values.
+///
+/// In the FFI these are represented as a byte buffer, which one side explicitly
+/// serializes the data into and the other serializes it out of. So I guess they're
+/// kind of like "pass by clone" values.
+#[derive(Debug, Clone, PartialEq, Eq, Checksum)]
+pub struct Record {
+ pub(super) name: String,
+ pub(super) module_path: String,
+ pub(super) fields: Vec<Field>,
+}
+
+impl Record {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn fields(&self) -> &[Field] {
+ &self.fields
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(self.fields.iter().flat_map(Field::iter_types))
+ }
+}
+
+impl AsType for Record {
+ fn as_type(&self) -> Type {
+ Type::Record {
+ name: self.name.clone(),
+ module_path: self.module_path.clone(),
+ }
+ }
+}
+
+impl TryFrom<uniffi_meta::RecordMetadata> for Record {
+ type Error = anyhow::Error;
+
+ fn try_from(meta: uniffi_meta::RecordMetadata) -> Result<Self> {
+ Ok(Self {
+ name: meta.name,
+ module_path: meta.module_path,
+ fields: meta
+ .fields
+ .into_iter()
+ .map(TryInto::try_into)
+ .collect::<Result<_>>()?,
+ })
+ }
+}
+
+// Represents an individual field on a Record.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Checksum)]
+pub struct Field {
+ pub(super) name: String,
+ pub(super) type_: Type,
+ pub(super) default: Option<Literal>,
+}
+
+impl Field {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn default_value(&self) -> Option<&Literal> {
+ self.default.as_ref()
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ self.type_.iter_types()
+ }
+}
+
+impl AsType for Field {
+ fn as_type(&self) -> Type {
+ self.type_.clone()
+ }
+}
+
+impl TryFrom<uniffi_meta::FieldMetadata> for Field {
+ type Error = anyhow::Error;
+
+ fn try_from(meta: uniffi_meta::FieldMetadata) -> Result<Self> {
+ let name = meta.name;
+ let type_ = meta.ty;
+ let default = meta.default;
+ Ok(Self {
+ name,
+ type_,
+ default,
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::super::ComponentInterface;
+ use super::*;
+ use uniffi_meta::Radix;
+
+ #[test]
+ fn test_multiple_record_types() {
+ const UDL: &str = r#"
+ namespace test{};
+ dictionary Empty {};
+ dictionary Simple {
+ u32 field;
+ };
+ dictionary Complex {
+ string? key;
+ u32 value = 0;
+ required boolean spin;
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.record_definitions().count(), 3);
+
+ let record = ci.get_record_definition("Empty").unwrap();
+ assert_eq!(record.name(), "Empty");
+ assert_eq!(record.fields().len(), 0);
+
+ let record = ci.get_record_definition("Simple").unwrap();
+ assert_eq!(record.name(), "Simple");
+ assert_eq!(record.fields().len(), 1);
+ assert_eq!(record.fields()[0].name(), "field");
+ assert_eq!(record.fields()[0].as_type(), Type::UInt32);
+ assert!(record.fields()[0].default_value().is_none());
+
+ let record = ci.get_record_definition("Complex").unwrap();
+ assert_eq!(record.name(), "Complex");
+ assert_eq!(record.fields().len(), 3);
+ assert_eq!(record.fields()[0].name(), "key");
+ assert_eq!(
+ record.fields()[0].as_type(),
+ Type::Optional {
+ inner_type: Box::new(Type::String)
+ },
+ );
+ assert!(record.fields()[0].default_value().is_none());
+ assert_eq!(record.fields()[1].name(), "value");
+ assert_eq!(record.fields()[1].as_type(), Type::UInt32);
+ assert!(matches!(
+ record.fields()[1].default_value(),
+ Some(Literal::UInt(0, Radix::Decimal, Type::UInt32))
+ ));
+ assert_eq!(record.fields()[2].name(), "spin");
+ assert_eq!(record.fields()[2].as_type(), Type::Boolean);
+ assert!(record.fields()[2].default_value().is_none());
+ }
+
+ #[test]
+ fn test_that_all_field_types_become_known() {
+ const UDL: &str = r#"
+ namespace test{};
+ dictionary Testing {
+ string? maybe_name;
+ u32 value;
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL, "crate_name").unwrap();
+ assert_eq!(ci.record_definitions().count(), 1);
+ let record = ci.get_record_definition("Testing").unwrap();
+ assert_eq!(record.fields().len(), 2);
+ assert_eq!(record.fields()[0].name(), "maybe_name");
+ assert_eq!(record.fields()[1].name(), "value");
+
+ assert_eq!(ci.iter_types().count(), 4);
+ assert!(ci.iter_types().any(|t| t == &Type::UInt32));
+ assert!(ci.iter_types().any(|t| t == &Type::String));
+ assert!(ci.iter_types().any(|t| t
+ == &Type::Optional {
+ inner_type: Box::new(Type::String)
+ }));
+ assert!(ci
+ .iter_types()
+ .any(|t| matches!(t, Type::Record { name, .. } if name == "Testing")));
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/universe.rs b/third_party/rust/uniffi_bindgen/src/interface/universe.rs
new file mode 100644
index 0000000000..e69d86e44f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/universe.rs
@@ -0,0 +1,136 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! The set of all [`Type`]s used in a component interface is represented by a `TypeUniverse`,
+//! which can be used by the bindings generator code to determine what type-related helper
+//! functions to emit for a given component.
+//!
+use anyhow::Result;
+use std::{collections::hash_map::Entry, collections::BTreeSet, collections::HashMap};
+
+pub use uniffi_meta::{AsType, ExternalKind, NamespaceMetadata, ObjectImpl, Type, TypeIterator};
+
+/// The set of all possible types used in a particular component interface.
+///
+/// Every component API uses a finite number of types, including primitive types, API-defined
+/// types like records and enums, and recursive types such as sequences of the above. Our
+/// component API doesn't support fancy generics so this is a finitely-enumerable set, which
+/// is useful to be able to operate on explicitly.
+///
+/// You could imagine this struct doing some clever interning of names and so-on in future,
+/// to reduce the overhead of passing around [Type] instances. For now we just do a whole
+/// lot of cloning.
+#[derive(Debug, Default)]
+pub(crate) struct TypeUniverse {
+ /// The unique prefixes that we'll use for namespacing when exposing this component's API.
+ pub namespace: NamespaceMetadata,
+
+ // Named type definitions (including aliases).
+ type_definitions: HashMap<String, Type>,
+ // All the types in the universe, by canonical type name, in a well-defined order.
+ all_known_types: BTreeSet<Type>,
+}
+
+impl TypeUniverse {
+ pub fn new(namespace: NamespaceMetadata) -> Self {
+ Self {
+ namespace,
+ ..Default::default()
+ }
+ }
+
+ /// Add the definition of a named [Type].
+ fn add_type_definition(&mut self, name: &str, type_: &Type) -> Result<()> {
+ match self.type_definitions.entry(name.to_string()) {
+ Entry::Occupied(o) => {
+ // all conflicts have been resolved by now in udl.
+ // I doubt procmacros could cause this?
+ assert_eq!(type_, o.get());
+ Ok(())
+ }
+ Entry::Vacant(e) => {
+ e.insert(type_.clone());
+ Ok(())
+ }
+ }
+ }
+
+ /// Get the [Type] corresponding to a given name, if any.
+ pub(super) fn get_type_definition(&self, name: &str) -> Option<Type> {
+ self.type_definitions.get(name).cloned()
+ }
+
+ /// Add a [Type] to the set of all types seen in the component interface.
+ pub fn add_known_type(&mut self, type_: &Type) -> Result<()> {
+ // Types are more likely to already be known than not, so avoid unnecessary cloning.
+ if !self.all_known_types.contains(type_) {
+ self.all_known_types.insert(type_.to_owned());
+ }
+ match type_ {
+ Type::UInt8 => self.add_type_definition("u8", type_)?,
+ Type::Int8 => self.add_type_definition("i8", type_)?,
+ Type::UInt16 => self.add_type_definition("u16", type_)?,
+ Type::Int16 => self.add_type_definition("i16", type_)?,
+ Type::UInt32 => self.add_type_definition("i32", type_)?,
+ Type::Int32 => self.add_type_definition("u32", type_)?,
+ Type::UInt64 => self.add_type_definition("u64", type_)?,
+ Type::Int64 => self.add_type_definition("i64", type_)?,
+ Type::Float32 => self.add_type_definition("f32", type_)?,
+ Type::Float64 => self.add_type_definition("f64", type_)?,
+ Type::Boolean => self.add_type_definition("bool", type_)?,
+ Type::String => self.add_type_definition("string", type_)?,
+ Type::Bytes => self.add_type_definition("bytes", type_)?,
+ Type::Timestamp => self.add_type_definition("timestamp", type_)?,
+ Type::Duration => self.add_type_definition("duration", type_)?,
+ Type::ForeignExecutor => {
+ self.add_type_definition("ForeignExecutor", type_)?;
+ }
+ Type::Object { name, .. }
+ | Type::Record { name, .. }
+ | Type::Enum { name, .. }
+ | Type::CallbackInterface { name, .. }
+ | Type::External { name, .. } => self.add_type_definition(name, type_)?,
+ Type::Custom { name, builtin, .. } => {
+ self.add_type_definition(name, type_)?;
+ self.add_known_type(builtin)?;
+ }
+ // Structurally recursive types.
+ Type::Optional { inner_type, .. } | Type::Sequence { inner_type, .. } => {
+ self.add_known_type(inner_type)?;
+ }
+ Type::Map {
+ key_type,
+ value_type,
+ } => {
+ self.add_known_type(key_type)?;
+ self.add_known_type(value_type)?;
+ }
+ }
+ Ok(())
+ }
+
+ /// Add many [`Type`]s...
+ pub fn add_known_types(&mut self, types: TypeIterator<'_>) -> Result<()> {
+ for t in types {
+ self.add_known_type(t)?
+ }
+ Ok(())
+ }
+
+ /// Check if a [Type] is present
+ pub fn contains(&self, type_: &Type) -> bool {
+ self.all_known_types.contains(type_)
+ }
+
+ /// Iterator over all the known types in this universe.
+ pub fn iter_known_types(&self) -> impl Iterator<Item = &Type> {
+ self.all_known_types.iter()
+ }
+}
+
+#[cfg(test)]
+mod test_type_universe {
+ // All the useful functionality of the `TypeUniverse` struct
+ // is tested as part of the `TypeFinder` and `TypeResolver` test suites.
+}