1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use std::cell::RefCell;
6 use std::collections::HashMap;
7 use std::fs;
8 use std::fs::File;
9 use std::io::{Read, Write};
10 use std::path;
11 use std::rc::Rc;
12 
13 use crate::bindgen::config::{Config, Language};
14 use crate::bindgen::ir::{
15     Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct,
16 };
17 use crate::bindgen::writer::{Source, SourceWriter};
18 
19 /// A bindings header that can be written.
20 pub struct Bindings {
21     pub config: Config,
22     /// The map from path to struct, used to lookup whether a given type is a
23     /// transparent struct. This is needed to generate code for constants.
24     struct_map: ItemMap<Struct>,
25     struct_fileds_memo: RefCell<HashMap<BindgenPath, Rc<Vec<String>>>>,
26     globals: Vec<Static>,
27     constants: Vec<Constant>,
28     items: Vec<ItemContainer>,
29     functions: Vec<Function>,
30 }
31 
32 #[derive(PartialEq)]
33 enum NamespaceOperation {
34     Open,
35     Close,
36 }
37 
38 impl Bindings {
new( config: Config, struct_map: ItemMap<Struct>, constants: Vec<Constant>, globals: Vec<Static>, items: Vec<ItemContainer>, functions: Vec<Function>, ) -> Bindings39     pub(crate) fn new(
40         config: Config,
41         struct_map: ItemMap<Struct>,
42         constants: Vec<Constant>,
43         globals: Vec<Static>,
44         items: Vec<ItemContainer>,
45         functions: Vec<Function>,
46     ) -> Bindings {
47         Bindings {
48             config,
49             struct_map,
50             struct_fileds_memo: Default::default(),
51             globals,
52             constants,
53             items,
54             functions,
55         }
56     }
57 
58     // FIXME(emilio): What to do when the configuration doesn't match?
struct_is_transparent(&self, path: &BindgenPath) -> bool59     pub fn struct_is_transparent(&self, path: &BindgenPath) -> bool {
60         let mut any = false;
61         self.struct_map.for_items(path, |s| any |= s.is_transparent);
62         any
63     }
64 
struct_exists(&self, path: &BindgenPath) -> bool65     pub fn struct_exists(&self, path: &BindgenPath) -> bool {
66         let mut any = false;
67         self.struct_map.for_items(path, |_| any = true);
68         any
69     }
70 
struct_field_names(&self, path: &BindgenPath) -> Rc<Vec<String>>71     pub fn struct_field_names(&self, path: &BindgenPath) -> Rc<Vec<String>> {
72         let mut memos = self.struct_fileds_memo.borrow_mut();
73         if let Some(memo) = memos.get(path) {
74             return memo.clone();
75         }
76 
77         let mut fields = Vec::<String>::new();
78         self.struct_map.for_items(path, |st| {
79             let mut pos: usize = 0;
80             for field in &st.fields {
81                 if let Some(found_pos) = fields.iter().position(|v| *v == field.0) {
82                     pos = found_pos + 1;
83                 } else {
84                     fields.insert(pos, field.0.clone());
85                     pos += 1;
86                 }
87             }
88         });
89 
90         let fields = Rc::new(fields);
91         memos.insert(path.clone(), fields.clone());
92         fields
93     }
94 
write_to_file<P: AsRef<path::Path>>(&self, path: P) -> bool95     pub fn write_to_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
96         // Don't compare files if we've never written this file before
97         if !path.as_ref().is_file() {
98             if let Some(parent) = path::Path::new(path.as_ref()).parent() {
99                 fs::create_dir_all(parent).unwrap();
100             }
101             self.write(File::create(path).unwrap());
102             return true;
103         }
104 
105         let mut new_file_contents = Vec::new();
106         self.write(&mut new_file_contents);
107 
108         let mut old_file_contents = Vec::new();
109         {
110             let mut old_file = File::open(&path).unwrap();
111             old_file.read_to_end(&mut old_file_contents).unwrap();
112         }
113 
114         if old_file_contents != new_file_contents {
115             let mut new_file = File::create(&path).unwrap();
116             new_file.write_all(&new_file_contents).unwrap();
117             true
118         } else {
119             false
120         }
121     }
122 
write_headers<F: Write>(&self, out: &mut SourceWriter<F>)123     pub fn write_headers<F: Write>(&self, out: &mut SourceWriter<F>) {
124         if let Some(ref f) = self.config.header {
125             out.new_line_if_not_start();
126             write!(out, "{}", f);
127             out.new_line();
128         }
129         if let Some(ref f) = self.config.include_guard {
130             out.new_line_if_not_start();
131             write!(out, "#ifndef {}", f);
132             out.new_line();
133             write!(out, "#define {}", f);
134             out.new_line();
135         }
136         if self.config.include_version {
137             out.new_line_if_not_start();
138             write!(
139                 out,
140                 "/* Generated with cbindgen:{} */",
141                 crate::bindgen::config::VERSION
142             );
143             out.new_line();
144         }
145         if let Some(ref f) = self.config.autogen_warning {
146             out.new_line_if_not_start();
147             write!(out, "{}", f);
148             out.new_line();
149         }
150 
151         if self.config.no_includes
152             && self.config.sys_includes.is_empty()
153             && self.config.includes.is_empty()
154         {
155             return;
156         }
157 
158         out.new_line_if_not_start();
159 
160         if !self.config.no_includes {
161             if self.config.language == Language::C {
162                 out.write("#include <stdarg.h>");
163                 out.new_line();
164                 out.write("#include <stdbool.h>");
165                 out.new_line();
166                 out.write("#include <stdint.h>");
167                 out.new_line();
168                 out.write("#include <stdlib.h>");
169                 out.new_line();
170             } else {
171                 out.write("#include <cstdarg>");
172                 out.new_line();
173                 out.write("#include <cstdint>");
174                 out.new_line();
175                 out.write("#include <cstdlib>");
176                 out.new_line();
177                 out.write("#include <new>");
178                 out.new_line();
179                 if self.config.enumeration.cast_assert_name.is_none()
180                     && (self.config.enumeration.derive_mut_casts
181                         || self.config.enumeration.derive_const_casts)
182                 {
183                     out.write("#include <cassert>");
184                     out.new_line();
185                 }
186             }
187         }
188 
189         for include in &self.config.sys_includes {
190             write!(out, "#include <{}>", include);
191             out.new_line();
192         }
193 
194         for include in &self.config.includes {
195             write!(out, "#include \"{}\"", include);
196             out.new_line();
197         }
198     }
199 
write<F: Write>(&self, file: F)200     pub fn write<F: Write>(&self, file: F) {
201         let mut out = SourceWriter::new(file, self);
202 
203         self.write_headers(&mut out);
204 
205         self.open_namespaces(&mut out);
206 
207         for constant in &self.constants {
208             if constant.ty.is_primitive_or_ptr_primitive() {
209                 out.new_line_if_not_start();
210                 constant.write(&self.config, &mut out, None);
211                 out.new_line();
212             }
213         }
214 
215         for item in &self.items {
216             if item
217                 .deref()
218                 .annotations()
219                 .bool("no-export")
220                 .unwrap_or(false)
221             {
222                 continue;
223             }
224 
225             out.new_line_if_not_start();
226             match *item {
227                 ItemContainer::Constant(..) => unreachable!(),
228                 ItemContainer::Static(..) => unreachable!(),
229                 ItemContainer::Enum(ref x) => x.write(&self.config, &mut out),
230                 ItemContainer::Struct(ref x) => x.write(&self.config, &mut out),
231                 ItemContainer::Union(ref x) => x.write(&self.config, &mut out),
232                 ItemContainer::OpaqueItem(ref x) => x.write(&self.config, &mut out),
233                 ItemContainer::Typedef(ref x) => x.write(&self.config, &mut out),
234             }
235             out.new_line();
236         }
237 
238         for constant in &self.constants {
239             if !constant.ty.is_primitive_or_ptr_primitive() {
240                 out.new_line_if_not_start();
241                 constant.write(&self.config, &mut out, None);
242                 out.new_line();
243             }
244         }
245 
246         if !self.functions.is_empty() || !self.globals.is_empty() {
247             if self.config.language == Language::C && self.config.cpp_compat {
248                 out.new_line_if_not_start();
249                 out.write("#ifdef __cplusplus");
250             }
251 
252             if self.config.language == Language::Cxx {
253                 if let Some(ref using_namespaces) = self.config.using_namespaces {
254                     for namespace in using_namespaces {
255                         out.new_line();
256                         write!(out, "using namespace {};", namespace);
257                     }
258                     out.new_line();
259                 }
260             }
261 
262             if self.config.language == Language::Cxx || self.config.cpp_compat {
263                 out.new_line();
264                 out.write("extern \"C\" {");
265                 out.new_line();
266             }
267 
268             if self.config.language == Language::C && self.config.cpp_compat {
269                 out.write("#endif // __cplusplus");
270                 out.new_line();
271             }
272 
273             for global in &self.globals {
274                 out.new_line_if_not_start();
275                 global.write(&self.config, &mut out);
276                 out.new_line();
277             }
278 
279             for function in &self.functions {
280                 out.new_line_if_not_start();
281                 function.write(&self.config, &mut out);
282                 out.new_line();
283             }
284 
285             if self.config.language == Language::C && self.config.cpp_compat {
286                 out.new_line();
287                 out.write("#ifdef __cplusplus");
288             }
289 
290             if self.config.language == Language::Cxx || self.config.cpp_compat {
291                 out.new_line();
292                 out.write("} // extern \"C\"");
293                 out.new_line();
294             }
295 
296             if self.config.language == Language::C && self.config.cpp_compat {
297                 out.write("#endif // __cplusplus");
298                 out.new_line();
299             }
300         }
301 
302         self.close_namespaces(&mut out);
303 
304         if let Some(ref f) = self.config.include_guard {
305             out.new_line_if_not_start();
306             if self.config.language == Language::C {
307                 write!(out, "#endif /* {} */", f);
308             } else {
309                 write!(out, "#endif // {}", f);
310             }
311             out.new_line();
312         }
313         if let Some(ref f) = self.config.trailer {
314             out.new_line_if_not_start();
315             write!(out, "{}", f);
316             if !f.ends_with('\n') {
317                 out.new_line();
318             }
319         }
320     }
321 
all_namespaces(&self) -> Vec<&str>322     fn all_namespaces(&self) -> Vec<&str> {
323         if self.config.language != Language::Cxx && !self.config.cpp_compat {
324             return vec![];
325         }
326         let mut ret = vec![];
327         if let Some(ref namespace) = self.config.namespace {
328             ret.push(&**namespace);
329         }
330         if let Some(ref namespaces) = self.config.namespaces {
331             for namespace in namespaces {
332                 ret.push(&**namespace);
333             }
334         }
335         ret
336     }
337 
open_close_namespaces<F: Write>(&self, op: NamespaceOperation, out: &mut SourceWriter<F>)338     fn open_close_namespaces<F: Write>(&self, op: NamespaceOperation, out: &mut SourceWriter<F>) {
339         let mut namespaces = self.all_namespaces();
340         if namespaces.is_empty() {
341             return;
342         }
343 
344         if op == NamespaceOperation::Close {
345             namespaces.reverse();
346         }
347 
348         let write_ifdefs = self.config.cpp_compat && self.config.language == Language::C;
349         if write_ifdefs {
350             out.new_line_if_not_start();
351             out.write("#ifdef __cplusplus");
352         }
353 
354         for namespace in namespaces {
355             out.new_line();
356             match op {
357                 NamespaceOperation::Open => write!(out, "namespace {} {{", namespace),
358                 NamespaceOperation::Close => write!(out, "}} // namespace {}", namespace),
359             }
360         }
361 
362         out.new_line();
363         if write_ifdefs {
364             out.write("#endif // __cplusplus");
365             out.new_line();
366         }
367     }
368 
open_namespaces<F: Write>(&self, out: &mut SourceWriter<F>)369     pub(crate) fn open_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
370         self.open_close_namespaces(NamespaceOperation::Open, out);
371     }
372 
close_namespaces<F: Write>(&self, out: &mut SourceWriter<F>)373     pub(crate) fn close_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
374         self.open_close_namespaces(NamespaceOperation::Close, out);
375     }
376 }
377