libgir/analysis/
mod.rs

1use std::collections::BTreeMap;
2
3use imports::Imports;
4use log::error;
5
6use crate::{
7    env::Env,
8    library::{self, Type, TypeId},
9};
10
11pub mod bounds;
12pub mod c_type;
13pub mod child_properties;
14pub mod class_builder;
15pub mod class_hierarchy;
16pub mod constants;
17pub mod conversion_type;
18pub mod enums;
19pub mod ffi_type;
20pub mod flags;
21pub mod function_parameters;
22pub use function_parameters::Parameter;
23pub mod functions;
24pub mod general;
25pub mod imports;
26pub mod info_base;
27pub mod namespaces;
28pub mod object;
29pub mod out_parameters;
30mod override_string_type;
31pub mod properties;
32pub mod record;
33pub mod record_type;
34pub mod ref_mode;
35pub mod return_value;
36pub mod rust_type;
37pub mod safety_assertion_mode;
38pub mod signals;
39pub mod signatures;
40pub mod special_functions;
41pub mod supertypes;
42pub mod symbols;
43pub mod trampoline_parameters;
44pub mod trampolines;
45pub mod try_from_glib;
46pub mod types;
47
48#[derive(Debug, Default)]
49pub struct Analysis {
50    pub objects: BTreeMap<String, object::Info>,
51    pub records: BTreeMap<String, record::Info>,
52    pub global_functions: Option<info_base::InfoBase>,
53    pub constants: Vec<constants::Info>,
54
55    pub enumerations: Vec<enums::Info>,
56    pub enum_imports: Imports,
57
58    pub flags: Vec<flags::Info>,
59    pub flags_imports: Imports,
60}
61
62fn find_function<'a>(
63    env: &Env,
64    mut functions: impl Iterator<Item = &'a functions::Info>,
65    search_fn: impl Fn(&functions::Info) -> bool + Copy,
66) -> Option<&'a functions::Info> {
67    functions.find(|fn_info| fn_info.should_be_doc_linked(env) && search_fn(fn_info))
68}
69
70impl Analysis {
71    pub fn find_global_function<F: Fn(&functions::Info) -> bool + Copy>(
72        &self,
73        env: &Env,
74        search: F,
75    ) -> Option<&functions::Info> {
76        self.global_functions
77            .as_ref()
78            .and_then(move |info| find_function(env, info.functions.iter(), search))
79    }
80
81    pub fn find_record_by_function<
82        F: Fn(&functions::Info) -> bool + Copy,
83        G: Fn(&record::Info) -> bool + Copy,
84    >(
85        &self,
86        env: &Env,
87        search_record: G,
88        search_fn: F,
89    ) -> Option<(&record::Info, &functions::Info)> {
90        self.records
91            .values()
92            .filter(|r| search_record(r))
93            .find_map(|record_info| {
94                find_function(env, record_info.functions.iter(), search_fn)
95                    .map(|fn_info| (record_info, fn_info))
96            })
97    }
98
99    pub fn find_object_by_virtual_method<
100        F: Fn(&functions::Info) -> bool + Copy,
101        G: Fn(&object::Info) -> bool + Copy,
102    >(
103        &self,
104        env: &Env,
105        search_obj: G,
106        search_fn: F,
107    ) -> Option<(&object::Info, &functions::Info)> {
108        self.objects
109            .values()
110            .filter(|o| search_obj(o))
111            .find_map(|obj_info| {
112                find_function(env, obj_info.virtual_methods.iter(), search_fn)
113                    .map(|fn_info| (obj_info, fn_info))
114            })
115    }
116
117    pub fn find_object_by_function<
118        F: Fn(&functions::Info) -> bool + Copy,
119        G: Fn(&object::Info) -> bool + Copy,
120    >(
121        &self,
122        env: &Env,
123        search_obj: G,
124        search_fn: F,
125    ) -> Option<(&object::Info, &functions::Info)> {
126        self.objects
127            .values()
128            .filter(|o| search_obj(o))
129            .find_map(|obj_info| {
130                find_function(env, obj_info.functions.iter(), search_fn)
131                    .map(|fn_info| (obj_info, fn_info))
132            })
133    }
134
135    pub fn find_enum_by_function<
136        F: Fn(&functions::Info) -> bool + Copy,
137        G: Fn(&enums::Info) -> bool + Copy,
138    >(
139        &self,
140        env: &Env,
141        search_enum: G,
142        search_fn: F,
143    ) -> Option<(&enums::Info, &functions::Info)> {
144        self.enumerations
145            .iter()
146            .filter(|o| search_enum(o))
147            .find_map(|obj_info| {
148                find_function(env, obj_info.functions.iter(), search_fn)
149                    .map(|fn_info| (obj_info, fn_info))
150            })
151    }
152
153    pub fn find_flag_by_function<
154        F: Fn(&functions::Info) -> bool + Copy,
155        G: Fn(&flags::Info) -> bool + Copy,
156    >(
157        &self,
158        env: &Env,
159        search_flag: G,
160        search_fn: F,
161    ) -> Option<(&flags::Info, &functions::Info)> {
162        self.flags
163            .iter()
164            .filter(|o| search_flag(o))
165            .find_map(|obj_info| {
166                find_function(env, obj_info.functions.iter(), search_fn)
167                    .map(|fn_info| (obj_info, fn_info))
168            })
169    }
170}
171
172pub fn run(env: &mut Env) {
173    let mut to_analyze: Vec<(TypeId, Vec<TypeId>)> = Vec::with_capacity(env.config.objects.len());
174    for obj in env.config.objects.values() {
175        if obj.status.ignored() {
176            continue;
177        }
178        let Some(tid) = env.library.find_type(0, &obj.name) else {
179            continue;
180        };
181        let deps = supertypes::dependencies(env, tid);
182        to_analyze.push((tid, deps));
183    }
184
185    let mut analyzed = 1;
186    while analyzed > 0 {
187        analyzed = 0;
188        let mut new_to_analyze: Vec<(TypeId, Vec<TypeId>)> = Vec::with_capacity(to_analyze.len());
189        for (tid, ref deps) in to_analyze {
190            if !is_all_deps_analyzed(env, deps) {
191                new_to_analyze.push((tid, deps.clone()));
192                continue;
193            }
194            analyze(env, tid, deps);
195            analyzed += 1;
196        }
197
198        to_analyze = new_to_analyze;
199    }
200
201    if !to_analyze.is_empty() {
202        error!(
203            "Not analyzed {} objects due unfinished dependencies",
204            to_analyze.len()
205        );
206        return;
207    }
208
209    analyze_enums(env);
210
211    analyze_flags(env);
212
213    analyze_constants(env);
214
215    // Analyze free functions as the last step once all types are analyzed
216    analyze_global_functions(env);
217}
218
219fn analyze_enums(env: &mut Env) {
220    let mut imports = Imports::new(&env.library);
221
222    for obj in env.config.objects.values() {
223        if obj.status.ignored() {
224            continue;
225        }
226        let Some(tid) = env.library.find_type(0, &obj.name) else {
227            continue;
228        };
229
230        if let Type::Enumeration(_) = env.library.type_(tid) {
231            if let Some(info) = enums::new(env, obj, &mut imports) {
232                env.analysis.enumerations.push(info);
233            }
234        }
235    }
236
237    env.analysis.enum_imports = imports;
238}
239
240fn analyze_flags(env: &mut Env) {
241    let mut imports = Imports::new(&env.library);
242
243    for obj in env.config.objects.values() {
244        if obj.status.ignored() {
245            continue;
246        }
247        let Some(tid) = env.library.find_type(0, &obj.name) else {
248            continue;
249        };
250
251        if let Type::Bitfield(_) = env.library.type_(tid) {
252            if let Some(info) = flags::new(env, obj, &mut imports) {
253                env.analysis.flags.push(info);
254            }
255        }
256    }
257
258    env.analysis.flags_imports = imports;
259}
260
261fn analyze_global_functions(env: &mut Env) {
262    let ns = env.library.namespace(library::MAIN_NAMESPACE);
263
264    let full_name = format!("{}.*", ns.name);
265
266    let obj = match env.config.objects.get(&*full_name) {
267        Some(obj) if obj.status.need_generate() => obj,
268        _ => return,
269    };
270
271    let functions: Vec<_> = ns
272        .functions
273        .iter()
274        .filter(|f| f.kind == library::FunctionKind::Global)
275        .collect();
276    if functions.is_empty() {
277        return;
278    }
279
280    let mut imports = imports::Imports::new(&env.library);
281    imports.add("glib::translate::*");
282    imports.add("crate:ffi");
283
284    let functions = functions::analyze(
285        env,
286        &functions,
287        None,
288        false,
289        false,
290        obj,
291        &mut imports,
292        None,
293        None,
294    );
295
296    env.analysis.global_functions = Some(info_base::InfoBase {
297        full_name,
298        type_id: TypeId::tid_none(),
299        name: "*".into(),
300        functions,
301        imports,
302        ..Default::default()
303    });
304}
305
306fn analyze_constants(env: &mut Env) {
307    let ns = env.library.namespace(library::MAIN_NAMESPACE);
308
309    let full_name = format!("{}.*", ns.name);
310
311    let obj = match env.config.objects.get(&*full_name) {
312        Some(obj) if obj.status.need_generate() => obj,
313        _ => return,
314    };
315
316    let constants: Vec<_> = ns.constants.iter().collect();
317    if constants.is_empty() {
318        return;
319    }
320
321    env.analysis.constants = constants::analyze(env, &constants, obj);
322}
323
324fn analyze(env: &mut Env, tid: TypeId, deps: &[TypeId]) {
325    let full_name = tid.full_name(&env.library);
326    let Some(obj) = env.config.objects.get(&*full_name) else {
327        return;
328    };
329    match env.library.type_(tid) {
330        Type::Class(_) => {
331            if let Some(info) = object::class(env, obj, deps) {
332                env.analysis.objects.insert(full_name, info);
333            }
334        }
335        Type::Interface(_) => {
336            if let Some(info) = object::interface(env, obj, deps) {
337                env.analysis.objects.insert(full_name, info);
338            }
339        }
340        Type::Record(_) => {
341            if let Some(info) = record::new(env, obj) {
342                env.analysis.records.insert(full_name, info);
343            }
344        }
345        _ => {}
346    }
347}
348
349fn is_all_deps_analyzed(env: &Env, deps: &[TypeId]) -> bool {
350    for tid in deps {
351        let full_name = tid.full_name(&env.library);
352        if !env.analysis.objects.contains_key(&full_name) {
353            return false;
354        }
355    }
356    true
357}
358
359pub fn is_gpointer(s: &str) -> bool {
360    s == "gpointer" || s == "void*"
361}