summaryrefslogtreecommitdiffstats
path: root/src/bindgen/cdecl.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bindgen/cdecl.rs')
-rw-r--r--src/bindgen/cdecl.rs372
1 files changed, 372 insertions, 0 deletions
diff --git a/src/bindgen/cdecl.rs b/src/bindgen/cdecl.rs
new file mode 100644
index 0000000..a9c5a0f
--- /dev/null
+++ b/src/bindgen/cdecl.rs
@@ -0,0 +1,372 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use 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::{Config, Language};
+
+// This code is for translating Rust types into C declarations.
+// See Section 6.7, Declarations, in the C standard for background.
+// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
+
+enum CDeclarator {
+ Ptr {
+ is_const: bool,
+ is_nullable: bool,
+ is_ref: bool,
+ },
+ Array(String),
+ Func {
+ args: Vec<(Option<String>, CDecl)>,
+ layout: Layout,
+ never_return: bool,
+ },
+}
+
+impl CDeclarator {
+ fn is_ptr(&self) -> bool {
+ matches!(self, CDeclarator::Ptr { .. } | CDeclarator::Func { .. })
+ }
+}
+
+struct CDecl {
+ type_qualifers: String,
+ type_name: String,
+ type_generic_args: Vec<GenericArgument>,
+ declarators: Vec<CDeclarator>,
+ type_ctype: Option<DeclarationType>,
+ deprecated: Option<String>,
+}
+
+impl CDecl {
+ fn new() -> CDecl {
+ CDecl {
+ type_qualifers: String::new(),
+ type_name: String::new(),
+ type_generic_args: Vec::new(),
+ declarators: Vec::new(),
+ type_ctype: None,
+ deprecated: None,
+ }
+ }
+
+ fn from_type(t: &Type, config: &Config) -> CDecl {
+ let mut cdecl = CDecl::new();
+ cdecl.build_type(t, false, config);
+ cdecl
+ }
+
+ fn from_func_arg(t: &Type, array_length: Option<&str>, config: &Config) -> CDecl {
+ let mut cdecl = CDecl::new();
+ let length = match array_length {
+ Some(l) => l,
+ None => return CDecl::from_type(t, config),
+ };
+ let (ty, is_const) = match t {
+ Type::Ptr { ty, is_const, .. } => (ty, is_const),
+ _ => unreachable!(
+ "Should never have an array length for a non pointer type {:?}",
+ t
+ ),
+ };
+ let ptr_as_array = Type::Array(ty.clone(), ConstExpr::Value(length.to_string()));
+ cdecl.build_type(&ptr_as_array, *is_const, config);
+ cdecl
+ }
+
+ fn from_func(f: &Function, layout: Layout, config: &Config) -> CDecl {
+ let mut cdecl = CDecl::new();
+ cdecl.build_func(f, layout, config);
+ cdecl
+ }
+
+ fn build_func(&mut self, f: &Function, layout: Layout, config: &Config) {
+ let args = f
+ .args
+ .iter()
+ .map(|arg| {
+ (
+ arg.name.clone(),
+ CDecl::from_func_arg(&arg.ty, arg.array_length.as_deref(), config),
+ )
+ })
+ .collect();
+ self.declarators.push(CDeclarator::Func {
+ args,
+ layout,
+ never_return: f.never_return,
+ });
+ self.deprecated = f.annotations.deprecated.clone();
+ self.build_type(&f.ret, false, config);
+ }
+
+ fn build_type(&mut self, t: &Type, is_const: bool, config: &Config) {
+ match t {
+ Type::Path(ref generic) => {
+ if is_const {
+ assert!(
+ self.type_qualifers.is_empty(),
+ "error generating cdecl for {:?}",
+ t
+ );
+ self.type_qualifers = "const".to_owned();
+ }
+
+ assert!(
+ self.type_name.is_empty(),
+ "error generating cdecl for {:?}",
+ t
+ );
+ self.type_name = generic.export_name().to_owned();
+ assert!(
+ self.type_generic_args.is_empty(),
+ "error generating cdecl for {:?}",
+ t
+ );
+ self.type_generic_args = generic.generics().to_owned();
+ self.type_ctype = generic.ctype().cloned();
+ }
+ Type::Primitive(ref p) => {
+ if is_const {
+ assert!(
+ self.type_qualifers.is_empty(),
+ "error generating cdecl for {:?}",
+ t
+ );
+ self.type_qualifers = "const".to_owned();
+ }
+
+ assert!(
+ self.type_name.is_empty(),
+ "error generating cdecl for {:?}",
+ t
+ );
+ self.type_name = p.to_repr_c(config).to_string();
+ }
+ Type::Ptr {
+ ref ty,
+ is_nullable,
+ is_const: ptr_is_const,
+ is_ref,
+ } => {
+ self.declarators.push(CDeclarator::Ptr {
+ is_const,
+ is_nullable: *is_nullable,
+ is_ref: *is_ref,
+ });
+ self.build_type(ty, *ptr_is_const, config);
+ }
+ Type::Array(ref t, ref constant) => {
+ let len = constant.as_str().to_owned();
+ self.declarators.push(CDeclarator::Array(len));
+ self.build_type(t, is_const, config);
+ }
+ Type::FuncPtr {
+ ref ret,
+ ref args,
+ is_nullable: _,
+ never_return,
+ } => {
+ let args = args
+ .iter()
+ .map(|(ref name, ref ty)| (name.clone(), CDecl::from_type(ty, config)))
+ .collect();
+ self.declarators.push(CDeclarator::Ptr {
+ is_const: false,
+ is_nullable: true,
+ is_ref: false,
+ });
+ self.declarators.push(CDeclarator::Func {
+ args,
+ layout: config.function.args.clone(),
+ never_return: *never_return,
+ });
+ self.build_type(ret, false, config);
+ }
+ }
+ }
+
+ fn write<F: Write>(&self, out: &mut SourceWriter<F>, ident: Option<&str>, config: &Config) {
+ // Write the type-specifier and type-qualifier first
+ if !self.type_qualifers.is_empty() {
+ write!(out, "{} ", self.type_qualifers);
+ }
+
+ if config.language != Language::Cython {
+ if let Some(ref ctype) = self.type_ctype {
+ write!(out, "{} ", ctype.to_str());
+ }
+ }
+
+ write!(out, "{}", self.type_name);
+
+ if !self.type_generic_args.is_empty() {
+ out.write("<");
+ out.write_horizontal_source_list(&self.type_generic_args, ListType::Join(", "));
+ out.write(">");
+ }
+
+ // When we have an identifier, put a space between the type and the declarators
+ if ident.is_some() {
+ out.write(" ");
+ }
+
+ // Write the left part of declarators before the identifier
+ let mut iter_rev = self.declarators.iter().rev().peekable();
+
+ #[allow(clippy::while_let_on_iterator)]
+ while let Some(declarator) = iter_rev.next() {
+ let next_is_pointer = iter_rev.peek().map_or(false, |x| x.is_ptr());
+
+ match *declarator {
+ CDeclarator::Ptr {
+ is_const,
+ is_nullable,
+ is_ref,
+ } => {
+ out.write(if is_ref { "&" } else { "*" });
+ if is_const {
+ out.write("const ");
+ }
+ if !is_nullable && !is_ref && config.language != Language::Cython {
+ if let Some(attr) = &config.pointer.non_null_attribute {
+ write!(out, "{} ", attr);
+ }
+ }
+ }
+ CDeclarator::Array(..) => {
+ if next_is_pointer {
+ out.write("(");
+ }
+ }
+ CDeclarator::Func { .. } => {
+ if next_is_pointer {
+ out.write("(");
+ }
+ }
+ }
+ }
+
+ // Write the identifier
+ if let Some(ident) = ident {
+ write!(out, "{}", ident);
+ }
+
+ // Write the right part of declarators after the identifier
+ let mut iter = self.declarators.iter();
+ let mut last_was_pointer = false;
+
+ #[allow(clippy::while_let_on_iterator)]
+ while let Some(declarator) = iter.next() {
+ match *declarator {
+ CDeclarator::Ptr { .. } => {
+ last_was_pointer = true;
+ }
+ CDeclarator::Array(ref constant) => {
+ if last_was_pointer {
+ out.write(")");
+ }
+ write!(out, "[{}]", constant);
+
+ last_was_pointer = false;
+ }
+ CDeclarator::Func {
+ ref args,
+ ref layout,
+ never_return,
+ } => {
+ if last_was_pointer {
+ out.write(")");
+ }
+
+ out.write("(");
+ if args.is_empty() && config.language == Language::C {
+ out.write("void");
+ }
+
+ fn write_vertical<F: Write>(
+ out: &mut SourceWriter<F>,
+ config: &Config,
+ args: &[(Option<String>, CDecl)],
+ ) {
+ let align_length = out.line_length_for_align();
+ out.push_set_spaces(align_length);
+ for (i, (arg_ident, arg_ty)) in args.iter().enumerate() {
+ if i != 0 {
+ out.write(",");
+ out.new_line();
+ }
+
+ // Convert &Option<String> to Option<&str>
+ let arg_ident = arg_ident.as_ref().map(|x| x.as_ref());
+
+ arg_ty.write(out, arg_ident, config);
+ }
+ out.pop_tab();
+ }
+
+ fn write_horizontal<F: Write>(
+ out: &mut SourceWriter<F>,
+ config: &Config,
+ args: &[(Option<String>, CDecl)],
+ ) {
+ for (i, (arg_ident, arg_ty)) in args.iter().enumerate() {
+ if i != 0 {
+ out.write(", ");
+ }
+
+ // Convert &Option<String> to Option<&str>
+ let arg_ident = arg_ident.as_ref().map(|x| x.as_ref());
+
+ arg_ty.write(out, arg_ident, config);
+ }
+ }
+
+ match layout {
+ Layout::Vertical => write_vertical(out, config, args),
+ Layout::Horizontal => write_horizontal(out, config, args),
+ Layout::Auto => {
+ if !out.try_write(
+ |out| write_horizontal(out, config, args),
+ config.line_length,
+ ) {
+ write_vertical(out, config, args)
+ }
+ }
+ }
+ out.write(")");
+
+ if never_return && config.language != Language::Cython {
+ if let Some(ref no_return_attr) = config.function.no_return {
+ out.write_fmt(format_args!(" {}", no_return_attr));
+ }
+ }
+
+ last_was_pointer = true;
+ }
+ }
+ }
+ }
+}
+
+pub fn write_func<F: Write>(
+ out: &mut SourceWriter<F>,
+ f: &Function,
+ layout: Layout,
+ config: &Config,
+) {
+ CDecl::from_func(f, layout, config).write(out, Some(f.path().name()), config);
+}
+
+pub fn write_field<F: Write>(out: &mut SourceWriter<F>, t: &Type, ident: &str, config: &Config) {
+ CDecl::from_type(t, config).write(out, Some(ident), config);
+}
+
+pub fn write_type<F: Write>(out: &mut SourceWriter<F>, t: &Type, config: &Config) {
+ CDecl::from_type(t, config).write(out, None, config);
+}