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_meta/.cargo-checksum.json | 2 +- third_party/rust/uniffi_meta/Cargo.toml | 5 +- third_party/rust/uniffi_meta/README.md | 81 ++++++++++++ third_party/rust/uniffi_meta/src/ffi_names.rs | 13 +- third_party/rust/uniffi_meta/src/group.rs | 9 +- third_party/rust/uniffi_meta/src/lib.rs | 65 +++++----- third_party/rust/uniffi_meta/src/metadata.rs | 13 +- third_party/rust/uniffi_meta/src/reader.rs | 148 ++++++++++++++++++---- third_party/rust/uniffi_meta/src/types.rs | 22 ++-- 9 files changed, 275 insertions(+), 83 deletions(-) create mode 100644 third_party/rust/uniffi_meta/README.md (limited to 'third_party/rust/uniffi_meta') diff --git a/third_party/rust/uniffi_meta/.cargo-checksum.json b/third_party/rust/uniffi_meta/.cargo-checksum.json index cb02cde83f..31b45ce807 100644 --- a/third_party/rust/uniffi_meta/.cargo-checksum.json +++ b/third_party/rust/uniffi_meta/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"cb9f8aad563572bd4f12ee234ede6773f189a79ba5bd3bfd7622d3c0ec49d6a3","src/ffi_names.rs":"422bbe9d49d5476de752a9f9b2330f59b37a79e67f19a828caceb64d1bdabff8","src/group.rs":"ae996e6b9f83d459af04eb392e36487d0fe19c7328a395823186cce76a0955ff","src/lib.rs":"a442e2271a0eb538ec1d4fc7573a3acc7e5f366e2b2ac8d0e659fd998fd7d995","src/metadata.rs":"4ae425a8eab7b8c19a6b96c914f2c02c5bee00358888fd55b936fd1fd175a93c","src/reader.rs":"57fb771584491b8e90b01c68f9d53bac7cfa3135888e11e24e14b59312185ff9","src/types.rs":"8c155ed1301e11a365863989e29c2271149048092fb7052ec145f58c948482d5"},"package":"71dc8573a7b1ac4b71643d6da34888273ebfc03440c525121f1b3634ad3417a2"} \ No newline at end of file +{"files":{"Cargo.toml":"5620cf9840477b158641547703ba353e3ad8427ec7b20b9dd5e5f5fe4df7d6d2","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","src/ffi_names.rs":"ca38b700a0a103c9faaf456ed91b67adf46d4e750aee9e9cd01ad97fb1840494","src/group.rs":"d0a43f3c528aba9403649715981ad3a8849d7a370f4ef9e2d618b88f60a3102f","src/lib.rs":"3f00d5214e2785e4b3045bc48899f6f6b1dce32ab3da6be3ebce716ee9d24c5f","src/metadata.rs":"3f236b337a1fd5082ea9cc4fee6800193a903ee88b81f1c3202843402f122a14","src/reader.rs":"579e2b87d8dd9d703b8811294abfb992621c0a46765800e4db2fad2906db2208","src/types.rs":"c2c5188da8cdf5af7f8496d4660bcfaa971b81ed73b64486c05b47256048544f"},"package":"f7224422c4cfd181c7ca9fca2154abca4d21db962f926f270f996edd38b0c4b8"} \ No newline at end of file diff --git a/third_party/rust/uniffi_meta/Cargo.toml b/third_party/rust/uniffi_meta/Cargo.toml index 34999eee18..04d8170011 100644 --- a/third_party/rust/uniffi_meta/Cargo.toml +++ b/third_party/rust/uniffi_meta/Cargo.toml @@ -12,9 +12,10 @@ [package] edition = "2021" name = "uniffi_meta" -version = "0.25.3" +version = "0.27.1" description = "uniffi_meta" homepage = "https://mozilla.github.io/uniffi-rs" +readme = "README.md" keywords = [ "ffi", "bindgen", @@ -32,4 +33,4 @@ version = "1.3" version = "0.3" [dependencies.uniffi_checksum_derive] -version = "0.25.3" +version = "0.27.1" diff --git a/third_party/rust/uniffi_meta/README.md b/third_party/rust/uniffi_meta/README.md new file mode 100644 index 0000000000..64ac3486a3 --- /dev/null +++ b/third_party/rust/uniffi_meta/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_meta/src/ffi_names.rs b/third_party/rust/uniffi_meta/src/ffi_names.rs index 44a5bc3e63..5c931a09e3 100644 --- a/third_party/rust/uniffi_meta/src/ffi_names.rs +++ b/third_party/rust/uniffi_meta/src/ffi_names.rs @@ -33,6 +33,12 @@ pub fn method_symbol_name(namespace: &str, object_name: &str, name: &str) -> Str format!("uniffi_{namespace}_fn_method_{object_name}_{name}") } +/// FFI symbol name for the `clone` function for an object. +pub fn clone_fn_symbol_name(namespace: &str, object_name: &str) -> String { + let object_name = object_name.to_ascii_lowercase(); + format!("uniffi_{namespace}_fn_clone_{object_name}") +} + /// FFI symbol name for the `free` function for an object. pub fn free_fn_symbol_name(namespace: &str, object_name: &str) -> String { let object_name = object_name.to_ascii_lowercase(); @@ -40,9 +46,12 @@ pub fn free_fn_symbol_name(namespace: &str, object_name: &str) -> String { } /// FFI symbol name for the `init_callback` function for a callback interface -pub fn init_callback_fn_symbol_name(namespace: &str, callback_interface_name: &str) -> String { +pub fn init_callback_vtable_fn_symbol_name( + namespace: &str, + callback_interface_name: &str, +) -> String { let callback_interface_name = callback_interface_name.to_ascii_lowercase(); - format!("uniffi_{namespace}_fn_init_callback_{callback_interface_name}") + format!("uniffi_{namespace}_fn_init_callback_vtable_{callback_interface_name}") } /// FFI checksum symbol name for a top-level function diff --git a/third_party/rust/uniffi_meta/src/group.rs b/third_party/rust/uniffi_meta/src/group.rs index f0be2e5a98..a41776bf8a 100644 --- a/third_party/rust/uniffi_meta/src/group.rs +++ b/third_party/rust/uniffi_meta/src/group.rs @@ -18,6 +18,7 @@ pub fn create_metadata_groups(items: &[Metadata]) -> MetadataGroupMap { Metadata::Namespace(namespace) => { let group = MetadataGroup { namespace: namespace.clone(), + namespace_docstring: None, items: BTreeSet::new(), }; Some((namespace.crate_name.clone(), group)) @@ -29,6 +30,7 @@ pub fn create_metadata_groups(items: &[Metadata]) -> MetadataGroupMap { }; let group = MetadataGroup { namespace, + namespace_docstring: None, items: BTreeSet::new(), }; Some((udl.module_path.clone(), group)) @@ -63,6 +65,7 @@ pub fn group_metadata(group_map: &mut MetadataGroupMap, items: Vec) -> #[derive(Debug)] pub struct MetadataGroup { pub namespace: NamespaceMetadata, + pub namespace_docstring: Option, pub items: BTreeSet, } @@ -127,12 +130,6 @@ impl<'a> ExternalTypeConverter<'a> { ..meta }), Metadata::Enum(meta) => Metadata::Enum(self.convert_enum(meta)), - Metadata::Error(meta) => Metadata::Error(match meta { - ErrorMetadata::Enum { enum_, is_flat } => ErrorMetadata::Enum { - enum_: self.convert_enum(enum_), - is_flat, - }, - }), _ => item, } } diff --git a/third_party/rust/uniffi_meta/src/lib.rs b/third_party/rust/uniffi_meta/src/lib.rs index e486d84d89..90f7b2d3cd 100644 --- a/third_party/rust/uniffi_meta/src/lib.rs +++ b/third_party/rust/uniffi_meta/src/lib.rs @@ -23,7 +23,7 @@ mod metadata; // `docs/uniffi-versioning.md` for details. // // Once we get to 1.0, then we'll need to update the scheme to something like 100 + major_version -pub const UNIFFI_CONTRACT_VERSION: u32 = 24; +pub const UNIFFI_CONTRACT_VERSION: u32 = 26; /// Similar to std::hash::Hash. /// @@ -143,6 +143,7 @@ pub struct FnMetadata { pub return_type: Option, pub throws: Option, pub checksum: Option, + pub docstring: Option, } impl FnMetadata { @@ -160,9 +161,11 @@ pub struct ConstructorMetadata { pub module_path: String, pub self_name: String, pub name: String, + pub is_async: bool, pub inputs: Vec, pub throws: Option, pub checksum: Option, + pub docstring: Option, } impl ConstructorMetadata { @@ -190,6 +193,7 @@ pub struct MethodMetadata { pub throws: Option, pub takes_self_by_arc: bool, // unused except by rust udl bindgen. pub checksum: Option, + pub docstring: Option, } impl MethodMetadata { @@ -216,6 +220,7 @@ pub struct TraitMethodMetadata { pub throws: Option, pub takes_self_by_arc: bool, // unused except by rust udl bindgen. pub checksum: Option, + pub docstring: Option, } impl TraitMethodMetadata { @@ -266,7 +271,17 @@ pub enum LiteralMetadata { Enum(String, Type), EmptySequence, EmptyMap, - Null, + None, + Some { inner: Box }, +} + +impl LiteralMetadata { + pub fn new_uint(v: u64) -> Self { + LiteralMetadata::UInt(v, Radix::Decimal, Type::UInt64) + } + pub fn new_int(v: i64) -> Self { + LiteralMetadata::Int(v, Radix::Decimal, Type::Int64) + } } // Represent the radix of integer literal values. @@ -283,6 +298,7 @@ pub struct RecordMetadata { pub module_path: String, pub name: String, pub fields: Vec, + pub docstring: Option, } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -290,19 +306,26 @@ pub struct FieldMetadata { pub name: String, pub ty: Type, pub default: Option, + pub docstring: Option, } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct EnumMetadata { pub module_path: String, pub name: String, + pub forced_flatness: Option, pub variants: Vec, + pub discr_type: Option, + pub non_exhaustive: bool, + pub docstring: Option, } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct VariantMetadata { pub name: String, + pub discr: Option, pub fields: Vec, + pub docstring: Option, } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -310,15 +333,25 @@ pub struct ObjectMetadata { pub module_path: String, pub name: String, pub imp: types::ObjectImpl, + pub docstring: Option, } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct CallbackInterfaceMetadata { pub module_path: String, pub name: String, + pub docstring: Option, } impl ObjectMetadata { + /// FFI symbol name for the `clone` function for this object. + /// + /// This function is used to increment the reference count before lowering an object to pass + /// back to Rust. + pub fn clone_ffi_symbol_name(&self) -> String { + clone_fn_symbol_name(&self.module_path, &self.name) + } + /// FFI symbol name for the `free` function for this object. /// /// This function is used to free the memory used by this object. @@ -368,6 +401,7 @@ impl UniffiTraitMetadata { } #[repr(u8)] +#[derive(Eq, PartialEq, Hash)] pub enum UniffiTraitDiscriminants { Debug, Display, @@ -387,25 +421,6 @@ impl UniffiTraitDiscriminants { } } -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum ErrorMetadata { - Enum { enum_: EnumMetadata, is_flat: bool }, -} - -impl ErrorMetadata { - pub fn name(&self) -> &String { - match self { - Self::Enum { enum_, .. } => &enum_.name, - } - } - - pub fn module_path(&self) -> &String { - match self { - Self::Enum { enum_, .. } => &enum_.module_path, - } - } -} - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct CustomTypeMetadata { pub module_path: String, @@ -433,7 +448,6 @@ pub enum Metadata { CallbackInterface(CallbackInterfaceMetadata), Record(RecordMetadata), Enum(EnumMetadata), - Error(ErrorMetadata), Constructor(ConstructorMetadata), Method(MethodMetadata), TraitMethod(TraitMethodMetadata), @@ -458,7 +472,6 @@ impl Metadata { Metadata::Object(meta) => &meta.module_path, Metadata::CallbackInterface(meta) => &meta.module_path, Metadata::TraitMethod(meta) => &meta.module_path, - Metadata::Error(meta) => meta.module_path(), Metadata::CustomType(meta) => &meta.module_path, Metadata::UniffiTrait(meta) => meta.module_path(), } @@ -507,12 +520,6 @@ impl From for Metadata { } } -impl From for Metadata { - fn from(e: ErrorMetadata) -> Self { - Self::Error(e) - } -} - impl From for Metadata { fn from(v: ObjectMetadata) -> Self { Self::Object(v) diff --git a/third_party/rust/uniffi_meta/src/metadata.rs b/third_party/rust/uniffi_meta/src/metadata.rs index 6e490a4866..9cfb77a244 100644 --- a/third_party/rust/uniffi_meta/src/metadata.rs +++ b/third_party/rust/uniffi_meta/src/metadata.rs @@ -7,7 +7,7 @@ // `uniffi_core`. // This is the easy way out of that issue and is a temporary hacky solution. -/// Metadata constants, make sure to keep this in sync with copy in `uniffi_meta::reader` +/// Metadata constants, make sure to keep this in sync with copy in `uniffi_core::metadata` pub mod codes { // Top-level metadata item codes pub const FUNC: u8 = 0; @@ -15,13 +15,14 @@ pub mod codes { pub const RECORD: u8 = 2; pub const ENUM: u8 = 3; pub const INTERFACE: u8 = 4; - pub const ERROR: u8 = 5; pub const NAMESPACE: u8 = 6; pub const CONSTRUCTOR: u8 = 7; pub const UDL_FILE: u8 = 8; pub const CALLBACK_INTERFACE: u8 = 9; pub const TRAIT_METHOD: u8 = 10; pub const UNIFFI_TRAIT: u8 = 11; + pub const TRAIT_INTERFACE: u8 = 12; + pub const CALLBACK_TRAIT_INTERFACE: u8 = 13; //pub const UNKNOWN: u8 = 255; // Type codes @@ -49,8 +50,8 @@ pub mod codes { pub const TYPE_CALLBACK_INTERFACE: u8 = 21; pub const TYPE_CUSTOM: u8 = 22; pub const TYPE_RESULT: u8 = 23; - //pub const TYPE_FUTURE: u8 = 24; - pub const TYPE_FOREIGN_EXECUTOR: u8 = 25; + pub const TYPE_TRAIT_INTERFACE: u8 = 24; + pub const TYPE_CALLBACK_TRAIT_INTERFACE: u8 = 25; pub const TYPE_UNIT: u8 = 255; // Literal codes @@ -58,7 +59,9 @@ pub mod codes { pub const LIT_INT: u8 = 1; pub const LIT_FLOAT: u8 = 2; pub const LIT_BOOL: u8 = 3; - pub const LIT_NULL: u8 = 4; + pub const LIT_NONE: u8 = 4; + pub const LIT_SOME: u8 = 5; + pub const LIT_EMPTY_SEQ: u8 = 6; } // Create a checksum for a MetadataBuffer diff --git a/third_party/rust/uniffi_meta/src/reader.rs b/third_party/rust/uniffi_meta/src/reader.rs index bf6525f2b5..6fec6cb3a3 100644 --- a/third_party/rust/uniffi_meta/src/reader.rs +++ b/third_party/rust/uniffi_meta/src/reader.rs @@ -52,9 +52,10 @@ impl<'a> MetadataReader<'a> { codes::CONSTRUCTOR => self.read_constructor()?.into(), codes::METHOD => self.read_method()?.into(), codes::RECORD => self.read_record()?.into(), - codes::ENUM => self.read_enum(false)?.into(), - codes::ERROR => self.read_error()?.into(), - codes::INTERFACE => self.read_object()?.into(), + codes::ENUM => self.read_enum()?.into(), + codes::INTERFACE => self.read_object(ObjectImpl::Struct)?.into(), + codes::TRAIT_INTERFACE => self.read_object(ObjectImpl::Trait)?.into(), + codes::CALLBACK_TRAIT_INTERFACE => self.read_object(ObjectImpl::CallbackTrait)?.into(), codes::CALLBACK_INTERFACE => self.read_callback_interface()?.into(), codes::TRAIT_METHOD => self.read_trait_method()?.into(), codes::UNIFFI_TRAIT => self.read_uniffi_trait()?.into(), @@ -80,6 +81,17 @@ impl<'a> MetadataReader<'a> { } } + fn read_u16(&mut self) -> Result { + if self.buf.len() >= 2 { + // read the value as little-endian + let value = u16::from_le_bytes([self.buf[0], self.buf[1]]); + self.buf = &self.buf[2..]; + Ok(value) + } else { + bail!("Not enough data left in buffer to read a u16 value"); + } + } + fn read_u32(&mut self) -> Result { if self.buf.len() >= 4 { // read the value as little-endian @@ -105,6 +117,17 @@ impl<'a> MetadataReader<'a> { String::from_utf8(slice.into()).context("Invalid string data") } + fn read_long_string(&mut self) -> Result { + let size = self.read_u16()? as usize; + let slice; + (slice, self.buf) = self.buf.split_at(size); + String::from_utf8(slice.into()).context("Invalid string data") + } + + fn read_optional_long_string(&mut self) -> Result> { + Ok(Some(self.read_long_string()?).filter(|str| !str.is_empty())) + } + fn read_type(&mut self) -> Result { let value = self.read_u8()?; Ok(match value { @@ -122,7 +145,6 @@ impl<'a> MetadataReader<'a> { codes::TYPE_STRING => Type::String, codes::TYPE_DURATION => Type::Duration, codes::TYPE_SYSTEM_TIME => Type::Timestamp, - codes::TYPE_FOREIGN_EXECUTOR => Type::ForeignExecutor, codes::TYPE_RECORD => Type::Record { module_path: self.read_string()?, name: self.read_string()?, @@ -134,7 +156,17 @@ impl<'a> MetadataReader<'a> { codes::TYPE_INTERFACE => Type::Object { module_path: self.read_string()?, name: self.read_string()?, - imp: ObjectImpl::from_is_trait(self.read_bool()?), + imp: ObjectImpl::Struct, + }, + codes::TYPE_TRAIT_INTERFACE => Type::Object { + module_path: self.read_string()?, + name: self.read_string()?, + imp: ObjectImpl::Trait, + }, + codes::TYPE_CALLBACK_TRAIT_INTERFACE => Type::Object { + module_path: self.read_string()?, + name: self.read_string()?, + imp: ObjectImpl::CallbackTrait, }, codes::TYPE_CALLBACK_INTERFACE => Type::CallbackInterface { module_path: self.read_string()?, @@ -198,6 +230,7 @@ impl<'a> MetadataReader<'a> { let is_async = self.read_bool()?; let inputs = self.read_inputs()?; let (return_type, throws) = self.read_return_type()?; + let docstring = self.read_optional_long_string()?; Ok(FnMetadata { module_path, name, @@ -205,6 +238,7 @@ impl<'a> MetadataReader<'a> { inputs, return_type, throws, + docstring, checksum: self.calc_checksum(), }) } @@ -213,8 +247,10 @@ impl<'a> MetadataReader<'a> { let module_path = self.read_string()?; let self_name = self.read_string()?; let name = self.read_string()?; + let is_async = self.read_bool()?; let inputs = self.read_inputs()?; let (return_type, throws) = self.read_return_type()?; + let docstring = self.read_optional_long_string()?; return_type .filter(|t| { @@ -228,10 +264,12 @@ impl<'a> MetadataReader<'a> { Ok(ConstructorMetadata { module_path, self_name, + is_async, name, inputs, throws, checksum: self.calc_checksum(), + docstring, }) } @@ -242,6 +280,7 @@ impl<'a> MetadataReader<'a> { let is_async = self.read_bool()?; let inputs = self.read_inputs()?; let (return_type, throws) = self.read_return_type()?; + let docstring = self.read_optional_long_string()?; Ok(MethodMetadata { module_path, self_name, @@ -252,6 +291,7 @@ impl<'a> MetadataReader<'a> { throws, takes_self_by_arc: false, // not emitted by macros checksum: self.calc_checksum(), + docstring, }) } @@ -260,13 +300,25 @@ impl<'a> MetadataReader<'a> { module_path: self.read_string()?, name: self.read_string()?, fields: self.read_fields()?, + docstring: self.read_optional_long_string()?, }) } - fn read_enum(&mut self, is_flat_error: bool) -> Result { + fn read_enum(&mut self) -> Result { let module_path = self.read_string()?; let name = self.read_string()?; - let variants = if is_flat_error { + let forced_flatness = match self.read_u8()? { + 0 => None, + 1 => Some(false), + 2 => Some(true), + _ => unreachable!("invalid flatness"), + }; + let discr_type = if self.read_bool()? { + Some(self.read_type()?) + } else { + None + }; + let variants = if forced_flatness == Some(true) { self.read_flat_variants()? } else { self.read_variants()? @@ -275,21 +327,20 @@ impl<'a> MetadataReader<'a> { Ok(EnumMetadata { module_path, name, + forced_flatness, + discr_type, variants, + non_exhaustive: self.read_bool()?, + docstring: self.read_optional_long_string()?, }) } - fn read_error(&mut self) -> Result { - let is_flat = self.read_bool()?; - let enum_ = self.read_enum(is_flat)?; - Ok(ErrorMetadata::Enum { enum_, is_flat }) - } - - fn read_object(&mut self) -> Result { + fn read_object(&mut self, imp: ObjectImpl) -> Result { Ok(ObjectMetadata { module_path: self.read_string()?, name: self.read_string()?, - imp: ObjectImpl::from_is_trait(self.read_bool()?), + imp, + docstring: self.read_optional_long_string()?, }) } @@ -322,6 +373,7 @@ impl<'a> MetadataReader<'a> { Ok(CallbackInterfaceMetadata { module_path: self.read_string()?, name: self.read_string()?, + docstring: self.read_optional_long_string()?, }) } @@ -333,6 +385,7 @@ impl<'a> MetadataReader<'a> { let is_async = self.read_bool()?; let inputs = self.read_inputs()?; let (return_type, throws) = self.read_return_type()?; + let docstring = self.read_optional_long_string()?; Ok(TraitMethodMetadata { module_path, trait_name, @@ -344,6 +397,7 @@ impl<'a> MetadataReader<'a> { throws, takes_self_by_arc: false, // not emitted by macros checksum: self.calc_checksum(), + docstring, }) } @@ -353,8 +407,13 @@ impl<'a> MetadataReader<'a> { .map(|_| { let name = self.read_string()?; let ty = self.read_type()?; - let default = self.read_default(&name, &ty)?; - Ok(FieldMetadata { name, ty, default }) + let default = self.read_optional_default(&name, &ty)?; + Ok(FieldMetadata { + name, + ty, + default, + docstring: self.read_optional_long_string()?, + }) }) .collect() } @@ -365,7 +424,9 @@ impl<'a> MetadataReader<'a> { .map(|_| { Ok(VariantMetadata { name: self.read_string()?, + discr: self.read_optional_default("", &Type::UInt64)?, fields: self.read_fields()?, + docstring: self.read_optional_long_string()?, }) }) .collect() @@ -377,7 +438,9 @@ impl<'a> MetadataReader<'a> { .map(|_| { Ok(VariantMetadata { name: self.read_string()?, + discr: None, fields: vec![], + docstring: self.read_optional_long_string()?, }) }) .collect() @@ -387,13 +450,16 @@ impl<'a> MetadataReader<'a> { let len = self.read_u8()?; (0..len) .map(|_| { + let name = self.read_string()?; + let ty = self.read_type()?; + let default = self.read_optional_default(&name, &ty)?; Ok(FnParamMetadata { - name: self.read_string()?, - ty: self.read_type()?, + name, + ty, + default, // not emitted by macros by_ref: false, optional: false, - default: None, }) }) .collect() @@ -405,14 +471,18 @@ impl<'a> MetadataReader<'a> { Some(checksum_metadata(metadata_buf)) } - fn read_default(&mut self, name: &str, ty: &Type) -> Result> { - let has_default = self.read_bool()?; - if !has_default { - return Ok(None); + fn read_optional_default(&mut self, name: &str, ty: &Type) -> Result> { + if self.read_bool()? { + Ok(Some(self.read_default(name, ty)?)) + } else { + Ok(None) } + } + fn read_default(&mut self, name: &str, ty: &Type) -> Result { let literal_kind = self.read_u8()?; - Ok(Some(match literal_kind { + + Ok(match literal_kind { codes::LIT_STR => { ensure!( matches!(ty, Type::String), @@ -422,12 +492,24 @@ impl<'a> MetadataReader<'a> { } codes::LIT_INT => { let base10_digits = self.read_string()?; + // procmacros emit the type for discriminant values based purely on whether the constant + // is positive or negative. + let ty = if !base10_digits.is_empty() + && base10_digits.as_bytes()[0] == b'-' + && ty == &Type::UInt64 + { + &Type::Int64 + } else { + ty + }; macro_rules! parse_int { ($ty:ident, $variant:ident) => { LiteralMetadata::$variant( base10_digits .parse::<$ty>() - .with_context(|| format!("parsing default for field {name}"))? + .with_context(|| { + format!("parsing default for field {name}: {base10_digits}") + })? .into(), Radix::Decimal, ty.to_owned(), @@ -458,8 +540,18 @@ impl<'a> MetadataReader<'a> { } }, codes::LIT_BOOL => LiteralMetadata::Boolean(self.read_bool()?), - codes::LIT_NULL => LiteralMetadata::Null, + codes::LIT_NONE => match ty { + Type::Optional { .. } => LiteralMetadata::None, + _ => bail!("field {name} of type {ty:?} can't have a default value of None"), + }, + codes::LIT_SOME => match ty { + Type::Optional { inner_type, .. } => LiteralMetadata::Some { + inner: Box::new(self.read_default(name, inner_type)?), + }, + _ => bail!("field {name} of type {ty:?} can't have a default value of None"), + }, + codes::LIT_EMPTY_SEQ => LiteralMetadata::EmptySequence, _ => bail!("Unexpected literal kind code: {literal_kind:?}"), - })) + }) } } diff --git a/third_party/rust/uniffi_meta/src/types.rs b/third_party/rust/uniffi_meta/src/types.rs index 24f8a6f2a8..51bf156b50 100644 --- a/third_party/rust/uniffi_meta/src/types.rs +++ b/third_party/rust/uniffi_meta/src/types.rs @@ -21,8 +21,12 @@ use crate::Checksum; #[derive(Debug, Copy, Clone, Eq, PartialEq, Checksum, Ord, PartialOrd)] pub enum ObjectImpl { + // A single Rust type Struct, + // A trait that's can be implemented by Rust types Trait, + // A trait + a callback interface -- can be implemented by both Rust and foreign types. + CallbackTrait, } impl ObjectImpl { @@ -31,27 +35,26 @@ impl ObjectImpl { /// Includes `r#`, traits get a leading `dyn`. If we ever supported associated types, then /// this would also include them. pub fn rust_name_for(&self, name: &str) -> String { - if self == &ObjectImpl::Trait { + if self.is_trait_interface() { format!("dyn r#{name}") } else { format!("r#{name}") } } - // uniffi_meta and procmacro support tend to carry around `is_trait` bools. This makes that - // mildly less painful - pub fn from_is_trait(is_trait: bool) -> Self { - if is_trait { - ObjectImpl::Trait - } else { - ObjectImpl::Struct - } + pub fn is_trait_interface(&self) -> bool { + matches!(self, Self::Trait | Self::CallbackTrait) + } + + pub fn has_callback_interface(&self) -> bool { + matches!(self, Self::CallbackTrait) } } #[derive(Debug, Clone, Copy, Eq, PartialEq, Checksum, Ord, PartialOrd)] pub enum ExternalKind { Interface, + Trait, // Either a record or enum DataClass, } @@ -85,7 +88,6 @@ pub enum Type { // How the object is implemented. imp: ObjectImpl, }, - ForeignExecutor, // Types defined in the component API, each of which has a string name. Record { module_path: String, -- cgit v1.2.3