| //! Helpers for code generation that don't need macro expansion. | |
| use proc_macro2::{Ident, Span}; | |
| use crate::ir::context::BindgenContext; | |
| use crate::ir::layout::Layout; | |
| use crate::ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT; | |
| pub(crate) mod attributes { | |
| use proc_macro2::{Ident, Span, TokenStream}; | |
| use std::{borrow::Cow, str::FromStr}; | |
| pub(crate) fn repr(which: &str) -> TokenStream { | |
| let which = Ident::new(which, Span::call_site()); | |
| quote! { | |
| #[repr( #which )] | |
| } | |
| } | |
| pub(crate) fn repr_list(which_ones: &[&str]) -> TokenStream { | |
| let which_ones = which_ones | |
| .iter() | |
| .map(|one| TokenStream::from_str(one).expect("repr to be valid")); | |
| quote! { | |
| #[repr( #( #which_ones ),* )] | |
| } | |
| } | |
| pub(crate) fn derives(which_ones: &[&str]) -> TokenStream { | |
| let which_ones = which_ones | |
| .iter() | |
| .map(|one| TokenStream::from_str(one).expect("derive to be valid")); | |
| quote! { | |
| #[derive( #( #which_ones ),* )] | |
| } | |
| } | |
| pub(crate) fn inline() -> TokenStream { | |
| quote! { | |
| #[inline] | |
| } | |
| } | |
| pub(crate) fn must_use() -> TokenStream { | |
| quote! { | |
| #[must_use] | |
| } | |
| } | |
| pub(crate) fn non_exhaustive() -> TokenStream { | |
| quote! { | |
| #[non_exhaustive] | |
| } | |
| } | |
| pub(crate) fn doc(comment: &str) -> TokenStream { | |
| if comment.is_empty() { | |
| quote!() | |
| } else { | |
| quote!(#[doc = #comment]) | |
| } | |
| } | |
| pub(crate) fn link_name<const MANGLE: bool>(name: &str) -> TokenStream { | |
| // LLVM mangles the name by default but it's already mangled. | |
| // Prefixing the name with \u{1} should tell LLVM to not mangle it. | |
| let name: Cow<'_, str> = if MANGLE { | |
| name.into() | |
| } else { | |
| format!("\u{1}{name}").into() | |
| }; | |
| quote! { | |
| #[link_name = #name] | |
| } | |
| } | |
| } | |
| /// The `ffi_safe` argument should be true if this is a type that the user might | |
| /// reasonably use, e.g. not struct padding, where the `__BindgenOpaqueArray` is | |
| /// just noise. | |
| /// TODO: Should this be `MaybeUninit`, since padding bytes are effectively | |
| /// uninitialized? | |
| pub(crate) fn blob( | |
| ctx: &BindgenContext, | |
| layout: Layout, | |
| ffi_safe: bool, | |
| ) -> syn::Type { | |
| let align = layout.align.max(1); | |
| // For alignments <= 4, it holds that the integer type of the same size aligns to that same | |
| // size. For bigger alignments that's not guaranteed, e.g. on x86 u64 is aligned to 4 bytes. | |
| if align <= 4 { | |
| let ty = Layout::known_type_for_size(align).unwrap(); | |
| let len = layout.size / align; | |
| return if len == 1 { | |
| ty | |
| } else if !ffi_safe && len <= RUST_DERIVE_IN_ARRAY_LIMIT { | |
| syn::parse_quote! { [#ty; #len] } | |
| } else { | |
| ctx.generated_opaque_array(1); | |
| if ctx.options().enable_cxx_namespaces { | |
| syn::parse_quote! { root::__BindgenOpaqueArray<[#ty; #len]> } | |
| } else { | |
| syn::parse_quote! { __BindgenOpaqueArray<[#ty; #len]> } | |
| } | |
| }; | |
| } | |
| ctx.generated_opaque_array(align); | |
| let ident = format_ident!("__BindgenOpaqueArray{align}"); | |
| let size = layout.size; | |
| if ctx.options().enable_cxx_namespaces { | |
| syn::parse_quote! { root::#ident<[u8; #size]> } | |
| } else { | |
| syn::parse_quote! { #ident<[u8; #size]> } | |
| } | |
| } | |
| /// Integer type of the same size as the given `Layout`. | |
| pub(crate) fn integer_type(layout: Layout) -> Option<syn::Type> { | |
| Layout::known_type_for_size(layout.size) | |
| } | |
| pub(crate) const BITFIELD_UNIT: &str = "__BindgenBitfieldUnit"; | |
| /// Generates a bitfield allocation unit type for a type with the given `Layout`. | |
| pub(crate) fn bitfield_unit(ctx: &BindgenContext, layout: Layout) -> syn::Type { | |
| let size = layout.size; | |
| let bitfield_unit_name = Ident::new(BITFIELD_UNIT, Span::call_site()); | |
| let ty = syn::parse_quote! { #bitfield_unit_name<[u8; #size]> }; | |
| if ctx.options().enable_cxx_namespaces { | |
| return syn::parse_quote! { root::#ty }; | |
| } | |
| ty | |
| } | |
| pub(crate) mod ast_ty { | |
| use crate::ir::context::BindgenContext; | |
| use crate::ir::function::FunctionSig; | |
| use crate::ir::layout::Layout; | |
| use crate::ir::ty::{FloatKind, IntKind}; | |
| use proc_macro2::TokenStream; | |
| use std::str::FromStr; | |
| pub(crate) fn c_void(ctx: &BindgenContext) -> syn::Type { | |
| // ctypes_prefix takes precedence | |
| match ctx.options().ctypes_prefix { | |
| Some(ref prefix) => { | |
| let prefix = TokenStream::from_str(prefix.as_str()).unwrap(); | |
| syn::parse_quote! { #prefix::c_void } | |
| } | |
| None => { | |
| if ctx.options().use_core { | |
| syn::parse_quote! { ::core::ffi::c_void } | |
| } else { | |
| syn::parse_quote! { ::std::os::raw::c_void } | |
| } | |
| } | |
| } | |
| } | |
| pub(crate) fn raw_type(ctx: &BindgenContext, name: &str) -> syn::Type { | |
| let ident = ctx.rust_ident_raw(name); | |
| match ctx.options().ctypes_prefix { | |
| Some(ref prefix) => { | |
| let prefix = TokenStream::from_str(prefix.as_str()).unwrap(); | |
| syn::parse_quote! { #prefix::#ident } | |
| } | |
| None => { | |
| if ctx.options().use_core && | |
| ctx.options().rust_features().core_ffi_c | |
| { | |
| syn::parse_quote! { ::core::ffi::#ident } | |
| } else { | |
| syn::parse_quote! { ::std::os::raw::#ident } | |
| } | |
| } | |
| } | |
| } | |
| pub(crate) fn int_kind_rust_type( | |
| ctx: &BindgenContext, | |
| ik: IntKind, | |
| layout: Option<Layout>, | |
| ) -> syn::Type { | |
| match ik { | |
| IntKind::Bool => syn::parse_quote! { bool }, | |
| IntKind::Char { .. } => raw_type(ctx, "c_char"), | |
| // The following is used only when an unusual command-line | |
| // argument is used. bindgen_cchar16_t is not a real type; | |
| // but this allows downstream postprocessors to distinguish | |
| // this case and do something special for C++ bindings | |
| // containing the C++ type char16_t. | |
| IntKind::Char16 => syn::parse_quote! { bindgen_cchar16_t }, | |
| IntKind::SChar => raw_type(ctx, "c_schar"), | |
| IntKind::UChar => raw_type(ctx, "c_uchar"), | |
| IntKind::Short => raw_type(ctx, "c_short"), | |
| IntKind::UShort => raw_type(ctx, "c_ushort"), | |
| IntKind::Int => raw_type(ctx, "c_int"), | |
| IntKind::UInt => raw_type(ctx, "c_uint"), | |
| IntKind::Long => raw_type(ctx, "c_long"), | |
| IntKind::ULong => raw_type(ctx, "c_ulong"), | |
| IntKind::LongLong => raw_type(ctx, "c_longlong"), | |
| IntKind::ULongLong => raw_type(ctx, "c_ulonglong"), | |
| IntKind::WChar => { | |
| let layout = | |
| layout.expect("Couldn't compute wchar_t's layout?"); | |
| Layout::known_type_for_size(layout.size) | |
| .expect("Non-representable wchar_t?") | |
| } | |
| IntKind::I8 => syn::parse_quote! { i8 }, | |
| IntKind::U8 => syn::parse_quote! { u8 }, | |
| IntKind::I16 => syn::parse_quote! { i16 }, | |
| IntKind::U16 => syn::parse_quote! { u16 }, | |
| IntKind::I32 => syn::parse_quote! { i32 }, | |
| IntKind::U32 => syn::parse_quote! { u32 }, | |
| IntKind::I64 => syn::parse_quote! { i64 }, | |
| IntKind::U64 => syn::parse_quote! { u64 }, | |
| IntKind::Custom { name, .. } => { | |
| syn::parse_str(name).expect("Invalid integer type.") | |
| } | |
| IntKind::U128 => { | |
| if true { | |
| syn::parse_quote! { u128 } | |
| } else { | |
| // Best effort thing, but wrong alignment | |
| // unfortunately. | |
| syn::parse_quote! { [u64; 2] } | |
| } | |
| } | |
| IntKind::I128 => { | |
| if true { | |
| syn::parse_quote! { i128 } | |
| } else { | |
| syn::parse_quote! { [u64; 2] } | |
| } | |
| } | |
| } | |
| } | |
| pub(crate) fn float_kind_rust_type( | |
| ctx: &BindgenContext, | |
| fk: FloatKind, | |
| layout: Option<Layout>, | |
| ) -> syn::Type { | |
| // TODO: we probably should take the type layout into account more | |
| // often? | |
| // | |
| // Also, maybe this one shouldn't be the default? | |
| match (fk, ctx.options().convert_floats) { | |
| (FloatKind::Float16, _) => { | |
| // TODO: do f16 when rust lands it | |
| ctx.generated_bindgen_float16(); | |
| if ctx.options().enable_cxx_namespaces { | |
| syn::parse_quote! { root::__BindgenFloat16 } | |
| } else { | |
| syn::parse_quote! { __BindgenFloat16 } | |
| } | |
| } | |
| (FloatKind::Float, true) => syn::parse_quote! { f32 }, | |
| (FloatKind::Double, true) => syn::parse_quote! { f64 }, | |
| (FloatKind::Float, false) => raw_type(ctx, "c_float"), | |
| (FloatKind::Double, false) => raw_type(ctx, "c_double"), | |
| (FloatKind::LongDouble, _) => { | |
| if let Some(layout) = layout { | |
| match layout.size { | |
| 4 => syn::parse_quote! { f32 }, | |
| 8 => syn::parse_quote! { f64 }, | |
| // TODO(emilio): If rust ever gains f128 we should | |
| // use it here and below. | |
| _ => super::integer_type(layout) | |
| .unwrap_or(syn::parse_quote! { f64 }), | |
| } | |
| } else { | |
| debug_assert!( | |
| false, | |
| "How didn't we know the layout for a primitive type?" | |
| ); | |
| syn::parse_quote! { f64 } | |
| } | |
| } | |
| (FloatKind::Float128, _) => { | |
| if true { | |
| syn::parse_quote! { u128 } | |
| } else { | |
| syn::parse_quote! { [u64; 2] } | |
| } | |
| } | |
| } | |
| } | |
| pub(crate) fn int_expr(val: i64) -> TokenStream { | |
| // Don't use quote! { #val } because that adds the type suffix. | |
| let val = proc_macro2::Literal::i64_unsuffixed(val); | |
| quote!(#val) | |
| } | |
| pub(crate) fn uint_expr(val: u64) -> TokenStream { | |
| // Don't use quote! { #val } because that adds the type suffix. | |
| let val = proc_macro2::Literal::u64_unsuffixed(val); | |
| quote!(#val) | |
| } | |
| pub(crate) fn cstr_expr(mut string: String) -> TokenStream { | |
| string.push('\0'); | |
| let b = proc_macro2::Literal::byte_string(string.as_bytes()); | |
| quote! { | |
| #b | |
| } | |
| } | |
| pub(crate) fn float_expr(f: f64) -> Result<TokenStream, ()> { | |
| if f.is_finite() { | |
| let val = proc_macro2::Literal::f64_unsuffixed(f); | |
| return Ok(quote!(#val)); | |
| } | |
| if f.is_nan() { | |
| return Ok(quote! { f64::NAN as _ }); | |
| } | |
| if f.is_infinite() { | |
| let tokens = if f.is_sign_positive() { | |
| quote! { f64::INFINITY as _ } | |
| } else { | |
| quote! { f64::NEG_INFINITY as _ } | |
| }; | |
| return Ok(tokens); | |
| } | |
| warn!("Unknown non-finite float number: {f:?}"); | |
| Err(()) | |
| } | |
| pub(crate) fn arguments_from_signature( | |
| signature: &FunctionSig, | |
| ctx: &BindgenContext, | |
| ) -> Vec<TokenStream> { | |
| let mut unnamed_arguments = 0; | |
| signature | |
| .argument_types() | |
| .iter() | |
| .map(|&(ref name, _ty)| { | |
| let name = if let Some(ref name) = *name { | |
| ctx.rust_ident(name) | |
| } else { | |
| unnamed_arguments += 1; | |
| ctx.rust_ident(format!("arg{unnamed_arguments}")) | |
| }; | |
| quote! { #name } | |
| }) | |
| .collect() | |
| } | |
| } |