Source code

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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::Result;
use crate::util::mod_path;
use uniffi_meta::UNIFFI_CONTRACT_VERSION;
pub fn setup_scaffolding(namespace: String) -> Result<TokenStream> {
let module_path = mod_path()?;
let ffi_contract_version_ident = format_ident!("ffi_{module_path}_uniffi_contract_version");
let namespace_upper = namespace.to_ascii_uppercase();
let namespace_const_ident = format_ident!("UNIFFI_META_CONST_NAMESPACE_{namespace_upper}");
let namespace_static_ident = format_ident!("UNIFFI_META_NAMESPACE_{namespace_upper}");
let ffi_rustbuffer_alloc_ident = format_ident!("ffi_{module_path}_rustbuffer_alloc");
let ffi_rustbuffer_from_bytes_ident = format_ident!("ffi_{module_path}_rustbuffer_from_bytes");
let ffi_rustbuffer_free_ident = format_ident!("ffi_{module_path}_rustbuffer_free");
let ffi_rustbuffer_reserve_ident = format_ident!("ffi_{module_path}_rustbuffer_reserve");
let reexport_hack_ident = format_ident!("{module_path}_uniffi_reexport_hack");
let ffi_rust_future_scaffolding_fns = rust_future_scaffolding_fns(&module_path);
Ok(quote! {
// Unit struct to parameterize the FfiConverter trait.
//
// We use FfiConverter<UniFfiTag> to handle lowering/lifting/serializing types for this crate. See
// for details.
//
// This is pub, since we need to access it to support external types
#[doc(hidden)]
pub struct UniFfiTag;
#[allow(clippy::missing_safety_doc, missing_docs)]
#[doc(hidden)]
#[no_mangle]
pub extern "C" fn #ffi_contract_version_ident() -> ::std::primitive::u32 {
#UNIFFI_CONTRACT_VERSION
}
/// Export namespace metadata.
///
/// See `uniffi_bindgen::macro_metadata` for how this is used.
const #namespace_const_ident: ::uniffi::MetadataBuffer =
::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::NAMESPACE)
.concat_str(#module_path)
.concat_str(#namespace);
#[doc(hidden)]
#[no_mangle]
pub static #namespace_static_ident: [::std::primitive::u8; #namespace_const_ident.size] =
#namespace_const_ident.into_array();
// Everybody gets basic buffer support, since it's needed for passing complex types over the FFI.
//
// See `uniffi/src/ffi/rustbuffer.rs` for documentation on these functions
#[allow(clippy::missing_safety_doc, missing_docs)]
#[doc(hidden)]
#[no_mangle]
pub extern "C" fn #ffi_rustbuffer_alloc_ident(
size: ::std::primitive::u64,
call_status: &mut ::uniffi::RustCallStatus,
) -> ::uniffi::RustBuffer {
::uniffi::ffi::uniffi_rustbuffer_alloc(size, call_status)
}
#[allow(clippy::missing_safety_doc, missing_docs)]
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn #ffi_rustbuffer_from_bytes_ident(
bytes: ::uniffi::ForeignBytes,
call_status: &mut ::uniffi::RustCallStatus,
) -> ::uniffi::RustBuffer {
::uniffi::ffi::uniffi_rustbuffer_from_bytes(bytes, call_status)
}
#[allow(clippy::missing_safety_doc, missing_docs)]
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn #ffi_rustbuffer_free_ident(
buf: ::uniffi::RustBuffer,
call_status: &mut ::uniffi::RustCallStatus,
) {
::uniffi::ffi::uniffi_rustbuffer_free(buf, call_status);
}
#[allow(clippy::missing_safety_doc, missing_docs)]
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn #ffi_rustbuffer_reserve_ident(
buf: ::uniffi::RustBuffer,
additional: ::std::primitive::u64,
call_status: &mut ::uniffi::RustCallStatus,
) -> ::uniffi::RustBuffer {
::uniffi::ffi::uniffi_rustbuffer_reserve(buf, additional, call_status)
}
#ffi_rust_future_scaffolding_fns
// Code to re-export the UniFFI scaffolding functions.
//
// Rust won't always re-export the functions from dependencies
//
// A workaround for this is to have the dependent crate reference a function from its dependency in
// an extern "C" function. This is clearly hacky and brittle, but at least we have some unittests
// that check if this works (fixtures/reexport-scaffolding-macro).
//
// The main way we use this macro is for that contain multiple UniFFI components (libxul,
// megazord). The combined library has a cargo dependency for each component and calls
// uniffi_reexport_scaffolding!() for each one.
#[allow(missing_docs)]
#[doc(hidden)]
pub const fn uniffi_reexport_hack() {}
#[doc(hidden)]
#[macro_export]
macro_rules! uniffi_reexport_scaffolding {
() => {
#[doc(hidden)]
#[no_mangle]
pub extern "C" fn #reexport_hack_ident() {
$crate::uniffi_reexport_hack()
}
};
}
// A trait that's in our crate for our external wrapped types to implement.
#[allow(unused)]
#[doc(hidden)]
pub trait UniffiCustomTypeConverter {
type Builtin;
fn into_custom(val: Self::Builtin) -> ::uniffi::Result<Self>
where
Self: ::std::marker::Sized;
fn from_custom(obj: Self) -> Self::Builtin;
}
})
}
/// Generates the rust_future_* functions
///
/// The foreign side uses a type-erased `Handle` to interact with futures, which presents
/// a problem when creating scaffolding functions. What is the `ReturnType` parameter of `RustFutureFfi`?
///
/// Handle this by using some brute-force monomorphization. For each possible ffi type, we
/// generate a set of scaffolding functions. The bindings code is responsible for calling the one
/// corresponds the scaffolding function that created the `Handle`.
///
/// This introduces safety issues, but we do get some type checking. If the bindings code calls
/// the wrong rust_future_complete function, they should get an unexpected return type, which
/// hopefully will result in a compile-time error.
fn rust_future_scaffolding_fns(module_path: &str) -> TokenStream {
let fn_info = [
(quote! { u8 }, "u8"),
(quote! { i8 }, "i8"),
(quote! { u16 }, "u16"),
(quote! { i16 }, "i16"),
(quote! { u32 }, "u32"),
(quote! { i32 }, "i32"),
(quote! { u64 }, "u64"),
(quote! { i64 }, "i64"),
(quote! { f32 }, "f32"),
(quote! { f64 }, "f64"),
(quote! { *const ::std::ffi::c_void }, "pointer"),
(quote! { ::uniffi::RustBuffer }, "rust_buffer"),
(quote! { () }, "void"),
];
fn_info.iter()
.map(|(return_type, fn_suffix)| {
let ffi_rust_future_poll = format_ident!("ffi_{module_path}_rust_future_poll_{fn_suffix}");
let ffi_rust_future_cancel = format_ident!("ffi_{module_path}_rust_future_cancel_{fn_suffix}");
let ffi_rust_future_complete = format_ident!("ffi_{module_path}_rust_future_complete_{fn_suffix}");
let ffi_rust_future_free = format_ident!("ffi_{module_path}_rust_future_free_{fn_suffix}");
quote! {
#[allow(clippy::missing_safety_doc, missing_docs)]
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn #ffi_rust_future_poll(handle: ::uniffi::Handle, callback: ::uniffi::RustFutureContinuationCallback, data: u64) {
::uniffi::ffi::rust_future_poll::<#return_type, crate::UniFfiTag>(handle, callback, data);
}
#[allow(clippy::missing_safety_doc, missing_docs)]
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn #ffi_rust_future_cancel(handle: ::uniffi::Handle) {
::uniffi::ffi::rust_future_cancel::<#return_type, crate::UniFfiTag>(handle)
}
#[allow(clippy::missing_safety_doc, missing_docs)]
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn #ffi_rust_future_complete(
handle: ::uniffi::Handle,
out_status: &mut ::uniffi::RustCallStatus
) -> #return_type {
::uniffi::ffi::rust_future_complete::<#return_type, crate::UniFfiTag>(handle, out_status)
}
#[allow(clippy::missing_safety_doc, missing_docs)]
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn #ffi_rust_future_free(handle: ::uniffi::Handle) {
::uniffi::ffi::rust_future_free::<#return_type, crate::UniFfiTag>(handle)
}
}
})
.collect()
}