/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! # Object definitions for a `ComponentInterface`. //! //! This module converts "interface" definitions from UDL into [`Object`] structures //! that can be added to a `ComponentInterface`, which are the main way we define stateful //! objects with behaviour for a UniFFI Rust Component. An [`Object`] is an opaque handle //! to some state on which methods can be invoked. //! //! (The terminology mismatch between "interface" and "object" is a historical artifact of //! this tool prior to committing to WebIDL syntax). //! //! A declaration in the UDL like this: //! //! ``` //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" //! # namespace example {}; //! interface Example { //! constructor(string? name); //! string my_name(); //! }; //! # "##, "crate_name")?; //! # Ok::<(), anyhow::Error>(()) //! ``` //! //! Will result in an [`Object`] member with one [`Constructor`] and one [`Method`] being added //! to the resulting [`crate::ComponentInterface`]: //! //! ``` //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" //! # namespace example {}; //! # interface Example { //! # constructor(string? name); //! # string my_name(); //! # }; //! # "##, "crate_name")?; //! let obj = ci.get_object_definition("Example").unwrap(); //! assert_eq!(obj.name(), "Example"); //! assert_eq!(obj.constructors().len(), 1); //! assert_eq!(obj.constructors()[0].arguments()[0].name(), "name"); //! assert_eq!(obj.methods().len(),1 ); //! assert_eq!(obj.methods()[0].name(), "my_name"); //! # Ok::<(), anyhow::Error>(()) //! ``` //! //! It's not necessary for all interfaces to have constructors. //! ``` //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" //! # namespace example {}; //! # interface Example {}; //! # "##, "crate_name")?; //! let obj = ci.get_object_definition("Example").unwrap(); //! assert_eq!(obj.name(), "Example"); //! assert_eq!(obj.constructors().len(), 0); //! # Ok::<(), anyhow::Error>(()) //! ```
use anyhow::Result; use uniffi_meta::{Checksum, ObjectTraitImplMetadata};
/// An "object" is an opaque type that is passed around by reference, can /// have methods called on it, and so on - basically your classic Object Oriented Programming /// type of deal, except without elaborate inheritance hierarchies. Some can be instantiated. /// /// In UDL these correspond to the `interface` keyword. /// /// At the FFI layer, objects are represented by an opaque integer handle and a set of functions /// a common prefix. The object's constructors are functions that return new objects by handle, /// and its methods are functions that take a handle as first argument. The foreign language /// binding code is expected to stitch these functions back together into an appropriate class /// definition (or that language's equivalent thereof). /// /// TODO: /// - maybe "Class" would be a better name than "Object" here? #[derive(Debug, Clone, Checksum)] pubstruct Object { pub(super) name: String, /// How this object is implemented in Rust pub(super) imp: ObjectImpl, pub(super) module_path: String, pub(super) remote: bool, pub(super) constructors: Vec<Constructor>, pub(super) methods: Vec<Method>, // The "trait" methods - they have a (presumably "well known") name, and // a regular method (albeit with a generated name) // XXX - this should really be a HashSet, but not enough transient types support hash to make it worthwhile now. pub(super) uniffi_traits: Vec<UniffiTrait>, // These are traits described in our CI which this object has declared it implements. // This allows foreign bindings to implement things like inheritance or whatever makes sense for them. pub(super) trait_impls: Vec<ObjectTraitImplMetadata>, // We don't include the FfiFuncs in the hash calculation, because: // - it is entirely determined by the other fields, // so excluding it is safe. // - its `name` property includes a checksum derived from the very // hash value we're trying to calculate here, so excluding it // avoids a weird circular dependency in the calculation.
// FFI function to clone a pointer for this object #[checksum_ignore] pub(super) ffi_func_clone: FfiFunction, // FFI function to free a pointer for this object #[checksum_ignore] pub(super) ffi_func_free: FfiFunction, // Ffi function to initialize the foreign callback for trait interfaces #[checksum_ignore] pub(super) ffi_init_callback: Option<FfiFunction>, #[checksum_ignore] pub(super) docstring: Option<String>,
}
/// Returns the fully qualified name that should be used by Rust code for this object. /// Includes `r#`, traits get a leading `dyn`. If we ever supported associated types, then /// this would also include them. pubfn rust_name(&self) -> String { self.imp.rust_name_for(&self.name)
}
for cons inself.constructors.iter_mut() {
cons.derive_ffi_func();
} for meth inself.methods.iter_mut() {
meth.derive_ffi_func()?;
} for ut inself.uniffi_traits.iter_mut() {
ut.derive_ffi_func()?;
}
Ok(())
}
/// For trait interfaces, FfiCallbacks to define for our methods, otherwise an empty vec. pubfn ffi_callbacks(&self) -> Vec<FfiCallbackFunction> { ifself.is_trait_interface() {
callbacks::ffi_callbacks(&self.name, &>self.methods)
} else {
vec![]
}
}
/// For trait interfaces, the VTable FFI type pubfn vtable(&self) -> Option<FfiType> { self.is_trait_interface()
.then(|| FfiType::Struct(callbacks::vtable_name(&self.name)))
}
/// For trait interfaces, the VTable struct to define. Otherwise None. pubfn vtable_definition(&self) -> Option<FfiStruct> { self.is_trait_interface()
.then(|| callbacks::vtable_struct(&self.name, &self.methods))
}
// Represents a constructor for an object type. // // In the FFI, this will be a function that returns a pointer to an instance // of the corresponding object type. #[derive(Debug, Clone, Checksum)] pubstruct Constructor { pub(super) name: String, pub(super) object_name: String, pub(super) object_module_path: String, pub(super) is_async: bool, pub(super) arguments: Vec<Argument>, // We don't include the FFIFunc in the hash calculation, because: // - it is entirely determined by the other fields, // so excluding it is safe. // - its `name` property includes a checksum derived from the very // hash value we're trying to calculate here, so excluding it // avoids a weird circular dependency in the calculation. #[checksum_ignore] pub(super) ffi_func: FfiFunction, #[checksum_ignore] pub(super) docstring: Option<String>, pub(super) throws: Option<Type>, pub(super) checksum_fn_name: String, // Force a checksum value, or we'll fallback to the trait. #[checksum_ignore] pub(super) checksum: Option<u16>, // to help with lifetimes elsewhere... pub(super) self_type: Type,
}
// Represents an instance method for an object type. // // The FFI will represent this as a function whose first/self argument is a // `FfiType::RustArcPtr` to the instance. #[derive(Debug, Clone, Checksum)] pubstruct Method { pub(super) name: String, pub(super) is_async: bool, // Ignore `self_type` for the checksum, we never compare the method checksums for methods from // different objects. #[checksum_ignore] pub(super) self_type: Type, pub(super) arguments: Vec<Argument>, pub(super) return_type: Option<Type>, // We don't include the FFIFunc in the hash calculation, because: // - it is entirely determined by the other fields, // so excluding it is safe. // - its `name` property includes a checksum derived from the very // hash value we're trying to calculate here, so excluding it // avoids a weird circular dependency in the calculation. #[checksum_ignore] pub(super) ffi_func: FfiFunction, #[checksum_ignore] pub(super) docstring: Option<String>, pub(super) throws: Option<Type>, pub(super) takes_self_by_arc: bool, pub(super) checksum_fn_name: String, // Force a checksum value, or we'll fallback to the trait. #[checksum_ignore] pub(super) checksum: Option<u16>,
}
/// For async callback interface methods, the FFI struct to pass to the completion function. pubfn foreign_future_ffi_result_struct(&self) -> FfiStruct {
callbacks::foreign_future_ffi_result_struct(self.return_type.as_ref().map(FfiType::from))
}
// construct from metadata - like `From<>` but with extra args pubfn from_metadata(meta: uniffi_meta::MethodMetadata, receiver: Type) -> Self { let ffi_name = meta.ffi_symbol_name(); let checksum_fn_name = meta.checksum_symbol_name(); let arguments = meta.inputs.into_iter().map(Into::into).collect();
pubfn checksum_from_metadata(meta: uniffi_meta::MethodMetadata) -> u16 { // We can use an arbitrary `self_type` for this, since it's ignored for the checksum
uniffi_meta::checksum(&Self::from_metadata(meta, Type::UInt8))
}
}
/// The list of traits we support generating helper methods for. #[allow(clippy::large_enum_variant)] #[derive(Clone, Debug, Checksum)] pubenum UniffiTrait {
Debug { fmt: Method },
Display { fmt: Method },
Eq { eq: Method, ne: Method },
Hash { hash: Method },
Ord { cmp: Method },
}
impl UniffiTrait { pubfn iter_types(&self) -> TypeIterator<'_> { Box::new( matchself {
UniffiTrait::Display { fmt: m }
| UniffiTrait::Debug { fmt: m }
| UniffiTrait::Hash { hash: m }
| UniffiTrait::Ord { cmp: m } => vec![m.iter_types()],
UniffiTrait::Eq { eq, ne } => vec![eq.iter_types(), ne.iter_types()],
}
.into_iter()
.flatten(),
)
}
pubfn derive_ffi_func(&mutself) -> Result<()> { matchself {
UniffiTrait::Display { fmt: m }
| UniffiTrait::Debug { fmt: m }
| UniffiTrait::Hash { hash: m }
| UniffiTrait::Ord { cmp: m } => {
m.derive_ffi_func()?;
}
UniffiTrait::Eq { eq, ne } => {
eq.derive_ffi_func()?;
ne.derive_ffi_func()?;
}
}
Ok(())
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.