mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			355 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			355 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
//! Helpers for code generation that don't need macro expansion.
 | 
						|
 | 
						|
use crate::ir::context::BindgenContext;
 | 
						|
use crate::ir::layout::Layout;
 | 
						|
 | 
						|
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()
 | 
						|
            .cloned()
 | 
						|
            .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()
 | 
						|
            .cloned()
 | 
						|
            .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: String) -> 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]
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/// Generates a proper type for a field or type with a given `Layout`, that is,
 | 
						|
/// a type with the correct size and alignment restrictions.
 | 
						|
pub(crate) fn blob(ctx: &BindgenContext, layout: Layout) -> syn::Type {
 | 
						|
    let opaque = layout.opaque();
 | 
						|
 | 
						|
    // FIXME(emilio, #412): We fall back to byte alignment, but there are
 | 
						|
    // some things that legitimately are more than 8-byte aligned.
 | 
						|
    //
 | 
						|
    // Eventually we should be able to `unwrap` here, but...
 | 
						|
    let ty = match opaque.known_rust_type_for_array(ctx) {
 | 
						|
        Some(ty) => ty,
 | 
						|
        None => {
 | 
						|
            warn!("Found unknown alignment on code generation!");
 | 
						|
            syn::parse_quote! { u8 }
 | 
						|
        }
 | 
						|
    };
 | 
						|
 | 
						|
    let data_len = opaque.array_size(ctx).unwrap_or(layout.size);
 | 
						|
 | 
						|
    if data_len == 1 {
 | 
						|
        ty
 | 
						|
    } else {
 | 
						|
        syn::parse_quote! { [ #ty ; #data_len ] }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/// Integer type of the same size as the given `Layout`.
 | 
						|
pub(crate) fn integer_type(
 | 
						|
    ctx: &BindgenContext,
 | 
						|
    layout: Layout,
 | 
						|
) -> Option<syn::Type> {
 | 
						|
    Layout::known_type_for_size(ctx, layout.size)
 | 
						|
}
 | 
						|
 | 
						|
/// 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 ty = syn::parse_quote! { __BindgenBitfieldUnit<[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::{self, 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 &&
 | 
						|
                    ctx.options().rust_features.core_ffi_c_void
 | 
						|
                {
 | 
						|
                    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"),
 | 
						|
            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(ctx, 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 ctx.options().rust_features.i128_and_u128 {
 | 
						|
                    syn::parse_quote! { u128 }
 | 
						|
                } else {
 | 
						|
                    // Best effort thing, but wrong alignment
 | 
						|
                    // unfortunately.
 | 
						|
                    syn::parse_quote! { [u64; 2] }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            IntKind::I128 => {
 | 
						|
                if ctx.options().rust_features.i128_and_u128 {
 | 
						|
                    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, _) => {
 | 
						|
                match layout {
 | 
						|
                    Some(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(ctx, layout)
 | 
						|
                                .unwrap_or(syn::parse_quote! { f64 }),
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    None => {
 | 
						|
                        debug_assert!(
 | 
						|
                            false,
 | 
						|
                            "How didn't we know the layout for a primitive type?"
 | 
						|
                        );
 | 
						|
                        syn::parse_quote! { f64 }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            (FloatKind::Float128, _) => {
 | 
						|
                if ctx.options().rust_features.i128_and_u128 {
 | 
						|
                    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(
 | 
						|
        ctx: &BindgenContext,
 | 
						|
        f: f64,
 | 
						|
    ) -> Result<TokenStream, ()> {
 | 
						|
        if f.is_finite() {
 | 
						|
            let val = proc_macro2::Literal::f64_unsuffixed(f);
 | 
						|
 | 
						|
            return Ok(quote!(#val));
 | 
						|
        }
 | 
						|
 | 
						|
        let prefix = ctx.trait_prefix();
 | 
						|
 | 
						|
        if f.is_nan() {
 | 
						|
            return Ok(quote! {
 | 
						|
                ::#prefix::f64::NAN
 | 
						|
            });
 | 
						|
        }
 | 
						|
 | 
						|
        if f.is_infinite() {
 | 
						|
            return Ok(if f.is_sign_positive() {
 | 
						|
                quote! {
 | 
						|
                    ::#prefix::f64::INFINITY
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                quote! {
 | 
						|
                    ::#prefix::f64::NEG_INFINITY
 | 
						|
                }
 | 
						|
            });
 | 
						|
        }
 | 
						|
 | 
						|
        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)| match *name {
 | 
						|
                Some(ref name) => {
 | 
						|
                    let name = ctx.rust_ident(name);
 | 
						|
                    quote! { #name }
 | 
						|
                }
 | 
						|
                None => {
 | 
						|
                    unnamed_arguments += 1;
 | 
						|
                    let name =
 | 
						|
                        ctx.rust_ident(format!("arg{}", unnamed_arguments));
 | 
						|
                    quote! { #name }
 | 
						|
                }
 | 
						|
            })
 | 
						|
            .collect()
 | 
						|
    }
 | 
						|
}
 |