Revision control
Copy as Markdown
Other Tools
/* 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
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 = 26;
/// 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>,
pub docstring: Option<String>,
}
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 is_async: bool,
pub inputs: Vec<FnParamMetadata>,
pub throws: Option<Type>,
pub checksum: Option<u16>,
pub docstring: Option<String>,
}
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>,
pub docstring: Option<String>,
}
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>,
pub docstring: Option<String>,
}
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,
None,
Some { inner: Box<LiteralMetadata> },
}
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.
// 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>,
pub docstring: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct FieldMetadata {
pub name: String,
pub ty: Type,
pub default: Option<LiteralMetadata>,
pub docstring: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Checksum)]
pub enum EnumShape {
Enum,
Error { flat: bool },
}
impl EnumShape {
pub fn as_u8(&self) -> u8 {
match self {
EnumShape::Enum => 0,
EnumShape::Error { flat: false } => 1,
EnumShape::Error { flat: true } => 2,
}
}
pub fn from(v: u8) -> anyhow::Result<Self> {
Ok(match v {
0 => EnumShape::Enum,
1 => EnumShape::Error { flat: false },
2 => EnumShape::Error { flat: true },
_ => anyhow::bail!("invalid enum shape discriminant {v}"),
})
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct EnumMetadata {
pub module_path: String,
pub name: String,
pub shape: EnumShape,
pub variants: Vec<VariantMetadata>,
pub discr_type: Option<Type>,
pub non_exhaustive: bool,
pub docstring: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct VariantMetadata {
pub name: String,
pub discr: Option<LiteralMetadata>,
pub fields: Vec<FieldMetadata>,
pub docstring: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct ObjectMetadata {
pub module_path: String,
pub name: String,
pub imp: types::ObjectImpl,
pub docstring: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct CallbackInterfaceMetadata {
pub module_path: String,
pub name: String,
pub docstring: Option<String>,
}
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.
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)]
#[derive(Eq, PartialEq, Hash)]
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 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),
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::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<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)
}
}