/* 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>(()) //! ```
/// 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) 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>, // 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>,
}
// 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) object_name: String, pub(super) object_module_path: String, pub(super) is_async: bool, pub(super) object_impl: ObjectImpl, 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>,
}
// Methods have a special implicit first argument for the object instance, // hence `arguments` and `full_arguments` are different. pubfn full_arguments(&self) -> Vec<Argument> {
vec![Argument {
name: "ptr".to_string(), // TODO: ideally we'd get this via `ci.resolve_type_expression` so that it // is contained in the proper `TypeUniverse`, but this works for now.
type_: Type::Object {
name: self.object_name.clone(),
module_path: self.object_module_path.clone(),
imp: self.object_impl,
},
by_ref: !self.takes_self_by_arc,
optional: false,
default: None,
}]
.into_iter()
.chain(self.arguments.iter().cloned())
.collect()
}
/// 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))
}
}
impl From<uniffi_meta::MethodMetadata> for Method { fn from(meta: uniffi_meta::MethodMetadata) -> Self { let ffi_name = meta.ffi_symbol_name(); let checksum_fn_name = meta.checksum_symbol_name(); let is_async = meta.is_async; let return_type = meta.return_type.map(Into::into); let arguments = meta.inputs.into_iter().map(Into::into).collect();
let ffi_func = FfiFunction {
name: ffi_name,
is_async,
..FfiFunction::default()
};
Self {
name: meta.name,
object_name: meta.self_name,
object_module_path: meta.module_path,
is_async,
object_impl: ObjectImpl::Struct, // will be filled in later
arguments,
return_type,
ffi_func,
docstring: meta.docstring.clone(),
throws: meta.throws.map(Into::into),
takes_self_by_arc: meta.takes_self_by_arc,
checksum_fn_name,
checksum: meta.checksum,
}
}
}
impl From<uniffi_meta::TraitMethodMetadata> for Method { fn from(meta: uniffi_meta::TraitMethodMetadata) -> Self { let ffi_name = meta.ffi_symbol_name(); let checksum_fn_name = meta.checksum_symbol_name(); let is_async = meta.is_async; let return_type = meta.return_type.map(Into::into); let arguments = meta.inputs.into_iter().map(Into::into).collect(); let ffi_func = FfiFunction {
name: ffi_name,
is_async,
..FfiFunction::default()
}; Self {
name: meta.name,
object_name: meta.trait_name,
object_module_path: meta.module_path,
is_async,
arguments,
return_type,
docstring: meta.docstring.clone(),
throws: meta.throws.map(Into::into),
takes_self_by_arc: meta.takes_self_by_arc,
checksum_fn_name,
checksum: meta.checksum,
ffi_func,
object_impl: ObjectImpl::Struct,
}
}
}
/// The list of traits we support generating helper methods for. #[derive(Clone, Debug, Checksum)] pubenum UniffiTrait {
Debug { fmt: Method },
Display { fmt: Method },
Eq { eq: Method, ne: Method },
Hash { hash: Method },
}
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.