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