diff options
Diffstat (limited to 'third_party/rust/bindgen/codegen/helpers.rs')
-rw-r--r-- | third_party/rust/bindgen/codegen/helpers.rs | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/third_party/rust/bindgen/codegen/helpers.rs b/third_party/rust/bindgen/codegen/helpers.rs new file mode 100644 index 0000000000..088c7f9363 --- /dev/null +++ b/third_party/rust/bindgen/codegen/helpers.rs @@ -0,0 +1,311 @@ +//! Helpers for code generation that don't need macro expansion. + +use crate::ir::context::BindgenContext; +use crate::ir::layout::Layout; +use proc_macro2::{Ident, Span, TokenStream}; +use quote::TokenStreamExt; + +pub mod attributes { + use proc_macro2::{Ident, Span, TokenStream}; + use std::str::FromStr; + + pub fn repr(which: &str) -> TokenStream { + let which = Ident::new(which, Span::call_site()); + quote! { + #[repr( #which )] + } + } + + pub 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 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 fn inline() -> TokenStream { + quote! { + #[inline] + } + } + + pub fn must_use() -> TokenStream { + quote! { + #[must_use] + } + } + + pub fn non_exhaustive() -> TokenStream { + quote! { + #[non_exhaustive] + } + } + + pub fn doc(comment: String) -> TokenStream { + if comment.is_empty() { + quote!() + } else { + quote!(#[doc = #comment]) + } + } + + pub fn link_name(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 = format!("\u{1}{}", name); + 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 fn blob(ctx: &BindgenContext, layout: Layout) -> TokenStream { + 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_name = match opaque.known_rust_type_for_array(ctx) { + Some(ty) => ty, + None => { + warn!("Found unknown alignment on code generation!"); + "u8" + } + }; + + let ty_name = Ident::new(ty_name, Span::call_site()); + + let data_len = opaque.array_size(ctx).unwrap_or(layout.size); + + if data_len == 1 { + quote! { + #ty_name + } + } else { + quote! { + [ #ty_name ; #data_len ] + } + } +} + +/// Integer type of the same size as the given `Layout`. +pub fn integer_type( + ctx: &BindgenContext, + layout: Layout, +) -> Option<TokenStream> { + let name = Layout::known_type_for_size(ctx, layout.size)?; + let name = Ident::new(name, Span::call_site()); + Some(quote! { #name }) +} + +/// Generates a bitfield allocation unit type for a type with the given `Layout`. +pub fn bitfield_unit(ctx: &BindgenContext, layout: Layout) -> TokenStream { + let mut tokens = quote! {}; + + if ctx.options().enable_cxx_namespaces { + tokens.append_all(quote! { root:: }); + } + + let size = layout.size; + tokens.append_all(quote! { + __BindgenBitfieldUnit<[u8; #size]> + }); + + tokens +} + +pub mod ast_ty { + use crate::ir::context::BindgenContext; + use crate::ir::function::FunctionSig; + use crate::ir::layout::Layout; + use crate::ir::ty::FloatKind; + use proc_macro2::{self, TokenStream}; + use std::str::FromStr; + + pub fn c_void(ctx: &BindgenContext) -> TokenStream { + // ctypes_prefix takes precedence + match ctx.options().ctypes_prefix { + Some(ref prefix) => { + let prefix = TokenStream::from_str(prefix.as_str()).unwrap(); + quote! { + #prefix::c_void + } + } + None => { + if ctx.options().use_core && + ctx.options().rust_features.core_ffi_c_void + { + quote! { ::core::ffi::c_void } + } else { + quote! { ::std::os::raw::c_void } + } + } + } + } + + pub fn raw_type(ctx: &BindgenContext, name: &str) -> TokenStream { + let ident = ctx.rust_ident_raw(name); + match ctx.options().ctypes_prefix { + Some(ref prefix) => { + let prefix = TokenStream::from_str(prefix.as_str()).unwrap(); + quote! { + #prefix::#ident + } + } + None => { + if ctx.options().use_core && + ctx.options().rust_features().core_ffi_c + { + quote! { + ::core::ffi::#ident + } + } else { + quote! { + ::std::os::raw::#ident + } + } + } + } + } + + pub fn float_kind_rust_type( + ctx: &BindgenContext, + fk: FloatKind, + layout: Option<Layout>, + ) -> TokenStream { + // 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::Float, true) => quote! { f32 }, + (FloatKind::Double, true) => 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 => quote! { f32 }, + 8 => quote! { f64 }, + // TODO(emilio): If rust ever gains f128 we should + // use it here and below. + _ => super::integer_type(ctx, layout) + .unwrap_or(quote! { f64 }), + } + } + None => { + debug_assert!( + false, + "How didn't we know the layout for a primitive type?" + ); + quote! { f64 } + } + } + } + (FloatKind::Float128, _) => { + if ctx.options().rust_features.i128_and_u128 { + quote! { u128 } + } else { + quote! { [u64; 2] } + } + } + } + } + + pub 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 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 fn byte_array_expr(bytes: &[u8]) -> TokenStream { + let mut bytes: Vec<_> = bytes.to_vec(); + bytes.push(0); + quote! { [ #(#bytes),* ] } + } + + pub fn cstr_expr(mut string: String) -> TokenStream { + string.push('\0'); + let b = proc_macro2::Literal::byte_string(string.as_bytes()); + quote! { + #b + } + } + + pub 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 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() + } +} |