diff --git a/src/bindgen/bindings.rs b/src/bindgen/bindings.rs index 43d899b62..a0630cc39 100644 --- a/src/bindgen/bindings.rs +++ b/src/bindgen/bindings.rs @@ -13,7 +13,8 @@ use std::rc::Rc; use crate::bindgen::config::{Config, Language}; use crate::bindgen::ir::{ - Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Typedef, + Constant, Documentation, Enum, Function, ItemContainer, ItemMap, Literal, OpaqueItem, + Path as BindgenPath, Static, Struct, Type, Typedef, Union, }; use crate::bindgen::language_backend::{ CLikeLanguageBackend, CythonLanguageBackend, LanguageBackend, NamespaceOperation, @@ -74,8 +75,6 @@ impl Bindings { /// Peels through typedefs to allow resolving structs. fn resolved_struct_path<'a>(&self, path: &'a BindgenPath) -> Cow<'a, BindgenPath> { - use crate::bindgen::ir::Type; - let mut resolved_path = Cow::Borrowed(path); loop { let mut found = None; @@ -204,15 +203,27 @@ impl Bindings { pub fn write(&self, file: F) { match self.config.language { Language::Cxx | Language::C => { - self.write_with_backend(file, CLikeLanguageBackend::new(self.config.clone())) + self.write_with_backend(file, &CLikeLanguageBackend::new(self.config.clone())) } Language::Cython => { - self.write_with_backend(file, CythonLanguageBackend::new(self.config.clone())) + self.write_with_backend(file, &CythonLanguageBackend::new(self.config.clone())) } } } - pub fn write_with_backend(&self, file: F, language_backend: LB) { + pub fn write_with_backend(&self, file: F, language_backend: &LB) + where + Enum: Source, + Struct: Source, + Union: Source, + OpaqueItem: Source, + Typedef: Source, + Static: Source, + Function: Source, + Type: Source, + Documentation: Source, + Literal: Source, + { if self.noop { return; } @@ -228,7 +239,7 @@ impl Bindings { for constant in &self.constants { if constant.uses_only_primitive_types() { out.new_line_if_not_start(); - constant.write(&self.config, &mut out, None); + constant.write(&self.config, language_backend, &mut out, None); out.new_line(); } } @@ -247,11 +258,11 @@ impl Bindings { match *item { ItemContainer::Constant(..) => unreachable!(), ItemContainer::Static(..) => unreachable!(), - ItemContainer::Enum(ref x) => x.write(&self.config, &mut out), - ItemContainer::Struct(ref x) => x.write(&self.config, &mut out), - ItemContainer::Union(ref x) => x.write(&self.config, &mut out), - ItemContainer::OpaqueItem(ref x) => x.write(&self.config, &mut out), - ItemContainer::Typedef(ref x) => x.write(&self.config, &mut out), + ItemContainer::Enum(ref x) => x.write(language_backend, &mut out), + ItemContainer::Struct(ref x) => x.write(language_backend, &mut out), + ItemContainer::Union(ref x) => x.write(language_backend, &mut out), + ItemContainer::OpaqueItem(ref x) => x.write(language_backend, &mut out), + ItemContainer::Typedef(ref x) => x.write(language_backend, &mut out), } out.new_line(); } @@ -259,7 +270,7 @@ impl Bindings { for constant in &self.constants { if !constant.uses_only_primitive_types() { out.new_line_if_not_start(); - constant.write(&self.config, &mut out, None); + constant.write(&self.config, language_backend, &mut out, None); out.new_line(); } } @@ -293,13 +304,13 @@ impl Bindings { for global in &self.globals { out.new_line_if_not_start(); - global.write(&self.config, &mut out); + global.write(language_backend, &mut out); out.new_line(); } for function in &self.functions { out.new_line_if_not_start(); - function.write(&self.config, &mut out); + function.write(language_backend, &mut out); out.new_line(); } diff --git a/src/bindgen/cdecl.rs b/src/bindgen/cdecl.rs index d331d6547..89ea85538 100644 --- a/src/bindgen/cdecl.rs +++ b/src/bindgen/cdecl.rs @@ -7,7 +7,8 @@ use std::io::Write; use crate::bindgen::config::Layout; use crate::bindgen::declarationtyperesolver::DeclarationType; use crate::bindgen::ir::{ConstExpr, Function, GenericArgument, Type}; -use crate::bindgen::writer::{ListType, SourceWriter}; +use crate::bindgen::language_backend::LanguageBackend; +use crate::bindgen::writer::{ListType, Source, SourceWriter}; use crate::bindgen::{Config, Language}; // This code is for translating Rust types into C declarations. @@ -188,7 +189,15 @@ impl CDecl { } } - fn write(&self, out: &mut SourceWriter, ident: Option<&str>, config: &Config) { + fn write( + &self, + language_backend: &LB, + out: &mut SourceWriter, + ident: Option<&str>, + config: &Config, + ) where + GenericArgument: Source, + { // Write the type-specifier and type-qualifier first if !self.type_qualifers.is_empty() { write!(out, "{} ", self.type_qualifers); @@ -204,7 +213,11 @@ impl CDecl { if !self.type_generic_args.is_empty() { out.write("<"); - out.write_horizontal_source_list(&self.type_generic_args, ListType::Join(", ")); + out.write_horizontal_source_list( + language_backend, + &self.type_generic_args, + ListType::Join(", "), + ); out.write(">"); } @@ -286,11 +299,14 @@ impl CDecl { out.write("void"); } - fn write_vertical( + fn write_vertical( + language_backend: &LB, out: &mut SourceWriter, config: &Config, args: &[(Option, CDecl)], - ) { + ) where + GenericArgument: Source, + { let align_length = out.line_length_for_align(); out.push_set_spaces(align_length); for (i, (arg_ident, arg_ty)) in args.iter().enumerate() { @@ -302,16 +318,19 @@ impl CDecl { // Convert &Option to Option<&str> let arg_ident = arg_ident.as_ref().map(|x| x.as_ref()); - arg_ty.write(out, arg_ident, config); + arg_ty.write(language_backend, out, arg_ident, config); } out.pop_tab(); } - fn write_horizontal( + fn write_horizontal( + language_backend: &LB, out: &mut SourceWriter, config: &Config, args: &[(Option, CDecl)], - ) { + ) where + GenericArgument: Source, + { for (i, (arg_ident, arg_ty)) in args.iter().enumerate() { if i != 0 { out.write(", "); @@ -320,19 +339,19 @@ impl CDecl { // Convert &Option to Option<&str> let arg_ident = arg_ident.as_ref().map(|x| x.as_ref()); - arg_ty.write(out, arg_ident, config); + arg_ty.write(language_backend, out, arg_ident, config); } } match layout { - Layout::Vertical => write_vertical(out, config, args), - Layout::Horizontal => write_horizontal(out, config, args), + Layout::Vertical => write_vertical(language_backend, out, config, args), + Layout::Horizontal => write_horizontal(language_backend, out, config, args), Layout::Auto => { if !out.try_write( - |out| write_horizontal(out, config, args), + |out| write_horizontal(language_backend, out, config, args), config.line_length, ) { - write_vertical(out, config, args) + write_vertical(language_backend, out, config, args) } } } @@ -351,19 +370,37 @@ impl CDecl { } } -pub fn write_func( +pub fn write_func( + language_backend: &LB, out: &mut SourceWriter, f: &Function, layout: Layout, config: &Config, -) { - CDecl::from_func(f, layout, config).write(out, Some(f.path().name()), config); +) where + GenericArgument: Source, +{ + CDecl::from_func(f, layout, config).write(language_backend, out, Some(f.path().name()), config); } -pub fn write_field(out: &mut SourceWriter, t: &Type, ident: &str, config: &Config) { - CDecl::from_type(t, config).write(out, Some(ident), config); +pub fn write_field( + language_backend: &LB, + out: &mut SourceWriter, + t: &Type, + ident: &str, + config: &Config, +) where + GenericArgument: Source, +{ + CDecl::from_type(t, config).write(language_backend, out, Some(ident), config); } -pub fn write_type(out: &mut SourceWriter, t: &Type, config: &Config) { - CDecl::from_type(t, config).write(out, None, config); +pub fn write_type( + language_backend: &LB, + out: &mut SourceWriter, + t: &Type, + config: &Config, +) where + GenericArgument: Source, +{ + CDecl::from_type(t, config).write(language_backend, out, None, config); } diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs index 42182ab92..b9118aff0 100644 --- a/src/bindgen/ir/constant.rs +++ b/src/bindgen/ir/constant.rs @@ -16,6 +16,7 @@ use crate::bindgen::ir::{ AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path, Struct, ToCondition, Type, }; +use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::library::Library; use crate::bindgen::writer::{Source, SourceWriter}; use crate::bindgen::Bindings; @@ -28,7 +29,7 @@ fn member_to_ident(member: &syn::Member) -> String { } // TODO: Maybe add support to more std associated constants. -fn to_known_assoc_constant(associated_to: &Path, name: &str) -> Option { +pub(crate) fn to_known_assoc_constant(associated_to: &Path, name: &str) -> Option { use crate::bindgen::ir::{IntKind, PrimitiveType}; if name != "MAX" && name != "MIN" { @@ -472,107 +473,6 @@ impl Literal { _ => Err(format!("Unsupported expression. {:?}", *expr)), } } - - pub(crate) fn write(&self, config: &Config, out: &mut SourceWriter) { - match self { - Literal::Expr(v) => match (&**v, config.language) { - ("true", Language::Cython) => write!(out, "True"), - ("false", Language::Cython) => write!(out, "False"), - (v, _) => write!(out, "{}", v), - }, - Literal::Path { - ref associated_to, - ref name, - } => { - if let Some((ref path, ref export_name)) = associated_to { - if let Some(known) = to_known_assoc_constant(path, name) { - return write!(out, "{}", known); - } - let path_separator = match config.language { - Language::Cython | Language::C => "_", - Language::Cxx => { - if config.structure.associated_constants_in_body { - "::" - } else { - "_" - } - } - }; - write!(out, "{}{}", export_name, path_separator) - } - write!(out, "{}", name) - } - Literal::FieldAccess { - ref base, - ref field, - } => { - write!(out, "("); - base.write(config, out); - write!(out, ").{}", field); - } - Literal::PostfixUnaryOp { op, ref value } => { - write!(out, "{}", op); - value.write(config, out); - } - Literal::BinOp { - ref left, - op, - ref right, - } => { - write!(out, "("); - left.write(config, out); - write!(out, " {} ", op); - right.write(config, out); - write!(out, ")"); - } - Literal::Cast { ref ty, ref value } => { - out.write(if config.language == Language::Cython { - "<" - } else { - "(" - }); - ty.write(config, out); - out.write(if config.language == Language::Cython { - ">" - } else { - ")" - }); - value.write(config, out); - } - Literal::Struct { - export_name, - fields, - path, - } => { - match config.language { - Language::C => write!(out, "({})", export_name), - Language::Cxx => write!(out, "{}", export_name), - Language::Cython => write!(out, "<{}>", export_name), - } - - write!(out, "{{ "); - let mut is_first_field = true; - // In C++, same order as defined is required. - let ordered_fields = out.bindings().struct_field_names(path); - for ordered_key in ordered_fields.iter() { - if let Some(lit) = fields.get(ordered_key) { - if !is_first_field { - write!(out, ", "); - } else { - is_first_field = false; - } - match config.language { - Language::Cxx => write!(out, "/* .{} = */ ", ordered_key), - Language::C => write!(out, ".{} = ", ordered_key), - Language::Cython => {} - } - lit.write(config, out); - } - } - write!(out, " }}"); - } - } - } } #[derive(Debug, Clone)] @@ -692,12 +592,15 @@ impl Item for Constant { } impl Constant { - pub fn write_declaration( + pub fn write_declaration( &self, config: &Config, + language_backend: &LB, out: &mut SourceWriter, associated_to_struct: &Struct, - ) { + ) where + Type: Source, + { debug_assert!(self.associated_to.is_some()); debug_assert!(config.language == Language::Cxx); debug_assert!(!associated_to_struct.is_transparent); @@ -709,16 +612,21 @@ impl Constant { } else { out.write("static const "); } - self.ty.write(config, out); + self.ty.write(language_backend, out); write!(out, " {};", self.export_name()) } - pub fn write( + pub fn write( &self, config: &Config, + language_backend: &LB, out: &mut SourceWriter, associated_to_struct: Option<&Struct>, - ) { + ) where + Type: Source, + Documentation: Source, + Literal: Source, + { if let Some(assoc) = associated_to_struct { if assoc.is_generic() { return; // Not tested / implemented yet, so bail out. @@ -770,7 +678,7 @@ impl Constant { _ => &self.value, }; - self.documentation.write(config, out); + self.documentation.write(language_backend, out); let allow_constexpr = config.constant.allow_constexpr && self.value.can_be_constexpr(); match config.language { @@ -789,22 +697,22 @@ impl Constant { out.write("const "); } - self.ty.write(config, out); + self.ty.write(language_backend, out); write!(out, " {} = ", name); - value.write(config, out); + value.write(language_backend, out); write!(out, ";"); } Language::Cxx | Language::C => { write!(out, "#define {} ", name); - value.write(config, out); + value.write(language_backend, out); } Language::Cython => { out.write("const "); - self.ty.write(config, out); + self.ty.write(language_backend, out); // For extern Cython declarations the initializer is ignored, // but still useful as documentation, so we write it as a comment. write!(out, " {} # = ", name); - value.write(config, out); + value.write(language_backend, out); } } diff --git a/src/bindgen/ir/documentation.rs b/src/bindgen/ir/documentation.rs index 6822c0eaf..e6dba9794 100644 --- a/src/bindgen/ir/documentation.rs +++ b/src/bindgen/ir/documentation.rs @@ -2,11 +2,7 @@ * 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 std::io::Write; - -use crate::bindgen::config::{Config, DocumentationLength, DocumentationStyle, Language}; use crate::bindgen::utilities::SynAttributeHelpers; -use crate::bindgen::writer::{Source, SourceWriter}; #[derive(Debug, Clone)] pub struct Documentation { @@ -36,76 +32,3 @@ impl Documentation { } } } - -impl Source for Documentation { - fn write(&self, config: &Config, out: &mut SourceWriter) { - if self.doc_comment.is_empty() || !config.documentation { - return; - } - - let end = match config.documentation_length { - DocumentationLength::Short => 1, - DocumentationLength::Full => self.doc_comment.len(), - }; - - // Cython uses Python-style comments, so `documentation_style` is not relevant. - if config.language == Language::Cython { - for line in &self.doc_comment[..end] { - write!(out, "#{}", line); - out.new_line(); - } - return; - } - - let style = match config.documentation_style { - DocumentationStyle::Auto if config.language == Language::C => DocumentationStyle::Doxy, - DocumentationStyle::Auto if config.language == Language::Cxx => DocumentationStyle::Cxx, - DocumentationStyle::Auto => DocumentationStyle::C, // Fallback if `Language` gets extended. - other => other, - }; - - // Following these documents for style conventions: - // https://en.wikibooks.org/wiki/C++_Programming/Code/Style_Conventions/Comments - // https://www.cs.cmu.edu/~410/doc/doxygen.html - match style { - DocumentationStyle::C => { - out.write("/*"); - out.new_line(); - } - - DocumentationStyle::Doxy => { - out.write("/**"); - out.new_line(); - } - - _ => (), - } - - for line in &self.doc_comment[..end] { - match style { - DocumentationStyle::C => out.write(""), - DocumentationStyle::Doxy => out.write(" *"), - DocumentationStyle::C99 => out.write("//"), - DocumentationStyle::Cxx => out.write("///"), - DocumentationStyle::Auto => unreachable!(), // Auto case should always be covered - } - - write!(out, "{}", line); - out.new_line(); - } - - match style { - DocumentationStyle::C => { - out.write(" */"); - out.new_line(); - } - - DocumentationStyle::Doxy => { - out.write(" */"); - out.new_line(); - } - - _ => (), - } - } -} diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs index b1b6c044f..1a72bd7c9 100644 --- a/src/bindgen/ir/enumeration.rs +++ b/src/bindgen/ir/enumeration.rs @@ -14,6 +14,7 @@ use crate::bindgen::ir::{ GenericParams, GenericPath, Item, ItemContainer, Literal, Path, Repr, ReprStyle, Struct, ToCondition, Type, }; +use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::library::Library; use crate::bindgen::mangle; use crate::bindgen::monomorph::Monomorphs; @@ -292,31 +293,6 @@ impl EnumVariant { } } -impl Source for EnumVariant { - fn write(&self, config: &Config, out: &mut SourceWriter) { - let condition = self.cfg.to_condition(config); - // Cython doesn't support conditional enum variants. - if config.language != Language::Cython { - condition.write_before(config, out); - } - self.documentation.write(config, out); - write!(out, "{}", self.export_name); - if let Some(discriminant) = &self.discriminant { - if config.language == Language::Cython { - // For extern Cython declarations the enumerator value is ignored, - // but still useful as documentation, so we write it as a comment. - out.write(" #") - } - out.write(" = "); - discriminant.write(config, out); - } - out.write(","); - if config.language != Language::Cython { - condition.write_after(config, out); - } - } -} - #[derive(Debug, Clone)] pub struct Enum { pub path: Path, @@ -332,12 +308,12 @@ pub struct Enum { impl Enum { /// Name of the generated tag enum. - fn tag_name(&self) -> &str { + pub(crate) fn tag_name(&self) -> &str { self.tag.as_deref().unwrap_or_else(|| self.export_name()) } /// Enum with data turns into a union of structs with each struct having its own tag field. - fn inline_tag_field(repr: &Repr) -> bool { + pub(crate) fn inline_tag_field(repr: &Repr) -> bool { repr.style != ReprStyle::C } @@ -654,99 +630,22 @@ impl Item for Enum { } } -impl Source for Enum { - fn write(&self, config: &Config, out: &mut SourceWriter) { - let size = self.repr.ty.map(|ty| ty.to_primitive().to_repr_c(config)); - let has_data = self.tag.is_some(); - let inline_tag_field = Self::inline_tag_field(&self.repr); - let tag_name = self.tag_name(); - - let condition = self.cfg.to_condition(config); - condition.write_before(config, out); - - self.documentation.write(config, out); - self.generic_params.write(config, out); - - // If the enum has data, we need to emit a struct or union for the data - // and enum for the tag. C++ supports nested type definitions, so we open - // the struct or union here and define the tag enum inside it (*). - if has_data && config.language == Language::Cxx { - self.open_struct_or_union(config, out, inline_tag_field); - } - - // Emit the tag enum and everything related to it. - self.write_tag_enum(config, out, size, has_data, tag_name); - - // If the enum has data, we need to emit structs for the variants and gather them together. - if has_data { - self.write_variant_defs(config, out); - out.new_line(); - out.new_line(); - - // Open the struct or union for the data (**), gathering all the variants with data - // together, unless it's C++, then we have already opened that struct/union at (*) and - // are currently inside it. - if config.language != Language::Cxx { - self.open_struct_or_union(config, out, inline_tag_field); - } - - // Emit tag field that is separate from all variants. - self.write_tag_field(config, out, size, inline_tag_field, tag_name); - out.new_line(); - - // Open union of all variants with data, only in the non-inline tag scenario. - // Cython extern declarations don't manage layouts, layouts are defined entierly by the - // corresponding C code. So we can inline the unnamed union into the struct and get the - // same observable result. Moreother we have to do it because Cython doesn't support - // unnamed unions. - if !inline_tag_field && config.language != Language::Cython { - out.write("union"); - out.open_brace(); - } - - // Emit fields for all variants with data. - self.write_variant_fields(config, out, inline_tag_field); - - // Close union of all variants with data, only in the non-inline tag scenario. - // See the comment about Cython on `open_brace`. - if !inline_tag_field && config.language != Language::Cython { - out.close_brace(true); - } - - // Emit convenience methods for the struct or enum for the data. - self.write_derived_functions_data(config, out, tag_name); - - // Emit the post_body section, if relevant. - if let Some(body) = config.export.post_body(&self.path) { - out.new_line(); - out.write_raw_block(body); - } - - // Close the struct or union opened either at (*) or at (**). - if config.language == Language::C && config.style.generate_typedef() { - out.close_brace(false); - write!(out, " {};", self.export_name); - } else { - out.close_brace(true); - } - } - - condition.write_after(config, out); - } -} - impl Enum { /// Emit the tag enum and convenience methods for it. /// For enums with data this is only a part of the output, /// but for enums without data it's the whole output (modulo doc comments etc.). - fn write_tag_enum( + pub(crate) fn write_tag_enum( &self, config: &Config, + language_backend: &LB, out: &mut SourceWriter, size: Option<&str>, has_data: bool, tag_name: &str, - ) { + ) where + EnumVariant: Source, + String: Source, + { // Open the tag enum. match config.language { Language::C => { @@ -809,7 +708,7 @@ impl Enum { if i != 0 { out.new_line() } - variant.write(config, out); + variant.write(language_backend, out); } // Close the tag enum. @@ -841,11 +740,11 @@ impl Enum { } // Emit convenience methods for the tag enum. - self.write_derived_functions_enum(config, out, has_data, tag_name); + self.write_derived_functions_enum(config, language_backend, out, has_data, tag_name); } /// The code here mirrors the beginning of `Struct::write` and `Union::write`. - fn open_struct_or_union( + pub(crate) fn open_struct_or_union( &self, config: &Config, out: &mut SourceWriter, @@ -879,7 +778,14 @@ impl Enum { } /// Emit struct definitions for variants having data. - fn write_variant_defs(&self, config: &Config, out: &mut SourceWriter) { + pub(crate) fn write_variant_defs( + &self, + config: &Config, + language_backend: &LB, // TODO probably need only one of Config/LanguageBackend + out: &mut SourceWriter, + ) where + Struct: Source, + { for variant in &self.variants { if let VariantBody::Body { ref body, @@ -894,7 +800,7 @@ impl Enum { if config.language != Language::Cython { condition.write_before(config, out); } - body.write(config, out); + body.write(language_backend, out); if config.language != Language::Cython { condition.write_after(config, out); } @@ -906,7 +812,7 @@ impl Enum { /// For non-inline tag scenario this is *the* tag field, and it does not exist in the variants. /// For the inline tag scenario this is just a convenience and another way /// to refer to the same tag that exist in all the variants. - fn write_tag_field( + pub(crate) fn write_tag_field( &self, config: &Config, out: &mut SourceWriter, @@ -935,12 +841,15 @@ impl Enum { } /// Emit fields for all variants with data. - fn write_variant_fields( + pub(crate) fn write_variant_fields( &self, config: &Config, + language_backend: &LB, out: &mut SourceWriter, inline_tag_field: bool, - ) { + ) where + Field: Source, + { let mut first = true; for variant in &self.variants { if let VariantBody::Body { @@ -970,7 +879,11 @@ impl Enum { } let start_field = usize::from(inline_tag_field && config.language == Language::Cython); - out.write_vertical_source_list(&body.fields[start_field..], ListType::Cap(";")); + out.write_vertical_source_list( + language_backend, + &body.fields[start_field..], + ListType::Cap(";"), + ); if config.language != Language::Cython { out.close_brace(true); } @@ -987,13 +900,16 @@ impl Enum { } // Emit convenience methods for enums themselves. - fn write_derived_functions_enum( + fn write_derived_functions_enum( &self, config: &Config, + language_backend: &LB, out: &mut SourceWriter, has_data: bool, tag_name: &str, - ) { + ) where + String: Source, + { if config.language != Language::Cxx { return; } @@ -1060,7 +976,7 @@ impl Enum { ) }) .collect(); - out.write_vertical_source_list(&vec[..], ListType::Join("")); + out.write_vertical_source_list(language_backend, &vec[..], ListType::Join("")); out.close_brace(false); out.new_line(); @@ -1121,7 +1037,7 @@ impl Enum { } }) .collect(); - out.write_vertical_source_list(&vec[..], ListType::Join("")); + out.write_vertical_source_list(language_backend, &vec[..], ListType::Join("")); out.close_brace(false); out.new_line(); @@ -1132,12 +1048,16 @@ impl Enum { } // Emit convenience methods for structs or unions produced for enums with data. - fn write_derived_functions_data( + pub(crate) fn write_derived_functions_data( &self, config: &Config, + language_backend: &LB, out: &mut SourceWriter, tag_name: &str, - ) { + ) where + Type: Source, + Field: Source, + { if config.language != Language::Cxx { return; } @@ -1188,7 +1108,7 @@ impl Enum { ) }) .collect(); - out.write_vertical_source_list(&vec[..], ListType::Join(",")); + out.write_vertical_source_list(language_backend, &vec[..], ListType::Join(",")); } write!(out, ")"); @@ -1212,13 +1132,13 @@ impl Enum { write!(out, "for (int i = 0; i < {}; i++)", length.as_str()); out.open_brace(); write!(out, "::new (&result.{}.{}[i]) (", variant_name, field.name); - ty.write(config, out); + ty.write(language_backend, out); write!(out, ")({}[i]);", arg_renamer(&field.name)); out.close_brace(false); } ref ty => { write!(out, "::new (&result.{}.{}) (", variant_name, field.name); - ty.write(config, out); + ty.write(language_backend, out); write!(out, ")({});", arg_renamer(&field.name)); } } @@ -1280,7 +1200,7 @@ impl Enum { is_ref: true, is_nullable: false, }; - return_type.write(config, out); + return_type.write(language_backend, out); } else if const_casts { write!(out, "const {}&", body.export_name()); } else { diff --git a/src/bindgen/ir/field.rs b/src/bindgen/ir/field.rs index 6e132bfaf..73019eee0 100644 --- a/src/bindgen/ir/field.rs +++ b/src/bindgen/ir/field.rs @@ -1,12 +1,7 @@ -use std::io::Write; - use syn::ext::IdentExt; -use crate::bindgen::cdecl; -use crate::bindgen::config::{Config, Language}; -use crate::bindgen::ir::{AnnotationSet, Cfg, ConditionWrite}; -use crate::bindgen::ir::{Documentation, Path, ToCondition, Type}; -use crate::bindgen::writer::{Source, SourceWriter}; +use crate::bindgen::ir::{AnnotationSet, Cfg}; +use crate::bindgen::ir::{Documentation, Path, Type}; #[derive(Debug, Clone)] pub struct Field { @@ -48,33 +43,3 @@ impl Field { }) } } - -impl Source for Field { - fn write(&self, config: &Config, out: &mut SourceWriter) { - // Cython doesn't support conditional fields. - let condition = self.cfg.to_condition(config); - if config.language != Language::Cython { - condition.write_before(config, out); - } - - self.documentation.write(config, out); - cdecl::write_field(out, &self.ty, &self.name, config); - // Cython extern declarations don't manage layouts, layouts are defined entierly by the - // corresponding C code. So we can omit bitfield sizes which are not supported by Cython. - if config.language != Language::Cython { - if let Some(bitfield) = self.annotations.atom("bitfield") { - write!(out, ": {}", bitfield.unwrap_or_default()); - } - } - - if config.language != Language::Cython { - condition.write_after(config, out); - // FIXME(#634): `write_vertical_source_list` should support - // configuring list elements natively. For now we print a newline - // here to avoid printing `#endif;` with semicolon. - if condition.is_some() { - out.new_line(); - } - } - } -} diff --git a/src/bindgen/ir/function.rs b/src/bindgen/ir/function.rs index da0920767..579c1cb9f 100644 --- a/src/bindgen/ir/function.rs +++ b/src/bindgen/ir/function.rs @@ -3,23 +3,18 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::collections::HashMap; -use std::io::Write; use syn::ext::IdentExt; -use crate::bindgen::cdecl; -use crate::bindgen::config::{Config, Language, Layout}; +use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; -use crate::bindgen::ir::{ - AnnotationSet, Cfg, ConditionWrite, Documentation, GenericPath, Path, ToCondition, Type, -}; +use crate::bindgen::ir::{AnnotationSet, Cfg, Documentation, GenericPath, Path, Type}; use crate::bindgen::library::Library; use crate::bindgen::monomorph::Monomorphs; use crate::bindgen::rename::{IdentifierType, RenameRule}; use crate::bindgen::reserved; use crate::bindgen::utilities::IterHelpers; -use crate::bindgen::writer::{Source, SourceWriter}; #[derive(Debug, Clone)] pub struct FunctionArgument { @@ -219,103 +214,6 @@ impl Function { } } -impl Source for Function { - fn write(&self, config: &Config, out: &mut SourceWriter) { - fn write_1(func: &Function, config: &Config, out: &mut SourceWriter) { - let prefix = config.function.prefix(&func.annotations); - let postfix = config.function.postfix(&func.annotations); - - let condition = func.cfg.to_condition(config); - condition.write_before(config, out); - - func.documentation.write(config, out); - - if func.extern_decl { - out.write("extern "); - } else { - if let Some(ref prefix) = prefix { - write!(out, "{} ", prefix); - } - if func.annotations.must_use(config) { - if let Some(ref anno) = config.function.must_use { - write!(out, "{} ", anno); - } - } - } - cdecl::write_func(out, func, Layout::Horizontal, config); - - if !func.extern_decl { - if let Some(ref postfix) = postfix { - write!(out, " {}", postfix); - } - } - - if let Some(ref swift_name_macro) = config.function.swift_name_macro { - if let Some(swift_name) = func.swift_name(config) { - write!(out, " {}({})", swift_name_macro, swift_name); - } - } - - out.write(";"); - - condition.write_after(config, out); - } - - fn write_2(func: &Function, config: &Config, out: &mut SourceWriter) { - let prefix = config.function.prefix(&func.annotations); - let postfix = config.function.postfix(&func.annotations); - - let condition = func.cfg.to_condition(config); - - condition.write_before(config, out); - - func.documentation.write(config, out); - - if func.extern_decl { - out.write("extern "); - } else { - if let Some(ref prefix) = prefix { - write!(out, "{}", prefix); - out.new_line(); - } - if func.annotations.must_use(config) { - if let Some(ref anno) = config.function.must_use { - write!(out, "{}", anno); - out.new_line(); - } - } - } - cdecl::write_func(out, func, Layout::Vertical, config); - if !func.extern_decl { - if let Some(ref postfix) = postfix { - out.new_line(); - write!(out, "{}", postfix); - } - } - - if let Some(ref swift_name_macro) = config.function.swift_name_macro { - if let Some(swift_name) = func.swift_name(config) { - write!(out, " {}({})", swift_name_macro, swift_name); - } - } - - out.write(";"); - - condition.write_after(config, out); - } - - match config.function.args { - Layout::Horizontal => write_1(self, config, out), - Layout::Vertical => write_2(self, config, out), - Layout::Auto => { - if !out.try_write(|out| write_1(self, config, out), config.line_length) { - write_2(self, config, out) - } - } - } - } -} - trait SynFnArgHelpers { fn as_argument(&self) -> Result, String>; } diff --git a/src/bindgen/ir/generic_path.rs b/src/bindgen/ir/generic_path.rs index ef14890ab..37a83b988 100644 --- a/src/bindgen/ir/generic_path.rs +++ b/src/bindgen/ir/generic_path.rs @@ -7,6 +7,7 @@ use crate::bindgen::cdecl; use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::{DeclarationType, DeclarationTypeResolver}; use crate::bindgen::ir::{ConstExpr, Path, Type}; +use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::utilities::IterHelpers; use crate::bindgen::writer::{Source, SourceWriter}; @@ -94,12 +95,15 @@ impl GenericParams { .collect() } - fn write_internal( + pub(crate) fn write_internal( &self, + language_backend: &LB, config: &Config, out: &mut SourceWriter, with_default: bool, - ) { + ) where + GenericArgument: Source, + { if !self.0.is_empty() && config.language == Language::Cxx { out.write("template<"); for (i, item) in self.0.iter().enumerate() { @@ -114,7 +118,7 @@ impl GenericParams { } } GenericParamType::Const(ref ty) => { - cdecl::write_field(out, ty, item.name.name(), config); + cdecl::write_field(language_backend, out, ty, item.name.name(), config); if with_default { write!(out, " = 0"); } @@ -126,8 +130,15 @@ impl GenericParams { } } - pub fn write_with_default(&self, config: &Config, out: &mut SourceWriter) { - self.write_internal(config, out, true); + pub fn write_with_default( + &self, + language_backend: &LB, + config: &Config, + out: &mut SourceWriter, + ) where + GenericArgument: Source, + { + self.write_internal(language_backend, config, out, true); } } @@ -139,12 +150,6 @@ impl Deref for GenericParams { } } -impl Source for GenericParams { - fn write(&self, config: &Config, out: &mut SourceWriter) { - self.write_internal(config, out, false); - } -} - /// A (non-lifetime) argument passed to a generic, either a type or a constant expression. /// /// Note: Both arguments in a type like `Array` are represented as @@ -185,11 +190,15 @@ impl GenericArgument { } } -impl Source for GenericArgument { - fn write(&self, config: &Config, out: &mut SourceWriter) { +impl Source for GenericArgument +where + Type: Source, + ConstExpr: Source, +{ + fn write(&self, language_backend: &LB, out: &mut SourceWriter) { match *self { - GenericArgument::Type(ref ty) => ty.write(config, out), - GenericArgument::Const(ref expr) => expr.write(config, out), + GenericArgument::Type(ref ty) => ty.write(language_backend, out), + GenericArgument::Const(ref expr) => expr.write(language_backend, out), } } } diff --git a/src/bindgen/ir/global.rs b/src/bindgen/ir/global.rs index 47c996055..23f339711 100644 --- a/src/bindgen/ir/global.rs +++ b/src/bindgen/ir/global.rs @@ -2,17 +2,13 @@ * 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 std::io::Write; - use syn::ext::IdentExt; -use crate::bindgen::cdecl; use crate::bindgen::config::Config; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{AnnotationSet, Cfg, Documentation, Item, ItemContainer, Path, Type}; use crate::bindgen::library::Library; -use crate::bindgen::writer::{Source, SourceWriter}; #[derive(Debug, Clone)] pub struct Static { @@ -105,15 +101,3 @@ impl Item for Static { self.ty.add_dependencies(library, out); } } - -impl Source for Static { - fn write(&self, config: &Config, out: &mut SourceWriter) { - out.write("extern "); - if let Type::Ptr { is_const: true, .. } = self.ty { - } else if !self.mutable { - out.write("const "); - } - cdecl::write_field(out, &self.ty, &self.export_name, config); - out.write(";"); - } -} diff --git a/src/bindgen/ir/opaque.rs b/src/bindgen/ir/opaque.rs index 4451d4a16..b14a1c3ba 100644 --- a/src/bindgen/ir/opaque.rs +++ b/src/bindgen/ir/opaque.rs @@ -2,19 +2,15 @@ * 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 std::io::Write; - -use crate::bindgen::config::{Config, Language}; +use crate::bindgen::config::Config; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ - AnnotationSet, Cfg, ConditionWrite, Documentation, GenericArgument, GenericParams, Item, - ItemContainer, Path, ToCondition, + AnnotationSet, Cfg, Documentation, GenericArgument, GenericParams, Item, ItemContainer, Path, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; use crate::bindgen::monomorph::Monomorphs; -use crate::bindgen::writer::{Source, SourceWriter}; #[derive(Debug, Clone)] pub struct OpaqueItem { @@ -136,41 +132,3 @@ impl Item for OpaqueItem { out.insert_opaque(self, monomorph, generic_values.to_owned()); } } - -impl Source for OpaqueItem { - fn write(&self, config: &Config, out: &mut SourceWriter) { - let condition = self.cfg.to_condition(config); - condition.write_before(config, out); - - self.documentation.write(config, out); - - self.generic_params.write_with_default(config, out); - - match config.language { - Language::C if config.style.generate_typedef() => { - write!( - out, - "typedef struct {} {};", - self.export_name(), - self.export_name() - ); - } - Language::C | Language::Cxx => { - write!(out, "struct {};", self.export_name()); - } - Language::Cython => { - write!( - out, - "{}struct {}", - config.style.cython_def(), - self.export_name() - ); - out.open_brace(); - out.write("pass"); - out.close_brace(false); - } - } - - condition.write_after(config, out); - } -} diff --git a/src/bindgen/ir/structure.rs b/src/bindgen/ir/structure.rs index 9f77739a6..868d095e4 100644 --- a/src/bindgen/ir/structure.rs +++ b/src/bindgen/ir/structure.rs @@ -10,9 +10,8 @@ use crate::bindgen::config::{Config, Language, LayoutConfig}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ - AnnotationSet, Cfg, ConditionWrite, Constant, Documentation, Field, GenericArgument, - GenericParams, Item, ItemContainer, Path, Repr, ReprAlign, ReprStyle, ToCondition, Type, - Typedef, + AnnotationSet, Cfg, Constant, Documentation, Field, GenericArgument, GenericParams, Item, + ItemContainer, Path, Repr, ReprAlign, ReprStyle, Type, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; @@ -20,7 +19,7 @@ use crate::bindgen::monomorph::Monomorphs; use crate::bindgen::rename::{IdentifierType, RenameRule}; use crate::bindgen::reserved; use crate::bindgen::utilities::IterHelpers; -use crate::bindgen::writer::{ListType, Source, SourceWriter}; +use crate::bindgen::writer::SourceWriter; #[derive(Debug, Clone)] pub struct Struct { @@ -203,7 +202,7 @@ impl Struct { ) } - fn emit_bitflags_binop( + pub(crate) fn emit_bitflags_binop( &self, constexpr_prefix: &str, operator: char, @@ -376,335 +375,3 @@ impl Item for Struct { out.insert_struct(library, self, monomorph, generic_values.to_owned()); } } - -impl Source for Struct { - fn write(&self, config: &Config, out: &mut SourceWriter) { - if self.is_transparent { - let typedef = Typedef { - path: self.path.clone(), - export_name: self.export_name.to_owned(), - generic_params: self.generic_params.clone(), - aliased: self.fields[0].ty.clone(), - cfg: self.cfg.clone(), - annotations: self.annotations.clone(), - documentation: self.documentation.clone(), - }; - typedef.write(config, out); - for constant in &self.associated_constants { - out.new_line(); - constant.write(config, out, Some(self)); - } - return; - } - - let condition = self.cfg.to_condition(config); - condition.write_before(config, out); - - self.documentation.write(config, out); - - if !self.is_enum_variant_body { - self.generic_params.write(config, out); - } - - // The following results in - // C++ or C with Tag as style: - // struct Name { - // C with Type only style: - // typedef struct { - // C with Both as style: - // typedef struct Name { - match config.language { - Language::C if config.style.generate_typedef() => out.write("typedef "), - Language::C | Language::Cxx => {} - Language::Cython => out.write(config.style.cython_def()), - } - - // Cython extern declarations don't manage layouts, layouts are defined entierly by the - // corresponding C code. So this `packed` is only for documentation, and missing - // `aligned(n)` is also not a problem. - if config.language == Language::Cython { - if let Some(align) = self.alignment { - match align { - ReprAlign::Packed => out.write("packed "), - ReprAlign::Align(_) => {} // Not supported - } - } - } - - out.write("struct"); - - if config.language != Language::Cython { - if let Some(align) = self.alignment { - match align { - ReprAlign::Packed => { - if let Some(ref anno) = config.layout.packed { - write!(out, " {}", anno); - } - } - ReprAlign::Align(n) => { - if let Some(ref anno) = config.layout.aligned_n { - write!(out, " {}({})", anno, n); - } - } - } - } - } - - if self.annotations.must_use(config) { - if let Some(ref anno) = config.structure.must_use { - write!(out, " {}", anno); - } - } - - if config.language != Language::C || config.style.generate_tag() { - write!(out, " {}", self.export_name()); - } - - out.open_brace(); - - // Emit the pre_body section, if relevant - if let Some(body) = config.export.pre_body(&self.path) { - out.write_raw_block(body); - out.new_line(); - } - - out.write_vertical_source_list(&self.fields, ListType::Cap(";")); - if config.language == Language::Cython && self.fields.is_empty() { - out.write("pass"); - } - - if config.language == Language::Cxx { - let mut wrote_start_newline = false; - - if config.structure.derive_constructor(&self.annotations) && !self.fields.is_empty() { - if !wrote_start_newline { - wrote_start_newline = true; - out.new_line(); - } - - out.new_line(); - - let arg_renamer = |name: &str| { - config - .function - .rename_args - .apply(name, IdentifierType::FunctionArg) - .into_owned() - }; - write!(out, "{}(", self.export_name()); - let vec: Vec<_> = self - .fields - .iter() - .map(|field| { - Field::from_name_and_type( - // const-ref args to constructor - format!("const& {}", arg_renamer(&field.name)), - field.ty.clone(), - ) - }) - .collect(); - out.write_vertical_source_list(&vec[..], ListType::Join(",")); - write!(out, ")"); - out.new_line(); - write!(out, " : "); - let vec: Vec<_> = self - .fields - .iter() - .map(|field| format!("{}({})", field.name, arg_renamer(&field.name))) - .collect(); - out.write_vertical_source_list(&vec[..], ListType::Join(",")); - out.new_line(); - write!(out, "{{}}"); - out.new_line(); - } - - let other = config - .function - .rename_args - .apply("other", IdentifierType::FunctionArg); - - if self - .annotations - .bool("internal-derive-bitflags") - .unwrap_or(false) - { - if !wrote_start_newline { - wrote_start_newline = true; - out.new_line(); - } - let constexpr_prefix = if config.constant.allow_constexpr { - "constexpr " - } else { - "" - }; - - out.new_line(); - write!(out, "{}explicit operator bool() const", constexpr_prefix); - out.open_brace(); - write!(out, "return !!bits;"); - out.close_brace(false); - - out.new_line(); - write!( - out, - "{}{} operator~() const", - constexpr_prefix, - self.export_name() - ); - out.open_brace(); - write!( - out, - "return {} {{ static_cast(~bits) }};", - self.export_name() - ); - out.close_brace(false); - self.emit_bitflags_binop(constexpr_prefix, '|', &other, out); - self.emit_bitflags_binop(constexpr_prefix, '&', &other, out); - self.emit_bitflags_binop(constexpr_prefix, '^', &other, out); - } - - // Generate a serializer function that allows dumping this struct - // to an std::ostream. It's defined as a friend function inside the - // struct definition, and doesn't need the `inline` keyword even - // though it's implemented right in the generated header file. - if config.structure.derive_ostream(&self.annotations) { - if !wrote_start_newline { - wrote_start_newline = true; - out.new_line(); - } - - out.new_line(); - let stream = config - .function - .rename_args - .apply("stream", IdentifierType::FunctionArg); - let instance = config - .function - .rename_args - .apply("instance", IdentifierType::FunctionArg); - write!( - out, - "friend std::ostream& operator<<(std::ostream& {}, const {}& {})", - stream, - self.export_name(), - instance, - ); - out.open_brace(); - write!(out, "return {} << \"{{ \"", stream); - let vec: Vec<_> = self - .fields - .iter() - .map(|x| format!(" << \"{}=\" << {}.{}", x.name, instance, x.name)) - .collect(); - out.write_vertical_source_list(&vec[..], ListType::Join(" << \", \"")); - out.write(" << \" }\";"); - out.close_brace(false); - } - - let skip_fields = self.has_tag_field as usize; - - macro_rules! emit_op { - ($op_name:expr, $op:expr, $conjuc:expr) => {{ - if !wrote_start_newline { - #[allow(unused_assignments)] - { - wrote_start_newline = true; - } - out.new_line(); - } - - out.new_line(); - - if let Some(Some(attrs)) = - self.annotations.atom(concat!($op_name, "-attributes")) - { - write!(out, "{} ", attrs); - } - - write!( - out, - "bool operator{}(const {}& {}) const", - $op, - self.export_name(), - other - ); - out.open_brace(); - out.write("return "); - let vec: Vec<_> = self - .fields - .iter() - .skip(skip_fields) - .map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name)) - .collect(); - out.write_vertical_source_list( - &vec[..], - ListType::Join(&format!(" {}", $conjuc)), - ); - out.write(";"); - out.close_brace(false); - }}; - } - - if config.structure.derive_eq(&self.annotations) && self.can_derive_eq() { - emit_op!("eq", "==", "&&"); - } - if config.structure.derive_neq(&self.annotations) && self.can_derive_eq() { - emit_op!("neq", "!=", "||"); - } - if config.structure.derive_lt(&self.annotations) - && self.fields.len() == 1 - && self.fields[0].ty.can_cmp_order() - { - emit_op!("lt", "<", "&&"); - } - if config.structure.derive_lte(&self.annotations) - && self.fields.len() == 1 - && self.fields[0].ty.can_cmp_order() - { - emit_op!("lte", "<=", "&&"); - } - if config.structure.derive_gt(&self.annotations) - && self.fields.len() == 1 - && self.fields[0].ty.can_cmp_order() - { - emit_op!("gt", ">", "&&"); - } - if config.structure.derive_gte(&self.annotations) - && self.fields.len() == 1 - && self.fields[0].ty.can_cmp_order() - { - emit_op!("gte", ">=", "&&"); - } - } - - // Emit the post_body section, if relevant - if let Some(body) = config.export.post_body(&self.path) { - out.new_line(); - out.write_raw_block(body); - } - - if config.language == Language::Cxx - && config.structure.associated_constants_in_body - && config.constant.allow_static_const - { - for constant in &self.associated_constants { - out.new_line(); - constant.write_declaration(config, out, self); - } - } - - if config.language == Language::C && config.style.generate_typedef() { - out.close_brace(false); - write!(out, " {};", self.export_name()); - } else { - out.close_brace(true); - } - - for constant in &self.associated_constants { - out.new_line(); - constant.write(config, out, Some(self)); - } - - condition.write_after(config, out); - } -} diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs index 5a31fb646..21a4c8be8 100644 --- a/src/bindgen/ir/ty.rs +++ b/src/bindgen/ir/ty.rs @@ -7,11 +7,11 @@ use std::io::Write; use syn::ext::IdentExt; -use crate::bindgen::cdecl; use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{GenericArgument, GenericParams, GenericPath, Path}; +use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::library::Library; use crate::bindgen::monomorph::Monomorphs; use crate::bindgen::utilities::IterHelpers; @@ -380,12 +380,6 @@ impl ConstExpr { } } -impl Source for ConstExpr { - fn write(&self, _config: &Config, out: &mut SourceWriter) { - write!(out, "{}", self.as_str()); - } -} - #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum Type { Ptr { @@ -1004,14 +998,15 @@ impl Type { } } -impl Source for String { - fn write(&self, _config: &Config, out: &mut SourceWriter) { - write!(out, "{}", self); +impl Source for ConstExpr { + fn write(&self, _language_backend: &LB, out: &mut SourceWriter) { + write!(out, "{}", self.as_str()); } } -impl Source for Type { - fn write(&self, config: &Config, out: &mut SourceWriter) { - cdecl::write_type(out, self, config); +// TODO this should probably remplace by some more specific structs +impl Source for String { + fn write(&self, _language_backend: &LB, out: &mut SourceWriter) { + write!(out, "{}", self); } } diff --git a/src/bindgen/ir/typedef.rs b/src/bindgen/ir/typedef.rs index 626732e2a..dbfdc5ba8 100644 --- a/src/bindgen/ir/typedef.rs +++ b/src/bindgen/ir/typedef.rs @@ -3,21 +3,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::collections::HashMap; -use std::io::Write; use syn::ext::IdentExt; -use crate::bindgen::config::{Config, Language}; +use crate::bindgen::config::Config; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ - AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericArgument, GenericParams, Item, - ItemContainer, Path, ToCondition, Type, + AnnotationSet, Cfg, Documentation, GenericArgument, GenericParams, Item, ItemContainer, Path, + Type, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; use crate::bindgen::monomorph::Monomorphs; -use crate::bindgen::writer::{Source, SourceWriter}; /// A type alias that is represented as a C typedef #[derive(Debug, Clone)] @@ -135,11 +133,6 @@ impl Item for Typedef { ItemContainer::Typedef(self.clone()) } - fn rename_for_config(&mut self, config: &Config) { - config.export.rename(&mut self.export_name); - self.aliased.rename_for_config(config, &self.generic_params); - } - fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) { resolver.add_none(&self.path); } @@ -148,6 +141,11 @@ impl Item for Typedef { self.aliased.resolve_declaration_types(resolver); } + fn rename_for_config(&mut self, config: &Config) { + config.export.rename(&mut self.export_name); + self.aliased.rename_for_config(config, &self.generic_params); + } + fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { self.aliased .add_dependencies_ignoring_generics(&self.generic_params, library, out); @@ -179,30 +177,3 @@ impl Item for Typedef { out.insert_typedef(library, self, monomorph, generic_values.to_owned()); } } - -impl Source for Typedef { - fn write(&self, config: &Config, out: &mut SourceWriter) { - let condition = self.cfg.to_condition(config); - condition.write_before(config, out); - - self.documentation.write(config, out); - - self.generic_params.write(config, out); - - match config.language { - Language::Cxx => { - write!(out, "using {} = ", self.export_name()); - self.aliased.write(config, out); - } - Language::C | Language::Cython => { - write!(out, "{} ", config.language.typedef()); - Field::from_name_and_type(self.export_name().to_owned(), self.aliased.clone()) - .write(config, out); - } - } - - out.write(";"); - - condition.write_after(config, out); - } -} diff --git a/src/bindgen/ir/union.rs b/src/bindgen/ir/union.rs index 07bd16c58..32db72648 100644 --- a/src/bindgen/ir/union.rs +++ b/src/bindgen/ir/union.rs @@ -2,23 +2,20 @@ * 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 std::io::Write; - use syn::ext::IdentExt; -use crate::bindgen::config::{Config, Language, LayoutConfig}; +use crate::bindgen::config::{Config, LayoutConfig}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ - AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericArgument, GenericParams, Item, - ItemContainer, Path, Repr, ReprAlign, ReprStyle, ToCondition, + AnnotationSet, Cfg, Documentation, Field, GenericArgument, GenericParams, Item, ItemContainer, + Path, Repr, ReprAlign, ReprStyle, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; use crate::bindgen::monomorph::Monomorphs; use crate::bindgen::rename::{IdentifierType, RenameRule}; use crate::bindgen::utilities::IterHelpers; -use crate::bindgen::writer::{ListType, Source, SourceWriter}; #[derive(Debug, Clone)] pub struct Union { @@ -258,79 +255,3 @@ impl Item for Union { out.insert_union(library, self, monomorph, generic_values.to_owned()); } } - -impl Source for Union { - fn write(&self, config: &Config, out: &mut SourceWriter) { - let condition = self.cfg.to_condition(config); - condition.write_before(config, out); - - self.documentation.write(config, out); - - self.generic_params.write(config, out); - - // The following results in - // C++ or C with Tag as style: - // union Name { - // C with Type only style: - // typedef union { - // C with Both as style: - // typedef union Name { - match config.language { - Language::C if config.style.generate_typedef() => out.write("typedef "), - Language::C | Language::Cxx => {} - Language::Cython => out.write(config.style.cython_def()), - } - - out.write("union"); - - // Cython supports `packed` on structs (see comments there), but not on unions. - if config.language != Language::Cython { - if let Some(align) = self.alignment { - match align { - ReprAlign::Packed => { - if let Some(ref anno) = config.layout.packed { - write!(out, " {}", anno); - } - } - ReprAlign::Align(n) => { - if let Some(ref anno) = config.layout.aligned_n { - write!(out, " {}({})", anno, n); - } - } - } - } - } - - if config.language != Language::C || config.style.generate_tag() { - write!(out, " {}", self.export_name); - } - - out.open_brace(); - - // Emit the pre_body section, if relevant - if let Some(body) = config.export.pre_body(&self.path) { - out.write_raw_block(body); - out.new_line(); - } - - out.write_vertical_source_list(&self.fields, ListType::Cap(";")); - if config.language == Language::Cython && self.fields.is_empty() { - out.write("pass"); - } - - // Emit the post_body section, if relevant - if let Some(body) = config.export.post_body(&self.path) { - out.new_line(); - out.write_raw_block(body); - } - - if config.language == Language::C && config.style.generate_typedef() { - out.close_brace(false); - write!(out, " {};", self.export_name); - } else { - out.close_brace(true); - } - - condition.write_after(config, out); - } -} diff --git a/src/bindgen/language_backend/clike.rs b/src/bindgen/language_backend/clike.rs index 6138981e8..93f4d04c9 100644 --- a/src/bindgen/language_backend/clike.rs +++ b/src/bindgen/language_backend/clike.rs @@ -1,6 +1,13 @@ +use crate::bindgen::ir::{ + to_known_assoc_constant, ConditionWrite, Documentation, Enum, EnumVariant, Field, Function, + GenericParams, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, ToCondition, Type, + Typedef, Union, +}; use crate::bindgen::language_backend::{LanguageBackend, NamespaceOperation}; -use crate::bindgen::writer::SourceWriter; -use crate::bindgen::{Config, Language}; +use crate::bindgen::rename::IdentifierType; +use crate::bindgen::writer::{ListType, Source, SourceWriter}; +use crate::bindgen::{cdecl, Config, Language, Layout}; +use crate::bindgen::{DocumentationLength, DocumentationStyle}; use std::io::Write; pub struct CLikeLanguageBackend { @@ -173,3 +180,967 @@ impl LanguageBackend for CLikeLanguageBackend { } } } + +impl Source for EnumVariant { + fn write(&self, language_backend: &CLikeLanguageBackend, out: &mut SourceWriter) { + let condition = self.cfg.to_condition(&language_backend.config); + + condition.write_before(&language_backend.config, out); + + self.documentation.write(language_backend, out); + write!(out, "{}", self.export_name); + if let Some(discriminant) = &self.discriminant { + out.write(" = "); + + discriminant.write(language_backend, out); + } + out.write(","); + condition.write_after(&language_backend.config, out); + } +} + +impl Source for Enum { + fn write(&self, language_backend: &CLikeLanguageBackend, out: &mut SourceWriter) { + let size = self + .repr + .ty + .map(|ty| ty.to_primitive().to_repr_c(&language_backend.config)); + let has_data = self.tag.is_some(); + let inline_tag_field = Self::inline_tag_field(&self.repr); + let tag_name = self.tag_name(); + + let condition = self.cfg.to_condition(&language_backend.config); + condition.write_before(&language_backend.config, out); + + self.documentation.write(language_backend, out); + self.generic_params.write(language_backend, out); + + // If the enum has data, we need to emit a struct or union for the data + // and enum for the tag. C++ supports nested type definitions, so we open + // the struct or union here and define the tag enum inside it (*). + if has_data && language_backend.config.language == Language::Cxx { + self.open_struct_or_union(&language_backend.config, out, inline_tag_field); + } + + // Emit the tag enum and everything related to it. + self.write_tag_enum( + &language_backend.config, + language_backend, + out, + size, + has_data, + tag_name, + ); + + // If the enum has data, we need to emit structs for the variants and gather them together. + if has_data { + self.write_variant_defs(&language_backend.config, language_backend, out); + out.new_line(); + out.new_line(); + + // Open the struct or union for the data (**), gathering all the variants with data + // together, unless it's C++, then we have already opened that struct/union at (*) and + // are currently inside it. + if language_backend.config.language != Language::Cxx { + self.open_struct_or_union(&language_backend.config, out, inline_tag_field); + } + + // Emit tag field that is separate from all variants. + self.write_tag_field( + &language_backend.config, + out, + size, + inline_tag_field, + tag_name, + ); + out.new_line(); + + // Open union of all variants with data, only in the non-inline tag scenario. + if !inline_tag_field { + out.write("union"); + out.open_brace(); + } + + // Emit fields for all variants with data. + self.write_variant_fields( + &language_backend.config, + language_backend, + out, + inline_tag_field, + ); + + // Close union of all variants with data, only in the non-inline tag scenario. + if !inline_tag_field { + out.close_brace(true); + } + + // Emit convenience methods for the struct or enum for the data. + self.write_derived_functions_data( + &language_backend.config, + language_backend, + out, + tag_name, + ); + + // Emit the post_body section, if relevant. + if let Some(body) = language_backend.config.export.post_body(&self.path) { + out.new_line(); + out.write_raw_block(body); + } + + // Close the struct or union opened either at (*) or at (**). + if language_backend.config.language == Language::C + && language_backend.config.style.generate_typedef() + { + out.close_brace(false); + write!(out, " {};", self.export_name); + } else { + out.close_brace(true); + } + } + + condition.write_after(&language_backend.config, out); + } +} + +impl Source for Struct { + fn write(&self, language_backend: &CLikeLanguageBackend, out: &mut SourceWriter) { + if self.is_transparent { + let typedef = Typedef { + path: self.path.clone(), + export_name: self.export_name.to_owned(), + generic_params: self.generic_params.clone(), + aliased: self.fields[0].ty.clone(), + cfg: self.cfg.clone(), + annotations: self.annotations.clone(), + documentation: self.documentation.clone(), + }; + typedef.write(language_backend, out); + for constant in &self.associated_constants { + out.new_line(); + constant.write(&language_backend.config, language_backend, out, Some(self)); + } + return; + } + + let condition = self.cfg.to_condition(&language_backend.config); + condition.write_before(&language_backend.config, out); + + self.documentation.write(language_backend, out); + + if !self.is_enum_variant_body { + self.generic_params.write(language_backend, out); + } + + // The following results in + // C++ or C with Tag as style: + // struct Name { + // C with Type only style: + // typedef struct { + // C with Both as style: + // typedef struct Name { + match language_backend.config.language { + Language::C if language_backend.config.style.generate_typedef() => { + out.write("typedef ") + } + Language::C | Language::Cxx => {} + _ => unreachable!(), + } + + out.write("struct"); + + if let Some(align) = self.alignment { + match align { + ReprAlign::Packed => { + if let Some(ref anno) = language_backend.config.layout.packed { + write!(out, " {}", anno); + } + } + ReprAlign::Align(n) => { + if let Some(ref anno) = language_backend.config.layout.aligned_n { + write!(out, " {}({})", anno, n); + } + } + } + } + + if self.annotations.must_use(&language_backend.config) { + if let Some(ref anno) = language_backend.config.structure.must_use { + write!(out, " {}", anno); + } + } + + if language_backend.config.language != Language::C + || language_backend.config.style.generate_tag() + { + write!(out, " {}", self.export_name()); + } + + out.open_brace(); + + // Emit the pre_body section, if relevant + if let Some(body) = language_backend.config.export.pre_body(&self.path) { + out.write_raw_block(body); + out.new_line(); + } + + out.write_vertical_source_list(language_backend, &self.fields, ListType::Cap(";")); + + if language_backend.config.language == Language::Cxx { + let mut wrote_start_newline = false; + + if language_backend + .config + .structure + .derive_constructor(&self.annotations) + && !self.fields.is_empty() + { + if !wrote_start_newline { + wrote_start_newline = true; + out.new_line(); + } + + out.new_line(); + + let arg_renamer = |name: &str| { + language_backend + .config + .function + .rename_args + .apply(name, IdentifierType::FunctionArg) + .into_owned() + }; + write!(out, "{}(", self.export_name()); + let vec: Vec<_> = self + .fields + .iter() + .map(|field| { + Field::from_name_and_type( + // const-ref args to constructor + format!("const& {}", arg_renamer(&field.name)), + field.ty.clone(), + ) + }) + .collect(); + out.write_vertical_source_list(language_backend, &vec[..], ListType::Join(",")); + write!(out, ")"); + out.new_line(); + write!(out, " : "); + let vec: Vec<_> = self + .fields + .iter() + .map(|field| format!("{}({})", field.name, arg_renamer(&field.name))) + .collect(); + out.write_vertical_source_list(language_backend, &vec[..], ListType::Join(",")); + out.new_line(); + write!(out, "{{}}"); + out.new_line(); + } + + let other = language_backend + .config + .function + .rename_args + .apply("other", IdentifierType::FunctionArg); + + if self + .annotations + .bool("internal-derive-bitflags") + .unwrap_or(false) + { + if !wrote_start_newline { + wrote_start_newline = true; + out.new_line(); + } + let constexpr_prefix = if language_backend.config.constant.allow_constexpr { + "constexpr " + } else { + "" + }; + + out.new_line(); + write!(out, "{}explicit operator bool() const", constexpr_prefix); + out.open_brace(); + write!(out, "return !!bits;"); + out.close_brace(false); + + out.new_line(); + write!( + out, + "{}{} operator~() const", + constexpr_prefix, + self.export_name() + ); + out.open_brace(); + write!( + out, + "return {} {{ static_cast(~bits) }};", + self.export_name() + ); + out.close_brace(false); + self.emit_bitflags_binop(constexpr_prefix, '|', &other, out); + self.emit_bitflags_binop(constexpr_prefix, '&', &other, out); + self.emit_bitflags_binop(constexpr_prefix, '^', &other, out); + } + + // Generate a serializer function that allows dumping this struct + // to an std::ostream. It's defined as a friend function inside the + // struct definition, and doesn't need the `inline` keyword even + // though it's implemented right in the generated header file. + if language_backend + .config + .structure + .derive_ostream(&self.annotations) + { + if !wrote_start_newline { + wrote_start_newline = true; + out.new_line(); + } + + out.new_line(); + let stream = language_backend + .config + .function + .rename_args + .apply("stream", IdentifierType::FunctionArg); + let instance = language_backend + .config + .function + .rename_args + .apply("instance", IdentifierType::FunctionArg); + write!( + out, + "friend std::ostream& operator<<(std::ostream& {}, const {}& {})", + stream, + self.export_name(), + instance, + ); + out.open_brace(); + write!(out, "return {} << \"{{ \"", stream); + let vec: Vec<_> = self + .fields + .iter() + .map(|x| format!(" << \"{}=\" << {}.{}", x.name, instance, x.name)) + .collect(); + out.write_vertical_source_list( + language_backend, + &vec[..], + ListType::Join(" << \", \""), + ); + out.write(" << \" }\";"); + out.close_brace(false); + } + + let skip_fields = self.has_tag_field as usize; + + macro_rules! emit_op { + ($op_name:expr, $op:expr, $conjuc:expr) => {{ + if !wrote_start_newline { + #[allow(unused_assignments)] + { + wrote_start_newline = true; + } + out.new_line(); + } + + out.new_line(); + + if let Some(Some(attrs)) = + self.annotations.atom(concat!($op_name, "-attributes")) + { + write!(out, "{} ", attrs); + } + + write!( + out, + "bool operator{}(const {}& {}) const", + $op, + self.export_name(), + other + ); + out.open_brace(); + out.write("return "); + let vec: Vec<_> = self + .fields + .iter() + .skip(skip_fields) + .map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name)) + .collect(); + out.write_vertical_source_list( + language_backend, + &vec[..], + ListType::Join(&format!(" {}", $conjuc)), + ); + out.write(";"); + out.close_brace(false); + }}; + } + + if language_backend + .config + .structure + .derive_eq(&self.annotations) + && self.can_derive_eq() + { + emit_op!("eq", "==", "&&"); + } + if language_backend + .config + .structure + .derive_neq(&self.annotations) + && self.can_derive_eq() + { + emit_op!("neq", "!=", "||"); + } + if language_backend + .config + .structure + .derive_lt(&self.annotations) + && self.fields.len() == 1 + && self.fields[0].ty.can_cmp_order() + { + emit_op!("lt", "<", "&&"); + } + if language_backend + .config + .structure + .derive_lte(&self.annotations) + && self.fields.len() == 1 + && self.fields[0].ty.can_cmp_order() + { + emit_op!("lte", "<=", "&&"); + } + if language_backend + .config + .structure + .derive_gt(&self.annotations) + && self.fields.len() == 1 + && self.fields[0].ty.can_cmp_order() + { + emit_op!("gt", ">", "&&"); + } + if language_backend + .config + .structure + .derive_gte(&self.annotations) + && self.fields.len() == 1 + && self.fields[0].ty.can_cmp_order() + { + emit_op!("gte", ">=", "&&"); + } + } + + // Emit the post_body section, if relevant + if let Some(body) = language_backend.config.export.post_body(&self.path) { + out.new_line(); + out.write_raw_block(body); + } + + if language_backend.config.language == Language::Cxx + && language_backend + .config + .structure + .associated_constants_in_body + && language_backend.config.constant.allow_static_const + { + for constant in &self.associated_constants { + out.new_line(); + constant.write_declaration(&language_backend.config, language_backend, out, self); + } + } + + if language_backend.config.language == Language::C + && language_backend.config.style.generate_typedef() + { + out.close_brace(false); + write!(out, " {};", self.export_name()); + } else { + out.close_brace(true); + } + + for constant in &self.associated_constants { + out.new_line(); + constant.write(&language_backend.config, language_backend, out, Some(self)); + } + + condition.write_after(&language_backend.config, out); + } +} + +impl Source for Union { + fn write(&self, language_backend: &CLikeLanguageBackend, out: &mut SourceWriter) { + let condition = self.cfg.to_condition(&language_backend.config); + condition.write_before(&language_backend.config, out); + + self.documentation.write(language_backend, out); + + self.generic_params.write(language_backend, out); + + // The following results in + // C++ or C with Tag as style: + // union Name { + // C with Type only style: + // typedef union { + // C with Both as style: + // typedef union Name { + match language_backend.config.language { + Language::C if language_backend.config.style.generate_typedef() => { + out.write("typedef ") + } + Language::C | Language::Cxx => {} + _ => unreachable!(), + } + + out.write("union"); + + if let Some(align) = self.alignment { + match align { + ReprAlign::Packed => { + if let Some(ref anno) = language_backend.config.layout.packed { + write!(out, " {}", anno); + } + } + ReprAlign::Align(n) => { + if let Some(ref anno) = language_backend.config.layout.aligned_n { + write!(out, " {}({})", anno, n); + } + } + } + } + + if language_backend.config.language != Language::C + || language_backend.config.style.generate_tag() + { + write!(out, " {}", self.export_name); + } + + out.open_brace(); + + // Emit the pre_body section, if relevant + if let Some(body) = language_backend.config.export.pre_body(&self.path) { + out.write_raw_block(body); + out.new_line(); + } + + out.write_vertical_source_list(language_backend, &self.fields, ListType::Cap(";")); + + // Emit the post_body section, if relevant + if let Some(body) = language_backend.config.export.post_body(&self.path) { + out.new_line(); + out.write_raw_block(body); + } + + if language_backend.config.language == Language::C + && language_backend.config.style.generate_typedef() + { + out.close_brace(false); + write!(out, " {};", self.export_name); + } else { + out.close_brace(true); + } + + condition.write_after(&language_backend.config, out); + } +} + +impl Source for OpaqueItem { + fn write(&self, language_backend: &CLikeLanguageBackend, out: &mut SourceWriter) { + let condition = self.cfg.to_condition(&language_backend.config); + condition.write_before(&language_backend.config, out); + + self.documentation.write(language_backend, out); + + self.generic_params + .write_with_default(language_backend, &language_backend.config, out); + + match language_backend.config.language { + Language::C if language_backend.config.style.generate_typedef() => { + write!( + out, + "typedef struct {} {};", + self.export_name(), + self.export_name() + ); + } + Language::C | Language::Cxx => { + write!(out, "struct {};", self.export_name()); + } + _ => unreachable!(), + } + + condition.write_after(&language_backend.config, out); + } +} + +impl Source for Field { + fn write(&self, language_backend: &CLikeLanguageBackend, out: &mut SourceWriter) { + let condition = self.cfg.to_condition(&language_backend.config); + condition.write_before(&language_backend.config, out); + + self.documentation.write(language_backend, out); + cdecl::write_field( + language_backend, + out, + &self.ty, + &self.name, + &language_backend.config, + ); + + if let Some(bitfield) = self.annotations.atom("bitfield") { + write!(out, ": {}", bitfield.unwrap_or_default()); + } + + condition.write_after(&language_backend.config, out); + // FIXME(#634): `write_vertical_source_list` should support + // configuring list elements natively. For now we print a newline + // here to avoid printing `#endif;` with semicolon. + if condition.is_some() { + out.new_line(); + } + } +} + +impl Source for GenericParams { + fn write(&self, language_backend: &CLikeLanguageBackend, out: &mut SourceWriter) { + self.write_internal(language_backend, &language_backend.config, out, false); + } +} + +impl Source for Typedef { + fn write(&self, language_backend: &CLikeLanguageBackend, out: &mut SourceWriter) { + let condition = self.cfg.to_condition(&language_backend.config); + condition.write_before(&language_backend.config, out); + + self.documentation.write(language_backend, out); + + self.generic_params.write(language_backend, out); + + match language_backend.config.language { + Language::Cxx => { + write!(out, "using {} = ", self.export_name()); + self.aliased.write(language_backend, out); + } + Language::C => { + write!(out, "{} ", language_backend.config.language.typedef()); + Field::from_name_and_type(self.export_name().to_owned(), self.aliased.clone()) + .write(language_backend, out); + } + _ => unreachable!(), + } + + out.write(";"); + + condition.write_after(&language_backend.config, out); + } +} + +impl Source for Static { + fn write(&self, language_backend: &CLikeLanguageBackend, out: &mut SourceWriter) { + out.write("extern "); + if let Type::Ptr { is_const: true, .. } = self.ty { + } else if !self.mutable { + out.write("const "); + } + cdecl::write_field( + language_backend, + out, + &self.ty, + &self.export_name, + &language_backend.config, + ); + out.write(";"); + } +} + +impl Source for Function { + fn write(&self, language_backend: &CLikeLanguageBackend, out: &mut SourceWriter) { + fn write_1( + func: &Function, + language_backend: &CLikeLanguageBackend, + out: &mut SourceWriter, + ) { + let prefix = language_backend.config.function.prefix(&func.annotations); + let postfix = language_backend.config.function.postfix(&func.annotations); + + let condition = func.cfg.to_condition(&language_backend.config); + condition.write_before(&language_backend.config, out); + + func.documentation.write(language_backend, out); + + if func.extern_decl { + out.write("extern "); + } else { + if let Some(ref prefix) = prefix { + write!(out, "{} ", prefix); + } + if func.annotations.must_use(&language_backend.config) { + if let Some(ref anno) = language_backend.config.function.must_use { + write!(out, "{} ", anno); + } + } + } + cdecl::write_func( + language_backend, + out, + func, + Layout::Horizontal, + &language_backend.config, + ); + + if !func.extern_decl { + if let Some(ref postfix) = postfix { + write!(out, " {}", postfix); + } + } + + if let Some(ref swift_name_macro) = language_backend.config.function.swift_name_macro { + if let Some(swift_name) = func.swift_name(&language_backend.config) { + write!(out, " {}({})", swift_name_macro, swift_name); + } + } + + out.write(";"); + + condition.write_after(&language_backend.config, out); + } + + fn write_2( + func: &Function, + language_backend: &CLikeLanguageBackend, + out: &mut SourceWriter, + ) { + let prefix = language_backend.config.function.prefix(&func.annotations); + let postfix = language_backend.config.function.postfix(&func.annotations); + + let condition = func.cfg.to_condition(&language_backend.config); + + condition.write_before(&language_backend.config, out); + + func.documentation.write(language_backend, out); + + if func.extern_decl { + out.write("extern "); + } else { + if let Some(ref prefix) = prefix { + write!(out, "{}", prefix); + out.new_line(); + } + if func.annotations.must_use(&language_backend.config) { + if let Some(ref anno) = language_backend.config.function.must_use { + write!(out, "{}", anno); + out.new_line(); + } + } + } + cdecl::write_func( + language_backend, + out, + func, + Layout::Vertical, + &language_backend.config, + ); + if !func.extern_decl { + if let Some(ref postfix) = postfix { + out.new_line(); + write!(out, "{}", postfix); + } + } + + if let Some(ref swift_name_macro) = language_backend.config.function.swift_name_macro { + if let Some(swift_name) = func.swift_name(&language_backend.config) { + write!(out, " {}({})", swift_name_macro, swift_name); + } + } + + out.write(";"); + + condition.write_after(&language_backend.config, out); + } + + match language_backend.config.function.args { + Layout::Horizontal => write_1(self, language_backend, out), + Layout::Vertical => write_2(self, language_backend, out), + Layout::Auto => { + if !out.try_write( + |out| write_1(self, language_backend, out), + language_backend.config.line_length, + ) { + write_2(self, language_backend, out) + } + } + } + } +} + +impl Source for Type { + fn write(&self, language_backend: &CLikeLanguageBackend, out: &mut SourceWriter) { + cdecl::write_type(language_backend, out, self, &language_backend.config); + } +} + +impl Source for Documentation { + fn write(&self, language_backend: &CLikeLanguageBackend, out: &mut SourceWriter) { + if self.doc_comment.is_empty() || !language_backend.config.documentation { + return; + } + + let end = match language_backend.config.documentation_length { + DocumentationLength::Short => 1, + DocumentationLength::Full => self.doc_comment.len(), + }; + + let style = match language_backend.config.documentation_style { + DocumentationStyle::Auto if language_backend.config.language == Language::C => { + DocumentationStyle::Doxy + } + DocumentationStyle::Auto if language_backend.config.language == Language::Cxx => { + DocumentationStyle::Cxx + } + DocumentationStyle::Auto => DocumentationStyle::C, // Fallback if `Language` gets extended. + other => other, + }; + + // Following these documents for style conventions: + // https://en.wikibooks.org/wiki/C++_Programming/Code/Style_Conventions/Comments + // https://www.cs.cmu.edu/~410/doc/doxygen.html + match style { + DocumentationStyle::C => { + out.write("/*"); + out.new_line(); + } + + DocumentationStyle::Doxy => { + out.write("/**"); + out.new_line(); + } + + _ => (), + } + + for line in &self.doc_comment[..end] { + match style { + DocumentationStyle::C => out.write(""), + DocumentationStyle::Doxy => out.write(" *"), + DocumentationStyle::C99 => out.write("//"), + DocumentationStyle::Cxx => out.write("///"), + DocumentationStyle::Auto => unreachable!(), // Auto case should always be covered + } + + write!(out, "{}", line); + out.new_line(); + } + + match style { + DocumentationStyle::C => { + out.write(" */"); + out.new_line(); + } + + DocumentationStyle::Doxy => { + out.write(" */"); + out.new_line(); + } + + _ => (), + } + } +} + +impl Source for Literal { + fn write(&self, language_backend: &CLikeLanguageBackend, out: &mut SourceWriter) { + match self { + Literal::Expr(v) => match (&**v, language_backend.config.language) { + ("true", Language::Cython) => write!(out, "True"), + ("false", Language::Cython) => write!(out, "False"), + (v, _) => write!(out, "{}", v), + }, + Literal::Path { + ref associated_to, + ref name, + } => { + if let Some((ref path, ref export_name)) = associated_to { + if let Some(known) = to_known_assoc_constant(path, name) { + return write!(out, "{}", known); + } + let path_separator = match language_backend.config.language { + Language::Cython | Language::C => "_", + Language::Cxx => { + if language_backend + .config + .structure + .associated_constants_in_body + { + "::" + } else { + "_" + } + } + }; + write!(out, "{}{}", export_name, path_separator) + } + write!(out, "{}", name) + } + Literal::FieldAccess { + ref base, + ref field, + } => { + write!(out, "("); + base.write(language_backend, out); + write!(out, ").{}", field); + } + Literal::PostfixUnaryOp { op, ref value } => { + write!(out, "{}", op); + value.write(language_backend, out); + } + Literal::BinOp { + ref left, + op, + ref right, + } => { + write!(out, "("); + left.write(language_backend, out); + write!(out, " {} ", op); + right.write(language_backend, out); + write!(out, ")"); + } + Literal::Cast { ref ty, ref value } => { + out.write("("); + ty.write(language_backend, out); + out.write(")"); + value.write(language_backend, out); + } + Literal::Struct { + export_name, + fields, + path, + } => { + match language_backend.config.language { + Language::C => write!(out, "({})", export_name), + Language::Cxx => write!(out, "{}", export_name), + _ => unreachable!(), + } + + write!(out, "{{ "); + let mut is_first_field = true; + // In C++, same order as defined is required. + let ordered_fields = out.bindings().struct_field_names(path); + for ordered_key in ordered_fields.iter() { + if let Some(lit) = fields.get(ordered_key) { + if !is_first_field { + write!(out, ", "); + } else { + is_first_field = false; + } + match language_backend.config.language { + Language::Cxx => write!(out, "/* .{} = */ ", ordered_key), + Language::C => write!(out, ".{} = ", ordered_key), + _ => unreachable!(), + } + lit.write(language_backend, out); + } + } + write!(out, " }}"); + } + } + } +} diff --git a/src/bindgen/language_backend/cython.rs b/src/bindgen/language_backend/cython.rs index 63f0bca68..54492130e 100644 --- a/src/bindgen/language_backend/cython.rs +++ b/src/bindgen/language_backend/cython.rs @@ -1,6 +1,13 @@ +use crate::bindgen::ir::{ + to_known_assoc_constant, ConditionWrite, Documentation, Enum, EnumVariant, Field, Function, + GenericParams, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, ToCondition, Type, + Typedef, Union, +}; use crate::bindgen::language_backend::{LanguageBackend, NamespaceOperation}; -use crate::bindgen::writer::SourceWriter; -use crate::bindgen::Config; +use crate::bindgen::writer::{ListType, Source, SourceWriter}; +use crate::bindgen::DocumentationLength; +use crate::bindgen::Layout; +use crate::bindgen::{cdecl, Config}; use std::io::Write; pub struct CythonLanguageBackend { @@ -85,3 +92,515 @@ impl LanguageBackend for CythonLanguageBackend { fn write_footers(&self, _out: &mut SourceWriter) {} } + +impl Source for EnumVariant { + fn write(&self, language_backend: &CythonLanguageBackend, out: &mut SourceWriter) { + self.documentation.write(language_backend, out); + write!(out, "{}", self.export_name); + if let Some(discriminant) = &self.discriminant { + // For extern Cython declarations the enumerator value is ignored, + // but still useful as documentation, so we write it as a comment. + out.write(" #"); + + out.write(" = "); + + discriminant.write(language_backend, out); + } + out.write(","); + } +} + +impl Source for Enum { + fn write(&self, language_backend: &CythonLanguageBackend, out: &mut SourceWriter) { + let size = self + .repr + .ty + .map(|ty| ty.to_primitive().to_repr_c(&language_backend.config)); + let has_data = self.tag.is_some(); + let inline_tag_field = Self::inline_tag_field(&self.repr); + let tag_name = self.tag_name(); + + let condition = self.cfg.to_condition(&language_backend.config); + condition.write_before(&language_backend.config, out); + + self.documentation.write(language_backend, out); + self.generic_params.write(language_backend, out); + + // Emit the tag enum and everything related to it. + self.write_tag_enum( + &language_backend.config, + language_backend, + out, + size, + has_data, + tag_name, + ); + + // If the enum has data, we need to emit structs for the variants and gather them together. + if has_data { + self.write_variant_defs(&language_backend.config, language_backend, out); + out.new_line(); + out.new_line(); + + self.open_struct_or_union(&language_backend.config, out, inline_tag_field); + + // Emit tag field that is separate from all variants. + self.write_tag_field( + &language_backend.config, + out, + size, + inline_tag_field, + tag_name, + ); + out.new_line(); + + // Emit fields for all variants with data. + self.write_variant_fields( + &language_backend.config, + language_backend, + out, + inline_tag_field, + ); + + // Emit the post_body section, if relevant. + if let Some(body) = language_backend.config.export.post_body(&self.path) { + out.new_line(); + out.write_raw_block(body); + } + + out.close_brace(true); + } + + condition.write_after(&language_backend.config, out); + } +} + +impl Source for Struct { + fn write(&self, language_backend: &CythonLanguageBackend, out: &mut SourceWriter) { + if self.is_transparent { + let typedef = Typedef { + path: self.path.clone(), + export_name: self.export_name.to_owned(), + generic_params: self.generic_params.clone(), + aliased: self.fields[0].ty.clone(), + cfg: self.cfg.clone(), + annotations: self.annotations.clone(), + documentation: self.documentation.clone(), + }; + typedef.write(language_backend, out); + for constant in &self.associated_constants { + out.new_line(); + constant.write(&language_backend.config, language_backend, out, Some(self)); + } + return; + } + + let condition = self.cfg.to_condition(&language_backend.config); + condition.write_before(&language_backend.config, out); + + self.documentation.write(language_backend, out); + + if !self.is_enum_variant_body { + self.generic_params.write(language_backend, out); + } + + out.write(language_backend.config.style.cython_def()); + + // Cython extern declarations don't manage layouts, layouts are defined entierly by the + // corresponding C code. So this `packed` is only for documentation, and missing + // `aligned(n)` is also not a problem. + if let Some(align) = self.alignment { + match align { + ReprAlign::Packed => out.write("packed "), + ReprAlign::Align(_) => {} // Not supported + } + } + + out.write("struct"); + + if self.annotations.must_use(&language_backend.config) { + if let Some(ref anno) = language_backend.config.structure.must_use { + write!(out, " {}", anno); + } + } + + write!(out, " {}", self.export_name()); + + out.open_brace(); + + // Emit the pre_body section, if relevant + if let Some(body) = language_backend.config.export.pre_body(&self.path) { + out.write_raw_block(body); + out.new_line(); + } + + out.write_vertical_source_list(language_backend, &self.fields, ListType::Cap(";")); + if self.fields.is_empty() { + out.write("pass"); + } + + // Emit the post_body section, if relevant + if let Some(body) = language_backend.config.export.post_body(&self.path) { + out.new_line(); + out.write_raw_block(body); + } + out.close_brace(true); + + for constant in &self.associated_constants { + out.new_line(); + constant.write(&language_backend.config, language_backend, out, Some(self)); + } + + condition.write_after(&language_backend.config, out); + } +} + +impl Source for Union { + fn write(&self, language_backend: &CythonLanguageBackend, out: &mut SourceWriter) { + let condition = self.cfg.to_condition(&language_backend.config); + condition.write_before(&language_backend.config, out); + + self.documentation.write(language_backend, out); + + self.generic_params.write(language_backend, out); + + out.write(language_backend.config.style.cython_def()); + + out.write("union"); + + write!(out, " {}", self.export_name); + + out.open_brace(); + + // Emit the pre_body section, if relevant + if let Some(body) = language_backend.config.export.pre_body(&self.path) { + out.write_raw_block(body); + out.new_line(); + } + + out.write_vertical_source_list(language_backend, &self.fields, ListType::Cap(";")); + if self.fields.is_empty() { + out.write("pass"); + } + + // Emit the post_body section, if relevant + if let Some(body) = language_backend.config.export.post_body(&self.path) { + out.new_line(); + out.write_raw_block(body); + } + + out.close_brace(true); + + condition.write_after(&language_backend.config, out); + } +} + +impl Source for OpaqueItem { + fn write(&self, language_backend: &CythonLanguageBackend, out: &mut SourceWriter) { + let condition = self.cfg.to_condition(&language_backend.config); + condition.write_before(&language_backend.config, out); + + self.documentation.write(language_backend, out); + + self.generic_params + .write_with_default(language_backend, &language_backend.config, out); + + write!( + out, + "{}struct {}", + language_backend.config.style.cython_def(), + self.export_name() + ); + out.open_brace(); + out.write("pass"); + out.close_brace(false); + + condition.write_after(&language_backend.config, out); + } +} + +impl Source for Field { + fn write(&self, language_backend: &CythonLanguageBackend, out: &mut SourceWriter) { + // Cython doesn't support conditional fields. + // let condition = self.cfg.to_condition(&language_backend.config); + + self.documentation.write(language_backend, out); + cdecl::write_field( + language_backend, + out, + &self.ty, + &self.name, + &language_backend.config, + ); + + // Cython extern declarations don't manage layouts, layouts are defined entierly by the + // corresponding C code. So we can omit bitfield sizes which are not supported by Cython. + // if let Some(bitfield) = self.annotations.atom("bitfield") { + // + // } + } +} + +impl Source for GenericParams { + fn write(&self, _language_backend: &CythonLanguageBackend, _: &mut SourceWriter) { + // not supported + } +} + +impl Source for Typedef { + fn write(&self, language_backend: &CythonLanguageBackend, out: &mut SourceWriter) { + let condition = self.cfg.to_condition(&language_backend.config); + condition.write_before(&language_backend.config, out); + + self.documentation.write(language_backend, out); + + self.generic_params.write(language_backend, out); + + write!(out, "{} ", language_backend.config.language.typedef()); + Field::from_name_and_type(self.export_name().to_owned(), self.aliased.clone()) + .write(language_backend, out); + + out.write(";"); + + condition.write_after(&language_backend.config, out); + } +} + +impl Source for Static { + fn write(&self, language_backend: &CythonLanguageBackend, out: &mut SourceWriter) { + out.write("extern "); + if let Type::Ptr { is_const: true, .. } = self.ty { + } else if !self.mutable { + out.write("const "); + } + cdecl::write_field( + language_backend, + out, + &self.ty, + &self.export_name, + &language_backend.config, + ); + out.write(";"); + } +} + +impl Source for Function { + fn write(&self, language_backend: &CythonLanguageBackend, out: &mut SourceWriter) { + fn write_1( + func: &Function, + language_backend: &CythonLanguageBackend, + out: &mut SourceWriter, + ) { + let prefix = language_backend.config.function.prefix(&func.annotations); + let postfix = language_backend.config.function.postfix(&func.annotations); + + let condition = func.cfg.to_condition(&language_backend.config); + condition.write_before(&language_backend.config, out); + + func.documentation.write(language_backend, out); + + if func.extern_decl { + out.write("extern "); + } else { + if let Some(ref prefix) = prefix { + write!(out, "{} ", prefix); + } + if func.annotations.must_use(&language_backend.config) { + if let Some(ref anno) = language_backend.config.function.must_use { + write!(out, "{} ", anno); + } + } + } + cdecl::write_func( + language_backend, + out, + func, + Layout::Horizontal, + &language_backend.config, + ); + + if !func.extern_decl { + if let Some(ref postfix) = postfix { + write!(out, " {}", postfix); + } + } + + if let Some(ref swift_name_macro) = language_backend.config.function.swift_name_macro { + if let Some(swift_name) = func.swift_name(&language_backend.config) { + write!(out, " {}({})", swift_name_macro, swift_name); + } + } + + out.write(";"); + + condition.write_after(&language_backend.config, out); + } + + fn write_2( + func: &Function, + language_backend: &CythonLanguageBackend, + out: &mut SourceWriter, + ) { + let prefix = language_backend.config.function.prefix(&func.annotations); + let postfix = language_backend.config.function.postfix(&func.annotations); + + let condition = func.cfg.to_condition(&language_backend.config); + + condition.write_before(&language_backend.config, out); + + func.documentation.write(language_backend, out); + + if func.extern_decl { + out.write("extern "); + } else { + if let Some(ref prefix) = prefix { + write!(out, "{}", prefix); + out.new_line(); + } + if func.annotations.must_use(&language_backend.config) { + if let Some(ref anno) = language_backend.config.function.must_use { + write!(out, "{}", anno); + out.new_line(); + } + } + } + cdecl::write_func( + language_backend, + out, + func, + Layout::Vertical, + &language_backend.config, + ); + if !func.extern_decl { + if let Some(ref postfix) = postfix { + out.new_line(); + write!(out, "{}", postfix); + } + } + + if let Some(ref swift_name_macro) = language_backend.config.function.swift_name_macro { + if let Some(swift_name) = func.swift_name(&language_backend.config) { + write!(out, " {}({})", swift_name_macro, swift_name); + } + } + + out.write(";"); + + condition.write_after(&language_backend.config, out); + } + + match language_backend.config.function.args { + Layout::Horizontal => write_1(self, language_backend, out), + Layout::Vertical => write_2(self, language_backend, out), + Layout::Auto => { + if !out.try_write( + |out| write_1(self, language_backend, out), + language_backend.config.line_length, + ) { + write_2(self, language_backend, out) + } + } + } + } +} + +impl Source for Type { + fn write(&self, language_backend: &CythonLanguageBackend, out: &mut SourceWriter) { + cdecl::write_type(language_backend, out, self, &language_backend.config); + } +} + +impl Source for Documentation { + fn write(&self, language_backend: &CythonLanguageBackend, out: &mut SourceWriter) { + if self.doc_comment.is_empty() || !language_backend.config.documentation { + return; + } + + let end = match language_backend.config.documentation_length { + DocumentationLength::Short => 1, + DocumentationLength::Full => self.doc_comment.len(), + }; + + // Cython uses Python-style comments, so `documentation_style` is not relevant. + for line in &self.doc_comment[..end] { + write!(out, "#{}", line); + out.new_line(); + } + } +} + +impl Source for Literal { + fn write(&self, language_backend: &CythonLanguageBackend, out: &mut SourceWriter) { + match self { + Literal::Expr(v) => match &**v { + "true" => write!(out, "True"), + "false" => write!(out, "False"), + v => write!(out, "{}", v), + }, + Literal::Path { + ref associated_to, + ref name, + } => { + if let Some((ref path, ref export_name)) = associated_to { + if let Some(known) = to_known_assoc_constant(path, name) { + return write!(out, "{}", known); + } + write!(out, "{}_", export_name) + } + write!(out, "{}", name) + } + Literal::FieldAccess { + ref base, + ref field, + } => { + write!(out, "("); + base.write(language_backend, out); + write!(out, ").{}", field); + } + Literal::PostfixUnaryOp { op, ref value } => { + write!(out, "{}", op); + value.write(language_backend, out); + } + Literal::BinOp { + ref left, + op, + ref right, + } => { + write!(out, "("); + left.write(language_backend, out); + write!(out, " {} ", op); + right.write(language_backend, out); + write!(out, ")"); + } + Literal::Cast { ref ty, ref value } => { + out.write("<"); + ty.write(language_backend, out); + out.write(">"); + value.write(language_backend, out); + } + Literal::Struct { + export_name, + fields, + path, + } => { + write!(out, "<{}>", export_name); + + write!(out, "{{ "); + let mut is_first_field = true; + // In C++, same order as defined is required. + let ordered_fields = out.bindings().struct_field_names(path); + for ordered_key in ordered_fields.iter() { + if let Some(lit) = fields.get(ordered_key) { + if !is_first_field { + write!(out, ", "); + } else { + is_first_field = false; + } + lit.write(language_backend, out); + } + } + write!(out, " }}"); + } + } + } +} diff --git a/src/bindgen/mod.rs b/src/bindgen/mod.rs index e0619438a..5d7882118 100644 --- a/src/bindgen/mod.rs +++ b/src/bindgen/mod.rs @@ -46,6 +46,7 @@ mod declarationtyperesolver; mod dependencies; mod error; mod ir; +mod language_backend; mod library; mod mangle; mod monomorph; @@ -54,7 +55,6 @@ mod rename; mod reserved; mod utilities; mod writer; -mod language_backend; #[allow(unused)] pub(crate) use self::cargo::*; @@ -63,4 +63,4 @@ pub use self::bindings::Bindings; pub use self::builder::Builder; pub use self::config::Profile; // disambiguate with cargo::Profile pub use self::config::*; -pub use self::error::Error; \ No newline at end of file +pub use self::error::Error; diff --git a/src/bindgen/writer.rs b/src/bindgen/writer.rs index eed691723..2c6fa2de2 100644 --- a/src/bindgen/writer.rs +++ b/src/bindgen/writer.rs @@ -6,7 +6,8 @@ use std::cmp; use std::io; use std::io::Write; -use crate::bindgen::config::{Braces, Config, Language}; +use crate::bindgen::config::{Braces, Language}; +use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::Bindings; /// A type of way to format a list. @@ -207,13 +208,14 @@ impl<'a, F: Write> SourceWriter<'a, F> { InnerWriter(self).write_fmt(fmt).unwrap(); } - pub fn write_horizontal_source_list( + pub fn write_horizontal_source_list>( &mut self, + language_backend: &LB, items: &[S], list_type: ListType<'_>, ) { for (i, item) in items.iter().enumerate() { - item.write(&self.bindings.config, self); + item.write(language_backend, self); match list_type { ListType::Join(text) => { @@ -228,11 +230,16 @@ impl<'a, F: Write> SourceWriter<'a, F> { } } - pub fn write_vertical_source_list(&mut self, items: &[S], list_type: ListType<'_>) { + pub fn write_vertical_source_list>( + &mut self, + language_backend: &LB, + items: &[S], + list_type: ListType<'_>, + ) { let align_length = self.line_length_for_align(); self.push_set_spaces(align_length); for (i, item) in items.iter().enumerate() { - item.write(&self.bindings.config, self); + item.write(language_backend, self); match list_type { ListType::Join(text) => { @@ -253,6 +260,6 @@ impl<'a, F: Write> SourceWriter<'a, F> { } } -pub trait Source { - fn write(&self, config: &Config, _: &mut SourceWriter); +pub trait Source { + fn write(&self, language_backend: &LB, _: &mut SourceWriter); }