opencv_binding_generator/
generator_env.rs1use std::cmp::Reverse;
2use std::collections::{HashMap, HashSet};
3use std::convert::TryFrom;
4use std::fmt;
5use std::fs::File;
6use std::io::{BufRead, BufReader, Read, Seek, SeekFrom};
7use std::ops::ControlFlow;
8use std::path::{Path, PathBuf};
9use std::rc::Rc;
10
11use clang::{Entity, EntityKind, EntityVisitResult};
12
13use crate::class::ClassKind;
14use crate::settings::Settings;
15use crate::type_ref::CppNameStyle;
16use crate::{
17 is_opencv_path, opencv_module_from_path, settings, Class, Element, EntityWalkerExt, EntityWalkerVisitor, MemoizeMap,
18 MemoizeMapExt, SupportedModule,
19};
20
21#[derive(Copy, Clone, Debug)]
22pub enum ClassKindOverride {
23 Boxed,
24 Simple,
25 BoxedForced,
26 System,
27}
28
29impl ClassKindOverride {
30 pub fn is_boxed(self) -> bool {
31 match self {
32 ClassKindOverride::Boxed | ClassKindOverride::BoxedForced => true,
33 ClassKindOverride::Simple | ClassKindOverride::System => false,
34 }
35 }
36}
37
38#[derive(Clone, Debug)]
39pub struct ExportConfig {
40 pub class_kind_override: ClassKindOverride,
41 pub deprecated: bool,
42 pub no_return: bool,
43 pub no_except: bool,
44 pub no_discard: bool,
45 pub only_generated_types: bool,
47}
48
49impl Default for ExportConfig {
50 fn default() -> Self {
51 Self {
52 class_kind_override: ClassKindOverride::Boxed,
53 deprecated: false,
54 no_return: false,
55 no_except: false,
56 no_discard: false,
57 only_generated_types: false,
58 }
59 }
60}
61
62impl ExportConfig {
63 pub fn export(src: ExportConfig) -> Option<ExportConfig> {
65 Some(src)
66 }
67
68 pub fn no_export(_src: ExportConfig) -> Option<ExportConfig> {
69 None
70 }
71
72 pub fn override_boxed(mut src: ExportConfig) -> Option<ExportConfig> {
73 src.class_kind_override = ClassKindOverride::Boxed;
74 Some(src)
75 }
76
77 pub fn force_boxed(mut src: ExportConfig) -> Option<ExportConfig> {
78 src.class_kind_override = ClassKindOverride::BoxedForced;
79 Some(src)
80 }
81
82 pub fn simple(mut src: ExportConfig) -> Option<ExportConfig> {
83 src.class_kind_override = ClassKindOverride::Simple;
84 Some(src)
85 }
86
87 pub fn system(mut src: ExportConfig) -> Option<ExportConfig> {
88 src.class_kind_override = ClassKindOverride::System;
89 Some(src)
90 }
91}
92
93pub struct RenameConfig {
94 pub rename: Rc<str>,
95}
96
97#[derive(Eq, PartialEq, Hash)]
98struct ExportIdx {
99 path: PathBuf,
100 line: u32,
101 line_offset: usize,
102}
103
104struct GeneratorEnvPopulator<'tu, 'ge> {
108 module: SupportedModule,
109 gen_env: &'ge mut GeneratorEnv<'tu>,
110}
111
112impl<'tu> GeneratorEnvPopulator<'tu, '_> {
113 fn add_func_comment(&mut self, entity: Entity) {
114 let raw_comment = entity.doc_comment();
115 if !raw_comment.is_empty() && !raw_comment.contains("@overload") && !raw_comment.contains("@copybrief") {
117 let name = entity.cpp_name(CppNameStyle::Reference).into_owned();
118 let line = entity.get_location().map_or(0, |l| l.get_file_location().line);
119 let defs = self.gen_env.func_comments.entry(name).or_default();
120 defs.push((line, raw_comment.into_owned()));
121 defs.sort_unstable_by_key(|(line, _)| Reverse(*line));
123 }
124 }
125
126 fn add_descendant(&mut self, base_class: Entity, descendant: Entity<'tu>) {
127 self
128 .gen_env
129 .descendants
130 .entry(base_class.cpp_name(CppNameStyle::Reference).into_owned())
131 .or_insert_with(|| HashSet::with_capacity(4))
132 .insert(descendant);
133 }
134}
135
136impl<'tu> EntityWalkerVisitor<'tu> for GeneratorEnvPopulator<'tu, '_> {
137 fn wants_file(&mut self, path: &Path) -> bool {
138 is_opencv_path(path) || opencv_module_from_path(path) == Some(self.module)
139 }
140
141 fn visit_entity(&mut self, entity: Entity<'tu>) -> ControlFlow<()> {
142 match entity.get_kind() {
143 EntityKind::ClassDecl | EntityKind::StructDecl => {
144 entity.visit_children(|child, _| {
145 match child.get_kind() {
146 EntityKind::BaseSpecifier => {
147 self.add_descendant(child.get_definition().expect("Can't get base class definition"), entity);
148 }
149 EntityKind::Constructor
150 | EntityKind::Method
151 | EntityKind::FunctionTemplate
152 | EntityKind::ConversionFunction => {
153 self.add_func_comment(child);
154 }
155 _ => {}
156 }
157 EntityVisitResult::Continue
158 });
159 }
160 EntityKind::FunctionDecl => {
161 self.add_func_comment(entity);
162 }
163 _ => {}
164 }
165 ControlFlow::Continue(())
166 }
167}
168
169pub struct GeneratorEnv<'tu> {
174 export_map: HashMap<ExportIdx, ExportConfig>,
175 rename_map: HashMap<ExportIdx, RenameConfig>,
176 func_comments: HashMap<String, Vec<(u32, String)>>,
178 class_kind_cache: MemoizeMap<String, Option<ClassKind>>,
180 descendants: HashMap<String, HashSet<Entity<'tu>>>,
181 pub settings: Settings,
182}
183
184impl<'tu> GeneratorEnv<'tu> {
185 pub fn empty() -> Self {
186 Self {
187 export_map: HashMap::new(),
188 rename_map: HashMap::new(),
189 func_comments: HashMap::new(),
190 class_kind_cache: MemoizeMap::new(HashMap::new()),
191 descendants: HashMap::new(),
192 settings: Settings::empty(),
193 }
194 }
195
196 pub fn global(module: SupportedModule, root_entity: Entity<'tu>) -> Self {
198 let mut out = Self {
199 export_map: HashMap::with_capacity(1024),
200 rename_map: HashMap::with_capacity(64),
201 func_comments: HashMap::with_capacity(2048),
202 class_kind_cache: MemoizeMap::new(HashMap::with_capacity(32)),
203 descendants: HashMap::with_capacity(16),
204 settings: Settings::for_module(module),
205 };
206 root_entity.walk_opencv_entities(GeneratorEnvPopulator {
207 module,
208 gen_env: &mut out,
209 });
210 out
211 }
212
213 fn key(entity: Entity) -> ExportIdx {
214 let (loc, line_offset) = if entity.get_kind() == EntityKind::MacroExpansion {
215 let l = entity
217 .get_range()
218 .expect("Can't get exported macro range")
219 .get_end()
220 .get_spelling_location();
221 let path = l.file.expect("Can't get exported macro file").get_path();
222 let mut f = BufReader::new(File::open(path).expect("Can't open export macro file"));
223 f.seek(SeekFrom::Start(u64::from(l.offset)))
224 .expect("Can't seek export macro file");
225 let mut line_offset = 0;
226 let mut line = String::with_capacity(8);
227 while f.read_line(&mut line).is_ok() {
228 if line.trim().is_empty() {
229 line_offset += 1;
230 } else {
231 break;
232 }
233 }
234 if line_offset > 1 {
235 panic!("Line offset more than 1 is not supported, modify fuzzy_key in get_export_config() to support higher values");
236 }
237 (l, line_offset)
238 } else {
239 let loc = entity
240 .get_range()
241 .map_or_else(
242 || entity.get_location().expect("Can't get entity location"),
245 |range| range.get_start(),
246 )
247 .get_expansion_location();
248 (loc, 0)
249 };
250 ExportIdx {
251 path: loc.file.expect("Can't get exported entry file").get_path(),
252 line: loc.line,
253 line_offset,
254 }
255 }
256
257 pub fn make_export_config(&mut self, entity: Entity) -> &mut ExportConfig {
258 let key = Self::key(entity);
259 self.export_map.entry(key).or_default()
260 }
261
262 #[inline]
263 fn get_with_fuzzy_key<T>(entity: Entity, getter: impl Fn(&ExportIdx) -> Option<T>) -> Option<T> {
264 let key = Self::key(entity);
265 getter(&key).or_else(|| {
266 let fuzzy_key = ExportIdx { line_offset: 1, ..key };
268 getter(&fuzzy_key).or_else(|| {
269 if fuzzy_key.line >= 1 {
270 let fuzzy_key = ExportIdx {
272 line: fuzzy_key.line - 1,
273 ..fuzzy_key
274 };
275 getter(&fuzzy_key)
276 } else {
277 None
278 }
279 })
280 })
281 }
282
283 pub fn get_export_config(&self, entity: Entity) -> Option<ExportConfig> {
284 let out = Self::get_with_fuzzy_key(entity, |key| self.export_map.get(key)).cloned();
285 let cpp_refname = entity.cpp_name(CppNameStyle::Reference);
286 if let Some(tweak) = settings::ELEMENT_EXPORT_TWEAK.get(cpp_refname.as_ref()) {
287 tweak(out.unwrap_or_default())
288 } else {
289 out
290 }
291 }
292
293 pub fn make_rename_config(&mut self, entity: Entity) -> &mut RenameConfig {
294 let key = Self::key(entity);
295 self
296 .rename_map
297 .entry(key)
298 .or_insert_with(|| RenameConfig { rename: Rc::from("") })
299 }
300
301 pub fn get_rename_config(&self, entity: Entity) -> Option<&RenameConfig> {
302 Self::get_with_fuzzy_key(entity, |key| self.rename_map.get(key))
303 }
304
305 pub fn get_func_comment(&self, line: u32, cpp_refname: &str) -> Option<&str> {
306 self
307 .func_comments
308 .get(cpp_refname)
309 .and_then(|comments| {
310 comments.iter()
311 .find(|(source_line, _)| *source_line <= line)
313 .or_else(|| comments.last())
315 })
316 .map(|(_, comment)| comment.as_str())
317 }
318
319 pub fn get_class_kind(&self, entity: Entity<'tu>) -> Option<ClassKind> {
322 let id = entity.cpp_name(CppNameStyle::Reference);
323 self.class_kind_cache.memo_get(id.as_ref(), || {
324 let entity = entity.get_template().unwrap_or(entity);
325 if let Some(range) = entity.get_range() {
326 let name_ranges = entity.get_name_ranges();
327 if !name_ranges.is_empty() {
328 let file_location = range.get_start().get_file_location();
329 if let Some(file) = file_location.file {
330 let start = u64::from(file_location.offset);
331 let end = u64::from(name_ranges[0].get_start().get_file_location().offset);
332 let len = usize::try_from(end - start).expect("Buffer length doesn't fit usize");
333 let mut buf = vec![0; len];
334 if let Ok(mut f) = File::open(file.get_path()) {
335 f.seek(SeekFrom::Start(start)).expect("Can't seek");
336 f.read_exact(buf.as_mut_slice()).expect("Can't read");
337 }
338 let export_decl = String::from_utf8(buf).expect("Not a valid UTF-8");
339 if export_decl.contains("CV_EXPORTS_W_SIMPLE") || export_decl.contains("CV_EXPORTS_W_MAP") {
340 return Some(ClassKind::Simple);
341 } else if export_decl.contains("CV_EXPORTS") || export_decl.contains("GAPI_EXPORTS") {
342 return Some(ClassKind::Boxed);
343 }
344 }
345 }
346 }
347 let cls = Class::new(entity, self);
348 if cls.can_be_simple() {
349 return Some(ClassKind::Simple);
350 }
351 None
352 })
353 }
354
355 pub fn descendants_of(&self, cpp_refname: &str) -> Option<&HashSet<Entity<'tu>>> {
357 self.descendants.get(cpp_refname)
358 }
359}
360
361impl fmt::Debug for GeneratorEnv<'_> {
362 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
363 f.debug_struct("GeneratorEnv")
364 .field("export_map", &format!("{} elements", self.export_map.len()))
365 .field("rename_map", &format!("{} elements", self.rename_map.len()))
366 .field("func_comments", &format!("{} elements", self.func_comments.len()))
367 .field(
368 "class_kind_cache",
369 &format!("{} elements", self.class_kind_cache.borrow().len()),
370 )
371 .field("descendants", &format!("{} elements", self.descendants.len()))
372 .field("settings", &self.settings)
373 .finish()
374 }
375}