From d8bbc7858622b6d9c278469aab701ca0b609cddf Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:35:49 +0200 Subject: Merging upstream version 126.0. Signed-off-by: Daniel Baumann --- third_party/rust/uniffi_udl/.cargo-checksum.json | 2 +- third_party/rust/uniffi_udl/Cargo.toml | 12 +- third_party/rust/uniffi_udl/README.md | 81 +++++++++ third_party/rust/uniffi_udl/src/attributes.rs | 199 +++++++++++++++++++-- third_party/rust/uniffi_udl/src/collectors.rs | 18 +- .../rust/uniffi_udl/src/converters/callables.rs | 16 +- .../rust/uniffi_udl/src/converters/enum_.rs | 79 ++------ .../rust/uniffi_udl/src/converters/interface.rs | 6 +- third_party/rust/uniffi_udl/src/converters/mod.rs | 10 ++ third_party/rust/uniffi_udl/src/finder.rs | 60 +++++-- third_party/rust/uniffi_udl/src/lib.rs | 2 +- third_party/rust/uniffi_udl/src/literal.rs | 8 +- third_party/rust/uniffi_udl/src/resolver.rs | 1 - 13 files changed, 372 insertions(+), 122 deletions(-) create mode 100644 third_party/rust/uniffi_udl/README.md (limited to 'third_party/rust/uniffi_udl') diff --git a/third_party/rust/uniffi_udl/.cargo-checksum.json b/third_party/rust/uniffi_udl/.cargo-checksum.json index 233a1e7795..88ed1cb4b7 100644 --- a/third_party/rust/uniffi_udl/.cargo-checksum.json +++ b/third_party/rust/uniffi_udl/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"8bb1bcb6089114c92a0d06f884e64769edc493bd26d271eb659d6592d8d2f5fa","src/attributes.rs":"faf1c53c17ff511f587113afdb37de4890087150f32e19c9af4d878daebe7c89","src/collectors.rs":"3d80f169ccb7a64c434a6890948e49a64947062c29487b5bea58721189cb2786","src/converters/callables.rs":"c41d15cf816f5838e74f0c1d7a5806c4f1c1b2d925ca11b2b6658a978b2373fb","src/converters/enum_.rs":"1742441b812949b83ec28c2c2c4684c34fd618195cff47af2934c7ee9c895c59","src/converters/interface.rs":"a3deb1915eb4be80d21116f24bec7163736bdffbad4eb2236fb531386a97c4f8","src/converters/mod.rs":"4035ec70c56917fa7c61cced5f8a8398c99028e13959662acc1f7a48aa71ae3b","src/finder.rs":"c496aca0ac640d4ca5fd57b32131297544e5468c9e792f8b5da1644df6d22d2e","src/lib.rs":"11a5d3c288f5786164471b0870b5ca9190305757461c6a6d20777a96622457f1","src/literal.rs":"30e50d7c1d3f061bb6aaa7ad3a6eb31d75a6ea21159dfe454f7ab9ae4bdb580e","src/resolver.rs":"96212b52c4f4f7637713d0a39f5c4972e6480e4ce98070dcb8647d67c9b8d5e2"},"package":"889edb7109c6078abe0e53e9b4070cf74a6b3468d141bdf5ef1bd4d1dc24a1c3"} \ No newline at end of file +{"files":{"Cargo.toml":"e4553df91528daadbc52244f436c3ae17d9c6a4629d232643a20ed63c1311625","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","src/attributes.rs":"f1cdee01db837920dbd109d60b39dd4b0ece56f83797fe4ace1e0caf97c02483","src/collectors.rs":"00c9cd8e8f7be6949996f069b449f120f0b0694ff2f1ca92b79201721c18c594","src/converters/callables.rs":"1ad26c2782629e98272edc75c38343f58194ebf9626ae451ba84546a60d45a48","src/converters/enum_.rs":"aa0ca7a7a50521a45e296c6f53be5f1981a41872443946f72c2ca8baebe4d69b","src/converters/interface.rs":"6042d4abd5e236ed6158c5f6d4d9b81bb01fbbcb8b42bf8a5b385b978c68f6f8","src/converters/mod.rs":"c34a30e3b7a2e3c092a7074f4bab6f6c34364177c096af59a7dda13e44ffdc3c","src/finder.rs":"3586ffd3772151eabbc3d6062725933c88978e1b5feb64312bbf42cd43af30fa","src/lib.rs":"56c50ce61ba5e7266fe7fc9fa9d0022cdbfbe9801730753bd4ee66fed040221c","src/literal.rs":"4f28ad49a17246b4dc30a677cfff65b345bfac0924856e19f58e7574a74c2c40","src/resolver.rs":"c4ff362055dc4ed489e94a063ec0fd3becb8787ad35ce65cdec437a1aae518a0"},"package":"8c43c9ed40a8d20a5c3eae2d23031092db6b96dc8e571beb449ba9757484cea0"} \ No newline at end of file diff --git a/third_party/rust/uniffi_udl/Cargo.toml b/third_party/rust/uniffi_udl/Cargo.toml index 346cd71c27..fd03f3434a 100644 --- a/third_party/rust/uniffi_udl/Cargo.toml +++ b/third_party/rust/uniffi_udl/Cargo.toml @@ -12,10 +12,11 @@ [package] edition = "2021" name = "uniffi_udl" -version = "0.25.3" +version = "0.27.1" description = "udl parsing for the uniffi project" homepage = "https://mozilla.github.io/uniffi-rs" documentation = "https://mozilla.github.io/uniffi-rs" +readme = "README.md" keywords = [ "ffi", "bindgen", @@ -26,11 +27,14 @@ repository = "https://github.com/mozilla/uniffi-rs" [dependencies.anyhow] version = "1" +[dependencies.textwrap] +version = "0.16" + [dependencies.uniffi_meta] -version = "=0.25.3" +version = "=0.27.1" [dependencies.uniffi_testing] -version = "=0.25.3" +version = "=0.27.1" [dependencies.weedle2] -version = "4.0.0" +version = "5.0.0" diff --git a/third_party/rust/uniffi_udl/README.md b/third_party/rust/uniffi_udl/README.md new file mode 100644 index 0000000000..64ac3486a3 --- /dev/null +++ b/third_party/rust/uniffi_udl/README.md @@ -0,0 +1,81 @@ +# UniFFI - a multi-language bindings generator for Rust + +UniFFI is a toolkit for building cross-platform software components in Rust. + +For the impatient, see [**the UniFFI user guide**](https://mozilla.github.io/uniffi-rs/) +or [**the UniFFI examples**](https://github.com/mozilla/uniffi-rs/tree/main/examples#example-uniffi-components). + +By writing your core business logic in Rust and describing its interface in an "object model", +you can use UniFFI to help you: + +* Compile your Rust code into a shared library for use on different target platforms. +* Generate bindings to load and use the library from different target languages. + +You can describe your object model in an [interface definition file](https://mozilla.github.io/uniffi-rs/udl_file_spec.html) +or [by using proc-macros](https://mozilla.github.io/uniffi-rs/proc_macro/index.html). + +UniFFI is currently used extensively by Mozilla in Firefox mobile and desktop browsers; +written once in Rust, auto-generated bindings allow that functionality to be called +from both Kotlin (for Android apps) and Swift (for iOS apps). +It also has a growing community of users shipping various cool things to many users. + +UniFFI comes with support for **Kotlin**, **Swift**, **Python** and **Ruby** with 3rd party bindings available for **C#** and **Golang**. +Additional foreign language bindings can be developed externally and we welcome contributions to list them here. +See [Third-party foreign language bindings](#third-party-foreign-language-bindings). + +## User Guide + +You can read more about using the tool in [**the UniFFI user guide**](https://mozilla.github.io/uniffi-rs/). + +We consider it ready for production use, but UniFFI is a long way from a 1.0 release with lots of internal work still going on. +We try hard to avoid breaking simple consumers, but more advanced things might break as you upgrade over time. + +### Etymology and Pronunciation + +ˈjuːnɪfaɪ. Pronounced to rhyme with "unify". + +A portmanteau word that also puns with "unify", to signify the joining of one codebase accessed from many languages. + +uni - [Latin ūni-, from ūnus, one] +FFI - [Abbreviation, Foreign Function Interface] + +## Alternative tools + +Other tools we know of which try and solve a similarly shaped problem are: + +* [Diplomat](https://github.com/rust-diplomat/diplomat/) - see our [writeup of + the different approach taken by that tool](docs/diplomat-and-macros.md) +* [Interoptopus](https://github.com/ralfbiedert/interoptopus/) + +(Please open a PR if you think other tools should be listed!) + +## Third-party foreign language bindings + +* [Kotlin Multiplatform support](https://gitlab.com/trixnity/uniffi-kotlin-multiplatform-bindings). The repository contains Kotlin Multiplatform bindings generation for UniFFI, letting you target both JVM and Native. +* [Go bindings](https://github.com/NordSecurity/uniffi-bindgen-go) +* [C# bindings](https://github.com/NordSecurity/uniffi-bindgen-cs) +* [Dart bindings](https://github.com/NiallBunting/uniffi-rs-dart) + +### External resources + +There are a few third-party resources that make it easier to work with UniFFI: + +* [Plugin support for `.udl` files](https://github.com/Lonami/uniffi-dl) for the IDEA platform ([*uniffi-dl* in the JetBrains marketplace](https://plugins.jetbrains.com/plugin/20527-uniffi-dl)). It provides syntax highlighting, code folding, code completion, reference resolution and navigation (among others features) for the [UniFFI Definition Language (UDL)](https://mozilla.github.io/uniffi-rs/). +* [cargo swift](https://github.com/antoniusnaumann/cargo-swift), a cargo plugin to build a Swift Package from Rust code. It provides an init command for setting up a UniFFI crate and a package command for building a Swift package from Rust code - without the need for additional configuration or build scripts. +* [Cargo NDK Gradle Plugin](https://github.com/willir/cargo-ndk-android-gradle) allows you to build Rust code using [`cargo-ndk`](https://github.com/bbqsrc/cargo-ndk), which generally makes Android library builds less painful. +* [`uniffi-starter`](https://github.com/ianthetechie/uniffi-starter) is a minimal project demonstrates a wide range of UniFFI in a complete project in a compact manner. It includes a full Android library build process, an XCFramework generation script, and example Swift package structure. + +(Please open a PR if you think other resources should be listed!) + +## Contributing + +If this tool sounds interesting to you, please help us develop it! You can: + +* View the [contributor guidelines](./docs/contributing.md). +* File or work on [issues](https://github.com/mozilla/uniffi-rs/issues) here in GitHub. +* Join discussions in the [#uniffi:mozilla.org](https://matrix.to/#/#uniffi:mozilla.org) + room on Matrix. + +## Code of Conduct + +This project is governed by Mozilla's [Community Participation Guidelines](./CODE_OF_CONDUCT.md). diff --git a/third_party/rust/uniffi_udl/src/attributes.rs b/third_party/rust/uniffi_udl/src/attributes.rs index f06b4f29c1..7bb05808c4 100644 --- a/third_party/rust/uniffi_udl/src/attributes.rs +++ b/third_party/rust/uniffi_udl/src/attributes.rs @@ -37,10 +37,29 @@ pub(super) enum Attribute { kind: ExternalKind, export: bool, }, + Rust { + kind: RustKind, + }, // Custom type on the scaffolding side Custom, // The interface described is implemented as a trait. Trait, + // Modifies `Trait` to enable foreign implementations (callback interfaces) + WithForeign, + Async, + NonExhaustive, +} + +// A type defined in Rust via procmacros but which should be available +// in UDL. +#[derive(Debug, Copy, Clone, Checksum)] +pub(super) enum RustKind { + Object, + CallbackTrait, + Trait, + Record, + Enum, + CallbackInterface, } impl Attribute { @@ -67,6 +86,9 @@ impl TryFrom<&weedle::attribute::ExtendedAttribute<'_>> for Attribute { "Error" => Ok(Attribute::Error), "Custom" => Ok(Attribute::Custom), "Trait" => Ok(Attribute::Trait), + "WithForeign" => Ok(Attribute::WithForeign), + "Async" => Ok(Attribute::Async), + "NonExhaustive" => Ok(Attribute::NonExhaustive), _ => anyhow::bail!("ExtendedAttributeNoArgs not supported: {:?}", (attr.0).0), }, // Matches assignment-style attributes like ["Throws=Error"] @@ -95,6 +117,19 @@ impl TryFrom<&weedle::attribute::ExtendedAttribute<'_>> for Attribute { kind: ExternalKind::Interface, export: true, }), + "ExternalTrait" => Ok(Attribute::External { + crate_name: name_from_id_or_string(&identity.rhs), + kind: ExternalKind::Trait, + export: false, + }), + "ExternalTraitExport" => Ok(Attribute::External { + crate_name: name_from_id_or_string(&identity.rhs), + kind: ExternalKind::Trait, + export: true, + }), + "Rust" => Ok(Attribute::Rust { + kind: rust_kind_from_id_or_string(&identity.rhs)?, + }), _ => anyhow::bail!( "Attribute identity Identifier not supported: {:?}", identity.lhs_identifier.0 @@ -130,6 +165,26 @@ fn name_from_id_or_string(nm: &weedle::attribute::IdentifierOrString<'_>) -> Str } } +fn rust_kind_from_id_or_string(nm: &weedle::attribute::IdentifierOrString<'_>) -> Result { + Ok(match nm { + weedle::attribute::IdentifierOrString::String(str_lit) => match str_lit.0 { + // support names which match either procmacro or udl + "interface" => RustKind::Object, + "object" => RustKind::Object, + "record" => RustKind::Record, + "dictionary" => RustKind::Record, + "enum" => RustKind::Enum, + "trait" => RustKind::Trait, + "callback" => RustKind::CallbackInterface, + "trait_with_foreign" => RustKind::CallbackTrait, + _ => anyhow::bail!("Unknown `[Rust=]` kind {:?}", str_lit.0), + }, + weedle::attribute::IdentifierOrString::Identifier(_) => { + anyhow::bail!("Expected string attribute value, got identifier") + } + }) +} + /// Parse a weedle `ExtendedAttributeList` into a list of `Attribute`s, /// erroring out on duplicates. fn parse_attributes( @@ -161,7 +216,6 @@ where } /// 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); @@ -169,6 +223,12 @@ impl EnumAttributes { pub fn contains_error_attr(&self) -> bool { self.0.iter().any(|attr| attr.is_error()) } + + pub fn contains_non_exhaustive_attr(&self) -> bool { + self.0 + .iter() + .any(|attr| matches!(attr, Attribute::NonExhaustive)) + } } impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for EnumAttributes { @@ -178,6 +238,10 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for EnumAttributes { ) -> Result { let attrs = parse_attributes(weedle_attributes, |attr| match attr { Attribute::Error => Ok(()), + Attribute::NonExhaustive => Ok(()), + // Allow `[Enum]`, since we may be parsing an attribute list from an interface with the + // `[Enum]` attribute. + Attribute::Enum => Ok(()), _ => bail!(format!("{attr:?} not supported for enums")), })?; Ok(Self(attrs)) @@ -196,8 +260,9 @@ impl> TryFrom> for E /// Represents UDL attributes that might appear on a function. /// -/// This supports the `[Throws=ErrorName]` attribute for functions that -/// can produce an error. +/// This supports: +/// * `[Throws=ErrorName]` attribute for functions that can produce an error. +/// * `[Async] for async functions #[derive(Debug, Clone, Checksum, Default)] pub(super) struct FunctionAttributes(Vec); @@ -210,6 +275,10 @@ impl FunctionAttributes { _ => None, }) } + + pub(super) fn is_async(&self) -> bool { + self.0.iter().any(|attr| matches!(attr, Attribute::Async)) + } } impl FromIterator for FunctionAttributes { @@ -224,7 +293,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for FunctionAttribut weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>, ) -> Result { let attrs = parse_attributes(weedle_attributes, |attr| match attr { - Attribute::Throws(_) => Ok(()), + Attribute::Throws(_) | Attribute::Async => Ok(()), _ => bail!(format!("{attr:?} not supported for functions")), })?; Ok(Self(attrs)) @@ -294,12 +363,25 @@ impl InterfaceAttributes { self.0.iter().any(|attr| attr.is_error()) } - pub fn object_impl(&self) -> ObjectImpl { - if self.0.iter().any(|attr| matches!(attr, Attribute::Trait)) { - ObjectImpl::Trait - } else { - ObjectImpl::Struct - } + pub fn contains_trait(&self) -> bool { + self.0.iter().any(|attr| matches!(attr, Attribute::Trait)) + } + + pub fn contains_with_foreign(&self) -> bool { + self.0 + .iter() + .any(|attr| matches!(attr, Attribute::WithForeign)) + } + + pub fn object_impl(&self) -> Result { + Ok( + match (self.contains_trait(), self.contains_with_foreign()) { + (true, true) => ObjectImpl::CallbackTrait, + (true, false) => ObjectImpl::Trait, + (false, false) => ObjectImpl::Struct, + (false, true) => bail!("WithForeign can't be specified without Trait"), + }, + ) } pub fn get_traits(&self) -> Vec { self.0 @@ -321,6 +403,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for InterfaceAttribu Attribute::Enum => Ok(()), Attribute::Error => Ok(()), Attribute::Trait => Ok(()), + Attribute::WithForeign => Ok(()), Attribute::Traits(_) => Ok(()), _ => bail!(format!("{attr:?} not supported for interface definition")), })?; @@ -373,6 +456,10 @@ impl ConstructorAttributes { _ => None, }) } + + pub(super) fn is_async(&self) -> bool { + self.0.iter().any(|attr| matches!(attr, Attribute::Async)) + } } impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for ConstructorAttributes { @@ -383,6 +470,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for ConstructorAttri let attrs = parse_attributes(weedle_attributes, |attr| match attr { Attribute::Throws(_) => Ok(()), Attribute::Name(_) => Ok(()), + Attribute::Async => Ok(()), _ => bail!(format!("{attr:?} not supported for constructors")), })?; Ok(Self(attrs)) @@ -406,6 +494,10 @@ impl MethodAttributes { }) } + pub(super) fn is_async(&self) -> bool { + self.0.iter().any(|attr| matches!(attr, Attribute::Async)) + } + pub(super) fn get_self_by_arc(&self) -> bool { self.0 .iter() @@ -425,8 +517,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for MethodAttributes weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>, ) -> Result { let attrs = parse_attributes(weedle_attributes, |attr| match attr { - Attribute::SelfType(_) => Ok(()), - Attribute::Throws(_) => Ok(()), + Attribute::SelfType(_) | Attribute::Throws(_) | Attribute::Async => Ok(()), _ => bail!(format!("{attr:?} not supported for methods")), })?; Ok(Self(attrs)) @@ -498,10 +589,18 @@ impl TypedefAttributes { }) } + pub(super) fn rust_kind(&self) -> Option { + self.0.iter().find_map(|attr| match attr { + Attribute::Rust { kind, .. } => Some(*kind), + _ => None, + }) + } + pub(super) fn external_tagged(&self) -> Option { // If it was "exported" via a proc-macro the FfiConverter was not tagged. self.0.iter().find_map(|attr| match attr { Attribute::External { export, .. } => Some(!*export), + Attribute::Rust { .. } => Some(false), _ => None, }) } @@ -513,7 +612,7 @@ impl TryFrom<&weedle::attribute::ExtendedAttributeList<'_>> for TypedefAttribute weedle_attributes: &weedle::attribute::ExtendedAttributeList<'_>, ) -> Result { let attrs = parse_attributes(weedle_attributes, |attr| match attr { - Attribute::External { .. } | Attribute::Custom => Ok(()), + Attribute::External { .. } | Attribute::Custom | Attribute::Rust { .. } => Ok(()), _ => bail!(format!("{attr:?} not supported for typedefs")), })?; Ok(Self(attrs)) @@ -641,14 +740,22 @@ mod test { } #[test] - fn test_throws_attribute() { + fn test_function_attributes() { let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Throws=Error]").unwrap(); let attrs = FunctionAttributes::try_from(&node).unwrap(); assert!(matches!(attrs.get_throws_err(), Some("Error"))); + assert!(!attrs.is_async()); let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap(); let attrs = FunctionAttributes::try_from(&node).unwrap(); assert!(attrs.get_throws_err().is_none()); + assert!(!attrs.is_async()); + + let (_, node) = + weedle::attribute::ExtendedAttributeList::parse("[Throws=Error, Async]").unwrap(); + let attrs = FunctionAttributes::try_from(&node).unwrap(); + assert!(matches!(attrs.get_throws_err(), Some("Error"))); + assert!(attrs.is_async()); } #[test] @@ -673,22 +780,34 @@ mod test { let attrs = MethodAttributes::try_from(&node).unwrap(); assert!(!attrs.get_self_by_arc()); assert!(matches!(attrs.get_throws_err(), Some("Error"))); + assert!(!attrs.is_async()); 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()); + assert!(!attrs.is_async()); 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()); + assert!(!attrs.is_async()); + + let (_, node) = + weedle::attribute::ExtendedAttributeList::parse("[Self=ByArc, Throws=Error, Async]") + .unwrap(); + let attrs = MethodAttributes::try_from(&node).unwrap(); + assert!(attrs.get_self_by_arc()); + assert!(attrs.get_throws_err().is_some()); + assert!(attrs.is_async()); 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()); + assert!(!attrs.is_async()); } #[test] @@ -710,6 +829,11 @@ mod test { let attrs = ConstructorAttributes::try_from(&node).unwrap(); assert!(matches!(attrs.get_throws_err(), Some("Error"))); assert!(matches!(attrs.get_name(), Some("MyFactory"))); + assert!(!attrs.is_async()); + + let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Async]").unwrap(); + let attrs = ConstructorAttributes::try_from(&node).unwrap(); + assert!(attrs.is_async()); } #[test] @@ -754,15 +878,24 @@ mod test { fn test_trait_attribute() { let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait]").unwrap(); let attrs = InterfaceAttributes::try_from(&node).unwrap(); - assert_eq!(attrs.object_impl(), ObjectImpl::Trait); + assert_eq!(attrs.object_impl().unwrap(), ObjectImpl::Trait); + + let (_, node) = + weedle::attribute::ExtendedAttributeList::parse("[Trait, WithForeign]").unwrap(); + let attrs = InterfaceAttributes::try_from(&node).unwrap(); + assert_eq!(attrs.object_impl().unwrap(), ObjectImpl::CallbackTrait); let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[]").unwrap(); let attrs = InterfaceAttributes::try_from(&node).unwrap(); - assert_eq!(attrs.object_impl(), ObjectImpl::Struct); + assert_eq!(attrs.object_impl().unwrap(), ObjectImpl::Struct); + + let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[WithForeign]").unwrap(); + let attrs = InterfaceAttributes::try_from(&node).unwrap(); + assert!(attrs.object_impl().is_err()) } #[test] - fn test_enum_attribute() { + fn test_enum_attribute_on_interface() { let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum]").unwrap(); let attrs = InterfaceAttributes::try_from(&node).unwrap(); assert!(matches!(attrs.contains_enum_attr(), true)); @@ -783,6 +916,38 @@ mod test { ); } + // Test parsing attributes for enum definitions + #[test] + fn test_enum_attributes() { + let (_, node) = + weedle::attribute::ExtendedAttributeList::parse("[Error, NonExhaustive]").unwrap(); + let attrs = EnumAttributes::try_from(&node).unwrap(); + assert!(attrs.contains_error_attr()); + assert!(attrs.contains_non_exhaustive_attr()); + + let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait]").unwrap(); + let err = EnumAttributes::try_from(&node).unwrap_err(); + assert_eq!(err.to_string(), "Trait not supported for enums"); + } + + // Test parsing attributes for interface definitions with the `[Enum]` attribute + #[test] + fn test_enum_attributes_from_interface() { + let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum]").unwrap(); + assert!(EnumAttributes::try_from(&node).is_ok()); + + let (_, node) = + weedle::attribute::ExtendedAttributeList::parse("[Enum, Error, NonExhaustive]") + .unwrap(); + let attrs = EnumAttributes::try_from(&node).unwrap(); + assert!(attrs.contains_error_attr()); + assert!(attrs.contains_non_exhaustive_attr()); + + let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Enum, Trait]").unwrap(); + let err = EnumAttributes::try_from(&node).unwrap_err(); + assert_eq!(err.to_string(), "Trait not supported for enums"); + } + #[test] fn test_other_attributes_not_supported_for_interfaces() { let (_, node) = weedle::attribute::ExtendedAttributeList::parse("[Trait, ByRef]").unwrap(); diff --git a/third_party/rust/uniffi_udl/src/collectors.rs b/third_party/rust/uniffi_udl/src/collectors.rs index 6a91ab4a93..de5489f5f9 100644 --- a/third_party/rust/uniffi_udl/src/collectors.rs +++ b/third_party/rust/uniffi_udl/src/collectors.rs @@ -5,7 +5,7 @@ //! # Collects metadata from UDL. use crate::attributes; -use crate::converters::APIConverter; +use crate::converters::{convert_docstring, APIConverter}; use crate::finder; use crate::resolver::TypeResolver; use anyhow::{bail, Result}; @@ -138,6 +138,7 @@ impl From for uniffi_meta::MetadataGroup { crate_name: value.types.module_path(), name: value.types.namespace, }, + namespace_docstring: value.types.namespace_docstring.clone(), items: value.items, } } @@ -171,15 +172,13 @@ impl APIBuilder for weedle::Definition<'_> { match self { weedle::Definition::Namespace(d) => d.process(ci)?, weedle::Definition::Enum(d) => { + let mut e: uniffi_meta::EnumMetadata = d.convert(ci)?; // We check if the enum represents an error... let attrs = attributes::EnumAttributes::try_from(d.attributes.as_ref())?; if attrs.contains_error_attr() { - let e: uniffi_meta::ErrorMetadata = d.convert(ci)?; - ci.add_definition(e.into())?; - } else { - let e: uniffi_meta::EnumMetadata = d.convert(ci)?; - ci.add_definition(e.into())?; + e.forced_flatness = Some(true); } + ci.add_definition(e.into())?; } weedle::Definition::Dictionary(d) => { let rec = d.convert(ci)?; @@ -187,12 +186,9 @@ impl APIBuilder for weedle::Definition<'_> { } weedle::Definition::Interface(d) => { let attrs = attributes::InterfaceAttributes::try_from(d.attributes.as_ref())?; - if attrs.contains_enum_attr() { + if attrs.contains_enum_attr() || attrs.contains_error_attr() { let e: uniffi_meta::EnumMetadata = d.convert(ci)?; ci.add_definition(e.into())?; - } else if attrs.contains_error_attr() { - let e: uniffi_meta::ErrorMetadata = d.convert(ci)?; - ci.add_definition(e.into())?; } else { let obj: uniffi_meta::ObjectMetadata = d.convert(ci)?; ci.add_definition(obj.into())?; @@ -218,6 +214,7 @@ impl APIBuilder for weedle::NamespaceDefinition<'_> { if self.identifier.0 != ci.types.namespace { bail!("duplicate namespace definition"); } + ci.types.namespace_docstring = self.docstring.as_ref().map(|v| convert_docstring(&v.0)); for func in self.members.body.convert(ci)? { ci.add_definition(func.into())?; } @@ -229,6 +226,7 @@ impl APIBuilder for weedle::NamespaceDefinition<'_> { pub(crate) struct TypeCollector { /// The unique prefix that we'll use for namespacing when exposing this component's API. pub namespace: String, + pub namespace_docstring: Option, pub crate_name: String, diff --git a/third_party/rust/uniffi_udl/src/converters/callables.rs b/third_party/rust/uniffi_udl/src/converters/callables.rs index 3e15bb8e02..dda3c3a3ce 100644 --- a/third_party/rust/uniffi_udl/src/converters/callables.rs +++ b/third_party/rust/uniffi_udl/src/converters/callables.rs @@ -5,6 +5,7 @@ use super::APIConverter; use crate::attributes::ArgumentAttributes; use crate::attributes::{ConstructorAttributes, FunctionAttributes, MethodAttributes}; +use crate::converters::convert_docstring; use crate::literal::convert_default_value; use crate::InterfaceCollector; use anyhow::{bail, Result}; @@ -41,6 +42,7 @@ impl APIConverter for weedle::argument::SingleArgument<'_> { name: self.identifier.0.to_string(), ty: type_, default: None, + docstring: None, }) } } @@ -89,6 +91,7 @@ impl APIConverter for weedle::namespace::OperationNamespaceMember<'_ Some(id) => id.0.to_string(), }; let attrs = FunctionAttributes::try_from(self.attributes.as_ref())?; + let is_async = attrs.is_async(); let throws = match attrs.get_throws_err() { None => None, Some(name) => match ci.get_type(name) { @@ -99,10 +102,11 @@ impl APIConverter for weedle::namespace::OperationNamespaceMember<'_ Ok(FnMetadata { module_path: ci.module_path(), name, - is_async: false, + is_async, return_type, inputs: self.args.body.list.convert(ci)?, throws, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), checksum: None, }) } @@ -122,10 +126,12 @@ impl APIConverter for weedle::interface::ConstructorInterfa name: String::from(attributes.get_name().unwrap_or("new")), // We don't know the name of the containing `Object` at this point, fill it in later. self_name: Default::default(), + is_async: attributes.is_async(), // Also fill in checksum_fn_name later, since it depends on object_name inputs: self.args.body.list.convert(ci)?, throws, checksum: None, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } @@ -140,6 +146,7 @@ impl APIConverter for weedle::interface::OperationInterfaceMembe } let return_type = ci.resolve_return_type_expression(&self.return_type)?; let attributes = MethodAttributes::try_from(self.attributes.as_ref())?; + let is_async = attributes.is_async(); let throws = match attributes.get_throws_err() { Some(name) => match ci.get_type(name) { @@ -164,12 +171,13 @@ impl APIConverter for weedle::interface::OperationInterfaceMembe }, // We don't know the name of the containing `Object` at this point, fill it in later. self_name: Default::default(), - is_async: false, // not supported in UDL + is_async, inputs: self.args.body.list.convert(ci)?, return_type, throws, takes_self_by_arc, checksum: None, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } @@ -184,6 +192,7 @@ impl APIConverter for weedle::interface::OperationInterface } let return_type = ci.resolve_return_type_expression(&self.return_type)?; let attributes = MethodAttributes::try_from(self.attributes.as_ref())?; + let is_async = attributes.is_async(); let throws = match attributes.get_throws_err() { Some(name) => match ci.get_type(name) { @@ -208,12 +217,13 @@ impl APIConverter for weedle::interface::OperationInterface name } }, - is_async: false, // not supported in udl + is_async, inputs: self.args.body.list.convert(ci)?, return_type, throws, takes_self_by_arc, checksum: None, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } diff --git a/third_party/rust/uniffi_udl/src/converters/enum_.rs b/third_party/rust/uniffi_udl/src/converters/enum_.rs index a3e68fd23e..1615a1a7ca 100644 --- a/third_party/rust/uniffi_udl/src/converters/enum_.rs +++ b/third_party/rust/uniffi_udl/src/converters/enum_.rs @@ -3,19 +3,22 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::APIConverter; -use crate::InterfaceCollector; +use crate::{attributes::EnumAttributes, converters::convert_docstring, InterfaceCollector}; use anyhow::{bail, Result}; -use uniffi_meta::{EnumMetadata, ErrorMetadata, VariantMetadata}; +use uniffi_meta::{EnumMetadata, VariantMetadata}; -// Note that we have four `APIConverter` impls here - one for the `enum` case, -// one for the `[Error] enum` case, and and one for the `[Enum] interface` case, -// and one for the `[Error] interface` case. +// Note that we have 2 `APIConverter` impls here - one for the `enum` case +// (including an enum with `[Error]`), and one for the `[Error] interface` cas +// (which is still an enum, but with different "flatness" characteristics.) impl APIConverter for weedle::EnumDefinition<'_> { fn convert(&self, ci: &mut InterfaceCollector) -> Result { + let attributes = EnumAttributes::try_from(self.attributes.as_ref())?; Ok(EnumMetadata { module_path: ci.module_path(), name: self.identifier.0.to_string(), + forced_flatness: None, + discr_type: None, variants: self .values .body @@ -23,35 +26,15 @@ impl APIConverter for weedle::EnumDefinition<'_> { .iter() .map::, _>(|v| { Ok(VariantMetadata { - name: v.0.to_string(), + name: v.value.0.to_string(), + discr: None, fields: vec![], + docstring: v.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) }) .collect::>>()?, - }) - } -} - -impl APIConverter for weedle::EnumDefinition<'_> { - fn convert(&self, ci: &mut InterfaceCollector) -> Result { - Ok(ErrorMetadata::Enum { - enum_: EnumMetadata { - module_path: ci.module_path(), - name: self.identifier.0.to_string(), - variants: self - .values - .body - .list - .iter() - .map::, _>(|v| { - Ok(VariantMetadata { - name: v.0.to_string(), - fields: vec![], - }) - }) - .collect::>>()?, - }, - is_flat: true, + non_exhaustive: attributes.contains_non_exhaustive_attr(), + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } @@ -61,11 +44,11 @@ impl APIConverter for weedle::InterfaceDefinition<'_> { 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. + let attributes = EnumAttributes::try_from(self.attributes.as_ref())?; Ok(EnumMetadata { module_path: ci.module_path(), name: self.identifier.0.to_string(), + forced_flatness: Some(false), variants: self .members .body @@ -78,41 +61,15 @@ impl APIConverter for weedle::InterfaceDefinition<'_> { ), }) .collect::>>()?, + discr_type: None, + non_exhaustive: attributes.contains_non_exhaustive_attr(), + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), // Enums declared using the `[Enum] interface` syntax might have variants with fields. //flat: false, }) } } -impl APIConverter for weedle::InterfaceDefinition<'_> { - fn convert(&self, ci: &mut InterfaceCollector) -> Result { - if self.inheritance.is_some() { - bail!("interface inheritance is not supported for enum interfaces"); - } - // We don't need to check `self.attributes` here; callers have already checked them - // to work out which version to dispatch to. - Ok(ErrorMetadata::Enum { - enum_: EnumMetadata { - module_path: ci.module_path(), - name: self.identifier.0.to_string(), - variants: self - .members - .body - .iter() - .map::, _>(|member| match member { - weedle::interface::InterfaceMember::Operation(t) => Ok(t.convert(ci)?), - _ => bail!( - "interface member type {:?} not supported in enum interface", - member - ), - }) - .collect::>>()?, - }, - is_flat: false, - }) - } -} - #[cfg(test)] mod test { use super::*; diff --git a/third_party/rust/uniffi_udl/src/converters/interface.rs b/third_party/rust/uniffi_udl/src/converters/interface.rs index 58e6a9c8a0..ef9bdd9540 100644 --- a/third_party/rust/uniffi_udl/src/converters/interface.rs +++ b/third_party/rust/uniffi_udl/src/converters/interface.rs @@ -4,7 +4,7 @@ use super::APIConverter; use crate::attributes::InterfaceAttributes; -use crate::InterfaceCollector; +use crate::{converters::convert_docstring, InterfaceCollector}; use anyhow::{bail, Result}; use std::collections::HashSet; use uniffi_meta::{ @@ -23,7 +23,7 @@ impl APIConverter for weedle::InterfaceDefinition<'_> { }; let object_name = self.identifier.0; - let object_impl = attributes.object_impl(); + let object_impl = attributes.object_impl()?; // Convert each member into a constructor or method, guarding against duplicate names. // They get added to the ci and aren't carried in ObjectMetadata. let mut member_names = HashSet::new(); @@ -70,6 +70,7 @@ impl APIConverter for weedle::InterfaceDefinition<'_> { throws: None, takes_self_by_arc: false, checksum: None, + docstring: None, }) }; // Trait methods are in the Metadata. @@ -130,6 +131,7 @@ impl APIConverter for weedle::InterfaceDefinition<'_> { module_path: ci.module_path(), name: object_name.to_string(), imp: object_impl, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } diff --git a/third_party/rust/uniffi_udl/src/converters/mod.rs b/third_party/rust/uniffi_udl/src/converters/mod.rs index 7a2d22ac42..195d9cc0b7 100644 --- a/third_party/rust/uniffi_udl/src/converters/mod.rs +++ b/third_party/rust/uniffi_udl/src/converters/mod.rs @@ -29,6 +29,11 @@ pub(crate) trait APIConverter { fn convert(&self, ci: &mut InterfaceCollector) -> Result; } +// Convert UDL docstring into metadata docstring +pub(crate) fn convert_docstring(docstring: &str) -> String { + textwrap::dedent(docstring) +} + /// Convert a list of weedle items into a list of `InterfaceCollector` items, /// by doing a direct item-by-item mapping. impl> APIConverter> for Vec { @@ -72,6 +77,7 @@ impl APIConverter for weedle::interface::OperationInterfaceMemb }; Ok(VariantMetadata { name, + discr: None, fields: self .args .body @@ -79,6 +85,7 @@ impl APIConverter for weedle::interface::OperationInterfaceMemb .iter() .map(|arg| arg.convert(ci)) .collect::>>()?, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } @@ -95,6 +102,7 @@ impl APIConverter for weedle::DictionaryDefinition<'_> { module_path: ci.module_path(), name: self.identifier.0.to_string(), fields: self.members.body.convert(ci)?, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } @@ -113,6 +121,7 @@ impl APIConverter for weedle::dictionary::DictionaryMember<'_> { name: self.identifier.0.to_string(), ty: type_, default, + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } @@ -150,6 +159,7 @@ impl APIConverter for weedle::CallbackInterfaceDefini Ok(CallbackInterfaceMetadata { module_path: ci.module_path(), name: object_name.to_string(), + docstring: self.docstring.as_ref().map(|v| convert_docstring(&v.0)), }) } } diff --git a/third_party/rust/uniffi_udl/src/finder.rs b/third_party/rust/uniffi_udl/src/finder.rs index 0c4c187dc0..259557ad07 100644 --- a/third_party/rust/uniffi_udl/src/finder.rs +++ b/third_party/rust/uniffi_udl/src/finder.rs @@ -22,8 +22,8 @@ use std::convert::TryFrom; use anyhow::{bail, Result}; use super::TypeCollector; -use crate::attributes::{InterfaceAttributes, TypedefAttributes}; -use uniffi_meta::Type; +use crate::attributes::{InterfaceAttributes, RustKind, TypedefAttributes}; +use uniffi_meta::{ObjectImpl, Type}; /// Trait to help with an early "type discovery" phase when processing the UDL. /// @@ -75,7 +75,7 @@ impl TypeFinder for weedle::InterfaceDefinition<'_> { Type::Object { name, module_path: types.module_path(), - imp: attrs.object_impl(), + imp: attrs.object_impl()?, }, ) } @@ -111,7 +111,6 @@ impl TypeFinder for weedle::EnumDefinition<'_> { impl TypeFinder for weedle::TypedefDefinition<'_> { fn add_type_definitions_to(&self, types: &mut TypeCollector) -> 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_)?; @@ -122,29 +121,52 @@ impl TypeFinder for weedle::TypedefDefinition<'_> { // `FfiConverter` implementation. let builtin = types.resolve_type_expression(&self.type_)?; types.add_type_definition( - name, + self.identifier.0, Type::Custom { module_path: types.module_path(), - name: name.to_string(), + name: self.identifier.0.to_string(), builtin: builtin.into(), }, ) } else { - let kind = attrs.external_kind().expect("External missing"); - let tagged = attrs.external_tagged().expect("External missing"); + let module_path = types.module_path(); + let name = self.identifier.0.to_string(); + let ty = match attrs.rust_kind() { + Some(RustKind::Object) => Type::Object { + module_path, + name, + imp: ObjectImpl::Struct, + }, + Some(RustKind::Trait) => Type::Object { + module_path, + name, + imp: ObjectImpl::Trait, + }, + Some(RustKind::CallbackTrait) => Type::Object { + module_path, + name, + imp: ObjectImpl::CallbackTrait, + }, + Some(RustKind::Record) => Type::Record { module_path, name }, + Some(RustKind::Enum) => Type::Enum { module_path, name }, + Some(RustKind::CallbackInterface) => Type::CallbackInterface { module_path, name }, + // must be external + None => { + let kind = attrs.external_kind().expect("External missing kind"); + let tagged = attrs.external_tagged().expect("External missing tagged"); + Type::External { + name, + namespace: "".to_string(), // we don't know this yet + module_path: attrs.get_crate_name(), + kind, + tagged, + } + } + }; // 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(), - namespace: "".to_string(), // we don't know this yet - module_path: attrs.get_crate_name(), - kind, - tagged, - }, - ) + types.add_type_definition(self.identifier.0, ty) } } } @@ -152,7 +174,7 @@ impl TypeFinder for weedle::TypedefDefinition<'_> { impl TypeFinder for weedle::CallbackInterfaceDefinition<'_> { fn add_type_definitions_to(&self, types: &mut TypeCollector) -> Result<()> { if self.attributes.is_some() { - bail!("no typedef attributes are currently supported"); + bail!("no callback interface attributes are currently supported"); } let name = self.identifier.0.to_string(); types.add_type_definition( diff --git a/third_party/rust/uniffi_udl/src/lib.rs b/third_party/rust/uniffi_udl/src/lib.rs index 5e6e72a7f7..a9dad5d6c5 100644 --- a/third_party/rust/uniffi_udl/src/lib.rs +++ b/third_party/rust/uniffi_udl/src/lib.rs @@ -6,7 +6,7 @@ //! //! This library is dedicated to parsing a string in a webidl syntax, as described by //! weedle and with our own custom take on the attributes etc, pushing the boundaries -//! of that syntax to describe a uniffi `MetatadataGroup`. +//! of that syntax to describe a uniffi `MetadataGroup`. //! //! The output of this module is consumed by uniffi_bindgen to generate stuff. diff --git a/third_party/rust/uniffi_udl/src/literal.rs b/third_party/rust/uniffi_udl/src/literal.rs index 78f2544254..5fbf022644 100644 --- a/third_party/rust/uniffi_udl/src/literal.rs +++ b/third_party/rust/uniffi_udl/src/literal.rs @@ -84,8 +84,10 @@ pub(super) fn convert_default_value( (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)?, + (weedle::literal::DefaultValue::Null(_), Type::Optional { .. }) => Literal::None, + (_, Type::Optional { inner_type, .. }) => Literal::Some { + inner: Box::new(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_)?, @@ -144,7 +146,7 @@ mod test { inner_type: Box::new(Type::String) } )?, - Literal::Null + Literal::None )); Ok(()) } diff --git a/third_party/rust/uniffi_udl/src/resolver.rs b/third_party/rust/uniffi_udl/src/resolver.rs index 14a7a4c6f1..ea98cd7a99 100644 --- a/third_party/rust/uniffi_udl/src/resolver.rs +++ b/third_party/rust/uniffi_udl/src/resolver.rs @@ -209,7 +209,6 @@ pub(crate) fn resolve_builtin_type(name: &str) -> Option { "f64" => Some(Type::Float64), "timestamp" => Some(Type::Timestamp), "duration" => Some(Type::Duration), - "ForeignExecutor" => Some(Type::ForeignExecutor), _ => None, } } -- cgit v1.2.3