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/attributes.rs759
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/callbacks.rs169
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/enum_.rs441
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/error.rs230
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/ffi.rs102
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/function.rs288
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/literal.rs186
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/mod.rs1218
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/namespace.rs132
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/object.rs599
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/record.rs243
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/types/finder.rs244
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/types/mod.rs342
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/types/resolver.rs367
14 files changed, 5320 insertions, 0 deletions
diff --git a/third_party/rust/uniffi_bindgen/src/interface/attributes.rs b/third_party/rust/uniffi_bindgen/src/interface/attributes.rs
new file mode 100644
index 0000000000..4df6042564
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/attributes.rs
@@ -0,0 +1,759 @@
+/* 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/. */
+
+//! # Attribute definitions for a `ComponentInterface`.
+//!
+//! This module provides some conveniences for working with attribute definitions
+//! from WebIDL. When encountering a weedle `ExtendedAttribute` node, use `TryFrom`
+//! to convert it into an [`Attribute`] representing one of the attributes that we
+//! support. You can also use the [`parse_attributes`] function to parse an
+//! `ExtendedAttributeList` into a vec of same.
+//!
+//! We only support a small number of attributes, so it's manageable to have them
+//! all handled by a single abstraction. This might need to be refactored in future
+//! if we grow significantly more complicated attribute handling.
+
+use anyhow::{bail, Result};
+use uniffi_meta::Checksum;
+
+/// Represents an attribute parsed from UDL, like `[ByRef]` or `[Throws]`.
+///
+/// This is a convenience enum for parsing UDL attributes and erroring out if we encounter
+/// any unsupported ones. These don't convert directly into parts of a `ComponentInterface`, but
+/// may influence the properties of things like functions and arguments.
+#[derive(Debug, Clone, Checksum)]
+pub(super) enum Attribute {
+ ByRef,
+ Enum,
+ Error,
+ Name(String),
+ SelfType(SelfType),
+ Threadsafe, // N.B. the `[Threadsafe]` attribute is deprecated and will be removed
+ Throws(String),
+ // `[External="crate_name"]` - We can `use crate_name::...` for the type.
+ External(String),
+ // Custom type on the scaffolding side
+ Custom,
+}
+
+impl Attribute {
+ pub fn is_error(&self) -> bool {
+ matches!(self, Attribute::Error)
+ }
+ pub fn is_enum(&self) -> bool {
+ matches!(self, Attribute::Enum)
+ }
+}
+
+/// Convert a weedle `ExtendedAttribute` into an `Attribute` for a `ComponentInterface` member,
+/// or error out if the attribute is not supported.
+impl TryFrom<&weedle::attribute::ExtendedAttribute<'_>> for Attribute {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attribute: &weedle::attribute::ExtendedAttribute<'_>,
+ ) -> Result<Self, anyhow::Error> {
+ match weedle_attribute {
+ // Matches plain named attributes like "[ByRef"].
+ weedle::attribute::ExtendedAttribute::NoArgs(attr) => match (attr.0).0 {
+ "ByRef" => Ok(Attribute::ByRef),
+ "Enum" => Ok(Attribute::Enum),
+ "Error" => Ok(Attribute::Error),
+ "Threadsafe" => Ok(Attribute::Threadsafe),
+ "Custom" => Ok(Attribute::Custom),
+ _ => anyhow::bail!("ExtendedAttributeNoArgs not supported: {:?}", (attr.0).0),
+ },
+ // Matches assignment-style attributes like ["Throws=Error"]
+ weedle::attribute::ExtendedAttribute::Ident(identity) => {
+ match identity.lhs_identifier.0 {
+ "Name" => Ok(Attribute::Name(name_from_id_or_string(&identity.rhs))),
+ "Throws" => Ok(Attribute::Throws(name_from_id_or_string(&identity.rhs))),
+ "Self" => Ok(Attribute::SelfType(SelfType::try_from(&identity.rhs)?)),
+ "External" => Ok(Attribute::External(name_from_id_or_string(&identity.rhs))),
+ _ => anyhow::bail!(
+ "Attribute identity Identifier not supported: {:?}",
+ identity.lhs_identifier.0
+ ),
+ }
+ }
+ _ => anyhow::bail!("Attribute not supported: {:?}", weedle_attribute),
+ }
+ }
+}
+
+fn name_from_id_or_string(nm: &weedle::attribute::IdentifierOrString<'_>) -> String {
+ match nm {
+ weedle::attribute::IdentifierOrString::Identifier(identifier) => identifier.0.to_string(),
+ weedle::attribute::IdentifierOrString::String(str_lit) => str_lit.0.to_string(),
+ }
+}
+
+/// Parse a weedle `ExtendedAttributeList` into a list of `Attribute`s,
+/// erroring out on duplicates.
+fn parse_attributes<F>(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ validator: F,
+) -> Result<Vec<Attribute>>
+where
+ F: Fn(&Attribute) -> Result<()>,
+{
+ let attrs = &weedle_attributes.body.list;
+
+ let mut hash_set = std::collections::HashSet::new();
+ for attr in attrs {
+ if !hash_set.insert(attr) {
+ anyhow::bail!("Duplicated ExtendedAttribute: {:?}", attr);
+ }
+ }
+
+ let attrs = attrs
+ .iter()
+ .map(Attribute::try_from)
+ .collect::<Result<Vec<_>, _>>()?;
+
+ for attr in &attrs {
+ validator(attr)?;
+ }
+
+ Ok(attrs)
+}
+
+/// Attributes that can be attached to an `enum` definition in the UDL.
+/// There's only one case here: using `[Error]` to mark an enum as an error class.
+#[derive(Debug, Clone, Checksum, Default)]
+pub(super) struct EnumAttributes(Vec<Attribute>);
+
+impl EnumAttributes {
+ pub fn contains_error_attr(&self) -> bool {
+ self.0.iter().any(|attr| attr.is_error())
+ }
+}
+
+impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for EnumAttributes {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ ) -> Result<Self, Self::Error> {
+ let attrs = parse_attributes(weedle_attributes, |attr| match attr {
+ Attribute::Error => Ok(()),
+ _ => bail!(format!("{attr:?} not supported for enums")),
+ })?;
+ Ok(Self(attrs))
+ }
+}
+
+impl<T: TryInto<EnumAttributes, Error = anyhow::Error>> TryFrom<Option<T>> for EnumAttributes {
+ type Error = anyhow::Error;
+ fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
+ match value {
+ None => Ok(Default::default()),
+ Some(v) => v.try_into(),
+ }
+ }
+}
+
+/// Represents UDL attributes that might appear on a function.
+///
+/// This supports the `[Throws=ErrorName]` attribute for functions that
+/// can produce an error.
+#[derive(Debug, Clone, Checksum, Default)]
+pub(super) struct FunctionAttributes(Vec<Attribute>);
+
+impl FunctionAttributes {
+ pub(super) fn get_throws_err(&self) -> Option<&str> {
+ self.0.iter().find_map(|attr| match attr {
+ // This will hopefully return a helpful compilation error
+ // if the error is not defined.
+ Attribute::Throws(inner) => Some(inner.as_ref()),
+ _ => None,
+ })
+ }
+}
+
+impl FromIterator<Attribute> for FunctionAttributes {
+ fn from_iter<T: IntoIterator<Item = Attribute>>(iter: T) -> Self {
+ Self(Vec::from_iter(iter))
+ }
+}
+
+impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for FunctionAttributes {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ ) -> Result<Self, Self::Error> {
+ let attrs = parse_attributes(weedle_attributes, |attr| match attr {
+ Attribute::Throws(_) => Ok(()),
+ _ => bail!(format!("{attr:?} not supported for functions")),
+ })?;
+ Ok(Self(attrs))
+ }
+}
+
+impl<T: TryInto<FunctionAttributes, Error = anyhow::Error>> TryFrom<Option<T>>
+ for FunctionAttributes
+{
+ type Error = anyhow::Error;
+ fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
+ match value {
+ None => Ok(Default::default()),
+ Some(v) => v.try_into(),
+ }
+ }
+}
+
+/// Represents UDL attributes that might appear on a function argument.
+///
+/// This supports the `[ByRef]` attribute for arguments that should be passed
+/// by reference in the generated Rust scaffolding.
+#[derive(Debug, Clone, Checksum, Default)]
+pub(super) struct ArgumentAttributes(Vec<Attribute>);
+
+impl ArgumentAttributes {
+ pub fn by_ref(&self) -> bool {
+ self.0.iter().any(|attr| matches!(attr, Attribute::ByRef))
+ }
+}
+
+impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for ArgumentAttributes {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ ) -> Result<Self, Self::Error> {
+ let attrs = parse_attributes(weedle_attributes, |attr| match attr {
+ Attribute::ByRef => Ok(()),
+ _ => bail!(format!("{attr:?} not supported for arguments")),
+ })?;
+ Ok(Self(attrs))
+ }
+}
+
+impl<T: TryInto<ArgumentAttributes, Error = anyhow::Error>> TryFrom<Option<T>>
+ for ArgumentAttributes
+{
+ type Error = anyhow::Error;
+ fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
+ match value {
+ None => Ok(Default::default()),
+ Some(v) => v.try_into(),
+ }
+ }
+}
+
+/// Represents UDL attributes that might appear on an `interface` definition.
+#[derive(Debug, Clone, Checksum, Default)]
+pub(super) struct InterfaceAttributes(Vec<Attribute>);
+
+impl InterfaceAttributes {
+ pub fn contains_enum_attr(&self) -> bool {
+ self.0.iter().any(|attr| attr.is_enum())
+ }
+
+ pub fn contains_error_attr(&self) -> bool {
+ self.0.iter().any(|attr| attr.is_error())
+ }
+
+ pub fn threadsafe(&self) -> bool {
+ self.0
+ .iter()
+ .any(|attr| matches!(attr, Attribute::Threadsafe))
+ }
+}
+
+impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for InterfaceAttributes {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ ) -> Result<Self, Self::Error> {
+ let attrs = parse_attributes(weedle_attributes, |attr| match attr {
+ Attribute::Enum => Ok(()),
+ Attribute::Error => Ok(()),
+ Attribute::Threadsafe => Ok(()),
+ _ => bail!(format!("{attr:?} not supported for interface definition")),
+ })?;
+ // Can't be both `[Threadsafe]` and an `[Enum]`.
+ if attrs.len() > 1 {
+ bail!("conflicting attributes on interface definition");
+ }
+ Ok(Self(attrs))
+ }
+}
+
+impl<T: TryInto<InterfaceAttributes, Error = anyhow::Error>> TryFrom<Option<T>>
+ for InterfaceAttributes
+{
+ type Error = anyhow::Error;
+ fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
+ match value {
+ None => Ok(Default::default()),
+ Some(v) => v.try_into(),
+ }
+ }
+}
+
+/// Represents UDL attributes that might appear on a constructor.
+///
+/// This supports the `[Throws=ErrorName]` attribute for constructors that can produce
+/// an error, and the `[Name=MethodName]` for non-default constructors.
+#[derive(Debug, Clone, Checksum, Default)]
+pub(super) struct ConstructorAttributes(Vec<Attribute>);
+
+impl ConstructorAttributes {
+ pub(super) fn get_throws_err(&self) -> Option<&str> {
+ self.0.iter().find_map(|attr| match attr {
+ // This will hopefully return a helpful compilation error
+ // if the error is not defined.
+ Attribute::Throws(inner) => Some(inner.as_ref()),
+ _ => None,
+ })
+ }
+
+ pub(super) fn get_name(&self) -> Option<&str> {
+ self.0.iter().find_map(|attr| match attr {
+ Attribute::Name(inner) => Some(inner.as_ref()),
+ _ => None,
+ })
+ }
+}
+
+impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for ConstructorAttributes {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ ) -> Result<Self, Self::Error> {
+ let attrs = parse_attributes(weedle_attributes, |attr| match attr {
+ Attribute::Throws(_) => Ok(()),
+ Attribute::Name(_) => Ok(()),
+ _ => bail!(format!("{attr:?} not supported for constructors")),
+ })?;
+ Ok(Self(attrs))
+ }
+}
+
+/// Represents UDL attributes that might appear on a method.
+///
+/// This supports the `[Throws=ErrorName]` attribute for methods that can produce
+/// an error, and the `[Self=ByArc]` attribute for methods that take `Arc<Self>` as receiver.
+#[derive(Debug, Clone, Checksum, Default)]
+pub(super) struct MethodAttributes(Vec<Attribute>);
+
+impl MethodAttributes {
+ pub(super) fn get_throws_err(&self) -> Option<&str> {
+ self.0.iter().find_map(|attr| match attr {
+ // This will hopefully return a helpful compilation error
+ // if the error is not defined.
+ Attribute::Throws(inner) => Some(inner.as_ref()),
+ _ => None,
+ })
+ }
+
+ pub(super) fn get_self_by_arc(&self) -> bool {
+ self.0
+ .iter()
+ .any(|attr| matches!(attr, Attribute::SelfType(SelfType::ByArc)))
+ }
+}
+
+impl FromIterator<Attribute> for MethodAttributes {
+ fn from_iter<T: IntoIterator<Item = Attribute>>(iter: T) -> Self {
+ Self(Vec::from_iter(iter))
+ }
+}
+
+impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for MethodAttributes {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ ) -> Result<Self, Self::Error> {
+ let attrs = parse_attributes(weedle_attributes, |attr| match attr {
+ Attribute::SelfType(_) => Ok(()),
+ Attribute::Throws(_) => Ok(()),
+ _ => bail!(format!("{attr:?} not supported for methods")),
+ })?;
+ Ok(Self(attrs))
+ }
+}
+
+impl<T: TryInto<MethodAttributes, Error = anyhow::Error>> TryFrom<Option<T>> for MethodAttributes {
+ type Error = anyhow::Error;
+ fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
+ match value {
+ None => Ok(Default::default()),
+ Some(v) => v.try_into(),
+ }
+ }
+}
+
+/// Represents the different possible types of method call receiver.
+///
+/// Actually we only support one of these right now, `[Self=ByArc]`.
+/// We might add more in future, e.g. a `[Self=ByRef]` if there are cases
+/// where we need to force the receiver to be taken by reference.
+#[derive(Debug, Clone, Checksum)]
+pub(super) enum SelfType {
+ ByArc, // Method receiver is `Arc<Self>`.
+}
+
+impl TryFrom<&weedle::attribute::IdentifierOrString<'_>> for SelfType {
+ type Error = anyhow::Error;
+ fn try_from(nm: &weedle::attribute::IdentifierOrString<'_>) -> Result<Self, Self::Error> {
+ Ok(match nm {
+ weedle::attribute::IdentifierOrString::Identifier(identifier) => match identifier.0 {
+ "ByArc" => SelfType::ByArc,
+ _ => bail!("Unsupported Self Type: {:?}", identifier.0),
+ },
+ weedle::attribute::IdentifierOrString::String(_) => {
+ bail!("Unsupported Self Type: {:?}", nm)
+ }
+ })
+ }
+}
+
+/// Represents UDL attributes that might appear on a typedef
+///
+/// This supports the `[External="crate_name"]` and `[Custom]` attributes for types.
+#[derive(Debug, Clone, Checksum, Default)]
+pub(super) struct TypedefAttributes(Vec<Attribute>);
+
+impl TypedefAttributes {
+ pub(super) fn get_crate_name(&self) -> String {
+ self.0
+ .iter()
+ .find_map(|attr| match attr {
+ Attribute::External(crate_name) => Some(crate_name.clone()),
+ _ => None,
+ })
+ .expect("must have a crate name")
+ }
+
+ pub(super) fn is_custom(&self) -> bool {
+ self.0
+ .iter()
+ .any(|attr| matches!(attr, Attribute::Custom { .. }))
+ }
+}
+
+impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for TypedefAttributes {
+ type Error = anyhow::Error;
+ fn try_from(
+ weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>,
+ ) -> Result<Self, Self::Error> {
+ let attrs = parse_attributes(weedle_attributes, |attr| match attr {
+ Attribute::External { .. } | Attribute::Custom => Ok(()),
+ _ => bail!(format!("{attr:?} not supported for typedefs")),
+ })?;
+ Ok(Self(attrs))
+ }
+}
+
+impl<T: TryInto<TypedefAttributes, Error = anyhow::Error>> TryFrom<Option<T>>
+ for TypedefAttributes
+{
+ type Error = anyhow::Error;
+ fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
+ match value {
+ None => Ok(Default::default()),
+ Some(v) => v.try_into(),
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use weedle::Parse;
+
+ #[test]
+ fn test_byref() -> Result<()> {
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("ByRef").unwrap();
+ let attr = Attribute::try_from(&node)?;
+ assert!(matches!(attr, Attribute::ByRef));
+ Ok(())
+ }
+
+ #[test]
+ fn test_enum() -> Result<()> {
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Enum").unwrap();
+ let attr = Attribute::try_from(&node)?;
+ assert!(matches!(attr, Attribute::Enum));
+ assert!(attr.is_enum());
+ Ok(())
+ }
+
+ #[test]
+ fn test_error() -> Result<()> {
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Error").unwrap();
+ let attr = Attribute::try_from(&node)?;
+ assert!(matches!(attr, Attribute::Error));
+ assert!(attr.is_error());
+ Ok(())
+ }
+
+ #[test]
+ fn test_name() -> Result<()> {
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Name=Value").unwrap();
+ let attr = Attribute::try_from(&node)?;
+ assert!(matches!(attr, Attribute::Name(nm) if nm == "Value"));
+
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Name").unwrap();
+ let err = Attribute::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "ExtendedAttributeNoArgs not supported: \"Name\""
+ );
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_selftype() -> Result<()> {
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Self=ByArc").unwrap();
+ let attr = Attribute::try_from(&node)?;
+ assert!(matches!(attr, Attribute::SelfType(SelfType::ByArc)));
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Self=ByMistake").unwrap();
+ let err = Attribute::try_from(&node).unwrap_err();
+ assert_eq!(err.to_string(), "Unsupported Self Type: \"ByMistake\"");
+ Ok(())
+ }
+
+ #[test]
+ fn test_threadsafe() -> Result<()> {
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Threadsafe").unwrap();
+ let attr = Attribute::try_from(&node)?;
+ assert!(matches!(attr, Attribute::Threadsafe));
+ Ok(())
+ }
+
+ #[test]
+ fn test_throws() -> Result<()> {
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Throws=Name").unwrap();
+ let attr = Attribute::try_from(&node)?;
+ assert!(matches!(attr, Attribute::Throws(nm) if nm == "Name"));
+
+ let (_, node) = weedle::attribute::ExtendedAttribute::parse("Throws").unwrap();
+ let err = Attribute::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "ExtendedAttributeNoArgs not supported: \"Throws\""
+ );
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_unsupported() {
+ let (_, node) =
+ weedle::attribute::ExtendedAttribute::parse("UnsupportedAttribute").unwrap();
+ let err = Attribute::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "ExtendedAttributeNoArgs not supported: \"UnsupportedAttribute\""
+ );
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttribute::parse("Unsupported=Attribute").unwrap();
+ let err = Attribute::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Attribute identity Identifier not supported: \"Unsupported\""
+ );
+ }
+
+ #[test]
+ fn test_other_attributes_not_supported_for_enums() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Error, ByRef]").unwrap();
+ let err = EnumAttributes::try_from(&node).unwrap_err();
+ assert_eq!(err.to_string(), "ByRef not supported for enums");
+ }
+
+ #[test]
+ fn test_throws_attribute() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Throws=Error]").unwrap();
+ let attrs = FunctionAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.get_throws_err(), Some("Error")));
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
+ let attrs = FunctionAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.get_throws_err(), None));
+ }
+
+ #[test]
+ fn test_other_attributes_not_supported_for_functions() {
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, ByRef]").unwrap();
+ let err = FunctionAttributes::try_from(&node).unwrap_err();
+ assert_eq!(err.to_string(), "ByRef not supported for functions");
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, Self=ByArc]").unwrap();
+ let err = FunctionAttributes::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "SelfType(ByArc) not supported for functions"
+ );
+ }
+
+ #[test]
+ fn test_method_attributes() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Throws=Error]").unwrap();
+ let attrs = MethodAttributes::try_from(&node).unwrap();
+ assert!(!attrs.get_self_by_arc());
+ assert!(matches!(attrs.get_throws_err(), Some("Error")));
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
+ let attrs = MethodAttributes::try_from(&node).unwrap();
+ assert!(!attrs.get_self_by_arc());
+ assert!(attrs.get_throws_err().is_none());
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Self=ByArc, Throws=Error]").unwrap();
+ let attrs = MethodAttributes::try_from(&node).unwrap();
+ assert!(attrs.get_self_by_arc());
+ assert!(attrs.get_throws_err().is_some());
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Self=ByArc]").unwrap();
+ let attrs = MethodAttributes::try_from(&node).unwrap();
+ assert!(attrs.get_self_by_arc());
+ assert!(attrs.get_throws_err().is_none());
+ }
+
+ #[test]
+ fn test_constructor_attributes() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Throws=Error]").unwrap();
+ let attrs = ConstructorAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.get_throws_err(), Some("Error")));
+ assert!(matches!(attrs.get_name(), None));
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Name=MyFactory]").unwrap();
+ let attrs = ConstructorAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.get_throws_err(), None));
+ assert!(matches!(attrs.get_name(), Some("MyFactory")));
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, Name=MyFactory]")
+ .unwrap();
+ let attrs = ConstructorAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.get_throws_err(), Some("Error")));
+ assert!(matches!(attrs.get_name(), Some("MyFactory")));
+ }
+
+ #[test]
+ fn test_other_attributes_not_supported_for_constructors() {
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, ByRef]").unwrap();
+ let err = ConstructorAttributes::try_from(&node).unwrap_err();
+ assert_eq!(err.to_string(), "ByRef not supported for constructors");
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, Self=ByArc]").unwrap();
+ let err = ConstructorAttributes::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "SelfType(ByArc) not supported for constructors"
+ );
+ }
+
+ #[test]
+ fn test_byref_attribute() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[ByRef]").unwrap();
+ let attrs = ArgumentAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.by_ref(), true));
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
+ let attrs = ArgumentAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.by_ref(), false));
+ }
+
+ #[test]
+ fn test_other_attributes_not_supported_for_arguments() {
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, ByRef]").unwrap();
+ let err = ArgumentAttributes::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Throws(\"Error\") not supported for arguments"
+ );
+ }
+
+ #[test]
+ fn test_threadsafe_attribute() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Threadsafe]").unwrap();
+ let attrs = InterfaceAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.threadsafe(), true));
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
+ let attrs = InterfaceAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.threadsafe(), false));
+ }
+
+ #[test]
+ fn test_enum_attribute() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum]").unwrap();
+ let attrs = InterfaceAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.contains_enum_attr(), true));
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap();
+ let attrs = InterfaceAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.contains_enum_attr(), false));
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Threadsafe]").unwrap();
+ let attrs = InterfaceAttributes::try_from(&node).unwrap();
+ assert!(matches!(attrs.contains_enum_attr(), false));
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Threadsafe, Enum]").unwrap();
+ let err = InterfaceAttributes::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "conflicting attributes on interface definition"
+ );
+ }
+
+ #[test]
+ fn test_other_attributes_not_supported_for_interfaces() {
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[Threadsafe, ByRef]").unwrap();
+ let err = InterfaceAttributes::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "ByRef not supported for interface definition"
+ );
+ }
+
+ #[test]
+ fn test_typedef_attribute() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Custom]").unwrap();
+ let attrs = TypedefAttributes::try_from(&node).unwrap();
+ assert!(attrs.is_custom());
+
+ let (_, node) =
+ weedle::attribute::ExtendedAttributeList::parse("[External=crate_name]").unwrap();
+ let attrs = TypedefAttributes::try_from(&node).unwrap();
+ assert!(!attrs.is_custom());
+ assert_eq!(attrs.get_crate_name(), "crate_name");
+ }
+
+ #[test]
+ fn test_typedef_attributes_malformed() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Custom=foo]").unwrap();
+ let err = TypedefAttributes::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Attribute identity Identifier not supported: \"Custom\""
+ );
+
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[External]").unwrap();
+ let err = TypedefAttributes::try_from(&node).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "ExtendedAttributeNoArgs not supported: \"External\""
+ );
+ }
+
+ #[test]
+ fn test_other_attributes_not_supported_for_typedef() {
+ let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[ByRef]").unwrap();
+ let err = TypedefAttributes::try_from(&node).unwrap_err();
+ assert_eq!(err.to_string(), "ByRef not supported for typedefs");
+ }
+}
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..a7164cec5b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs
@@ -0,0 +1,169 @@
+/* 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();
+//! };
+//! # "##)?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in a [`CallbackInterface`] member being added to the resulting
+//! [`ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # callback interface Example {
+//! # string hello();
+//! # };
+//! # "##)?;
+//! 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 anyhow::{bail, Result};
+use uniffi_meta::Checksum;
+
+use super::ffi::{FfiArgument, FfiFunction, FfiType};
+use super::object::Method;
+use super::types::{Type, TypeIterator};
+use super::{APIConverter, ComponentInterface};
+
+#[derive(Debug, Clone, Checksum)]
+pub struct CallbackInterface {
+ pub(super) name: 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 {
+ fn new(name: String) -> CallbackInterface {
+ CallbackInterface {
+ name,
+ methods: Default::default(),
+ ffi_init_callback: Default::default(),
+ }
+ }
+
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn type_(&self) -> Type {
+ Type::CallbackInterface(self.name.clone())
+ }
+
+ 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, ci_prefix: &str) {
+ self.ffi_init_callback.name = format!("ffi_{ci_prefix}_{}_init_callback", 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 APIConverter<CallbackInterface> for weedle::CallbackInterfaceDefinition<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<CallbackInterface> {
+ if self.attributes.is_some() {
+ bail!("callback interface attributes are not supported yet");
+ }
+ if self.inheritance.is_some() {
+ bail!("callback interface inheritance is not supported");
+ }
+ let mut object = CallbackInterface::new(self.identifier.0.to_string());
+ for member in &self.members.body {
+ match member {
+ weedle::interface::InterfaceMember::Operation(t) => {
+ let mut method: Method = t.convert(ci)?;
+ method.object_name = object.name.clone();
+ object.methods.push(method);
+ }
+ _ => bail!(
+ "no support for callback interface member type {:?} yet",
+ member
+ ),
+ }
+ }
+ Ok(object)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_empty_interface() {
+ const UDL: &str = r#"
+ namespace test{};
+ // Weird, but allowed.
+ callback interface Testing {};
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL).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).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..c85e6ecc46
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/enum_.rs
@@ -0,0 +1,441 @@
+/* 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"
+//! };
+//! # "##)?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in a [`Enum`] member being added to the resulting [`ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # enum Example {
+//! # "one",
+//! # "two"
+//! # };
+//! # "##)?;
+//! 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);
+//! };
+//! # "##)?;
+//! # 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);
+//! # };
+//! # "##)?;
+//! 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>(())
+//! ```
+
+use anyhow::{bail, Result};
+use uniffi_meta::Checksum;
+
+use super::record::Field;
+use super::types::{Type, TypeIterator};
+use super::{APIConverter, ComponentInterface};
+
+/// 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) variants: Vec<Variant>,
+ // "Flat" enums do not have variants with associated data.
+ pub(super) flat: bool,
+}
+
+impl Enum {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn type_(&self) -> Type {
+ // *sigh* at the clone here, the relationship between a ComponentInterface
+ // and its contained types could use a bit of a cleanup.
+ Type::Enum(self.name.clone())
+ }
+
+ 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))
+ }
+}
+
+impl From<uniffi_meta::EnumMetadata> for Enum {
+ fn from(meta: uniffi_meta::EnumMetadata) -> Self {
+ let flat = meta.variants.iter().all(|v| v.fields.is_empty());
+ Self {
+ name: meta.name,
+ variants: meta.variants.into_iter().map(Into::into).collect(),
+ flat,
+ }
+ }
+}
+
+// Note that we have two `APIConverter` impls here - one for the `enum` case
+// and one for the `[Enum] interface` case.
+
+impl APIConverter<Enum> for weedle::EnumDefinition<'_> {
+ fn convert(&self, _ci: &mut ComponentInterface) -> Result<Enum> {
+ Ok(Enum {
+ name: self.identifier.0.to_string(),
+ variants: self
+ .values
+ .body
+ .list
+ .iter()
+ .map::<Result<_>, _>(|v| {
+ Ok(Variant {
+ name: v.0.to_string(),
+ ..Default::default()
+ })
+ })
+ .collect::<Result<Vec<_>>>()?,
+ // Enums declared using the `enum` syntax can never have variants with fields.
+ flat: true,
+ })
+ }
+}
+
+impl APIConverter<Enum> for weedle::InterfaceDefinition<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Enum> {
+ if self.inheritance.is_some() {
+ bail!("interface inheritance is not supported for enum interfaces");
+ }
+ // We don't need to check `self.attributes` here; if calling code has dispatched
+ // to this impl then we already know there was an `[Enum]` attribute.
+ Ok(Enum {
+ name: self.identifier.0.to_string(),
+ variants: self
+ .members
+ .body
+ .iter()
+ .map::<Result<Variant>, _>(|member| match member {
+ weedle::interface::InterfaceMember::Operation(t) => Ok(t.convert(ci)?),
+ _ => bail!(
+ "interface member type {:?} not supported in enum interface",
+ member
+ ),
+ })
+ .collect::<Result<Vec<_>>>()?,
+ // Enums declared using the `[Enum] interface` syntax might have variants with fields.
+ flat: false,
+ })
+ }
+}
+
+/// 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 From<uniffi_meta::VariantMetadata> for Variant {
+ fn from(meta: uniffi_meta::VariantMetadata) -> Self {
+ Self {
+ name: meta.name,
+ fields: meta.fields.into_iter().map(Into::into).collect(),
+ }
+ }
+}
+
+impl APIConverter<Variant> for weedle::interface::OperationInterfaceMember<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Variant> {
+ if self.special.is_some() {
+ bail!("special operations not supported");
+ }
+ if let Some(weedle::interface::StringifierOrStatic::Stringifier(_)) = self.modifier {
+ bail!("stringifiers are not supported");
+ }
+ // OK, so this is a little weird.
+ // The syntax we use for enum interface members is `Name(type arg, ...);`, which parses
+ // as an anonymous operation where `Name` is the return type. We re-interpret it to
+ // use `Name` as the name of the variant.
+ if self.identifier.is_some() {
+ bail!("enum interface members must not have a method name");
+ }
+ let name: String = {
+ use weedle::types::{
+ NonAnyType::Identifier, ReturnType, SingleType::NonAny, Type::Single,
+ };
+ match &self.return_type {
+ ReturnType::Type(Single(NonAny(Identifier(id)))) => id.type_.0.to_owned(),
+ _ => bail!("enum interface members must have plain identifiers as names"),
+ }
+ };
+ Ok(Variant {
+ name,
+ fields: self
+ .args
+ .body
+ .list
+ .iter()
+ .map(|arg| arg.convert(ci))
+ .collect::<Result<Vec<_>>>()?,
+ })
+ }
+}
+
+impl APIConverter<Field> for weedle::argument::Argument<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Field> {
+ match self {
+ weedle::argument::Argument::Single(t) => t.convert(ci),
+ weedle::argument::Argument::Variadic(_) => bail!("variadic arguments not supported"),
+ }
+ }
+}
+
+impl APIConverter<Field> for weedle::argument::SingleArgument<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Field> {
+ let type_ = ci.resolve_type_expression(&self.type_)?;
+ if let Type::Object(_) = type_ {
+ bail!("Objects cannot currently be used in enum variant data");
+ }
+ if self.default.is_some() {
+ bail!("enum interface variant fields must not have default values");
+ }
+ if self.attributes.is_some() {
+ bail!("enum interface variant fields must not have attributes");
+ }
+ // TODO: maybe we should use our own `Field` type here with just name and type,
+ // rather than appropriating record::Field..?
+ Ok(Field {
+ name: self.identifier.0.to_string(),
+ type_,
+ default: None,
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::super::ffi::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).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).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.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.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!(!ewd.is_flat());
+ 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].type_(), Type::Enum("TestEnum".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(nm)) if nm == "TestEnum"));
+ 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].type_(),
+ Type::Enum("TestEnumWithData".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(nm)) if nm == "TestEnumWithData"));
+ assert!(matches!(
+ fret.ffi_func().return_type(),
+ Some(FfiType::RustBuffer(None))
+ ));
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/error.rs b/third_party/rust/uniffi_bindgen/src/interface/error.rs
new file mode 100644
index 0000000000..9aa57255e8
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/error.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/. */
+
+//! # Error definitions for a `ComponentInterface`.
+//!
+//! This module converts error 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 {};
+//! [Error]
+//! enum Example {
+//! "one",
+//! "two"
+//! };
+//! # "##)?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in an [`Error`] member with fieldless variants being added to the resulting [`ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # [Error]
+//! # enum Example {
+//! # "one",
+//! # "two"
+//! # };
+//! # "##)?;
+//! let err = ci.get_error_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);
+//! # 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);
+//! };
+//! # "##)?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in an [`Error`] member with variants that have fields being added to the resulting [`ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # [Error]
+//! # interface Example {
+//! # one();
+//! # two(string reason);
+//! # three(i32 x, i32 y);
+//! # };
+//! # "##)?;
+//! let err = ci.get_error_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);
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+
+use anyhow::Result;
+use uniffi_meta::Checksum;
+
+use super::enum_::{Enum, Variant};
+use super::types::{Type, TypeIterator};
+use super::{APIConverter, ComponentInterface};
+
+/// Represents an Error that might be thrown by functions/methods in the component interface.
+///
+/// Errors are represented in the UDL as enums with the special `[Error]` attribute, but
+/// they're handled in the FFI very differently. We create them in `uniffi::call_with_result()` if
+/// the wrapped function returns an `Err` value
+/// struct and assign an integer error code to each variant.
+#[derive(Debug, Clone, PartialEq, Eq, Checksum)]
+pub struct Error {
+ pub name: String,
+ enum_: Enum,
+}
+
+impl Error {
+ pub fn from_enum(enum_: Enum) -> Self {
+ Self {
+ name: enum_.name.clone(),
+ enum_,
+ }
+ }
+
+ pub fn type_(&self) -> Type {
+ // *sigh* at the clone here, the relationship between a ComponentInterface
+ // and its contained types could use a bit of a cleanup.
+ Type::Error(self.name.clone())
+ }
+
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn wrapped_enum(&self) -> &Enum {
+ &self.enum_
+ }
+
+ pub fn variants(&self) -> &[Variant] {
+ self.enum_.variants()
+ }
+
+ pub fn is_flat(&self) -> bool {
+ self.enum_.is_flat()
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ self.wrapped_enum().iter_types()
+ }
+}
+
+impl From<uniffi_meta::ErrorMetadata> for Error {
+ fn from(meta: uniffi_meta::ErrorMetadata) -> Self {
+ Self {
+ name: meta.name.clone(),
+ enum_: Enum {
+ name: meta.name,
+ variants: meta.variants.into_iter().map(Into::into).collect(),
+ flat: meta.flat,
+ },
+ }
+ }
+}
+
+impl APIConverter<Error> for weedle::EnumDefinition<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Error> {
+ Ok(Error::from_enum(APIConverter::<Enum>::convert(self, ci)?))
+ }
+}
+
+impl APIConverter<Error> for weedle::InterfaceDefinition<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Error> {
+ Ok(Error::from_enum(APIConverter::<Enum>::convert(self, ci)?))
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_variants() {
+ const UDL: &str = r#"
+ namespace test{};
+ [Error]
+ enum Testing { "one", "two", "three" };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL).unwrap();
+ assert_eq!(ci.error_definitions().count(), 1);
+ let error = ci.get_error_definition("Testing").unwrap();
+ assert_eq!(
+ error
+ .variants()
+ .iter()
+ .map(|v| v.name())
+ .collect::<Vec<&str>>(),
+ vec!("one", "two", "three")
+ );
+ assert!(error.is_flat());
+ }
+
+ #[test]
+ fn test_duplicate_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).unwrap();
+ assert_eq!(ci.error_definitions().count(), 1);
+ assert_eq!(
+ ci.get_error_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).unwrap();
+ assert_eq!(ci.error_definitions().count(), 1);
+ let error: &Error = ci.get_error_definition("Testing").unwrap();
+ assert_eq!(
+ error
+ .variants()
+ .iter()
+ .map(|v| v.name())
+ .collect::<Vec<&str>>(),
+ vec!("One", "Two")
+ );
+ assert!(!error.is_flat());
+ }
+}
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..e1f9fe9737
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/ffi.rs
@@ -0,0 +1,102 @@
+/* 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 [`super::types::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).
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+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,
+ /// A pointer to a single function in to the foreign language.
+ /// This function contains all the machinery to make callbacks work on the foreign language side.
+ ForeignCallback,
+ // 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.
+}
+
+/// 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, Default, Clone)]
+pub struct FfiFunction {
+ pub(super) name: String,
+ pub(super) arguments: Vec<FfiArgument>,
+ pub(super) return_type: Option<FfiType>,
+}
+
+impl FfiFunction {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+ pub fn arguments(&self) -> Vec<&FfiArgument> {
+ self.arguments.iter().collect()
+ }
+ pub fn return_type(&self) -> Option<&FfiType> {
+ self.return_type.as_ref()
+ }
+}
+
+/// 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..61724816cb
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/function.rs
@@ -0,0 +1,288 @@
+/* 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();
+//! };
+//! # "##)?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in a [`Function`] member being added to the resulting [`ComponentInterface`]:
+//!
+//! ```
+//! # use uniffi_bindgen::interface::Type;
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {
+//! # string hello();
+//! # };
+//! # "##)?;
+//! 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 std::convert::TryFrom;
+
+use anyhow::{bail, Result};
+use uniffi_meta::Checksum;
+
+use super::attributes::{ArgumentAttributes, Attribute, FunctionAttributes};
+use super::ffi::{FfiArgument, FfiFunction};
+use super::literal::{convert_default_value, Literal};
+use super::types::{Type, TypeIterator};
+use super::{convert_type, APIConverter, ComponentInterface};
+
+/// 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) 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) attributes: FunctionAttributes,
+}
+
+impl Function {
+ 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 return_type(&self) -> Option<&Type> {
+ self.return_type.as_ref()
+ }
+
+ pub fn ffi_func(&self) -> &FfiFunction {
+ &self.ffi_func
+ }
+
+ pub fn throws(&self) -> bool {
+ self.attributes.get_throws_err().is_some()
+ }
+
+ pub fn throws_name(&self) -> Option<&str> {
+ self.attributes.get_throws_err()
+ }
+
+ pub fn throws_type(&self) -> Option<Type> {
+ self.attributes
+ .get_throws_err()
+ .map(|name| Type::Error(name.to_owned()))
+ }
+
+ pub fn derive_ffi_func(&mut self, ci_prefix: &str) -> Result<()> {
+ // The name is already set if the function is defined through a proc-macro invocation
+ // rather than in UDL. Don't overwrite it in that case.
+ if self.ffi_func.name.is_empty() {
+ self.ffi_func.name = format!("{ci_prefix}_{}", self.name);
+ }
+
+ self.ffi_func.arguments = self.arguments.iter().map(|arg| arg.into()).collect();
+ self.ffi_func.return_type = self.return_type.as_ref().map(|rt| rt.into());
+ Ok(())
+ }
+}
+
+impl From<uniffi_meta::FnParamMetadata> for Argument {
+ fn from(meta: uniffi_meta::FnParamMetadata) -> Self {
+ Argument {
+ name: meta.name,
+ type_: convert_type(&meta.ty),
+ by_ref: false,
+ optional: false,
+ default: None,
+ }
+ }
+}
+
+impl From<uniffi_meta::FnMetadata> for Function {
+ fn from(meta: uniffi_meta::FnMetadata) -> Self {
+ let ffi_name = meta.ffi_symbol_name();
+
+ let return_type = meta.return_type.map(|out| convert_type(&out));
+ let arguments = meta.inputs.into_iter().map(Into::into).collect();
+
+ let ffi_func = FfiFunction {
+ name: ffi_name,
+ ..FfiFunction::default()
+ };
+
+ Self {
+ name: meta.name,
+ arguments,
+ return_type,
+ ffi_func,
+ attributes: meta.throws.map(Attribute::Throws).into_iter().collect(),
+ }
+ }
+}
+
+impl APIConverter<Function> for weedle::namespace::NamespaceMember<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Function> {
+ match self {
+ weedle::namespace::NamespaceMember::Operation(f) => f.convert(ci),
+ _ => bail!("no support for namespace member type {:?} yet", self),
+ }
+ }
+}
+
+impl APIConverter<Function> for weedle::namespace::OperationNamespaceMember<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Function> {
+ let return_type = ci.resolve_return_type_expression(&self.return_type)?;
+ Ok(Function {
+ name: match self.identifier {
+ None => bail!("anonymous functions are not supported {:?}", self),
+ Some(id) => id.0.to_string(),
+ },
+ return_type,
+ arguments: self.args.body.list.convert(ci)?,
+ ffi_func: Default::default(),
+ attributes: FunctionAttributes::try_from(self.attributes.as_ref())?,
+ })
+ }
+}
+
+/// 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 type_(&self) -> &Type {
+ &self.type_
+ }
+
+ pub fn by_ref(&self) -> bool {
+ self.by_ref
+ }
+
+ pub fn default_value(&self) -> Option<&Literal> {
+ self.default.as_ref()
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ self.type_.iter_types()
+ }
+}
+
+impl From<&Argument> for FfiArgument {
+ fn from(a: &Argument) -> FfiArgument {
+ FfiArgument {
+ name: a.name.clone(),
+ type_: (&a.type_).into(),
+ }
+ }
+}
+
+impl APIConverter<Argument> for weedle::argument::Argument<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Argument> {
+ match self {
+ weedle::argument::Argument::Single(t) => t.convert(ci),
+ weedle::argument::Argument::Variadic(_) => bail!("variadic arguments not supported"),
+ }
+ }
+}
+
+impl APIConverter<Argument> for weedle::argument::SingleArgument<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Argument> {
+ let type_ = ci.resolve_type_expression(&self.type_)?;
+ let default = match self.default {
+ None => None,
+ Some(v) => Some(convert_default_value(&v.value, &type_)?),
+ };
+ let by_ref = ArgumentAttributes::try_from(self.attributes.as_ref())?.by_ref();
+ Ok(Argument {
+ name: self.identifier.0.to_string(),
+ type_,
+ by_ref,
+ optional: self.optional.is_some(),
+ default,
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ 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;
+ };
+ "##,
+ )?;
+
+ 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().canonical_name(),
+ "SequenceOptionalstring"
+ );
+ assert!(matches!(func2.throws_type(), Some(Type::Error(s)) if s == "TestError"));
+ assert_eq!(func2.arguments().len(), 2);
+ assert_eq!(func2.arguments()[0].name(), "arg1");
+ assert_eq!(func2.arguments()[0].type_().canonical_name(), "u32");
+ assert_eq!(func2.arguments()[1].name(), "arg2");
+ assert_eq!(
+ func2.arguments()[1].type_().canonical_name(),
+ "TypeTestDict"
+ );
+ Ok(())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/literal.rs b/third_party/rust/uniffi_bindgen/src/interface/literal.rs
new file mode 100644
index 0000000000..da02613684
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/literal.rs
@@ -0,0 +1,186 @@
+/* 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/. */
+
+//! # Support for literal values.
+//!
+//! This module provides support for interpreting literal values from the UDL,
+//! which appear in places such as default arguments.
+
+use anyhow::{bail, Result};
+use uniffi_meta::Checksum;
+
+use super::types::Type;
+
+// Represents a literal value.
+// Used for e.g. default argument values.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Checksum)]
+pub enum Literal {
+ Boolean(bool),
+ String(String),
+ // Integers are represented as the widest representation we can.
+ // Number formatting vary with language and radix, so we avoid a lot of parsing and
+ // formatting duplication by using only signed and unsigned variants.
+ UInt(u64, Radix, Type),
+ Int(i64, Radix, Type),
+ // Pass the string representation through as typed in the UDL.
+ // This avoids a lot of uncertainty around precision and accuracy,
+ // though bindings for languages less sophisticated number parsing than WebIDL
+ // will have to do extra work.
+ Float(String, Type),
+ Enum(String, Type),
+ EmptySequence,
+ EmptyMap,
+ Null,
+}
+
+// Represent the radix of integer literal values.
+// We preserve the radix into the generated bindings for readability reasons.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Checksum)]
+pub enum Radix {
+ Decimal = 10,
+ Octal = 8,
+ Hexadecimal = 16,
+}
+
+pub(super) fn convert_default_value(
+ default_value: &weedle::literal::DefaultValue<'_>,
+ type_: &Type,
+) -> Result<Literal> {
+ fn convert_integer(literal: &weedle::literal::IntegerLit<'_>, type_: &Type) -> Result<Literal> {
+ let (string, radix) = match literal {
+ weedle::literal::IntegerLit::Dec(v) => (v.0, Radix::Decimal),
+ weedle::literal::IntegerLit::Hex(v) => (v.0, Radix::Hexadecimal),
+ weedle::literal::IntegerLit::Oct(v) => (v.0, Radix::Octal),
+ };
+ // This is the radix of the parsed number, passed to `from_str_radix`.
+ let src_radix = radix as u32;
+ // This radix tells the backends how to represent the number in the output languages.
+ let dest_radix = if string == "0" || string.starts_with('-') {
+ // 1. weedle parses "0" as an octal literal, but we most likely want to treat this as a decimal.
+ // 2. Explicitly negatively signed hex numbers won't convert via i64 very well if they're not 64 bit.
+ // For ease of implementation, output will use decimal.
+ Radix::Decimal
+ } else {
+ radix
+ };
+
+ // Clippy seems to think we should be using `strip_prefix` here, but
+ // it seems confused as to what this is actually doing.
+ #[allow(clippy::manual_strip)]
+ let string = if string.starts_with('-') {
+ ("-".to_string() + string[1..].trim_start_matches("0x")).to_lowercase()
+ } else {
+ string.trim_start_matches("0x").to_lowercase()
+ };
+
+ Ok(match type_ {
+ Type::Int8 | Type::Int16 | Type::Int32 | Type::Int64 => Literal::Int(
+ i64::from_str_radix(&string, src_radix)?,
+ dest_radix,
+ type_.clone(),
+ ),
+ Type::UInt8 | Type::UInt16 | Type::UInt32 | Type::UInt64 => Literal::UInt(
+ u64::from_str_radix(&string, src_radix)?,
+ dest_radix,
+ type_.clone(),
+ ),
+
+ _ => bail!("Cannot coerce literal {} into a non-integer type", string),
+ })
+ }
+
+ fn convert_float(literal: &weedle::literal::FloatLit<'_>, type_: &Type) -> Result<Literal> {
+ let string = match literal {
+ weedle::literal::FloatLit::Value(v) => v.0,
+
+ _ => bail!("Infinity and NaN is not currently supported"),
+ };
+
+ Ok(match type_ {
+ Type::Float32 | Type::Float64 => Literal::Float(string.to_string(), type_.clone()),
+ _ => bail!("Cannot coerce literal {} into a non-float type", string),
+ })
+ }
+
+ Ok(match (default_value, type_) {
+ (weedle::literal::DefaultValue::Boolean(b), Type::Boolean) => Literal::Boolean(b.0),
+ (weedle::literal::DefaultValue::String(s), Type::String) => {
+ // Note that weedle doesn't parse escaped double quotes.
+ // Keeping backends using double quotes (possible for all to date)
+ // means we don't need to escape single quotes. But we haven't spent a lot of time
+ // trying to break default values with weird escapes and quotes.
+ Literal::String(s.0.to_string())
+ }
+ (weedle::literal::DefaultValue::EmptyArray(_), Type::Sequence(_)) => Literal::EmptySequence,
+ (weedle::literal::DefaultValue::String(s), Type::Enum(_)) => {
+ Literal::Enum(s.0.to_string(), type_.clone())
+ }
+ (weedle::literal::DefaultValue::Null(_), Type::Optional(_)) => Literal::Null,
+ (_, Type::Optional(inner_type)) => convert_default_value(default_value, inner_type)?,
+
+ // We'll ensure the type safety in the convert_* number methods.
+ (weedle::literal::DefaultValue::Integer(i), _) => convert_integer(i, type_)?,
+ (weedle::literal::DefaultValue::Float(i), _) => convert_float(i, type_)?,
+
+ _ => bail!("No support for {:?} literal yet", default_value),
+ })
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use weedle::Parse;
+
+ fn parse_and_convert(expr: &str, t: Type) -> Result<Literal> {
+ let (_, node) = weedle::literal::DefaultValue::parse(expr).unwrap();
+ convert_default_value(&node, &t)
+ }
+
+ #[test]
+ fn test_default_value_conversion() -> Result<()> {
+ assert!(matches!(
+ parse_and_convert("0", Type::UInt8)?,
+ Literal::UInt(0, Radix::Decimal, Type::UInt8)
+ ));
+ assert!(matches!(
+ parse_and_convert("-12", Type::Int32)?,
+ Literal::Int(-12, Radix::Decimal, Type::Int32)
+ ));
+ assert!(
+ matches!(parse_and_convert("3.14", Type::Float32)?, Literal::Float(v, Type::Float32) if v == "3.14")
+ );
+ assert!(matches!(
+ parse_and_convert("false", Type::Boolean)?,
+ Literal::Boolean(false)
+ ));
+ assert!(
+ matches!(parse_and_convert("\"TEST\"", Type::String)?, Literal::String(v) if v == "TEST")
+ );
+ assert!(
+ matches!(parse_and_convert("\"one\"", Type::Enum("E".into()))?, Literal::Enum(v, Type::Enum(e)) if v == "one" && e == "E")
+ );
+ assert!(matches!(
+ parse_and_convert("[]", Type::Sequence(Box::new(Type::String)))?,
+ Literal::EmptySequence
+ ));
+ assert!(matches!(
+ parse_and_convert("null", Type::Optional(Box::new(Type::String)))?,
+ Literal::Null
+ ));
+ Ok(())
+ }
+ #[test]
+ fn test_error_on_type_mismatch() {
+ assert_eq!(
+ parse_and_convert("0", Type::Boolean)
+ .unwrap_err()
+ .to_string(),
+ "Cannot coerce literal 0 into a non-integer type"
+ );
+ assert!(parse_and_convert("{}", Type::Boolean)
+ .unwrap_err()
+ .to_string()
+ .starts_with("No support for"));
+ }
+}
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..3daf50ef4a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/mod.rs
@@ -0,0 +1,1218 @@
+/* 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, HashSet},
+ convert::TryFrom,
+ iter,
+};
+
+use anyhow::{bail, ensure, Result};
+
+pub mod types;
+pub use types::Type;
+use types::{TypeIterator, TypeUniverse};
+
+mod attributes;
+mod callbacks;
+pub use callbacks::CallbackInterface;
+mod enum_;
+pub use enum_::Enum;
+mod error;
+pub use error::Error;
+mod function;
+pub use function::{Argument, Function};
+mod literal;
+pub use literal::{Literal, Radix};
+mod namespace;
+pub use namespace::Namespace;
+mod object;
+pub use object::{Constructor, Method, Object};
+mod record;
+pub use record::{Field, Record};
+
+pub mod ffi;
+pub use ffi::{FfiArgument, FfiFunction, FfiType};
+use uniffi_meta::{Checksum, FnMetadata, MethodMetadata, ObjectMetadata};
+
+/// This needs to match the major/minor version of the `uniffi` crate. See
+/// `docs/uniffi-versioning.md` for details.
+///
+/// Once we get to 1.0, then we should reformat this to only include the major version number.
+const UNIFFI_CONTRACT_VERSION: &str = "0.22";
+
+/// 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, Checksum)]
+pub struct ComponentInterface {
+ /// This always points to `UNIFFI_CONTRACT_VERSION`. By including it in the checksum, we
+ /// prevent consumers from combining scaffolding and bindings that were created with different
+ /// `uniffi` versions.
+ uniffi_version: &'static str,
+ /// 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.
+ #[checksum_ignore]
+ pub(super) types: TypeUniverse,
+ /// The unique prefix that we'll use for namespacing when exposing this component's API.
+ namespace: String,
+ /// The internal unique prefix used to namespace FFI symbols
+ #[checksum_ignore]
+ ffi_namespace: String,
+ /// 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>,
+ errors: BTreeMap<String, Error>,
+}
+
+impl ComponentInterface {
+ /// Parse a `ComponentInterface` from a string containing a WebIDL definition.
+ pub fn from_webidl(idl: &str) -> Result<Self> {
+ let mut ci = Self {
+ uniffi_version: UNIFFI_CONTRACT_VERSION,
+ ..Default::default()
+ };
+ // There's some lifetime thing with the errors returned from weedle::Definitions::parse
+ // that my own lifetime is too short to worry about figuring out; unwrap and move on.
+
+ // Note we use `weedle::Definitions::parse` instead of `weedle::parse` so
+ // on parse errors we can see how far weedle got, which helps locate the problem.
+ use weedle::Parse; // this trait must be in scope for parse to work.
+ let (remaining, defns) = weedle::Definitions::parse(idl.trim()).unwrap();
+ if !remaining.is_empty() {
+ println!("Error parsing the IDL. Text remaining to be parsed is:");
+ println!("{remaining}");
+ bail!("parse error");
+ }
+ // Unconditionally add the String type, which is used by the panic handling
+ ci.types.add_known_type(&Type::String)?;
+ // We process the WebIDL definitions in two passes.
+ // First, go through and look for all the named types.
+ ci.types.add_type_definitions_from(defns.as_slice())?;
+ // With those names resolved, we can build a complete representation of the API.
+ APIBuilder::process(&defns, &mut ci)?;
+
+ // The FFI namespace must not be computed on the fly because it could otherwise be
+ // influenced by things added later from proc-macro metadata. Those have their own
+ // namespacing mechanism.
+ assert!(!ci.namespace.is_empty());
+ ci.ffi_namespace = format!("{}_{:x}", ci.namespace, ci.checksum());
+
+ // The following two methods will be called later anyways, but we call them here because
+ // it's convenient for UDL-only tests.
+ ci.check_consistency()?;
+ // Now that the high-level API is settled, we can derive the low-level FFI.
+ ci.derive_ffi_funcs()?;
+
+ Ok(ci)
+ }
+
+ /// 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.namespace.as_str()
+ }
+
+ /// 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 Error type in the interface.
+ pub fn error_definitions(&self) -> impl Iterator<Item = &Error> {
+ self.errors.values()
+ }
+
+ /// Get an Error definition by name, or None if no such Error is defined.
+ pub fn get_error_definition(&self, name: &str) -> Option<&Error> {
+ self.errors.get(name)
+ }
+
+ /// 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, error: &Error) -> bool {
+ // We can and should always generate read() methods for fielded errors
+ let fielded = !error.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(error.type_()));
+
+ fielded || used_in_callback_interface
+ }
+
+ /// Get details about all `Type::External` types
+ pub fn iter_external_types(&self) -> impl Iterator<Item = (&String, &String)> {
+ self.types.iter_known_types().filter_map(|t| match t {
+ Type::External { name, crate_name } => Some((name, crate_name)),
+ _ => 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(_, _)))
+ }
+
+ /// Calculate a numeric checksum for this ComponentInterface.
+ ///
+ /// The checksum can be used to guard against accidentally using foreign-language bindings
+ /// generated from one version of an interface with the compiled Rust code from a different
+ /// version of that interface. It offers the following properties:
+ ///
+ /// - Two ComponentIntefaces generated from the same WebIDL file, using the same version of uniffi
+ /// and the same version of Rust, will always have the same checksum value.
+ /// - Two ComponentInterfaces will, with high probability, have different checksum values if:
+ /// - They were generated from two different WebIDL files.
+ /// - They were generated by two different versions of uniffi
+ ///
+ /// Note that this is designed to prevent accidents, not attacks, so there is no need for the
+ /// checksum to be cryptographically secure.
+ pub fn checksum(&self) -> u16 {
+ uniffi_meta::checksum(self)
+ }
+
+ /// The namespace to use in FFI-level function definitions.
+ ///
+ /// The value returned by this method is used as a prefix to namespace all UDL-defined FFI
+ /// functions used in this ComponentInterface.
+ ///
+ /// Since these names are an internal implementation detail that is not typically visible to
+ /// consumers, we take the opportunity to add an additional safety guard by including a 4-hex-char
+ /// checksum in each name. If foreign-language bindings attempt to load and use a version of the
+ /// Rust code compiled from a different UDL definition than the one used for the bindings themselves,
+ /// then there is a high probability of checksum mismatch and they will fail to link against the
+ /// compiled Rust code. The result will be an ugly inscrutable link-time error, but that is a lot
+ /// better than triggering potentially arbitrary memory unsafety!
+ pub fn ffi_namespace(&self) -> &str {
+ assert!(!self.ffi_namespace.is_empty());
+ &self.ffi_namespace
+ }
+
+ /// 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()),
+ arguments: vec![FfiArgument {
+ name: "size".to_string(),
+ type_: FfiType::Int32,
+ }],
+ return_type: Some(FfiType::RustBuffer(None)),
+ }
+ }
+
+ /// 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()),
+ arguments: vec![FfiArgument {
+ name: "bytes".to_string(),
+ type_: FfiType::ForeignBytes,
+ }],
+ return_type: Some(FfiType::RustBuffer(None)),
+ }
+ }
+
+ /// 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()),
+ arguments: vec![FfiArgument {
+ name: "buf".to_string(),
+ type_: FfiType::RustBuffer(None),
+ }],
+ return_type: None,
+ }
+ }
+
+ /// 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()),
+ 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)),
+ }
+ }
+
+ /// 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())
+ }
+
+ /// 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()
+ }
+
+ //
+ // Private methods for building a ComponentInterface.
+ //
+
+ /// Resolve a weedle type expression into a `Type`.
+ ///
+ /// This method uses the current state of our `TypeUniverse` to turn a weedle type expression
+ /// into a concrete `Type` (or error if the type expression is not well defined). It abstracts
+ /// away the complexity of walking weedle's type struct hierarchy by dispatching to the `TypeResolver`
+ /// trait.
+ fn resolve_type_expression<T: types::TypeResolver>(&mut self, expr: T) -> Result<Type> {
+ self.types.resolve_type_expression(expr)
+ }
+
+ /// Resolve a weedle `ReturnType` expression into an optional `Type`.
+ ///
+ /// This method is similar to `resolve_type_expression`, but tailored specifically for return types.
+ /// It can return `None` to represent a non-existent return value.
+ fn resolve_return_type_expression(
+ &mut self,
+ expr: &weedle::types::ReturnType<'_>,
+ ) -> Result<Option<Type>> {
+ Ok(match expr {
+ weedle::types::ReturnType::Undefined(_) => None,
+ weedle::types::ReturnType::Type(t) => {
+ // Older versions of WebIDL used `void` for functions that don't return a value,
+ // while newer versions have replaced it with `undefined`. Special-case this for
+ // backwards compatibility for our consumers.
+ use weedle::types::{NonAnyType::Identifier, SingleType::NonAny, Type::Single};
+ match t {
+ Single(NonAny(Identifier(id))) if id.type_.0 == "void" => None,
+ _ => Some(self.resolve_type_expression(t)?),
+ }
+ }
+ })
+ }
+
+ /// Called by `APIBuilder` impls to add a newly-parsed namespace definition to the `ComponentInterface`.
+ fn add_namespace_definition(&mut self, defn: Namespace) -> Result<()> {
+ if !self.namespace.is_empty() {
+ bail!("duplicate namespace definition");
+ }
+ self.namespace = defn.name;
+ Ok(())
+ }
+
+ /// 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) => {
+ 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(())
+ }
+
+ /// Called by `APIBuilder` impls to add 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) => {
+ 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(())
+ }
+
+ fn add_function_impl(&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 !matches!(self.types.get_type_definition(defn.name()), None) {
+ bail!("Conflicting type definition for \"{}\"", defn.name());
+ }
+ self.functions.push(defn);
+
+ Ok(())
+ }
+
+ /// Called by `APIBuilder` impls to add a newly-parsed function definition to the `ComponentInterface`.
+ fn add_function_definition(&mut self, defn: Function) -> Result<()> {
+ for arg in &defn.arguments {
+ self.types.add_known_type(&arg.type_)?;
+ }
+ if let Some(ty) = &defn.return_type {
+ self.types.add_known_type(ty)?;
+ }
+
+ self.add_function_impl(defn)
+ }
+
+ pub(super) fn add_fn_meta(&mut self, meta: FnMetadata) -> Result<()> {
+ self.add_function_impl(meta.into())
+ }
+
+ pub(super) fn add_method_meta(&mut self, meta: MethodMetadata) {
+ let object = get_or_insert_object(&mut self.objects, &meta.self_name);
+ let defn: Method = meta.into();
+ object.methods.push(defn);
+ }
+
+ pub(super) fn add_object_free_fn(&mut self, meta: ObjectMetadata) {
+ let object = get_or_insert_object(&mut self.objects, &meta.name);
+ object.ffi_func_free.name = meta.free_ffi_symbol_name();
+ }
+
+ /// Called by `APIBuilder` impls to add a newly-parsed object definition to the `ComponentInterface`.
+ fn add_object_definition(&mut self, defn: Object) {
+ // Note that there will be no duplicates thanks to the previous type-finding pass.
+ self.objects.push(defn);
+ }
+
+ /// Called by `APIBuilder` impls to add a newly-parsed callback interface definition to the `ComponentInterface`.
+ fn add_callback_interface_definition(&mut self, defn: CallbackInterface) {
+ // Note that there will be no duplicates thanks to the previous type-finding pass.
+ self.callback_interfaces.push(defn);
+ }
+
+ /// Called by `APIBuilder` impls to add a newly-parsed error definition to the `ComponentInterface`.
+ pub(super) fn add_error_definition(&mut self, defn: Error) -> Result<()> {
+ match self.errors.entry(defn.name().to_owned()) {
+ Entry::Vacant(v) => {
+ v.insert(defn);
+ }
+ Entry::Occupied(o) => {
+ let existing_def = o.get();
+ if defn != *existing_def {
+ bail!(
+ "Mismatching definition for error `{}`!\n\
+ existing definition: {existing_def:#?},\n\
+ new definition: {defn:#?}",
+ defn.name(),
+ );
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Resolve unresolved types within proc-macro function / method signatures.
+ pub fn resolve_types(&mut self) -> Result<()> {
+ fn handle_unresolved_in(
+ ty: &mut Type,
+ f: impl Fn(&str) -> Result<Type> + Clone,
+ ) -> Result<()> {
+ match ty {
+ Type::Unresolved { name } => {
+ *ty = f(name)?;
+ }
+ Type::Optional(inner) => {
+ handle_unresolved_in(inner, f)?;
+ }
+ Type::Sequence(inner) => {
+ handle_unresolved_in(inner, f)?;
+ }
+ Type::Map(k, v) => {
+ handle_unresolved_in(k, f.clone())?;
+ handle_unresolved_in(v, f)?;
+ }
+ _ => {}
+ }
+
+ Ok(())
+ }
+
+ let fn_sig_types = self.functions.iter_mut().flat_map(|fun| {
+ fun.arguments
+ .iter_mut()
+ .map(|arg| &mut arg.type_)
+ .chain(&mut fun.return_type)
+ });
+ let method_sig_types = self.objects.iter_mut().flat_map(|obj| {
+ obj.methods.iter_mut().flat_map(|m| {
+ m.arguments
+ .iter_mut()
+ .map(|arg| &mut arg.type_)
+ .chain(&mut m.return_type)
+ })
+ });
+
+ let record_fields_types = self
+ .records
+ .values_mut()
+ .flat_map(|r| r.fields.iter_mut().map(|f| &mut f.type_));
+ let enum_fields_types = self.enums.values_mut().flat_map(|e| {
+ e.variants
+ .iter_mut()
+ .flat_map(|r| r.fields.iter_mut().map(|f| &mut f.type_))
+ });
+
+ let possibly_unresolved_types = fn_sig_types
+ .chain(method_sig_types)
+ .chain(record_fields_types)
+ .chain(enum_fields_types);
+
+ for ty in possibly_unresolved_types {
+ handle_unresolved_in(ty, |unresolved_ty_name| {
+ match self.types.get_type_definition(unresolved_ty_name) {
+ Some(def) => {
+ assert!(
+ !matches!(&def, Type::Unresolved { .. }),
+ "unresolved types must not be part of TypeUniverse"
+ );
+ Ok(def)
+ }
+ None => bail!("Failed to resolve type `{unresolved_ty_name}`"),
+ }
+ })?;
+
+ // The proc-macro scanning metadata code doesn't add known types
+ // when they could contain unresolved types, so we have to do it
+ // here after replacing unresolveds.
+ self.types.add_known_type(ty)?;
+ }
+
+ 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");
+ }
+
+ // To keep codegen tractable, enum variant names must not shadow type names.
+ for e in self.enums.values() {
+ for variant in &e.variants {
+ if self.types.get_type_definition(variant.name()).is_some() {
+ bail!(
+ "Enum variant names must not shadow type names: \"{}\"",
+ variant.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",
+ );
+ }
+ Type::Unresolved { name } => {
+ bail!("Type `{name}` should be resolved at this point");
+ }
+ _ => {}
+ }
+ }
+
+ 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<()> {
+ let ci_prefix = self.ffi_namespace().to_owned();
+ for func in self.functions.iter_mut() {
+ func.derive_ffi_func(&ci_prefix)?;
+ }
+ for obj in self.objects.iter_mut() {
+ obj.derive_ffi_funcs(&ci_prefix)?;
+ }
+ for callback in self.callback_interfaces.iter_mut() {
+ callback.derive_ffi_funcs(&ci_prefix);
+ }
+ Ok(())
+ }
+}
+
+fn get_or_insert_object<'a>(objects: &'a mut Vec<Object>, name: &str) -> &'a mut Object {
+ // The find-based way of writing this currently runs into a borrow checker
+ // error, so we use position
+ match objects.iter_mut().position(|o| o.name == name) {
+ Some(idx) => &mut objects[idx],
+ None => {
+ objects.push(Object::new(name.to_owned()));
+ objects.last_mut().unwrap()
+ }
+ }
+}
+
+/// 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(nm)
+ | Type::Enum(nm)
+ | Type::Error(nm)
+ | Type::Object(nm)
+ | Type::CallbackInterface(nm) => {
+ if !self.seen.contains(nm.as_str()) {
+ self.pending.push(type_);
+ self.seen.insert(nm.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(nm) => self.ci.get_record_definition(nm).map(Record::iter_types),
+ Type::Enum(nm) => self.ci.get_enum_definition(nm).map(Enum::iter_types),
+ Type::Error(nm) => self.ci.get_error_definition(nm).map(Error::iter_types),
+ Type::Object(nm) => self.ci.get_object_definition(nm).map(Object::iter_types),
+ Type::CallbackInterface(nm) => self
+ .ci
+ .get_callback_interface_definition(nm)
+ .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()
+ }
+ }
+}
+
+/// Trait to help build a `ComponentInterface` from WedIDL syntax nodes.
+///
+/// This trait does structural matching on the various weedle AST nodes and
+/// uses them to build up the records, enums, objects etc in the provided
+/// `ComponentInterface`.
+trait APIBuilder {
+ fn process(&self, ci: &mut ComponentInterface) -> Result<()>;
+}
+
+/// Add to a `ComponentInterface` from a list of weedle definitions,
+/// by processing each in turn.
+impl<T: APIBuilder> APIBuilder for Vec<T> {
+ fn process(&self, ci: &mut ComponentInterface) -> Result<()> {
+ for item in self {
+ item.process(ci)?;
+ }
+ Ok(())
+ }
+}
+
+/// Add to a `ComponentInterface` from a weedle definition.
+/// This is conceptually the root of the parser, and dispatches to implementations
+/// for the various specific WebIDL types that we support.
+impl APIBuilder for weedle::Definition<'_> {
+ fn process(&self, ci: &mut ComponentInterface) -> Result<()> {
+ match self {
+ weedle::Definition::Namespace(d) => d.process(ci)?,
+ weedle::Definition::Enum(d) => {
+ // We check if the enum represents an error...
+ let attrs = attributes::EnumAttributes::try_from(d.attributes.as_ref())?;
+ if attrs.contains_error_attr() {
+ let err = d.convert(ci)?;
+ ci.add_error_definition(err)?;
+ } else {
+ let e = d.convert(ci)?;
+ ci.add_enum_definition(e)?;
+ }
+ }
+ weedle::Definition::Dictionary(d) => {
+ let rec = d.convert(ci)?;
+ ci.add_record_definition(rec)?;
+ }
+ weedle::Definition::Interface(d) => {
+ let attrs = attributes::InterfaceAttributes::try_from(d.attributes.as_ref())?;
+ if attrs.contains_enum_attr() {
+ let e = d.convert(ci)?;
+ ci.add_enum_definition(e)?;
+ } else if attrs.contains_error_attr() {
+ let e = d.convert(ci)?;
+ ci.add_error_definition(e)?;
+ } else {
+ let obj = d.convert(ci)?;
+ ci.add_object_definition(obj);
+ }
+ }
+ weedle::Definition::CallbackInterface(d) => {
+ let obj = d.convert(ci)?;
+ ci.add_callback_interface_definition(obj);
+ }
+ // everything needed for typedefs is done in finder.rs.
+ weedle::Definition::Typedef(_) => {}
+ _ => bail!("don't know how to deal with {:?}", self),
+ }
+ Ok(())
+ }
+}
+
+/// Trait to help convert WedIDL syntax nodes into `ComponentInterface` objects.
+///
+/// This trait does structural matching on the various weedle AST nodes and converts
+/// them into appropriate structs that we can use to build up the contents of a
+/// `ComponentInterface`. It is basically the `TryFrom` trait except that the conversion
+/// always happens in the context of a given `ComponentInterface`, which is used for
+/// resolving e.g. type definitions.
+///
+/// The difference between this trait and `APIBuilder` is that `APIConverter` treats the
+/// `ComponentInterface` as a read-only data source for resolving types, while `APIBuilder`
+/// actually mutates the `ComponentInterface` to add new definitions.
+trait APIConverter<T> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<T>;
+}
+
+/// Convert a list of weedle items into a list of `ComponentInterface` items,
+/// by doing a direct item-by-item mapping.
+impl<U, T: APIConverter<U>> APIConverter<Vec<U>> for Vec<T> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Vec<U>> {
+ self.iter().map(|v| v.convert(ci)).collect::<Result<_>>()
+ }
+}
+
+fn convert_type(s: &uniffi_meta::Type) -> Type {
+ use uniffi_meta::Type as Ty;
+
+ match s {
+ Ty::U8 => Type::UInt8,
+ Ty::U16 => Type::UInt16,
+ Ty::U32 => Type::UInt32,
+ Ty::U64 => Type::UInt64,
+ Ty::I8 => Type::Int8,
+ Ty::I16 => Type::Int16,
+ Ty::I32 => Type::Int32,
+ Ty::I64 => Type::Int64,
+ Ty::F32 => Type::Float32,
+ Ty::F64 => Type::Float64,
+ Ty::Bool => Type::Boolean,
+ Ty::String => Type::String,
+ Ty::Option { inner_type } => Type::Optional(convert_type(inner_type).into()),
+ Ty::Vec { inner_type } => Type::Sequence(convert_type(inner_type).into()),
+ Ty::HashMap {
+ key_type,
+ value_type,
+ } => Type::Map(
+ convert_type(key_type).into(),
+ convert_type(value_type).into(),
+ ),
+ Ty::ArcObject { object_name } => Type::Object(object_name.clone()),
+ Ty::Unresolved { name } => Type::Unresolved { name: name.clone() },
+ }
+}
+
+#[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.
+
+ const UDL1: &str = r#"
+ namespace foobar{};
+ enum Test {
+ "test_me",
+ };
+ "#;
+
+ const UDL2: &str = r#"
+ namespace hello {
+ u64 world();
+ };
+ dictionary Test {
+ boolean me;
+ };
+ "#;
+
+ #[test]
+ fn test_checksum_always_matches_for_same_webidl() {
+ for udl in &[UDL1, UDL2] {
+ let ci1 = ComponentInterface::from_webidl(udl).unwrap();
+ let ci2 = ComponentInterface::from_webidl(udl).unwrap();
+ assert_eq!(ci1.checksum(), ci2.checksum());
+ }
+ }
+
+ #[test]
+ fn test_checksum_differs_for_different_webidl() {
+ // There is a small probability of this test spuriously failing due to hash collision.
+ // If it happens often enough to be a problem, probably this whole "checksum" thing
+ // is not working out as intended.
+ let ci1 = ComponentInterface::from_webidl(UDL1).unwrap();
+ let ci2 = ComponentInterface::from_webidl(UDL2).unwrap();
+ assert_ne!(ci1.checksum(), ci2.checksum());
+ }
+
+ #[test]
+ fn test_checksum_differs_for_different_uniffi_version() {
+ // There is a small probability of this test spuriously failing due to hash collision.
+ // If it happens often enough to be a problem, probably this whole "checksum" thing
+ // is not working out as intended.
+ for udl in &[UDL1, UDL2] {
+ let ci1 = ComponentInterface::from_webidl(udl).unwrap();
+ let mut ci2 = ComponentInterface::from_webidl(udl).unwrap();
+ ci2.uniffi_version = "99.99";
+ assert_ne!(ci1.checksum(), ci2.checksum());
+ }
+ }
+
+ #[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).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Conflicting type definition for `Testing`! \
+ existing definition: Object(\"Testing\"), \
+ new definition: Record(\"Testing\")"
+ );
+
+ const UDL2: &str = r#"
+ namespace test{};
+ enum Testing {
+ "one", "two"
+ };
+ [Error]
+ enum Testing { "three", "four" };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL2).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Conflicting type definition for `Testing`! \
+ existing definition: Enum(\"Testing\"), \
+ new definition: Error(\"Testing\")"
+ );
+
+ const UDL3: &str = r#"
+ namespace test{
+ u32 Testing();
+ };
+ enum Testing {
+ "one", "two"
+ };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL3).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Conflicting type definition for \"Testing\""
+ );
+ }
+
+ #[test]
+ fn test_enum_variant_names_dont_shadow_types() {
+ // There are some edge-cases during codegen where we don't know how to disambiguate
+ // between an enum variant reference and a top-level type reference, so we
+ // disallow it in order to give a more scrutable error to the consumer.
+ const UDL: &str = r#"
+ namespace test{};
+ interface Testing {
+ constructor();
+ };
+ [Enum]
+ interface HardToCodegenFor {
+ Testing();
+ OtherVariant(u32 field);
+ };
+ "#;
+ let err = ComponentInterface::from_webidl(UDL).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "Enum variant names must not shadow type names: \"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_type_definition("TestOptional{}", Type::Optional(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_type_definition("TestSequence{}", Type::Sequence(Box::new(Type::UInt64)))
+ .is_ok());
+ assert!(ci.contains_sequence_types());
+ }
+
+ #[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_type_definition(
+ "Map{}",
+ Type::Map(Box::new(Type::String), Box::new(Type::Boolean))
+ )
+ .is_ok());
+ assert!(ci.contains_map_types());
+ }
+
+ #[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).unwrap();
+ assert!(!ci.item_contains_unsigned_types(&Type::Object("Testing".into())));
+ }
+
+ #[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).unwrap();
+ assert!(ci.item_contains_unsigned_types(&Type::Object("TestObj".into())));
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/namespace.rs b/third_party/rust/uniffi_bindgen/src/interface/namespace.rs
new file mode 100644
index 0000000000..4a57d0ff41
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/namespace.rs
@@ -0,0 +1,132 @@
+/* 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/. */
+
+//! # Namespace definition for a `ComponentInterface`.
+//!
+//! This module converts a namespace definition from UDL into structures that
+//! can be added to a `ComponentInterface`.
+//!
+//! In WebIDL proper, each `namespace` declares a set of functions and attributes that
+//! are exposed as a global object of that name, and there can be any number of such
+//! namespace definitions.
+//!
+//! For our purposes with UDL, we expect just a single `namespace` declaration, which
+//! defines properties of the component as a whole (currently just the name). It also
+//! contains the functions that will be exposed as individual plain functions exported by
+//! the component, if any. So something like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! namespace example {
+//! string hello();
+//! };
+//! # "##)?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Declares a component named "example" with a single exported function named "hello":
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {
+//! # string hello();
+//! # };
+//! # "##)?;
+//! assert_eq!(ci.namespace(), "example");
+//! assert_eq!(ci.get_function_definition("hello").unwrap().name(), "hello");
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! While this awkward-looking syntax:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! namespace example {};
+//! # "##)?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Declares a component named "example" with no exported functions:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # "##)?;
+//! assert_eq!(ci.namespace(), "example");
+//! assert_eq!(ci.function_definitions().len(), 0);
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Yeah, it's a bit of an awkward fit syntactically, but it's enough
+//! to get us up and running for a first version of this tool.
+
+use anyhow::{bail, Result};
+
+use super::{APIBuilder, APIConverter, ComponentInterface};
+
+/// A namespace is currently just a name, but might hold more metadata about
+/// the component in future.
+///
+#[derive(Debug, Clone, Hash)]
+pub struct Namespace {
+ pub(super) name: String,
+}
+
+impl APIBuilder for weedle::NamespaceDefinition<'_> {
+ fn process(&self, ci: &mut ComponentInterface) -> Result<()> {
+ if self.attributes.is_some() {
+ bail!("namespace attributes are not supported yet");
+ }
+ ci.add_namespace_definition(Namespace {
+ name: self.identifier.0.to_string(),
+ })?;
+ for func in self.members.body.convert(ci)? {
+ ci.add_function_definition(func)?;
+ }
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_empty_namespace() {
+ const UDL: &str = r#"
+ namespace foobar{};
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL).unwrap();
+ assert_eq!(ci.namespace(), "foobar");
+ }
+
+ #[test]
+ fn test_namespace_with_functions() {
+ const UDL: &str = r#"
+ namespace foobar{
+ boolean hello();
+ void world();
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL).unwrap();
+ assert_eq!(ci.namespace(), "foobar");
+ assert_eq!(ci.function_definitions().len(), 2);
+ assert!(ci.get_function_definition("hello").is_some());
+ assert!(ci.get_function_definition("world").is_some());
+ assert!(ci.get_function_definition("potato").is_none());
+ }
+
+ #[test]
+ fn test_rejects_duplicate_namespaces() {
+ const UDL: &str = r#"
+ namespace foobar{
+ boolean hello();
+ void world();
+ };
+ namespace something_else{};
+ "#;
+ let err = ComponentInterface::from_webidl(UDL).unwrap_err();
+ assert_eq!(err.to_string(), "duplicate namespace definition");
+ }
+}
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..e8ba089261
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/object.rs
@@ -0,0 +1,599 @@
+/* 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();
+//! };
+//! # "##)?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in an [`Object`] member with one [`Constructor`] and one [`Method`] being added
+//! to the resulting [`ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # interface Example {
+//! # constructor(string? name);
+//! # string my_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 {};
+//! # "##)?;
+//! let obj = ci.get_object_definition("Example").unwrap();
+//! assert_eq!(obj.name(), "Example");
+//! assert_eq!(obj.constructors().len(), 0);
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+
+use std::convert::TryFrom;
+use std::{collections::HashSet, iter};
+
+use anyhow::{bail, Result};
+use uniffi_meta::Checksum;
+
+use super::attributes::{Attribute, ConstructorAttributes, InterfaceAttributes, MethodAttributes};
+use super::ffi::{FfiArgument, FfiFunction, FfiType};
+use super::function::Argument;
+use super::types::{Type, TypeIterator};
+use super::{convert_type, APIConverter, ComponentInterface};
+
+/// An "object" is an opaque type that can be instantiated and passed around by reference,
+/// have methods called on it, and so on - basically your classic Object Oriented Programming
+/// type of deal, except without elaborate inheritance hierarchies.
+///
+/// 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,
+ pub(super) constructors: Vec<Constructor>,
+ 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_func_free: FfiFunction,
+ #[checksum_ignore]
+ pub(super) uses_deprecated_threadsafe_attribute: bool,
+}
+
+impl Object {
+ pub(super) fn new(name: String) -> Object {
+ Object {
+ name,
+ constructors: Default::default(),
+ methods: Default::default(),
+ ffi_func_free: Default::default(),
+ uses_deprecated_threadsafe_attribute: false,
+ }
+ }
+
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn type_(&self) -> Type {
+ Type::Object(self.name.clone())
+ }
+
+ 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 ffi_object_free(&self) -> &FfiFunction {
+ &self.ffi_func_free
+ }
+
+ pub fn uses_deprecated_threadsafe_attribute(&self) -> bool {
+ self.uses_deprecated_threadsafe_attribute
+ }
+
+ 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))
+ }
+
+ pub fn derive_ffi_funcs(&mut self, ci_prefix: &str) -> Result<()> {
+ // The name is already set if the function is defined through a proc-macro invocation
+ // rather than in UDL. Don't overwrite it in that case.
+ if self.ffi_func_free.name().is_empty() {
+ self.ffi_func_free.name = format!("ffi_{ci_prefix}_{}_object_free", self.name);
+ }
+ 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;
+
+ for cons in self.constructors.iter_mut() {
+ cons.derive_ffi_func(ci_prefix, &self.name);
+ }
+ for meth in self.methods.iter_mut() {
+ meth.derive_ffi_func(ci_prefix, &self.name)?;
+ }
+
+ Ok(())
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(
+ self.methods
+ .iter()
+ .map(Method::iter_types)
+ .chain(self.constructors.iter().map(Constructor::iter_types))
+ .flatten(),
+ )
+ }
+}
+
+impl APIConverter<Object> for weedle::InterfaceDefinition<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Object> {
+ if self.inheritance.is_some() {
+ bail!("interface inheritance is not supported");
+ }
+ let mut object = Object::new(self.identifier.0.to_string());
+ let attributes = match &self.attributes {
+ Some(attrs) => InterfaceAttributes::try_from(attrs)?,
+ None => Default::default(),
+ };
+ object.uses_deprecated_threadsafe_attribute = attributes.threadsafe();
+ // Convert each member into a constructor or method, guarding against duplicate names.
+ let mut member_names = HashSet::new();
+ for member in &self.members.body {
+ match member {
+ weedle::interface::InterfaceMember::Constructor(t) => {
+ let cons: Constructor = t.convert(ci)?;
+ if !member_names.insert(cons.name.clone()) {
+ bail!("Duplicate interface member name: \"{}\"", cons.name())
+ }
+ object.constructors.push(cons);
+ }
+ weedle::interface::InterfaceMember::Operation(t) => {
+ let mut method: Method = t.convert(ci)?;
+ if !member_names.insert(method.name.clone()) {
+ bail!("Duplicate interface member name: \"{}\"", method.name())
+ }
+ method.object_name = object.name.clone();
+ object.methods.push(method);
+ }
+ _ => bail!("no support for interface member type {:?} yet", member),
+ }
+ }
+ Ok(object)
+ }
+}
+
+// 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) 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) attributes: ConstructorAttributes,
+}
+
+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 throws(&self) -> bool {
+ self.attributes.get_throws_err().is_some()
+ }
+
+ pub fn throws_name(&self) -> Option<&str> {
+ self.attributes.get_throws_err()
+ }
+
+ pub fn throws_type(&self) -> Option<Type> {
+ self.attributes
+ .get_throws_err()
+ .map(|name| Type::Error(name.to_owned()))
+ }
+
+ pub fn is_primary_constructor(&self) -> bool {
+ self.name == "new"
+ }
+
+ fn derive_ffi_func(&mut self, ci_prefix: &str, obj_name: &str) {
+ self.ffi_func.name = format!("{ci_prefix}_{obj_name}_{}", self.name);
+ self.ffi_func.arguments = self.arguments.iter().map(Into::into).collect();
+ self.ffi_func.return_type = Some(FfiType::RustArcPtr(obj_name.to_string()));
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(self.arguments.iter().flat_map(Argument::iter_types))
+ }
+}
+
+impl Default for Constructor {
+ fn default() -> Self {
+ Constructor {
+ name: String::from("new"),
+ arguments: Vec::new(),
+ ffi_func: Default::default(),
+ attributes: Default::default(),
+ }
+ }
+}
+
+impl APIConverter<Constructor> for weedle::interface::ConstructorInterfaceMember<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Constructor> {
+ let attributes = match &self.attributes {
+ Some(attr) => ConstructorAttributes::try_from(attr)?,
+ None => Default::default(),
+ };
+ Ok(Constructor {
+ name: String::from(attributes.get_name().unwrap_or("new")),
+ arguments: self.args.body.list.convert(ci)?,
+ ffi_func: Default::default(),
+ attributes,
+ })
+ }
+}
+
+// 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) 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) attributes: MethodAttributes,
+}
+
+impl Method {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ 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(self.object_name.clone()),
+ by_ref: !self.attributes.get_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 throws(&self) -> bool {
+ self.attributes.get_throws_err().is_some()
+ }
+
+ pub fn throws_name(&self) -> Option<&str> {
+ self.attributes.get_throws_err()
+ }
+
+ pub fn throws_type(&self) -> Option<Type> {
+ self.attributes
+ .get_throws_err()
+ .map(|name| Type::Error(name.to_owned()))
+ }
+
+ pub fn takes_self_by_arc(&self) -> bool {
+ self.attributes.get_self_by_arc()
+ }
+
+ pub fn derive_ffi_func(&mut self, ci_prefix: &str, obj_prefix: &str) -> Result<()> {
+ // The name is already set if the function is defined through a proc-macro invocation
+ // rather than in UDL. Don't overwrite it in that case.
+ if self.ffi_func.name.is_empty() {
+ self.ffi_func.name = format!("{ci_prefix}_{obj_prefix}_{}", self.name);
+ }
+
+ self.ffi_func.arguments = self.full_arguments().iter().map(Into::into).collect();
+ self.ffi_func.return_type = self.return_type.as_ref().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 return_type = meta.return_type.map(|out| convert_type(&out));
+ 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,
+ arguments,
+ return_type,
+ ffi_func,
+ attributes: meta.throws.map(Attribute::Throws).into_iter().collect(),
+ }
+ }
+}
+
+impl APIConverter<Method> for weedle::interface::OperationInterfaceMember<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Method> {
+ if self.special.is_some() {
+ bail!("special operations not supported");
+ }
+ if self.modifier.is_some() {
+ bail!("method modifiers are not supported")
+ }
+ let return_type = ci.resolve_return_type_expression(&self.return_type)?;
+ Ok(Method {
+ name: match self.identifier {
+ None => bail!("anonymous methods are not supported {:?}", self),
+ Some(id) => {
+ let name = id.0.to_string();
+ if name == "new" {
+ bail!("the method name \"new\" is reserved for the default constructor");
+ }
+ name
+ }
+ },
+ // We don't know the name of the containing `Object` at this point, fill it in later.
+ object_name: Default::default(),
+ arguments: self.args.body.list.convert(ci)?,
+ return_type,
+ ffi_func: Default::default(),
+ attributes: MethodAttributes::try_from(self.attributes.as_ref())?,
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ 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).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.canonical_name() == "u16"));
+ assert!(ci.iter_types().any(|t| t.canonical_name() == "u32"));
+ assert!(ci.iter_types().any(|t| t.canonical_name() == "Sequenceu32"));
+ assert!(ci.iter_types().any(|t| t.canonical_name() == "string"));
+ assert!(ci
+ .iter_types()
+ .any(|t| t.canonical_name() == "Optionalstring"));
+ assert!(ci.iter_types().any(|t| t.canonical_name() == "TypeTesting"));
+ }
+
+ #[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).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).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).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).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).unwrap_err();
+ assert_eq!(err.to_string(), "Duplicate interface member name: \"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..b000510884
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/record.rs
@@ -0,0 +1,243 @@
+/* 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;
+//! };
+//! # "##)?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in a [`Record`] member with two [`Field`]s being added to the resulting
+//! [`ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # dictionary Example {
+//! # string name;
+//! # u32 value;
+//! # };
+//! # "##)?;
+//! 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::{bail, Result};
+use uniffi_meta::Checksum;
+
+use super::types::{Type, TypeIterator};
+use super::{
+ convert_type,
+ literal::{convert_default_value, Literal},
+};
+use super::{APIConverter, ComponentInterface};
+
+/// 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) fields: Vec<Field>,
+}
+
+impl Record {
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn type_(&self) -> Type {
+ // *sigh* at the clone here, the relationship between a ComponentInterface
+ // and its contained types could use a bit of a cleanup.
+ Type::Record(self.name.clone())
+ }
+
+ pub fn fields(&self) -> &[Field] {
+ &self.fields
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(self.fields.iter().flat_map(Field::iter_types))
+ }
+}
+
+impl From<uniffi_meta::RecordMetadata> for Record {
+ fn from(meta: uniffi_meta::RecordMetadata) -> Self {
+ Self {
+ name: meta.name,
+ fields: meta.fields.into_iter().map(Into::into).collect(),
+ }
+ }
+}
+
+impl APIConverter<Record> for weedle::DictionaryDefinition<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Record> {
+ if self.attributes.is_some() {
+ bail!("dictionary attributes are not supported yet");
+ }
+ if self.inheritance.is_some() {
+ bail!("dictionary inheritance is not supported");
+ }
+ Ok(Record {
+ name: self.identifier.0.to_string(),
+ fields: self.members.body.convert(ci)?,
+ })
+ }
+}
+
+// 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 type_(&self) -> &Type {
+ &self.type_
+ }
+
+ pub fn default_value(&self) -> Option<&Literal> {
+ self.default.as_ref()
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ self.type_.iter_types()
+ }
+}
+
+impl From<uniffi_meta::FieldMetadata> for Field {
+ fn from(meta: uniffi_meta::FieldMetadata) -> Self {
+ Self {
+ name: meta.name,
+ type_: convert_type(&meta.ty),
+ default: None,
+ }
+ }
+}
+
+impl APIConverter<Field> for weedle::dictionary::DictionaryMember<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<Field> {
+ if self.attributes.is_some() {
+ bail!("dictionary member attributes are not supported yet");
+ }
+ let type_ = ci.resolve_type_expression(&self.type_)?;
+ let default = match self.default {
+ None => None,
+ Some(v) => Some(convert_default_value(&v.value, &type_)?),
+ };
+ Ok(Field {
+ name: self.identifier.0.to_string(),
+ type_,
+ default,
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::super::literal::Radix;
+ use super::*;
+
+ #[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).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].type_().canonical_name(), "u32");
+ 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].type_().canonical_name(),
+ "Optionalstring"
+ );
+ assert!(record.fields()[0].default_value().is_none());
+ assert_eq!(record.fields()[1].name(), "value");
+ assert_eq!(record.fields()[1].type_().canonical_name(), "u32");
+ 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].type_().canonical_name(), "bool");
+ 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).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.canonical_name() == "u32"));
+ assert!(ci.iter_types().any(|t| t.canonical_name() == "string"));
+ assert!(ci
+ .iter_types()
+ .any(|t| t.canonical_name() == "Optionalstring"));
+ assert!(ci.iter_types().any(|t| t.canonical_name() == "TypeTesting"));
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/types/finder.rs b/third_party/rust/uniffi_bindgen/src/interface/types/finder.rs
new file mode 100644
index 0000000000..991f910a4d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/types/finder.rs
@@ -0,0 +1,244 @@
+/* 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/. */
+
+//! # Helpers for finding the named types defined in a UDL interface.
+//!
+//! This module provides the [`TypeFinder`] trait, an abstraction for walking
+//! the weedle parse tree, looking for type definitions, and accumulating them
+//! in a [`TypeUniverse`].
+//!
+//! The type-finding process only discovers very basic information about names
+//! and their corresponding types. For example, it can discover that "Foobar"
+//! names a Record, but it won't discover anything about the fields of that
+//! record.
+//!
+//! Factoring this functionality out into a separate phase makes the subsequent
+//! work of more *detailed* parsing of the UDL a lot simpler, we know how to resolve
+//! names to types when building up the full interface definition.
+
+use std::convert::TryFrom;
+
+use anyhow::{bail, Result};
+
+use super::super::attributes::{EnumAttributes, InterfaceAttributes, TypedefAttributes};
+use super::{Type, TypeUniverse};
+
+/// Trait to help with an early "type discovery" phase when processing the UDL.
+///
+/// This trait does structural matching against weedle AST nodes from a parsed
+/// UDL file, looking for all the newly-defined types in the file and accumulating
+/// them in the given `TypeUniverse`.
+pub(in super::super) trait TypeFinder {
+ fn add_type_definitions_to(&self, types: &mut TypeUniverse) -> Result<()>;
+}
+
+impl<T: TypeFinder> TypeFinder for &[T] {
+ fn add_type_definitions_to(&self, types: &mut TypeUniverse) -> Result<()> {
+ for item in *self {
+ item.add_type_definitions_to(types)?;
+ }
+ Ok(())
+ }
+}
+
+impl TypeFinder for weedle::Definition<'_> {
+ fn add_type_definitions_to(&self, types: &mut TypeUniverse) -> Result<()> {
+ match self {
+ weedle::Definition::Interface(d) => d.add_type_definitions_to(types),
+ weedle::Definition::Dictionary(d) => d.add_type_definitions_to(types),
+ weedle::Definition::Enum(d) => d.add_type_definitions_to(types),
+ weedle::Definition::Typedef(d) => d.add_type_definitions_to(types),
+ weedle::Definition::CallbackInterface(d) => d.add_type_definitions_to(types),
+ _ => Ok(()),
+ }
+ }
+}
+
+impl TypeFinder for weedle::InterfaceDefinition<'_> {
+ fn add_type_definitions_to(&self, types: &mut TypeUniverse) -> Result<()> {
+ let name = self.identifier.0.to_string();
+ // Some enum types are defined using an `interface` with a special attribute.
+ if InterfaceAttributes::try_from(self.attributes.as_ref())?.contains_enum_attr() {
+ types.add_type_definition(self.identifier.0, Type::Enum(name))
+ } else if InterfaceAttributes::try_from(self.attributes.as_ref())?.contains_error_attr() {
+ types.add_type_definition(self.identifier.0, Type::Error(name))
+ } else {
+ types.add_type_definition(self.identifier.0, Type::Object(name))
+ }
+ }
+}
+
+impl TypeFinder for weedle::DictionaryDefinition<'_> {
+ fn add_type_definitions_to(&self, types: &mut TypeUniverse) -> Result<()> {
+ let name = self.identifier.0.to_string();
+ types.add_type_definition(self.identifier.0, Type::Record(name))
+ }
+}
+
+impl TypeFinder for weedle::EnumDefinition<'_> {
+ fn add_type_definitions_to(&self, types: &mut TypeUniverse) -> Result<()> {
+ let name = self.identifier.0.to_string();
+ // Our error types are defined using an `enum` with a special attribute.
+ if EnumAttributes::try_from(self.attributes.as_ref())?.contains_error_attr() {
+ types.add_type_definition(self.identifier.0, Type::Error(name))
+ } else {
+ types.add_type_definition(self.identifier.0, Type::Enum(name))
+ }
+ }
+}
+
+impl TypeFinder for weedle::TypedefDefinition<'_> {
+ fn add_type_definitions_to(&self, types: &mut TypeUniverse) -> Result<()> {
+ let name = self.identifier.0;
+ let attrs = TypedefAttributes::try_from(self.attributes.as_ref())?;
+ // If we wanted simple `typedef`s, it would be as easy as:
+ // > let t = types.resolve_type_expression(&self.type_)?;
+ // > types.add_type_definition(name, t)
+ // But we don't - `typedef`s are reserved for external types.
+ if attrs.is_custom() {
+ // A local type which wraps a builtin and for which we will generate an
+ // `FfiConverter` implementation.
+ let builtin = types.resolve_type_expression(&self.type_)?;
+ types.add_type_definition(
+ name,
+ Type::Custom {
+ name: name.to_string(),
+ builtin: builtin.into(),
+ },
+ )
+ } else {
+ // A crate which can supply an `FfiConverter`.
+ // We don't reference `self._type`, so ideally we could insist on it being
+ // the literal 'extern' but that's tricky
+ types.add_type_definition(
+ name,
+ Type::External {
+ name: name.to_string(),
+ crate_name: attrs.get_crate_name(),
+ },
+ )
+ }
+ }
+}
+
+impl TypeFinder for weedle::CallbackInterfaceDefinition<'_> {
+ fn add_type_definitions_to(&self, types: &mut TypeUniverse) -> Result<()> {
+ if self.attributes.is_some() {
+ bail!("no typedef attributes are currently supported");
+ }
+ let name = self.identifier.0.to_string();
+ types.add_type_definition(self.identifier.0, Type::CallbackInterface(name))
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ // A helper to take valid UDL and a closure to check what's in it.
+ fn test_a_finding<F>(udl: &str, tester: F)
+ where
+ F: FnOnce(TypeUniverse),
+ {
+ let idl = weedle::parse(udl).unwrap();
+ let mut types = TypeUniverse::default();
+ types.add_type_definitions_from(idl.as_ref()).unwrap();
+ tester(types);
+ }
+
+ #[test]
+ fn test_type_finding() {
+ test_a_finding(
+ r#"
+ callback interface TestCallbacks {
+ string hello(u32 count);
+ };
+ "#,
+ |types| {
+ assert!(
+ matches!(types.get_type_definition("TestCallbacks").unwrap(), Type::CallbackInterface(nm) if nm == "TestCallbacks")
+ );
+ },
+ );
+
+ test_a_finding(
+ r#"
+ dictionary TestRecord {
+ u32 field;
+ };
+ "#,
+ |types| {
+ assert!(
+ matches!(types.get_type_definition("TestRecord").unwrap(), Type::Record(nm) if nm == "TestRecord")
+ );
+ },
+ );
+
+ test_a_finding(
+ r#"
+ enum TestItems { "one", "two" };
+
+ [Error]
+ enum TestError { "ErrorOne", "ErrorTwo" };
+ "#,
+ |types| {
+ assert!(
+ matches!(types.get_type_definition("TestItems").unwrap(), Type::Enum(nm) if nm == "TestItems")
+ );
+ assert!(
+ matches!(types.get_type_definition("TestError").unwrap(), Type::Error(nm) if nm == "TestError")
+ );
+ },
+ );
+
+ test_a_finding(
+ r#"
+ interface TestObject {
+ constructor();
+ };
+ "#,
+ |types| {
+ assert!(
+ matches!(types.get_type_definition("TestObject").unwrap(), Type::Object(nm) if nm == "TestObject")
+ );
+ },
+ );
+
+ test_a_finding(
+ r#"
+ [External="crate-name"]
+ typedef extern ExternalType;
+
+ [Custom]
+ typedef string CustomType;
+ "#,
+ |types| {
+ assert!(
+ matches!(types.get_type_definition("ExternalType").unwrap(), Type::External { name, crate_name }
+ if name == "ExternalType" && crate_name == "crate-name")
+ );
+ assert!(
+ matches!(types.get_type_definition("CustomType").unwrap(), Type::Custom { name, builtin }
+ if name == "CustomType" && builtin == Box::new(Type::String))
+ );
+ },
+ );
+ }
+
+ fn get_err(udl: &str) -> String {
+ let parsed = weedle::parse(udl).unwrap();
+ let mut types = TypeUniverse::default();
+ let err = types
+ .add_type_definitions_from(parsed.as_ref())
+ .unwrap_err();
+ err.to_string()
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_typedef_error_on_no_attr() {
+ // Sorry, still working out what we want for non-imported typedefs..
+ get_err("typedef string Custom;");
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/types/mod.rs b/third_party/rust/uniffi_bindgen/src/interface/types/mod.rs
new file mode 100644
index 0000000000..c31c9b3ebd
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/types/mod.rs
@@ -0,0 +1,342 @@
+/* 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/. */
+
+//! # Basic typesystem for defining a component interface.
+//!
+//! This module provides the "API-level" typesystem of a UniFFI Rust Component, that is,
+//! the types provided by the Rust implementation and consumed callers of the foreign language
+//! bindings. Think "objects" and "enums" and "records".
+//!
+//! The [`Type`] enum represents high-level types that would appear in the public API of
+//! a component, such as enums and records as well as primitives like ints and strings.
+//! The Rust code that implements a component, and the foreign language bindings that consume it,
+//! will both typically deal with such types as their core concern.
+//!
+//! 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.
+//!
+//! As a developer working on UniFFI itself, you're likely to spend a fair bit of time thinking
+//! about how these API-level types map into the lower-level types of the FFI layer as represented
+//! by the [`ffi::FfiType`](super::ffi::FfiType) enum, but that's a detail that is invisible to end users.
+
+use std::{collections::hash_map::Entry, collections::BTreeSet, collections::HashMap, iter};
+
+use anyhow::{bail, Result};
+use heck::ToUpperCamelCase;
+use uniffi_meta::Checksum;
+
+use super::ffi::FfiType;
+
+mod finder;
+pub(super) use finder::TypeFinder;
+mod resolver;
+pub(super) use resolver::{resolve_builtin_type, TypeResolver};
+
+/// Represents all the different high-level types that can be used in a component interface.
+/// At this level we identify user-defined types by name, without knowing any details
+/// of their internal structure apart from what type of thing they are (record, enum, etc).
+#[derive(Debug, Clone, Eq, PartialEq, Checksum, Ord, PartialOrd)]
+pub enum Type {
+ // Primitive types.
+ UInt8,
+ Int8,
+ UInt16,
+ Int16,
+ UInt32,
+ Int32,
+ UInt64,
+ Int64,
+ Float32,
+ Float64,
+ Boolean,
+ String,
+ Timestamp,
+ Duration,
+ // Types defined in the component API, each of which has a string name.
+ Object(String),
+ Record(String),
+ Enum(String),
+ Error(String),
+ CallbackInterface(String),
+ // Structurally recursive types.
+ Optional(Box<Type>),
+ Sequence(Box<Type>),
+ Map(Box<Type>, Box<Type>),
+ // An FfiConverter we `use` from an external crate
+ External { name: String, crate_name: String },
+ // Custom type on the scaffolding side
+ Custom { name: String, builtin: Box<Type> },
+ // An unresolved user-defined type inside a proc-macro exported function
+ // signature. Must be replaced by another type before bindings generation.
+ Unresolved { name: String },
+}
+
+impl Type {
+ /// Get the canonical, unique-within-this-component name for a type.
+ ///
+ /// When generating helper code for foreign language bindings, it's sometimes useful to be
+ /// able to name a particular type in order to e.g. call a helper function that is specific
+ /// to that type. We support this by defining a naming convention where each type gets a
+ /// unique canonical name, constructed recursively from the names of its component types (if any).
+ pub fn canonical_name(&self) -> String {
+ match self {
+ // Builtin primitive types, with plain old names.
+ Type::Int8 => "i8".into(),
+ Type::UInt8 => "u8".into(),
+ Type::Int16 => "i16".into(),
+ Type::UInt16 => "u16".into(),
+ Type::Int32 => "i32".into(),
+ Type::UInt32 => "u32".into(),
+ Type::Int64 => "i64".into(),
+ Type::UInt64 => "u64".into(),
+ Type::Float32 => "f32".into(),
+ Type::Float64 => "f64".into(),
+ Type::String => "string".into(),
+ Type::Boolean => "bool".into(),
+ // API defined types.
+ // Note that these all get unique names, and the parser ensures that the names do not
+ // conflict with a builtin type. We add a prefix to the name to guard against pathological
+ // cases like a record named `SequenceRecord` interfering with `sequence<Record>`.
+ // However, types that support importing all end up with the same prefix of "Type", so
+ // that the import handling code knows how to find the remote reference.
+ Type::Object(nm) => format!("Type{nm}"),
+ Type::Error(nm) => format!("Type{nm}"),
+ Type::Enum(nm) => format!("Type{nm}"),
+ Type::Record(nm) => format!("Type{nm}"),
+ Type::CallbackInterface(nm) => format!("CallbackInterface{nm}"),
+ Type::Timestamp => "Timestamp".into(),
+ Type::Duration => "Duration".into(),
+ // Recursive types.
+ // These add a prefix to the name of the underlying type.
+ // The component API definition cannot give names to recursive types, so as long as the
+ // prefixes we add here are all unique amongst themselves, then we have no chance of
+ // acccidentally generating name collisions.
+ Type::Optional(t) => format!("Optional{}", t.canonical_name()),
+ Type::Sequence(t) => format!("Sequence{}", t.canonical_name()),
+ Type::Map(k, v) => format!(
+ "Map{}{}",
+ k.canonical_name().to_upper_camel_case(),
+ v.canonical_name().to_upper_camel_case()
+ ),
+ // A type that exists externally.
+ Type::External { name, .. } | Type::Custom { name, .. } => format!("Type{name}"),
+ Type::Unresolved { name } => {
+ unreachable!("Type `{name}` must be resolved before calling canonical_name")
+ }
+ }
+ }
+
+ pub fn ffi_type(&self) -> FfiType {
+ self.into()
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ let nested_types = match self {
+ Type::Optional(t) | Type::Sequence(t) => t.iter_types(),
+ Type::Map(k, v) => Box::new(k.iter_types().chain(v.iter_types())),
+ _ => Box::new(iter::empty()),
+ };
+ Box::new(std::iter::once(self).chain(nested_types))
+ }
+}
+
+/// 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),
+ // 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,
+ // Other types are serialized into a bytebuffer and deserialized on the other side.
+ Type::Enum(_)
+ | Type::Error(_)
+ | Type::Record(_)
+ | Type::Optional(_)
+ | Type::Sequence(_)
+ | Type::Map(_, _)
+ | Type::Timestamp
+ | Type::Duration => FfiType::RustBuffer(None),
+ Type::External { name, .. } => FfiType::RustBuffer(Some(name.clone())),
+ Type::Custom { builtin, .. } => FfiType::from(builtin.as_ref()),
+ Type::Unresolved { name } => {
+ unreachable!("Type `{name}` must be resolved before lowering to FfiType")
+ }
+ }
+ }
+}
+
+// Needed for rust scaffolding askama template
+impl From<&&Type> for FfiType {
+ fn from(ty: &&Type) -> Self {
+ (*ty).into()
+ }
+}
+
+/// 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 {
+ // 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 {
+ /// Add the definitions of all named [Type]s from a given WebIDL definition.
+ ///
+ /// This will fail if you try to add a name for which an existing type definition exists.
+ pub(super) fn add_type_definitions_from<T: TypeFinder>(&mut self, defn: T) -> Result<()> {
+ defn.add_type_definitions_to(self)
+ }
+
+ /// Add the definition of a named [Type].
+ ///
+ /// This will fail if you try to add a name for which an existing type definition exists.
+ pub fn add_type_definition(&mut self, name: &str, type_: Type) -> Result<()> {
+ if let Type::Unresolved { name: name_ } = &type_ {
+ assert_eq!(name, name_);
+ bail!("attempted to add type definition of Unresolved for `{name}`");
+ }
+
+ if resolve_builtin_type(name).is_some() {
+ bail!(
+ "please don't shadow builtin types ({name}, {})",
+ type_.canonical_name(),
+ );
+ }
+ self.add_known_type(&type_)?;
+ match self.type_definitions.entry(name.to_string()) {
+ Entry::Occupied(o) => {
+ let existing_def = o.get();
+ if type_ == *existing_def
+ && matches!(type_, Type::Record(_) | Type::Enum(_) | Type::Error(_))
+ {
+ // UDL and proc-macro metadata are allowed to define the same record, enum and
+ // error types, if the definitions match (fields and variants are checked in
+ // add_{record,enum,error}_definition)
+ Ok(())
+ } else {
+ bail!(
+ "Conflicting type definition for `{name}`! \
+ existing definition: {existing_def:?}, \
+ new definition: {type_:?}"
+ );
+ }
+ }
+ Entry::Vacant(e) => {
+ e.insert(type_);
+ 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()
+ }
+
+ /// Get the [Type] corresponding to a given WebIDL type node.
+ ///
+ /// If the node is a structural type (e.g. a sequence) then this will also add
+ /// it to the set of all types seen in the component interface.
+ pub(crate) fn resolve_type_expression<T: TypeResolver>(&mut self, expr: T) -> Result<Type> {
+ expr.resolve_type_expression(self)
+ }
+
+ /// Add a [Type] to the set of all types seen in the component interface.
+ pub fn add_known_type(&mut self, type_: &Type) -> Result<()> {
+ // Adding potentially-unresolved types is a footgun, make sure we don't do that.
+ if matches!(type_, Type::Unresolved { .. }) {
+ bail!("Unresolved types must be resolved before being added to known types");
+ }
+
+ // 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());
+
+ // Add inner types. For UDL, this is actually pointless extra work (as is calling
+ // add_known_type from add_function_definition), but for the proc-macro frontend
+ // this is important if the inner type isn't ever mentioned outside one of these
+ // generic builtin types.
+ match type_ {
+ Type::Optional(t) => self.add_known_type(t)?,
+ Type::Sequence(t) => self.add_known_type(t)?,
+ Type::Map(k, v) => {
+ self.add_known_type(k)?;
+ self.add_known_type(v)?;
+ }
+ _ => {}
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Iterator over all the known types in this universe.
+ pub fn iter_known_types(&self) -> impl Iterator<Item = &Type> {
+ self.all_known_types.iter()
+ }
+}
+
+/// An abstract type for an iterator over &Type references.
+///
+/// Ideally we would not need to name this type explicitly, and could just
+/// use an `impl Iterator<Item = &Type>` on any method that yields types.
+pub type TypeIterator<'a> = Box<dyn Iterator<Item = &'a Type> + 'a>;
+
+#[cfg(test)]
+mod test_type {
+ use super::*;
+
+ #[test]
+ fn test_canonical_names() {
+ // Non-exhaustive, but gives a bit of a flavour of what we want.
+ assert_eq!(Type::UInt8.canonical_name(), "u8");
+ assert_eq!(Type::String.canonical_name(), "string");
+ assert_eq!(
+ Type::Optional(Box::new(Type::Sequence(Box::new(Type::Object(
+ "Example".into()
+ )))))
+ .canonical_name(),
+ "OptionalSequenceTypeExample"
+ );
+ }
+}
+
+#[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.
+}
diff --git a/third_party/rust/uniffi_bindgen/src/interface/types/resolver.rs b/third_party/rust/uniffi_bindgen/src/interface/types/resolver.rs
new file mode 100644
index 0000000000..35ae640254
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/types/resolver.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/. */
+
+//! # Helpers for resolving UDL type expressions into concrete types.
+//!
+//! This module provides the [`TypeResolver`] trait, an abstraction for walking
+//! the parse tree of a weedle type expression and using a [`TypeUniverse`] to
+//! convert it into a concrete type definition (so it assumes that you're already
+//! used a [`TypeFinder`](super::TypeFinder) to populate the universe).
+//!
+//! Perhaps most importantly, it knows how to error out if the UDL tries to reference
+//! an undefined or invalid type.
+
+use anyhow::{bail, Result};
+
+use super::{Type, TypeUniverse};
+
+/// Trait to help resolving an UDL type node to a [`Type`].
+///
+/// This trait does structural matching against type-related weedle AST nodes from
+/// a parsed UDL file, turning them into a corresponding [`Type`] struct. It uses the
+/// known type definitions in a [`TypeUniverse`] to resolve names to types.
+///
+/// As a side-effect, resolving a type expression will grow the type universe with
+/// references to the types seen during traversal. For example resolving the type
+/// expression `sequence<<TestRecord>?` will:
+///
+/// * add `Optional<Sequence<TestRecord>` and `Sequence<TestRecord>` to the
+/// known types in the universe.
+/// * error out if the type name `TestRecord` is not already known.
+///
+pub(crate) trait TypeResolver {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type>;
+}
+
+impl TypeResolver for &weedle::types::Type<'_> {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type> {
+ (*self).resolve_type_expression(types)
+ }
+}
+
+impl TypeResolver for weedle::types::Type<'_> {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type> {
+ match self {
+ weedle::types::Type::Single(t) => match t {
+ weedle::types::SingleType::Any(_) => bail!("no support for `any` types"),
+ weedle::types::SingleType::NonAny(t) => t.resolve_type_expression(types),
+ },
+ weedle::types::Type::Union(_) => bail!("no support for union types yet"),
+ }
+ }
+}
+
+impl TypeResolver for weedle::types::NonAnyType<'_> {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type> {
+ match self {
+ weedle::types::NonAnyType::Boolean(t) => t.resolve_type_expression(types),
+ weedle::types::NonAnyType::Identifier(t) => t.resolve_type_expression(types),
+ weedle::types::NonAnyType::Integer(t) => t.resolve_type_expression(types),
+ weedle::types::NonAnyType::FloatingPoint(t) => t.resolve_type_expression(types),
+ weedle::types::NonAnyType::Sequence(t) => t.resolve_type_expression(types),
+ weedle::types::NonAnyType::RecordType(t) => t.resolve_type_expression(types),
+ _ => bail!("no support for type {:?}", self),
+ }
+ }
+}
+
+impl TypeResolver for &weedle::types::AttributedNonAnyType<'_> {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type> {
+ if self.attributes.is_some() {
+ bail!("type attributes are not supported yet");
+ }
+ self.type_.resolve_type_expression(types)
+ }
+}
+
+impl TypeResolver for &weedle::types::AttributedType<'_> {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type> {
+ if self.attributes.is_some() {
+ bail!("type attributes are not supported yet");
+ }
+ self.type_.resolve_type_expression(types)
+ }
+}
+
+impl<T: TypeResolver> TypeResolver for weedle::types::MayBeNull<T> {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type> {
+ let type_ = self.type_.resolve_type_expression(types)?;
+ match self.q_mark {
+ None => Ok(type_),
+ Some(_) => {
+ let ty = Type::Optional(Box::new(type_));
+ types.add_known_type(&ty)?;
+ Ok(ty)
+ }
+ }
+ }
+}
+
+impl TypeResolver for weedle::types::IntegerType {
+ fn resolve_type_expression(&self, _types: &mut TypeUniverse) -> Result<Type> {
+ bail!(
+ "WebIDL integer types not implemented ({:?}); consider using u8, u16, u32 or u64",
+ self
+ )
+ }
+}
+
+impl TypeResolver for weedle::types::FloatingPointType {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type> {
+ match self {
+ weedle::types::FloatingPointType::Float(t) => t.resolve_type_expression(types),
+ weedle::types::FloatingPointType::Double(t) => t.resolve_type_expression(types),
+ }
+ }
+}
+
+impl TypeResolver for weedle::types::SequenceType<'_> {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type> {
+ let t = self.generics.body.as_ref().resolve_type_expression(types)?;
+ let ty = Type::Sequence(Box::new(t));
+ types.add_known_type(&ty)?;
+ Ok(ty)
+ }
+}
+
+impl TypeResolver for weedle::types::RecordKeyType<'_> {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type> {
+ use weedle::types::RecordKeyType::*;
+ match self {
+ Byte(_) | USV(_) => bail!(
+ "WebIDL Byte or USV string type not implemented ({self:?}); \
+ consider using DOMString or string",
+ ),
+ DOM(_) => {
+ types.add_known_type(&Type::String)?;
+ Ok(Type::String)
+ }
+ NonAny(t) => t.resolve_type_expression(types),
+ }
+ }
+}
+
+impl TypeResolver for weedle::types::RecordType<'_> {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type> {
+ let key_type = self.generics.body.0.resolve_type_expression(types)?;
+ let value_type = self.generics.body.2.resolve_type_expression(types)?;
+ let map = Type::Map(Box::new(key_type), Box::new(value_type));
+ types.add_known_type(&map)?;
+ Ok(map)
+ }
+}
+
+impl TypeResolver for weedle::common::Identifier<'_> {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type> {
+ match resolve_builtin_type(self.0) {
+ Some(type_) => {
+ types.add_known_type(&type_)?;
+ Ok(type_)
+ }
+ None => match types.get_type_definition(self.0) {
+ Some(type_) => {
+ types.add_known_type(&type_)?;
+ Ok(type_)
+ }
+ None => bail!("unknown type reference: {}", self.0),
+ },
+ }
+ }
+}
+
+impl TypeResolver for weedle::term::Boolean {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type> {
+ types.add_known_type(&Type::Boolean)?;
+ Ok(Type::Boolean)
+ }
+}
+
+impl TypeResolver for weedle::types::FloatType {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type> {
+ if self.unrestricted.is_some() {
+ bail!("we don't support `unrestricted float`");
+ }
+ types.add_known_type(&Type::Float32)?;
+ Ok(Type::Float32)
+ }
+}
+
+impl TypeResolver for weedle::types::DoubleType {
+ fn resolve_type_expression(&self, types: &mut TypeUniverse) -> Result<Type> {
+ if self.unrestricted.is_some() {
+ bail!("we don't support `unrestricted double`");
+ }
+ types.add_known_type(&Type::Float64)?;
+ Ok(Type::Float64)
+ }
+}
+
+/// Resolve built-in API types by name.
+///
+/// Given an identifier from the UDL, this will return `Some(Type)` if it names one of the
+/// built-in primitive types or `None` if it names something else.
+pub(in super::super) fn resolve_builtin_type(name: &str) -> Option<Type> {
+ match name {
+ "string" => Some(Type::String),
+ "u8" => Some(Type::UInt8),
+ "i8" => Some(Type::Int8),
+ "u16" => Some(Type::UInt16),
+ "i16" => Some(Type::Int16),
+ "u32" => Some(Type::UInt32),
+ "i32" => Some(Type::Int32),
+ "u64" => Some(Type::UInt64),
+ "i64" => Some(Type::Int64),
+ "f32" => Some(Type::Float32),
+ "f64" => Some(Type::Float64),
+ "timestamp" => Some(Type::Timestamp),
+ "duration" => Some(Type::Duration),
+ _ => None,
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use weedle::Parse;
+
+ #[test]
+ fn test_named_type_resolution() -> Result<()> {
+ let mut types = TypeUniverse::default();
+ types.add_type_definition("TestRecord", Type::Record("TestRecord".into()))?;
+ assert_eq!(types.iter_known_types().count(), 1);
+
+ let (_, expr) = weedle::types::Type::parse("TestRecord").unwrap();
+ let t = types.resolve_type_expression(expr).unwrap();
+ assert!(matches!(t, Type::Record(nm) if nm == "TestRecord"));
+ assert_eq!(types.iter_known_types().count(), 1);
+
+ let (_, expr) = weedle::types::Type::parse("TestRecord?").unwrap();
+ let t = types.resolve_type_expression(expr).unwrap();
+ assert!(matches!(t, Type::Optional(_)));
+ // Matching the Box<T> is hard, use names as a convenient workaround.
+ assert_eq!(t.canonical_name(), "OptionalTypeTestRecord");
+ assert_eq!(types.iter_known_types().count(), 2);
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_resolving_optional_type_adds_inner_type() {
+ let mut types = TypeUniverse::default();
+ assert_eq!(types.iter_known_types().count(), 0);
+ let (_, expr) = weedle::types::Type::parse("u32?").unwrap();
+ let t = types.resolve_type_expression(expr).unwrap();
+ assert_eq!(t.canonical_name(), "Optionalu32");
+ assert_eq!(types.iter_known_types().count(), 2);
+ assert!(types
+ .iter_known_types()
+ .any(|t| t.canonical_name() == "u32"));
+ assert!(types
+ .iter_known_types()
+ .any(|t| t.canonical_name() == "Optionalu32"));
+ }
+
+ #[test]
+ fn test_resolving_sequence_type_adds_inner_type() {
+ let mut types = TypeUniverse::default();
+ assert_eq!(types.iter_known_types().count(), 0);
+ let (_, expr) = weedle::types::Type::parse("sequence<string>").unwrap();
+ let t = types.resolve_type_expression(expr).unwrap();
+ assert_eq!(t.canonical_name(), "Sequencestring");
+ assert_eq!(types.iter_known_types().count(), 2);
+ assert!(types
+ .iter_known_types()
+ .any(|t| t.canonical_name() == "Sequencestring"));
+ assert!(types
+ .iter_known_types()
+ .any(|t| t.canonical_name() == "string"));
+ }
+
+ #[test]
+ fn test_resolving_map_type_adds_string_and_inner_type() {
+ let mut types = TypeUniverse::default();
+ assert_eq!(types.iter_known_types().count(), 0);
+ let (_, expr) = weedle::types::Type::parse("record<DOMString, float>").unwrap();
+ let t = types.resolve_type_expression(expr).unwrap();
+ assert_eq!(t.canonical_name(), "MapStringF32");
+ assert_eq!(types.iter_known_types().count(), 3);
+ assert!(types
+ .iter_known_types()
+ .any(|t| t.canonical_name() == "MapStringF32"));
+ assert!(types
+ .iter_known_types()
+ .any(|t| t.canonical_name() == "string"));
+ assert!(types
+ .iter_known_types()
+ .any(|t| t.canonical_name() == "f32"));
+ }
+
+ #[test]
+ fn test_resolving_map_type_adds_key_type_and_inner_type() {
+ let mut types = TypeUniverse::default();
+ assert_eq!(types.iter_known_types().count(), 0);
+ let (_, expr) = weedle::types::Type::parse("record<u64, float>").unwrap();
+ let t = types.resolve_type_expression(expr).unwrap();
+ assert_eq!(t.canonical_name(), "MapU64F32");
+ assert_eq!(types.iter_known_types().count(), 3);
+ assert!(types
+ .iter_known_types()
+ .any(|t| t.canonical_name() == "MapU64F32"));
+ assert!(types
+ .iter_known_types()
+ .any(|t| t.canonical_name() == "u64"));
+ assert!(types
+ .iter_known_types()
+ .any(|t| t.canonical_name() == "f32"));
+ }
+
+ #[test]
+ fn test_error_on_unknown_type() -> Result<()> {
+ let mut types = TypeUniverse::default();
+ types.add_type_definition("TestRecord", Type::Record("TestRecord".into()))?;
+ // Oh no, someone made a typo in the type-o...
+ let (_, expr) = weedle::types::Type::parse("TestRecrd").unwrap();
+ let err = types.resolve_type_expression(expr).unwrap_err();
+ assert_eq!(err.to_string(), "unknown type reference: TestRecrd");
+ Ok(())
+ }
+
+ #[test]
+ fn test_error_on_union_type() -> Result<()> {
+ let mut types = TypeUniverse::default();
+ types.add_type_definition("TestRecord", Type::Record("TestRecord".into()))?;
+ let (_, expr) = weedle::types::Type::parse("(TestRecord or u32)").unwrap();
+ let err = types.resolve_type_expression(expr).unwrap_err();
+ assert_eq!(err.to_string(), "no support for union types yet");
+ Ok(())
+ }
+
+ #[test]
+ fn test_type_set_is_well_ordered() -> Result<()> {
+ // The set (universe) of types should have a well-defined order. When
+ // the data structure does not guarantee the order of its elements, such as
+ // HashSet, then the resulting generated source code is likely not
+ // deterministic, and the compiled binary file may not be reproducible. We
+ // avoid this issue by using an implementation that defines the order of its
+ // elements. This test verifies that the elements are sorted as expected.
+ let mut types = TypeUniverse::default();
+ types.add_type_definition("TestRecord", Type::Record("TestRecord".into()))?;
+ assert_eq!(types.iter_known_types().count(), 1);
+ types.add_type_definition("TestRecord2", Type::Record("TestRecord2".into()))?;
+ assert_eq!(types.iter_known_types().count(), 2);
+ types.add_type_definition("TestInt64", Type::Int64)?;
+ types.add_type_definition("TestInt8", Type::Int8)?;
+ types.add_type_definition("TestUInt8", Type::UInt8)?;
+ types.add_type_definition("TestBoolean", Type::Boolean)?;
+ assert_eq!(types.iter_known_types().count(), 6);
+ let mut iter = types.iter_known_types();
+ assert_eq!(Some(&Type::UInt8), iter.next());
+ assert_eq!(Some(&Type::Int8), iter.next());
+ assert_eq!(Some(&Type::Int64), iter.next());
+ assert_eq!(Some(&Type::Boolean), iter.next());
+ assert_eq!(Some(&Type::Record("TestRecord".into())), iter.next());
+ assert_eq!(Some(&Type::Record("TestRecord2".into())), iter.next());
+ Ok(())
+ }
+}