fn add_vtable_ffi_definitions()

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(())
}