summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_meta
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/uniffi_meta
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/uniffi_meta')
-rw-r--r--third_party/rust/uniffi_meta/.cargo-checksum.json1
-rw-r--r--third_party/rust/uniffi_meta/Cargo.toml35
-rw-r--r--third_party/rust/uniffi_meta/src/ffi_names.rs66
-rw-r--r--third_party/rust/uniffi_meta/src/group.rs273
-rw-r--r--third_party/rust/uniffi_meta/src/lib.rs544
-rw-r--r--third_party/rust/uniffi_meta/src/metadata.rs87
-rw-r--r--third_party/rust/uniffi_meta/src/reader.rs465
-rw-r--r--third_party/rust/uniffi_meta/src/types.rs172
8 files changed, 1643 insertions, 0 deletions
diff --git a/third_party/rust/uniffi_meta/.cargo-checksum.json b/third_party/rust/uniffi_meta/.cargo-checksum.json
new file mode 100644
index 0000000000..cb02cde83f
--- /dev/null
+++ b/third_party/rust/uniffi_meta/.cargo-checksum.json
@@ -0,0 +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
diff --git a/third_party/rust/uniffi_meta/Cargo.toml b/third_party/rust/uniffi_meta/Cargo.toml
new file mode 100644
index 0000000000..34999eee18
--- /dev/null
+++ b/third_party/rust/uniffi_meta/Cargo.toml
@@ -0,0 +1,35 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+name = "uniffi_meta"
+version = "0.25.3"
+description = "uniffi_meta"
+homepage = "https://mozilla.github.io/uniffi-rs"
+keywords = [
+ "ffi",
+ "bindgen",
+]
+license = "MPL-2.0"
+repository = "https://github.com/mozilla/uniffi-rs"
+
+[dependencies.anyhow]
+version = "1"
+
+[dependencies.bytes]
+version = "1.3"
+
+[dependencies.siphasher]
+version = "0.3"
+
+[dependencies.uniffi_checksum_derive]
+version = "0.25.3"
diff --git a/third_party/rust/uniffi_meta/src/ffi_names.rs b/third_party/rust/uniffi_meta/src/ffi_names.rs
new file mode 100644
index 0000000000..44a5bc3e63
--- /dev/null
+++ b/third_party/rust/uniffi_meta/src/ffi_names.rs
@@ -0,0 +1,66 @@
+/* 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/. */
+
+//! Functions to calculate names for FFI symbols
+//!
+//! All of these functions input a `namespace` parameter which is:
+//! - The UDL namespace for UDL-based generation
+//! - The "module path" of the item for proc-macro based generation. Right now this is actually just the
+//! crate name, but we eventually hope to make this the full module path.
+//!
+//! This could cause collisions in the case where you combine UDL and proc-macro generation and you
+//! set the UDL namespace to the name of another crate. This seems so pathological that it's not
+//! worth the code complexity to prevent it.
+
+/// FFI symbol name for a top-level function
+pub fn fn_symbol_name(namespace: &str, name: &str) -> String {
+ let name = name.to_ascii_lowercase();
+ format!("uniffi_{namespace}_fn_func_{name}")
+}
+
+/// FFI symbol name for an object constructor
+pub fn constructor_symbol_name(namespace: &str, object_name: &str, name: &str) -> String {
+ let object_name = object_name.to_ascii_lowercase();
+ let name = name.to_ascii_lowercase();
+ format!("uniffi_{namespace}_fn_constructor_{object_name}_{name}")
+}
+
+/// FFI symbol name for an object method
+pub fn method_symbol_name(namespace: &str, object_name: &str, name: &str) -> String {
+ let object_name = object_name.to_ascii_lowercase();
+ let name = name.to_ascii_lowercase();
+ format!("uniffi_{namespace}_fn_method_{object_name}_{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();
+ format!("uniffi_{namespace}_fn_free_{object_name}")
+}
+
+/// 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 {
+ let callback_interface_name = callback_interface_name.to_ascii_lowercase();
+ format!("uniffi_{namespace}_fn_init_callback_{callback_interface_name}")
+}
+
+/// FFI checksum symbol name for a top-level function
+pub fn fn_checksum_symbol_name(namespace: &str, name: &str) -> String {
+ let name = name.to_ascii_lowercase();
+ format!("uniffi_{namespace}_checksum_func_{name}")
+}
+
+/// FFI checksum symbol name for an object constructor
+pub fn constructor_checksum_symbol_name(namespace: &str, object_name: &str, name: &str) -> String {
+ let object_name = object_name.to_ascii_lowercase();
+ let name = name.to_ascii_lowercase();
+ format!("uniffi_{namespace}_checksum_constructor_{object_name}_{name}")
+}
+
+/// FFI checksum symbol name for an object method
+pub fn method_checksum_symbol_name(namespace: &str, object_name: &str, name: &str) -> String {
+ let object_name = object_name.to_ascii_lowercase();
+ let name = name.to_ascii_lowercase();
+ format!("uniffi_{namespace}_checksum_method_{object_name}_{name}")
+}
diff --git a/third_party/rust/uniffi_meta/src/group.rs b/third_party/rust/uniffi_meta/src/group.rs
new file mode 100644
index 0000000000..f0be2e5a98
--- /dev/null
+++ b/third_party/rust/uniffi_meta/src/group.rs
@@ -0,0 +1,273 @@
+/* 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/. */
+
+use std::collections::{BTreeSet, HashMap};
+
+use crate::*;
+use anyhow::{bail, Result};
+
+type MetadataGroupMap = HashMap<String, MetadataGroup>;
+
+// Create empty metadata groups based on the metadata items.
+pub fn create_metadata_groups(items: &[Metadata]) -> MetadataGroupMap {
+ // Map crate names to MetadataGroup instances
+ items
+ .iter()
+ .filter_map(|i| match i {
+ Metadata::Namespace(namespace) => {
+ let group = MetadataGroup {
+ namespace: namespace.clone(),
+ items: BTreeSet::new(),
+ };
+ Some((namespace.crate_name.clone(), group))
+ }
+ Metadata::UdlFile(udl) => {
+ let namespace = NamespaceMetadata {
+ crate_name: udl.module_path.clone(),
+ name: udl.namespace.clone(),
+ };
+ let group = MetadataGroup {
+ namespace,
+ items: BTreeSet::new(),
+ };
+ Some((udl.module_path.clone(), group))
+ }
+ _ => None,
+ })
+ .collect::<HashMap<_, _>>()
+}
+
+/// Consume the items into the previously created metadata groups.
+pub fn group_metadata(group_map: &mut MetadataGroupMap, items: Vec<Metadata>) -> Result<()> {
+ for item in items {
+ if matches!(&item, Metadata::Namespace(_)) {
+ continue;
+ }
+
+ let crate_name = calc_crate_name(item.module_path()).to_owned(); // XXX - kill clone?
+
+ let item = fixup_external_type(item, group_map);
+ let group = match group_map.get_mut(&crate_name) {
+ Some(ns) => ns,
+ None => bail!("Unknown namespace for {item:?} ({crate_name})"),
+ };
+ if group.items.contains(&item) {
+ bail!("Duplicate metadata item: {item:?}");
+ }
+ group.add_item(item);
+ }
+ Ok(())
+}
+
+#[derive(Debug)]
+pub struct MetadataGroup {
+ pub namespace: NamespaceMetadata,
+ pub items: BTreeSet<Metadata>,
+}
+
+impl MetadataGroup {
+ pub fn add_item(&mut self, item: Metadata) {
+ self.items.insert(item);
+ }
+}
+
+pub fn fixup_external_type(item: Metadata, group_map: &MetadataGroupMap) -> Metadata {
+ let crate_name = calc_crate_name(item.module_path()).to_owned();
+ let converter = ExternalTypeConverter {
+ crate_name: &crate_name,
+ crate_to_namespace: group_map,
+ };
+ converter.convert_item(item)
+}
+
+/// Convert metadata items by replacing types from external crates with Type::External
+struct ExternalTypeConverter<'a> {
+ crate_name: &'a str,
+ crate_to_namespace: &'a MetadataGroupMap,
+}
+
+impl<'a> ExternalTypeConverter<'a> {
+ fn crate_to_namespace(&self, crate_name: &str) -> String {
+ self.crate_to_namespace
+ .get(crate_name)
+ .unwrap_or_else(|| panic!("Can't find namespace for module {crate_name}"))
+ .namespace
+ .name
+ .clone()
+ }
+
+ fn convert_item(&self, item: Metadata) -> Metadata {
+ match item {
+ Metadata::Func(meta) => Metadata::Func(FnMetadata {
+ inputs: self.convert_params(meta.inputs),
+ return_type: self.convert_optional(meta.return_type),
+ throws: self.convert_optional(meta.throws),
+ ..meta
+ }),
+ Metadata::Method(meta) => Metadata::Method(MethodMetadata {
+ inputs: self.convert_params(meta.inputs),
+ return_type: self.convert_optional(meta.return_type),
+ throws: self.convert_optional(meta.throws),
+ ..meta
+ }),
+ Metadata::TraitMethod(meta) => Metadata::TraitMethod(TraitMethodMetadata {
+ inputs: self.convert_params(meta.inputs),
+ return_type: self.convert_optional(meta.return_type),
+ throws: self.convert_optional(meta.throws),
+ ..meta
+ }),
+ Metadata::Constructor(meta) => Metadata::Constructor(ConstructorMetadata {
+ inputs: self.convert_params(meta.inputs),
+ throws: self.convert_optional(meta.throws),
+ ..meta
+ }),
+ Metadata::Record(meta) => Metadata::Record(RecordMetadata {
+ fields: self.convert_fields(meta.fields),
+ ..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,
+ }
+ }
+
+ fn convert_params(&self, params: Vec<FnParamMetadata>) -> Vec<FnParamMetadata> {
+ params
+ .into_iter()
+ .map(|param| FnParamMetadata {
+ ty: self.convert_type(param.ty),
+ ..param
+ })
+ .collect()
+ }
+
+ fn convert_fields(&self, fields: Vec<FieldMetadata>) -> Vec<FieldMetadata> {
+ fields
+ .into_iter()
+ .map(|field| FieldMetadata {
+ ty: self.convert_type(field.ty),
+ ..field
+ })
+ .collect()
+ }
+
+ fn convert_enum(&self, enum_: EnumMetadata) -> EnumMetadata {
+ EnumMetadata {
+ variants: enum_
+ .variants
+ .into_iter()
+ .map(|variant| VariantMetadata {
+ fields: self.convert_fields(variant.fields),
+ ..variant
+ })
+ .collect(),
+ ..enum_
+ }
+ }
+
+ fn convert_optional(&self, ty: Option<Type>) -> Option<Type> {
+ ty.map(|ty| self.convert_type(ty))
+ }
+
+ fn convert_type(&self, ty: Type) -> Type {
+ match ty {
+ // Convert `ty` if it's external
+ Type::Enum { module_path, name } | Type::Record { module_path, name }
+ if self.is_module_path_external(&module_path) =>
+ {
+ Type::External {
+ namespace: self.crate_to_namespace(&module_path),
+ module_path,
+ name,
+ kind: ExternalKind::DataClass,
+ tagged: false,
+ }
+ }
+ Type::Custom {
+ module_path, name, ..
+ } if self.is_module_path_external(&module_path) => {
+ // For now, it's safe to assume that all custom types are data classes.
+ // There's no reason to use a custom type with an interface.
+ Type::External {
+ namespace: self.crate_to_namespace(&module_path),
+ module_path,
+ name,
+ kind: ExternalKind::DataClass,
+ tagged: false,
+ }
+ }
+ Type::Object {
+ module_path, name, ..
+ } if self.is_module_path_external(&module_path) => Type::External {
+ namespace: self.crate_to_namespace(&module_path),
+ module_path,
+ name,
+ kind: ExternalKind::Interface,
+ tagged: false,
+ },
+ Type::CallbackInterface { module_path, name }
+ if self.is_module_path_external(&module_path) =>
+ {
+ panic!("External callback interfaces not supported ({name})")
+ }
+ // Convert child types
+ Type::Custom {
+ module_path,
+ name,
+ builtin,
+ ..
+ } => Type::Custom {
+ module_path,
+ name,
+ builtin: Box::new(self.convert_type(*builtin)),
+ },
+ Type::Optional { inner_type } => Type::Optional {
+ inner_type: Box::new(self.convert_type(*inner_type)),
+ },
+ Type::Sequence { inner_type } => Type::Sequence {
+ inner_type: Box::new(self.convert_type(*inner_type)),
+ },
+ Type::Map {
+ key_type,
+ value_type,
+ } => Type::Map {
+ key_type: Box::new(self.convert_type(*key_type)),
+ value_type: Box::new(self.convert_type(*value_type)),
+ },
+ // Existing External types probably need namespace fixed.
+ Type::External {
+ namespace,
+ module_path,
+ name,
+ kind,
+ tagged,
+ } => {
+ assert!(namespace.is_empty());
+ Type::External {
+ namespace: self.crate_to_namespace(&module_path),
+ module_path,
+ name,
+ kind,
+ tagged,
+ }
+ }
+
+ // Otherwise, just return the type unchanged
+ _ => ty,
+ }
+ }
+
+ fn is_module_path_external(&self, module_path: &str) -> bool {
+ calc_crate_name(module_path) != self.crate_name
+ }
+}
+
+fn calc_crate_name(module_path: &str) -> &str {
+ module_path.split("::").next().unwrap()
+}
diff --git a/third_party/rust/uniffi_meta/src/lib.rs b/third_party/rust/uniffi_meta/src/lib.rs
new file mode 100644
index 0000000000..e486d84d89
--- /dev/null
+++ b/third_party/rust/uniffi_meta/src/lib.rs
@@ -0,0 +1,544 @@
+/* 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/. */
+
+use std::{collections::BTreeMap, hash::Hasher};
+pub use uniffi_checksum_derive::Checksum;
+
+mod ffi_names;
+pub use ffi_names::*;
+
+mod group;
+pub use group::{create_metadata_groups, fixup_external_type, group_metadata, MetadataGroup};
+
+mod reader;
+pub use reader::{read_metadata, read_metadata_type};
+
+mod types;
+pub use types::{AsType, ExternalKind, ObjectImpl, Type, TypeIterator};
+
+mod metadata;
+
+// This needs to match the minor version of the `uniffi` crate. See
+// `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;
+
+/// Similar to std::hash::Hash.
+///
+/// Implementations of this trait are expected to update the hasher state in
+/// the same way across platforms. #[derive(Checksum)] will do the right thing.
+pub trait Checksum {
+ fn checksum<H: Hasher>(&self, state: &mut H);
+}
+
+impl Checksum for bool {
+ fn checksum<H: Hasher>(&self, state: &mut H) {
+ state.write_u8(*self as u8);
+ }
+}
+
+impl Checksum for u64 {
+ fn checksum<H: Hasher>(&self, state: &mut H) {
+ state.write(&self.to_le_bytes());
+ }
+}
+
+impl Checksum for i64 {
+ fn checksum<H: Hasher>(&self, state: &mut H) {
+ state.write(&self.to_le_bytes());
+ }
+}
+
+impl<T: Checksum> Checksum for Box<T> {
+ fn checksum<H: Hasher>(&self, state: &mut H) {
+ (**self).checksum(state)
+ }
+}
+
+impl<T: Checksum> Checksum for [T] {
+ fn checksum<H: Hasher>(&self, state: &mut H) {
+ state.write(&(self.len() as u64).to_le_bytes());
+ for item in self {
+ Checksum::checksum(item, state);
+ }
+ }
+}
+
+impl<T: Checksum> Checksum for Vec<T> {
+ fn checksum<H: Hasher>(&self, state: &mut H) {
+ Checksum::checksum(&**self, state);
+ }
+}
+
+impl<K: Checksum, V: Checksum> Checksum for BTreeMap<K, V> {
+ fn checksum<H: Hasher>(&self, state: &mut H) {
+ state.write(&(self.len() as u64).to_le_bytes());
+ for (key, value) in self {
+ Checksum::checksum(key, state);
+ Checksum::checksum(value, state);
+ }
+ }
+}
+
+impl<T: Checksum> Checksum for Option<T> {
+ fn checksum<H: Hasher>(&self, state: &mut H) {
+ match self {
+ None => state.write(&0u64.to_le_bytes()),
+ Some(value) => {
+ state.write(&1u64.to_le_bytes());
+ Checksum::checksum(value, state)
+ }
+ }
+ }
+}
+
+impl Checksum for str {
+ fn checksum<H: Hasher>(&self, state: &mut H) {
+ state.write(self.as_bytes());
+ state.write_u8(0xff);
+ }
+}
+
+impl Checksum for String {
+ fn checksum<H: Hasher>(&self, state: &mut H) {
+ (**self).checksum(state)
+ }
+}
+
+impl Checksum for &str {
+ fn checksum<H: Hasher>(&self, state: &mut H) {
+ (**self).checksum(state)
+ }
+}
+
+// The namespace of a Component interface.
+//
+// This is used to match up the macro metadata with the UDL items.
+#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
+pub struct NamespaceMetadata {
+ pub crate_name: String,
+ pub name: String,
+}
+
+// UDL file included with `include_scaffolding!()`
+//
+// This is to find the UDL files in library mode generation
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct UdlFile {
+ // The module path specified when the UDL file was parsed.
+ pub module_path: String,
+ pub namespace: String,
+ // the base filename of the udl file - no path, no extension.
+ pub file_stub: String,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct FnMetadata {
+ pub module_path: String,
+ pub name: String,
+ pub is_async: bool,
+ pub inputs: Vec<FnParamMetadata>,
+ pub return_type: Option<Type>,
+ pub throws: Option<Type>,
+ pub checksum: Option<u16>,
+}
+
+impl FnMetadata {
+ pub fn ffi_symbol_name(&self) -> String {
+ fn_symbol_name(&self.module_path, &self.name)
+ }
+
+ pub fn checksum_symbol_name(&self) -> String {
+ fn_checksum_symbol_name(&self.module_path, &self.name)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct ConstructorMetadata {
+ pub module_path: String,
+ pub self_name: String,
+ pub name: String,
+ pub inputs: Vec<FnParamMetadata>,
+ pub throws: Option<Type>,
+ pub checksum: Option<u16>,
+}
+
+impl ConstructorMetadata {
+ pub fn ffi_symbol_name(&self) -> String {
+ constructor_symbol_name(&self.module_path, &self.self_name, &self.name)
+ }
+
+ pub fn checksum_symbol_name(&self) -> String {
+ constructor_checksum_symbol_name(&self.module_path, &self.self_name, &self.name)
+ }
+
+ pub fn is_primary(&self) -> bool {
+ self.name == "new"
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct MethodMetadata {
+ pub module_path: String,
+ pub self_name: String,
+ pub name: String,
+ pub is_async: bool,
+ pub inputs: Vec<FnParamMetadata>,
+ pub return_type: Option<Type>,
+ pub throws: Option<Type>,
+ pub takes_self_by_arc: bool, // unused except by rust udl bindgen.
+ pub checksum: Option<u16>,
+}
+
+impl MethodMetadata {
+ pub fn ffi_symbol_name(&self) -> String {
+ method_symbol_name(&self.module_path, &self.self_name, &self.name)
+ }
+
+ pub fn checksum_symbol_name(&self) -> String {
+ method_checksum_symbol_name(&self.module_path, &self.self_name, &self.name)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct TraitMethodMetadata {
+ pub module_path: String,
+ pub trait_name: String,
+ // Note: the position of `index` is important since it causes callback interface methods to be
+ // ordered correctly in MetadataGroup.items
+ pub index: u32,
+ pub name: String,
+ pub is_async: bool,
+ pub inputs: Vec<FnParamMetadata>,
+ pub return_type: Option<Type>,
+ pub throws: Option<Type>,
+ pub takes_self_by_arc: bool, // unused except by rust udl bindgen.
+ pub checksum: Option<u16>,
+}
+
+impl TraitMethodMetadata {
+ pub fn ffi_symbol_name(&self) -> String {
+ method_symbol_name(&self.module_path, &self.trait_name, &self.name)
+ }
+
+ pub fn checksum_symbol_name(&self) -> String {
+ method_checksum_symbol_name(&self.module_path, &self.trait_name, &self.name)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct FnParamMetadata {
+ pub name: String,
+ pub ty: Type,
+ pub by_ref: bool,
+ pub optional: bool,
+ pub default: Option<LiteralMetadata>,
+}
+
+impl FnParamMetadata {
+ pub fn simple(name: &str, ty: Type) -> Self {
+ Self {
+ name: name.to_string(),
+ ty,
+ by_ref: false,
+ optional: false,
+ default: None,
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Checksum)]
+pub enum LiteralMetadata {
+ 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,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct RecordMetadata {
+ pub module_path: String,
+ pub name: String,
+ pub fields: Vec<FieldMetadata>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct FieldMetadata {
+ pub name: String,
+ pub ty: Type,
+ pub default: Option<LiteralMetadata>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct EnumMetadata {
+ pub module_path: String,
+ pub name: String,
+ pub variants: Vec<VariantMetadata>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct VariantMetadata {
+ pub name: String,
+ pub fields: Vec<FieldMetadata>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct ObjectMetadata {
+ pub module_path: String,
+ pub name: String,
+ pub imp: types::ObjectImpl,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct CallbackInterfaceMetadata {
+ pub module_path: String,
+ pub name: String,
+}
+
+impl ObjectMetadata {
+ /// FFI symbol name for the `free` function for this object.
+ ///
+ /// This function is used to free the memory used by this object.
+ pub fn free_ffi_symbol_name(&self) -> String {
+ free_fn_symbol_name(&self.module_path, &self.name)
+ }
+}
+
+/// The list of traits we support generating helper methods for.
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum UniffiTraitMetadata {
+ Debug {
+ fmt: MethodMetadata,
+ },
+ Display {
+ fmt: MethodMetadata,
+ },
+ Eq {
+ eq: MethodMetadata,
+ ne: MethodMetadata,
+ },
+ Hash {
+ hash: MethodMetadata,
+ },
+}
+
+impl UniffiTraitMetadata {
+ fn module_path(&self) -> &String {
+ &match self {
+ UniffiTraitMetadata::Debug { fmt } => fmt,
+ UniffiTraitMetadata::Display { fmt } => fmt,
+ UniffiTraitMetadata::Eq { eq, .. } => eq,
+ UniffiTraitMetadata::Hash { hash } => hash,
+ }
+ .module_path
+ }
+
+ pub fn self_name(&self) -> &String {
+ &match self {
+ UniffiTraitMetadata::Debug { fmt } => fmt,
+ UniffiTraitMetadata::Display { fmt } => fmt,
+ UniffiTraitMetadata::Eq { eq, .. } => eq,
+ UniffiTraitMetadata::Hash { hash } => hash,
+ }
+ .self_name
+ }
+}
+
+#[repr(u8)]
+pub enum UniffiTraitDiscriminants {
+ Debug,
+ Display,
+ Eq,
+ Hash,
+}
+
+impl UniffiTraitDiscriminants {
+ pub fn from(v: u8) -> anyhow::Result<Self> {
+ Ok(match v {
+ 0 => UniffiTraitDiscriminants::Debug,
+ 1 => UniffiTraitDiscriminants::Display,
+ 2 => UniffiTraitDiscriminants::Eq,
+ 3 => UniffiTraitDiscriminants::Hash,
+ _ => anyhow::bail!("invalid trait discriminant {v}"),
+ })
+ }
+}
+
+#[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,
+ pub name: String,
+ pub builtin: Type,
+}
+
+/// Returns the last 16 bits of the value's hash as computed with [`SipHasher13`].
+///
+/// This is used as a safeguard against different UniFFI versions being used for scaffolding and
+/// bindings generation.
+pub fn checksum<T: Checksum>(val: &T) -> u16 {
+ let mut hasher = siphasher::sip::SipHasher13::new();
+ val.checksum(&mut hasher);
+ (hasher.finish() & 0x000000000000FFFF) as u16
+}
+
+/// Enum covering all the possible metadata types
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum Metadata {
+ Namespace(NamespaceMetadata),
+ UdlFile(UdlFile),
+ Func(FnMetadata),
+ Object(ObjectMetadata),
+ CallbackInterface(CallbackInterfaceMetadata),
+ Record(RecordMetadata),
+ Enum(EnumMetadata),
+ Error(ErrorMetadata),
+ Constructor(ConstructorMetadata),
+ Method(MethodMetadata),
+ TraitMethod(TraitMethodMetadata),
+ CustomType(CustomTypeMetadata),
+ UniffiTrait(UniffiTraitMetadata),
+}
+
+impl Metadata {
+ pub fn read(data: &[u8]) -> anyhow::Result<Self> {
+ read_metadata(data)
+ }
+
+ pub(crate) fn module_path(&self) -> &String {
+ match self {
+ Metadata::Namespace(meta) => &meta.crate_name,
+ Metadata::UdlFile(meta) => &meta.module_path,
+ Metadata::Func(meta) => &meta.module_path,
+ Metadata::Constructor(meta) => &meta.module_path,
+ Metadata::Method(meta) => &meta.module_path,
+ Metadata::Record(meta) => &meta.module_path,
+ Metadata::Enum(meta) => &meta.module_path,
+ 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(),
+ }
+ }
+}
+
+impl From<NamespaceMetadata> for Metadata {
+ fn from(value: NamespaceMetadata) -> Metadata {
+ Self::Namespace(value)
+ }
+}
+
+impl From<UdlFile> for Metadata {
+ fn from(value: UdlFile) -> Metadata {
+ Self::UdlFile(value)
+ }
+}
+
+impl From<FnMetadata> for Metadata {
+ fn from(value: FnMetadata) -> Metadata {
+ Self::Func(value)
+ }
+}
+
+impl From<ConstructorMetadata> for Metadata {
+ fn from(c: ConstructorMetadata) -> Self {
+ Self::Constructor(c)
+ }
+}
+
+impl From<MethodMetadata> for Metadata {
+ fn from(m: MethodMetadata) -> Self {
+ Self::Method(m)
+ }
+}
+
+impl From<RecordMetadata> for Metadata {
+ fn from(r: RecordMetadata) -> Self {
+ Self::Record(r)
+ }
+}
+
+impl From<EnumMetadata> for Metadata {
+ fn from(e: EnumMetadata) -> Self {
+ Self::Enum(e)
+ }
+}
+
+impl From<ErrorMetadata> for Metadata {
+ fn from(e: ErrorMetadata) -> Self {
+ Self::Error(e)
+ }
+}
+
+impl From<ObjectMetadata> for Metadata {
+ fn from(v: ObjectMetadata) -> Self {
+ Self::Object(v)
+ }
+}
+
+impl From<CallbackInterfaceMetadata> for Metadata {
+ fn from(v: CallbackInterfaceMetadata) -> Self {
+ Self::CallbackInterface(v)
+ }
+}
+
+impl From<TraitMethodMetadata> for Metadata {
+ fn from(v: TraitMethodMetadata) -> Self {
+ Self::TraitMethod(v)
+ }
+}
+
+impl From<CustomTypeMetadata> for Metadata {
+ fn from(v: CustomTypeMetadata) -> Self {
+ Self::CustomType(v)
+ }
+}
+
+impl From<UniffiTraitMetadata> for Metadata {
+ fn from(v: UniffiTraitMetadata) -> Self {
+ Self::UniffiTrait(v)
+ }
+}
diff --git a/third_party/rust/uniffi_meta/src/metadata.rs b/third_party/rust/uniffi_meta/src/metadata.rs
new file mode 100644
index 0000000000..6e490a4866
--- /dev/null
+++ b/third_party/rust/uniffi_meta/src/metadata.rs
@@ -0,0 +1,87 @@
+/* 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/. */
+
+// Copied from uniffi_core/src/metadata.rs
+// Due to a [Rust bug](https://github.com/rust-lang/rust/issues/113104) we don't want to pull in
+// `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`
+pub mod codes {
+ // Top-level metadata item codes
+ pub const FUNC: u8 = 0;
+ pub const METHOD: u8 = 1;
+ 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 UNKNOWN: u8 = 255;
+
+ // Type codes
+ pub const TYPE_U8: u8 = 0;
+ pub const TYPE_U16: u8 = 1;
+ pub const TYPE_U32: u8 = 2;
+ pub const TYPE_U64: u8 = 3;
+ pub const TYPE_I8: u8 = 4;
+ pub const TYPE_I16: u8 = 5;
+ pub const TYPE_I32: u8 = 6;
+ pub const TYPE_I64: u8 = 7;
+ pub const TYPE_F32: u8 = 8;
+ pub const TYPE_F64: u8 = 9;
+ pub const TYPE_BOOL: u8 = 10;
+ pub const TYPE_STRING: u8 = 11;
+ pub const TYPE_OPTION: u8 = 12;
+ pub const TYPE_RECORD: u8 = 13;
+ pub const TYPE_ENUM: u8 = 14;
+ // 15 no longer used.
+ pub const TYPE_INTERFACE: u8 = 16;
+ pub const TYPE_VEC: u8 = 17;
+ pub const TYPE_HASH_MAP: u8 = 18;
+ pub const TYPE_SYSTEM_TIME: u8 = 19;
+ pub const TYPE_DURATION: u8 = 20;
+ 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_UNIT: u8 = 255;
+
+ // Literal codes
+ pub const LIT_STR: u8 = 0;
+ pub const LIT_INT: u8 = 1;
+ pub const LIT_FLOAT: u8 = 2;
+ pub const LIT_BOOL: u8 = 3;
+ pub const LIT_NULL: u8 = 4;
+}
+
+// Create a checksum for a MetadataBuffer
+//
+// This is used by the bindings code to verify that the library they link to is the same one
+// that the bindings were generated from.
+pub const fn checksum_metadata(buf: &[u8]) -> u16 {
+ calc_checksum(buf, buf.len())
+}
+
+const fn calc_checksum(bytes: &[u8], size: usize) -> u16 {
+ // Taken from the fnv_hash() function from the FNV crate (https://github.com/servo/rust-fnv/blob/master/lib.rs).
+ // fnv_hash() hasn't been released in a version yet.
+ const INITIAL_STATE: u64 = 0xcbf29ce484222325;
+ const PRIME: u64 = 0x100000001b3;
+
+ let mut hash = INITIAL_STATE;
+ let mut i = 0;
+ while i < size {
+ hash ^= bytes[i] as u64;
+ hash = hash.wrapping_mul(PRIME);
+ i += 1;
+ }
+ // Convert the 64-bit hash to a 16-bit hash by XORing everything together
+ (hash ^ (hash >> 16) ^ (hash >> 32) ^ (hash >> 48)) as u16
+}
diff --git a/third_party/rust/uniffi_meta/src/reader.rs b/third_party/rust/uniffi_meta/src/reader.rs
new file mode 100644
index 0000000000..bf6525f2b5
--- /dev/null
+++ b/third_party/rust/uniffi_meta/src/reader.rs
@@ -0,0 +1,465 @@
+/* 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/. */
+
+use crate::metadata::{checksum_metadata, codes};
+use crate::*;
+use anyhow::{bail, ensure, Context, Result};
+
+pub fn read_metadata(data: &[u8]) -> Result<Metadata> {
+ MetadataReader::new(data).read_metadata()
+}
+
+// Read a metadata type, this is pub so that we can test it in the metadata fixture
+pub fn read_metadata_type(data: &[u8]) -> Result<Type> {
+ MetadataReader::new(data).read_type()
+}
+
+/// Helper struct for read_metadata()
+struct MetadataReader<'a> {
+ // This points to the initial data we were passed in
+ initial_data: &'a [u8],
+ // This points to the remaining data to be read
+ buf: &'a [u8],
+}
+
+impl<'a> MetadataReader<'a> {
+ fn new(data: &'a [u8]) -> Self {
+ Self {
+ initial_data: data,
+ buf: data,
+ }
+ }
+
+ // Read a top-level metadata item
+ //
+ // This consumes self because MetadataReader is only intended to read a single item.
+ fn read_metadata(mut self) -> Result<Metadata> {
+ let value = self.read_u8()?;
+ Ok(match value {
+ codes::NAMESPACE => NamespaceMetadata {
+ crate_name: self.read_string()?,
+ name: self.read_string()?,
+ }
+ .into(),
+ codes::UDL_FILE => UdlFile {
+ module_path: self.read_string()?,
+ namespace: self.read_string()?,
+ file_stub: self.read_string()?,
+ }
+ .into(),
+ codes::FUNC => self.read_func()?.into(),
+ 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::CALLBACK_INTERFACE => self.read_callback_interface()?.into(),
+ codes::TRAIT_METHOD => self.read_trait_method()?.into(),
+ codes::UNIFFI_TRAIT => self.read_uniffi_trait()?.into(),
+ _ => bail!("Unexpected metadata code: {value:?}"),
+ })
+ }
+
+ fn read_u8(&mut self) -> Result<u8> {
+ if !self.buf.is_empty() {
+ let value = self.buf[0];
+ self.buf = &self.buf[1..];
+ Ok(value)
+ } else {
+ bail!("Buffer is empty")
+ }
+ }
+
+ fn peek_u8(&mut self) -> Result<u8> {
+ if !self.buf.is_empty() {
+ Ok(self.buf[0])
+ } else {
+ bail!("Buffer is empty")
+ }
+ }
+
+ fn read_u32(&mut self) -> Result<u32> {
+ if self.buf.len() >= 4 {
+ // read the value as little-endian
+ let value = self.buf[0] as u32
+ + ((self.buf[1] as u32) << 8)
+ + ((self.buf[2] as u32) << 16)
+ + ((self.buf[3] as u32) << 24);
+ self.buf = &self.buf[4..];
+ Ok(value)
+ } else {
+ bail!("Not enough data left in buffer to read a u32 value");
+ }
+ }
+
+ fn read_bool(&mut self) -> Result<bool> {
+ Ok(self.read_u8()? == 1)
+ }
+
+ fn read_string(&mut self) -> Result<String> {
+ let size = self.read_u8()? as usize;
+ let slice;
+ (slice, self.buf) = self.buf.split_at(size);
+ String::from_utf8(slice.into()).context("Invalid string data")
+ }
+
+ fn read_type(&mut self) -> Result<Type> {
+ let value = self.read_u8()?;
+ Ok(match value {
+ codes::TYPE_U8 => Type::UInt8,
+ codes::TYPE_I8 => Type::Int8,
+ codes::TYPE_U16 => Type::UInt16,
+ codes::TYPE_I16 => Type::Int16,
+ codes::TYPE_U32 => Type::UInt32,
+ codes::TYPE_I32 => Type::Int32,
+ codes::TYPE_U64 => Type::UInt64,
+ codes::TYPE_I64 => Type::Int64,
+ codes::TYPE_F32 => Type::Float32,
+ codes::TYPE_F64 => Type::Float64,
+ codes::TYPE_BOOL => Type::Boolean,
+ 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()?,
+ },
+ codes::TYPE_ENUM => Type::Enum {
+ module_path: self.read_string()?,
+ name: self.read_string()?,
+ },
+ codes::TYPE_INTERFACE => Type::Object {
+ module_path: self.read_string()?,
+ name: self.read_string()?,
+ imp: ObjectImpl::from_is_trait(self.read_bool()?),
+ },
+ codes::TYPE_CALLBACK_INTERFACE => Type::CallbackInterface {
+ module_path: self.read_string()?,
+ name: self.read_string()?,
+ },
+ codes::TYPE_CUSTOM => Type::Custom {
+ module_path: self.read_string()?,
+ name: self.read_string()?,
+ builtin: Box::new(self.read_type()?),
+ },
+ codes::TYPE_OPTION => Type::Optional {
+ inner_type: Box::new(self.read_type()?),
+ },
+ codes::TYPE_VEC => {
+ let inner_type = self.read_type()?;
+ if inner_type == Type::UInt8 {
+ Type::Bytes
+ } else {
+ Type::Sequence {
+ inner_type: Box::new(inner_type),
+ }
+ }
+ }
+ codes::TYPE_HASH_MAP => Type::Map {
+ key_type: Box::new(self.read_type()?),
+ value_type: Box::new(self.read_type()?),
+ },
+ codes::TYPE_UNIT => bail!("Unexpected TYPE_UNIT"),
+ codes::TYPE_RESULT => bail!("Unexpected TYPE_RESULT"),
+ _ => bail!("Unexpected metadata type code: {value:?}"),
+ })
+ }
+
+ fn read_optional_type(&mut self) -> Result<Option<Type>> {
+ Ok(match self.peek_u8()? {
+ codes::TYPE_UNIT => {
+ _ = self.read_u8();
+ None
+ }
+ _ => Some(self.read_type()?),
+ })
+ }
+
+ fn read_return_type(&mut self) -> Result<(Option<Type>, Option<Type>)> {
+ Ok(match self.peek_u8()? {
+ codes::TYPE_UNIT => {
+ _ = self.read_u8();
+ (None, None)
+ }
+ codes::TYPE_RESULT => {
+ _ = self.read_u8();
+ (self.read_optional_type()?, self.read_optional_type()?)
+ }
+ _ => (Some(self.read_type()?), None),
+ })
+ }
+
+ fn read_func(&mut self) -> Result<FnMetadata> {
+ let module_path = 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()?;
+ Ok(FnMetadata {
+ module_path,
+ name,
+ is_async,
+ inputs,
+ return_type,
+ throws,
+ checksum: self.calc_checksum(),
+ })
+ }
+
+ fn read_constructor(&mut self) -> Result<ConstructorMetadata> {
+ let module_path = self.read_string()?;
+ let self_name = self.read_string()?;
+ let name = self.read_string()?;
+ let inputs = self.read_inputs()?;
+ let (return_type, throws) = self.read_return_type()?;
+
+ return_type
+ .filter(|t| {
+ matches!(
+ t,
+ Type::Object { name, imp: ObjectImpl::Struct, .. } if name == &self_name
+ )
+ })
+ .context("Constructor return type must be Arc<Self>")?;
+
+ Ok(ConstructorMetadata {
+ module_path,
+ self_name,
+ name,
+ inputs,
+ throws,
+ checksum: self.calc_checksum(),
+ })
+ }
+
+ fn read_method(&mut self) -> Result<MethodMetadata> {
+ 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()?;
+ Ok(MethodMetadata {
+ module_path,
+ self_name,
+ name,
+ is_async,
+ inputs,
+ return_type,
+ throws,
+ takes_self_by_arc: false, // not emitted by macros
+ checksum: self.calc_checksum(),
+ })
+ }
+
+ fn read_record(&mut self) -> Result<RecordMetadata> {
+ Ok(RecordMetadata {
+ module_path: self.read_string()?,
+ name: self.read_string()?,
+ fields: self.read_fields()?,
+ })
+ }
+
+ fn read_enum(&mut self, is_flat_error: bool) -> Result<EnumMetadata> {
+ let module_path = self.read_string()?;
+ let name = self.read_string()?;
+ let variants = if is_flat_error {
+ self.read_flat_variants()?
+ } else {
+ self.read_variants()?
+ };
+
+ Ok(EnumMetadata {
+ module_path,
+ name,
+ variants,
+ })
+ }
+
+ fn read_error(&mut self) -> Result<ErrorMetadata> {
+ 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<ObjectMetadata> {
+ Ok(ObjectMetadata {
+ module_path: self.read_string()?,
+ name: self.read_string()?,
+ imp: ObjectImpl::from_is_trait(self.read_bool()?),
+ })
+ }
+
+ fn read_uniffi_trait(&mut self) -> Result<UniffiTraitMetadata> {
+ let code = self.read_u8()?;
+ let mut read_metadata_method = || -> Result<MethodMetadata> {
+ let code = self.read_u8()?;
+ ensure!(code == codes::METHOD, "expected METHOD but read {code}");
+ self.read_method()
+ };
+
+ Ok(match UniffiTraitDiscriminants::from(code)? {
+ UniffiTraitDiscriminants::Debug => UniffiTraitMetadata::Debug {
+ fmt: read_metadata_method()?,
+ },
+ UniffiTraitDiscriminants::Display => UniffiTraitMetadata::Display {
+ fmt: read_metadata_method()?,
+ },
+ UniffiTraitDiscriminants::Eq => UniffiTraitMetadata::Eq {
+ eq: read_metadata_method()?,
+ ne: read_metadata_method()?,
+ },
+ UniffiTraitDiscriminants::Hash => UniffiTraitMetadata::Hash {
+ hash: read_metadata_method()?,
+ },
+ })
+ }
+
+ fn read_callback_interface(&mut self) -> Result<CallbackInterfaceMetadata> {
+ Ok(CallbackInterfaceMetadata {
+ module_path: self.read_string()?,
+ name: self.read_string()?,
+ })
+ }
+
+ fn read_trait_method(&mut self) -> Result<TraitMethodMetadata> {
+ let module_path = self.read_string()?;
+ let trait_name = self.read_string()?;
+ let index = self.read_u32()?;
+ let name = self.read_string()?;
+ let is_async = self.read_bool()?;
+ let inputs = self.read_inputs()?;
+ let (return_type, throws) = self.read_return_type()?;
+ Ok(TraitMethodMetadata {
+ module_path,
+ trait_name,
+ index,
+ name,
+ is_async,
+ inputs,
+ return_type,
+ throws,
+ takes_self_by_arc: false, // not emitted by macros
+ checksum: self.calc_checksum(),
+ })
+ }
+
+ fn read_fields(&mut self) -> Result<Vec<FieldMetadata>> {
+ let len = self.read_u8()?;
+ (0..len)
+ .map(|_| {
+ let name = self.read_string()?;
+ let ty = self.read_type()?;
+ let default = self.read_default(&name, &ty)?;
+ Ok(FieldMetadata { name, ty, default })
+ })
+ .collect()
+ }
+
+ fn read_variants(&mut self) -> Result<Vec<VariantMetadata>> {
+ let len = self.read_u8()?;
+ (0..len)
+ .map(|_| {
+ Ok(VariantMetadata {
+ name: self.read_string()?,
+ fields: self.read_fields()?,
+ })
+ })
+ .collect()
+ }
+
+ fn read_flat_variants(&mut self) -> Result<Vec<VariantMetadata>> {
+ let len = self.read_u8()?;
+ (0..len)
+ .map(|_| {
+ Ok(VariantMetadata {
+ name: self.read_string()?,
+ fields: vec![],
+ })
+ })
+ .collect()
+ }
+
+ fn read_inputs(&mut self) -> Result<Vec<FnParamMetadata>> {
+ let len = self.read_u8()?;
+ (0..len)
+ .map(|_| {
+ Ok(FnParamMetadata {
+ name: self.read_string()?,
+ ty: self.read_type()?,
+ // not emitted by macros
+ by_ref: false,
+ optional: false,
+ default: None,
+ })
+ })
+ .collect()
+ }
+
+ fn calc_checksum(&self) -> Option<u16> {
+ let bytes_read = self.initial_data.len() - self.buf.len();
+ let metadata_buf = &self.initial_data[..bytes_read];
+ Some(checksum_metadata(metadata_buf))
+ }
+
+ fn read_default(&mut self, name: &str, ty: &Type) -> Result<Option<LiteralMetadata>> {
+ let has_default = self.read_bool()?;
+ if !has_default {
+ return Ok(None);
+ }
+
+ let literal_kind = self.read_u8()?;
+ Ok(Some(match literal_kind {
+ codes::LIT_STR => {
+ ensure!(
+ matches!(ty, Type::String),
+ "field {name} of type {ty:?} can't have a default value of type string"
+ );
+ LiteralMetadata::String(self.read_string()?)
+ }
+ codes::LIT_INT => {
+ let base10_digits = self.read_string()?;
+ macro_rules! parse_int {
+ ($ty:ident, $variant:ident) => {
+ LiteralMetadata::$variant(
+ base10_digits
+ .parse::<$ty>()
+ .with_context(|| format!("parsing default for field {name}"))?
+ .into(),
+ Radix::Decimal,
+ ty.to_owned(),
+ )
+ };
+ }
+
+ match ty {
+ Type::UInt8 => parse_int!(u8, UInt),
+ Type::Int8 => parse_int!(i8, Int),
+ Type::UInt16 => parse_int!(u16, UInt),
+ Type::Int16 => parse_int!(i16, Int),
+ Type::UInt32 => parse_int!(u32, UInt),
+ Type::Int32 => parse_int!(i32, Int),
+ Type::UInt64 => parse_int!(u64, UInt),
+ Type::Int64 => parse_int!(i64, Int),
+ _ => {
+ bail!("field {name} of type {ty:?} can't have a default value of type integer");
+ }
+ }
+ }
+ codes::LIT_FLOAT => match ty {
+ Type::Float32 | Type::Float64 => {
+ LiteralMetadata::Float(self.read_string()?, ty.to_owned())
+ }
+ _ => {
+ bail!("field {name} of type {ty:?} can't have a default value of type float");
+ }
+ },
+ codes::LIT_BOOL => LiteralMetadata::Boolean(self.read_bool()?),
+ codes::LIT_NULL => LiteralMetadata::Null,
+ _ => 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
new file mode 100644
index 0000000000..24f8a6f2a8
--- /dev/null
+++ b/third_party/rust/uniffi_meta/src/types.rs
@@ -0,0 +1,172 @@
+/* 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.
+//!
+//! 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 crate::Checksum;
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Checksum, Ord, PartialOrd)]
+pub enum ObjectImpl {
+ Struct,
+ Trait,
+}
+
+impl ObjectImpl {
+ /// Return the fully qualified name which should be used by Rust code for
+ /// an object with the given name.
+ /// 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 {
+ 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
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Checksum, Ord, PartialOrd)]
+pub enum ExternalKind {
+ Interface,
+ // Either a record or enum
+ DataClass,
+}
+
+/// 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,
+ Bytes,
+ Timestamp,
+ Duration,
+ Object {
+ // The module path to the object
+ module_path: String,
+ // The name in the "type universe"
+ name: String,
+ // 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,
+ name: String,
+ },
+ Enum {
+ module_path: String,
+ name: String,
+ },
+ CallbackInterface {
+ module_path: String,
+ name: String,
+ },
+ // Structurally recursive types.
+ Optional {
+ inner_type: Box<Type>,
+ },
+ Sequence {
+ inner_type: Box<Type>,
+ },
+ Map {
+ key_type: Box<Type>,
+ value_type: Box<Type>,
+ },
+ // An FfiConverter we `use` from an external crate
+ External {
+ module_path: String,
+ name: String,
+ #[checksum_ignore] // The namespace is not known generating scaffolding.
+ namespace: String,
+ kind: ExternalKind,
+ tagged: bool, // does its FfiConverter use <UniFFITag>?
+ },
+ // Custom type on the scaffolding side
+ Custom {
+ module_path: String,
+ name: String,
+ builtin: Box<Type>,
+ },
+}
+
+impl Type {
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ let nested_types = match self {
+ Type::Optional { inner_type } | Type::Sequence { inner_type } => {
+ inner_type.iter_types()
+ }
+ Type::Map {
+ key_type,
+ value_type,
+ } => Box::new(key_type.iter_types().chain(value_type.iter_types())),
+ _ => Box::new(std::iter::empty()),
+ };
+ Box::new(std::iter::once(self).chain(nested_types))
+ }
+}
+
+// A trait so various things can turn into a type.
+pub trait AsType: core::fmt::Debug {
+ fn as_type(&self) -> Type;
+}
+
+impl AsType for Type {
+ fn as_type(&self) -> Type {
+ self.clone()
+ }
+}
+
+// Needed to handle &&Type and &&&Type values, which we sometimes end up with in the template code
+impl<T, C> AsType for T
+where
+ T: std::ops::Deref<Target = C> + std::fmt::Debug,
+ C: AsType,
+{
+ fn as_type(&self) -> Type {
+ self.deref().as_type()
+ }
+}
+
+/// 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>;