in uniffi_bindgen/src/pipeline/general/callback_interfaces.rs [77:208]
fn add_vtable_ffi_definitions(module: &mut Module) -> Result<()> {
let mut ffi_definitions = vec![];
let mut seen_return_types = HashSet::new();
let module_name = module.name.clone();
module.try_visit(|vtable: &VTable| {
let interface_name = &vtable.interface_name;
// FFI Function Type for each method in the VTable
for (i, meth) in vtable.methods.iter().enumerate() {
let method_name = format!("CallbackInterface{interface_name}Method{i}");
match &meth.callable.async_data {
Some(async_data) => {
ffi_definitions.push(vtable_method_async(
&module_name,
interface_name,
method_name,
async_data,
&meth.callable,
)?);
}
None => {
ffi_definitions.push(vtable_method(
&module_name,
interface_name,
method_name,
&meth.callable,
));
}
}
// Async-related FFI definitions
let Some(async_info) = &meth.callable.async_data else {
continue;
};
let ffi_return_type = meth
.callable
.return_type
.ty
.clone()
.map(|return_type| return_type.ffi_type);
if seen_return_types.insert(ffi_return_type.clone()) {
ffi_definitions.extend([
FfiStruct {
name: async_info.ffi_foreign_future_result.clone(),
fields: match ffi_return_type {
Some(return_ffi_type) => vec![
FfiField::new("return_value", return_ffi_type),
FfiField::new("call_status", FfiType::RustCallStatus),
],
None => vec![
// In Rust, `return_value` is `()` -- a ZST.
// ZSTs are not valid in `C`, but they also take up 0 space.
// Skip the `return_value` field to make the layout correct.
FfiField::new("call_status", FfiType::RustCallStatus),
],
},
}
.into(),
FfiFunctionType {
name: async_info.ffi_foreign_future_complete.clone(),
arguments: vec![
FfiArgument::new(
"callback_data",
FfiType::Handle(HandleKind::ForeignFuture),
),
FfiArgument::new(
"result",
FfiType::Struct(async_info.ffi_foreign_future_result.clone()),
),
],
return_type: FfiReturnType { ty: None },
has_rust_call_status_arg: false,
}
.into(),
]);
}
}
// FFIStruct for the VTable
ffi_definitions.extend([
FfiFunctionType {
name: FfiFunctionTypeName(format!(
"CallbackInterfaceFree{module_name}_{interface_name}"
)),
arguments: vec![FfiArgument::new(
"handle",
FfiType::Handle(HandleKind::CallbackInterface {
module_name: module_name.to_string(),
interface_name: interface_name.to_string(),
}),
)],
return_type: FfiReturnType { ty: None },
has_rust_call_status_arg: false,
}
.into(),
FfiStruct {
name: FfiStructName(format!("VTableCallbackInterface{}", interface_name)),
fields: vtable
.methods
.iter()
.map(|vtable_meth| {
FfiField::new(&vtable_meth.callable.name, vtable_meth.ffi_type.clone())
})
.chain([FfiField::new(
"uniffi_free",
FfiType::Function(FfiFunctionTypeName(format!(
"CallbackInterfaceFree{module_name}_{interface_name}"
))),
)])
.collect(),
}
.into(),
]);
// FFI Function to initialize the VTable
ffi_definitions.push(
FfiFunction {
name: vtable.init_fn.clone(),
arguments: vec![FfiArgument {
name: "vtable".into(),
ty: FfiType::Reference(Box::new(vtable.struct_type.clone())),
}],
return_type: FfiReturnType { ty: None },
async_data: None,
has_rust_call_status_arg: false,
kind: FfiFunctionKind::RustVtableInit,
..FfiFunction::default()
}
.into(),
);
Ok(())
})?;
module.ffi_definitions.extend(ffi_definitions);
Ok(())
}