libgir/
library_postprocessing.rs

1use std::collections::HashMap;
2
3use log::{error, info};
4
5use crate::{
6    analysis::types::IsIncomplete,
7    config::{
8        gobjects::{GObject, GStatus},
9        matchable::Matchable,
10        Config, WorkMode,
11    },
12    library::*,
13    nameutil,
14    parser::is_empty_c_type,
15    traits::MaybeRefAs,
16};
17
18impl Namespace {
19    fn unresolved(&self) -> Vec<&str> {
20        self.index
21            .iter()
22            .filter_map(|(name, &id)| {
23                if self.types[id as usize].is_none() {
24                    Some(name.as_str())
25                } else {
26                    None
27                }
28            })
29            .collect()
30    }
31}
32
33type DetectedCTypes = HashMap<TypeId, String>;
34
35impl Library {
36    pub fn postprocessing(&mut self, config: &Config) {
37        self.fix_gtype();
38        self.check_resolved();
39        self.fill_empty_signals_c_types();
40        self.resolve_class_structs();
41        self.correlate_class_structs();
42        self.fix_fields();
43        self.make_unrepresentable_types_opaque();
44        self.mark_final_types(config);
45        self.update_error_domain_functions(config);
46        self.mark_ignored_enum_members(config);
47    }
48
49    fn fix_gtype(&mut self) {
50        if let Some(ns_id) = self.find_namespace("GObject") {
51            // hide the `GType` type alias in `GObject`
52            self.add_type(ns_id, "Type", Type::Basic(Basic::Unsupported));
53        }
54    }
55
56    fn check_resolved(&self) {
57        let list: Vec<_> = self
58            .index
59            .iter()
60            .flat_map(|(name, &id)| {
61                let name = name.clone();
62                self.namespace(id)
63                    .unresolved()
64                    .into_iter()
65                    .map(move |s| format!("{name}.{s}"))
66            })
67            .collect();
68
69        assert!(list.is_empty(), "Incomplete library, unresolved: {list:?}");
70    }
71
72    fn fill_empty_signals_c_types(&mut self) {
73        fn update_empty_signals_c_types(signals: &mut [Signal], c_types: &DetectedCTypes) {
74            for signal in signals {
75                update_empty_signal_c_types(signal, c_types);
76            }
77        }
78
79        fn update_empty_signal_c_types(signal: &mut Signal, c_types: &DetectedCTypes) {
80            for par in &mut signal.parameters {
81                update_empty_c_type(&mut par.c_type, par.typ, c_types);
82            }
83            update_empty_c_type(&mut signal.ret.c_type, signal.ret.typ, c_types);
84        }
85
86        fn update_empty_c_type(c_type: &mut String, tid: TypeId, c_types: &DetectedCTypes) {
87            if !is_empty_c_type(c_type) {
88                return;
89            }
90            if let Some(s) = c_types.get(&tid) {
91                *c_type = s.clone();
92            }
93        }
94
95        let mut tids = Vec::new();
96        let mut c_types = DetectedCTypes::new();
97        for (ns_id, ns) in self.namespaces.iter().enumerate() {
98            for (id, type_) in ns.types.iter().enumerate() {
99                let type_ = type_.as_ref().unwrap(); // Always contains something
100                let tid = TypeId {
101                    ns_id: ns_id as u16,
102                    id: id as u32,
103                };
104                match type_ {
105                    Type::Class(klass) => {
106                        if self.detect_empty_signals_c_types(&klass.signals, &mut c_types) {
107                            tids.push(tid);
108                        }
109                    }
110                    Type::Interface(iface) => {
111                        if self.detect_empty_signals_c_types(&iface.signals, &mut c_types) {
112                            tids.push(tid);
113                        }
114                    }
115                    _ => (),
116                }
117            }
118        }
119
120        for tid in tids {
121            match self.type_mut(tid) {
122                Type::Class(klass) => update_empty_signals_c_types(&mut klass.signals, &c_types),
123                Type::Interface(iface) => {
124                    update_empty_signals_c_types(&mut iface.signals, &c_types);
125                }
126                _ => (),
127            }
128        }
129    }
130
131    fn detect_empty_signals_c_types(
132        &self,
133        signals: &[Signal],
134        c_types: &mut DetectedCTypes,
135    ) -> bool {
136        let mut detected = false;
137        for signal in signals {
138            if self.detect_empty_signal_c_types(signal, c_types) {
139                detected = true;
140            }
141        }
142        detected
143    }
144
145    fn detect_empty_signal_c_types(&self, signal: &Signal, c_types: &mut DetectedCTypes) -> bool {
146        let mut detected = false;
147        for par in &signal.parameters {
148            if self.detect_empty_c_type(&par.c_type, par.typ, c_types) {
149                detected = true;
150            }
151        }
152        if self.detect_empty_c_type(&signal.ret.c_type, signal.ret.typ, c_types) {
153            detected = true;
154        }
155        detected
156    }
157
158    fn detect_empty_c_type(&self, c_type: &str, tid: TypeId, c_types: &mut DetectedCTypes) -> bool {
159        if !is_empty_c_type(c_type) {
160            return false;
161        }
162        if let std::collections::hash_map::Entry::Vacant(entry) = c_types.entry(tid) {
163            if let Some(detected_c_type) = self.c_type_by_type_id(tid) {
164                entry.insert(detected_c_type);
165            }
166        }
167        true
168    }
169
170    fn c_type_by_type_id(&self, tid: TypeId) -> Option<String> {
171        let type_ = self.type_(tid);
172        type_.get_glib_name().map(|glib_name| {
173            if self.is_referenced_type(type_) {
174                format!("{glib_name}*")
175            } else {
176                glib_name.to_string()
177            }
178        })
179    }
180
181    fn is_referenced_type(&self, type_: &Type) -> bool {
182        use crate::library::Type::*;
183        match type_ {
184            Alias(alias) => self.is_referenced_type(self.type_(alias.typ)),
185            Record(..) | Union(..) | Class(..) | Interface(..) => true,
186            _ => false,
187        }
188    }
189
190    fn resolve_class_structs(&mut self) {
191        // stores pairs of (gtype-struct-c-name, type-name)
192        let mut structs_and_types = Vec::new();
193
194        for (ns_id, ns) in self.namespaces.iter().enumerate() {
195            for type_ in &ns.types {
196                let type_ = type_.as_ref().unwrap(); // Always contains something
197
198                if let Type::Record(record) = type_ {
199                    if let Some(ref struct_for) = record.gtype_struct_for {
200                        if let Some(struct_for_tid) = self.find_type(ns_id as u16, struct_for) {
201                            structs_and_types.push((record.c_type.clone(), struct_for_tid));
202                        }
203                    }
204                }
205            }
206        }
207
208        for (gtype_struct_c_type, struct_for_tid) in structs_and_types {
209            match self.type_mut(struct_for_tid) {
210                Type::Class(klass) => {
211                    klass.c_class_type = Some(gtype_struct_c_type);
212                }
213
214                Type::Interface(iface) => {
215                    iface.c_class_type = Some(gtype_struct_c_type);
216                }
217
218                x => unreachable!(
219                    "Something other than a class or interface has a class struct: {:?}",
220                    x
221                ),
222            }
223        }
224    }
225
226    fn correlate_class_structs(&self) {
227        for (ns_id, ns) in self.namespaces.iter().enumerate() {
228            for type_ in &ns.types {
229                let type_ = type_.as_ref().unwrap(); // Always contains something
230                let name;
231                let type_struct;
232                let c_class_type;
233
234                match type_ {
235                    Type::Class(klass) => {
236                        name = &klass.name;
237                        type_struct = &klass.type_struct;
238                        c_class_type = &klass.c_class_type;
239                    }
240
241                    Type::Interface(iface) => {
242                        name = &iface.name;
243                        type_struct = &iface.type_struct;
244                        c_class_type = &iface.c_class_type;
245                    }
246
247                    _ => {
248                        continue;
249                    }
250                }
251
252                if let Some(type_struct) = type_struct {
253                    let type_struct_tid = self.find_type(ns_id as u16, type_struct);
254                    assert!(
255                        type_struct_tid.is_some(),
256                        "\"{name}\" has glib:type-struct=\"{type_struct}\" but there is no such record"
257                    );
258
259                    let type_struct_type = self.type_(type_struct_tid.unwrap());
260
261                    if let Type::Record(r) = type_struct_type {
262                        if r.gtype_struct_for.as_ref() != Some(name) {
263                            if let Some(ref gtype_struct_for) = r.gtype_struct_for {
264                                panic!("\"{}\" has glib:type-struct=\"{}\" but the corresponding record \"{}\" has glib:is-gtype-struct-for={:?}",
265                                       name,
266                                       type_struct,
267                                       r.name,
268                                       gtype_struct_for);
269                            } else {
270                                panic!("\"{}\" has glib:type-struct=\"{}\" but the corresponding record \"{}\" has no glib:is-gtype-struct-for attribute",
271                                       name,
272                                       type_struct,
273                                       r.name);
274                            }
275                        }
276                    } else {
277                        panic!(
278                            "Element with name=\"{type_struct}\" should be a record but it isn't"
279                        );
280                    }
281                } else if let Some(c) = c_class_type {
282                    panic!("\"{name}\" has no glib:type-struct but there is an element with glib:is-gtype-struct-for=\"{c}\"");
283                }
284                // else both type_struct and c_class_type are None,
285                // and that's fine because they don't reference each
286                // other.
287            }
288        }
289    }
290
291    fn fix_fields(&mut self) {
292        enum Action {
293            SetCType(String),
294            SetName(String),
295        }
296        let mut actions: Vec<(TypeId, usize, Action)> = Vec::new();
297        for (ns_id, ns) in self.namespaces.iter().enumerate() {
298            for (id, type_) in ns.types.iter().enumerate() {
299                let type_ = type_.as_ref().unwrap(); // Always contains something
300                let tid = TypeId {
301                    ns_id: ns_id as u16,
302                    id: id as u32,
303                };
304                match type_ {
305                    Type::Class(Class { name, fields, .. })
306                    | Type::Record(Record { name, fields, .. })
307                    | Type::Union(Union { name, fields, .. }) => {
308                        for (fid, field) in fields.iter().enumerate() {
309                            if nameutil::needs_mangling(&field.name) {
310                                let new_name = nameutil::mangle_keywords(&*field.name).into_owned();
311                                actions.push((tid, fid, Action::SetName(new_name)));
312                            }
313                            if field.c_type.is_some() {
314                                continue;
315                            }
316                            let field_type = self.type_(field.typ);
317                            if field_type.maybe_ref_as::<Function>().is_some() {
318                                // Function pointers generally don't have c_type.
319                                continue;
320                            }
321                            if let Some(c_type) = field_type.get_glib_name() {
322                                actions.push((tid, fid, Action::SetCType(c_type.to_owned())));
323                                continue;
324                            }
325                            if let Type::Basic(Basic::Pointer) = field_type {
326                                // For example SoupBuffer is missing c:type for data field.
327                                actions.push((tid, fid, Action::SetCType("void*".to_owned())));
328                                continue;
329                            }
330                            if let Type::FixedArray(..) = field_type {
331                                // fixed-size Arrays can only have inner c_type
332                                // HACK: field c_type used only in sys mode for pointer checking
333                                // so any string without * will work
334                                let array_c_type = "fixed_array".to_owned();
335                                actions.push((tid, fid, Action::SetCType(array_c_type)));
336                                continue;
337                            }
338                            error!("Field `{}::{}` is missing c:type", name, &field.name);
339                        }
340                    }
341                    _ => {}
342                }
343            }
344        }
345        let ignore_missing_ctype = ["padding", "reserved", "_padding", "_reserved"];
346        for (tid, fid, action) in actions {
347            match self.type_mut(tid) {
348                Type::Class(Class { name, fields, .. })
349                | Type::Record(Record { name, fields, .. })
350                | Type::Union(Union { name, fields, .. }) => match action {
351                    Action::SetCType(c_type) => {
352                        // Don't be verbose when internal fields such as padding don't provide a
353                        // c-type
354                        if !ignore_missing_ctype.contains(&fields[fid].name.as_str()) {
355                            warn_main!(
356                                tid,
357                                "Field `{}::{}` missing c:type assumed to be `{}`",
358                                name,
359                                &fields[fid].name,
360                                c_type
361                            );
362                        }
363                        fields[fid].c_type = Some(c_type);
364                    }
365                    Action::SetName(name) => fields[fid].name = name,
366                },
367                _ => unreachable!("Expected class, record or union"),
368            }
369        }
370    }
371
372    fn make_unrepresentable_types_opaque(&mut self) {
373        // Unions with non-`Copy` fields are unstable (see issue #32836).
374        // It would seem that this shouldn't be cause for concern as one can
375        // always make all types in the union copyable.
376        //
377        // Unfortunately, this is not that simple, as some types are currently
378        // unrepresentable in Rust, and they do occur inside the unions.
379        // Thus to avoid the problem, we mark all unions with such unrepresentable
380        // types as opaque, and don't generate their definitions.
381        let mut unrepresentable: Vec<TypeId> = Vec::new();
382        for (ns_id, ns) in self.namespaces.iter().enumerate() {
383            for (id, type_) in ns.types.iter().enumerate() {
384                let type_ = type_.as_ref().unwrap();
385                let tid = TypeId {
386                    ns_id: ns_id as u16,
387                    id: id as u32,
388                };
389                match type_ {
390                    Type::Union(Union { fields, .. }) if fields.as_slice().is_incomplete(self) => {
391                        unrepresentable.push(tid);
392                    }
393                    _ => {}
394                }
395            }
396        }
397        for tid in unrepresentable {
398            match self.type_mut(tid) {
399                Type::Union(Union { name, fields, .. }) => {
400                    info!("Type `{}` is not representable.", name);
401                    fields.clear();
402                }
403                _ => unreachable!("Expected a union"),
404            }
405        }
406    }
407
408    fn has_subtypes(&self, parent_tid: TypeId) -> bool {
409        for (tid, _) in self.types() {
410            if let Type::Class(class) = self.type_(tid) {
411                if class.parent == Some(parent_tid) {
412                    return true;
413                }
414            }
415        }
416
417        false
418    }
419
420    fn mark_final_types(&mut self, config: &Config) {
421        // Here we mark all class types as final types if configured so in the config or
422        // otherwise if there is no public class struct for the type or the instance
423        // struct has no fields (i.e. is not known!), and there are no known
424        // subtypes.
425        //
426        // Final types can't have any subclasses and we handle them slightly different
427        // for that reason.
428        // FIXME: without class_hierarchy this function O(n2) due inner loop in
429        // `has_subtypes`
430        let mut overridden_final_types: Vec<(TypeId, bool)> = Vec::new();
431
432        for (ns_id, ns) in self.namespaces.iter().enumerate() {
433            for (id, type_) in ns.types.iter().enumerate() {
434                let type_ = type_.as_ref().unwrap(); // Always contains something
435
436                if let Type::Class(klass) = type_ {
437                    let tid = TypeId {
438                        ns_id: ns_id as u16,
439                        id: id as u32,
440                    };
441
442                    let full_name = tid.full_name(self);
443                    let obj = config.objects.get(&*full_name);
444
445                    if let Some(GObject {
446                        final_type: Some(final_type),
447                        ..
448                    }) = obj
449                    {
450                        // The config might also be used to override a type that is wrongly
451                        // detected as final type otherwise
452                        overridden_final_types.push((tid, *final_type));
453                    } else if klass.final_type {
454                        continue;
455                    } else if klass.type_struct.is_none() {
456                        let is_final = !self.has_subtypes(tid);
457                        if is_final {
458                            overridden_final_types.push((tid, true));
459                        }
460                    } else {
461                        let has_subtypes = self.has_subtypes(tid);
462                        let instance_struct_known = !klass.fields.is_empty();
463
464                        let class_struct_known = if let Some(class_record_tid) =
465                            self.find_type(ns_id as u16, klass.type_struct.as_ref().unwrap())
466                        {
467                            if let Type::Record(record) = self.type_(class_record_tid) {
468                                !record.disguised && !record.pointer
469                            } else {
470                                unreachable!("Type {} with non-record class", full_name);
471                            }
472                        } else {
473                            unreachable!("Can't find class for {}", full_name);
474                        };
475
476                        let is_final =
477                            !has_subtypes && (!instance_struct_known || !class_struct_known);
478                        if is_final {
479                            overridden_final_types.push((tid, true));
480                        }
481                    };
482                }
483            }
484        }
485
486        for (tid, new_is_final) in overridden_final_types {
487            if let Type::Class(Class { final_type, .. }) = self.type_mut(tid) {
488                *final_type = new_is_final;
489            } else {
490                unreachable!();
491            }
492        }
493    }
494
495    fn update_error_domain_functions(&mut self, config: &Config) {
496        // Find find all error domains that have corresponding functions
497        let mut error_domains = vec![];
498        for (ns_id, ns) in self.namespaces.iter().enumerate() {
499            'next_enum: for (id, type_) in ns.types.iter().enumerate() {
500                let type_ = type_.as_ref().unwrap(); // Always contains something
501                let enum_tid = TypeId {
502                    ns_id: ns_id as u16,
503                    id: id as u32,
504                };
505
506                if let Type::Enumeration(enum_) = type_ {
507                    if let Some(ErrorDomain::Quark(ref domain)) = enum_.error_domain {
508                        let domain = domain.replace('-', "_");
509
510                        let mut function_candidates = vec![domain.clone()];
511                        if !domain.ends_with("_quark") {
512                            function_candidates.push(format!("{domain}_quark"));
513                        }
514                        if !domain.ends_with("_error_quark") {
515                            if domain.ends_with("_quark") {
516                                function_candidates
517                                    .push(format!("{}_error_quark", &domain[..(domain.len() - 6)]));
518                            } else {
519                                function_candidates.push(format!("{domain}_error_quark"));
520                            }
521                        }
522                        if let Some(domain) = domain.strip_suffix("_error_quark") {
523                            function_candidates.push(domain.to_owned());
524                        }
525                        if let Some(domain) = domain.strip_suffix("_quark") {
526                            function_candidates.push(domain.to_owned());
527                        }
528
529                        if let Some(func) = ns.functions.iter().find(|f| {
530                            function_candidates
531                                .iter()
532                                .any(|c| f.c_identifier.as_ref() == Some(c))
533                        }) {
534                            error_domains.push((
535                                ns_id,
536                                enum_tid,
537                                None,
538                                func.c_identifier.as_ref().unwrap().clone(),
539                            ));
540                            continue 'next_enum;
541                        }
542
543                        // Quadratic in number of types...
544                        for (id, type_) in ns.types.iter().enumerate() {
545                            let type_ = type_.as_ref().unwrap(); // Always contains something
546                            let domain_tid = TypeId {
547                                ns_id: ns_id as u16,
548                                id: id as u32,
549                            };
550
551                            let functions = match type_ {
552                                Type::Enumeration(Enumeration { functions, .. })
553                                | Type::Class(Class { functions, .. })
554                                | Type::Record(Record { functions, .. })
555                                | Type::Interface(Interface { functions, .. }) => functions,
556                                _ => continue,
557                            };
558
559                            if let Some(func) = functions.iter().find(|f| {
560                                function_candidates
561                                    .iter()
562                                    .any(|c| f.c_identifier.as_ref() == Some(c))
563                            }) {
564                                error_domains.push((
565                                    ns_id,
566                                    enum_tid,
567                                    Some(domain_tid),
568                                    func.c_identifier.as_ref().unwrap().clone(),
569                                ));
570                                continue 'next_enum;
571                            }
572                        }
573                    }
574                }
575            }
576        }
577
578        for (ns_id, enum_tid, domain_tid, function_name) in error_domains {
579            if config.work_mode != WorkMode::Sys {
580                if let Some(domain_tid) = domain_tid {
581                    match self.type_mut(domain_tid) {
582                        Type::Enumeration(Enumeration { functions, .. })
583                        | Type::Class(Class { functions, .. })
584                        | Type::Record(Record { functions, .. })
585                        | Type::Interface(Interface { functions, .. }) => {
586                            let pos = functions
587                                .iter()
588                                .position(|f| f.c_identifier.as_ref() == Some(&function_name))
589                                .unwrap();
590                            functions.remove(pos);
591                        }
592                        _ => unreachable!(),
593                    }
594                } else {
595                    let pos = self.namespaces[ns_id]
596                        .functions
597                        .iter()
598                        .position(|f| f.c_identifier.as_ref() == Some(&function_name))
599                        .unwrap();
600                    self.namespaces[ns_id].functions.remove(pos);
601                }
602            }
603
604            if let Type::Enumeration(enum_) = self.type_mut(enum_tid) {
605                assert!(enum_.error_domain.is_some());
606                enum_.error_domain = Some(ErrorDomain::Function(function_name));
607            } else {
608                unreachable!();
609            }
610        }
611    }
612
613    fn mark_ignored_enum_members(&mut self, config: &Config) {
614        let mut members_to_change = vec![];
615        for (ns_id, ns) in self.namespaces.iter().enumerate() {
616            for (id, _type_) in ns.types.iter().enumerate() {
617                let type_id = TypeId {
618                    ns_id: ns_id as u16,
619                    id: id as u32,
620                };
621
622                match self.type_(type_id) {
623                    Type::Bitfield(Bitfield { name, members, .. })
624                    | Type::Enumeration(Enumeration { name, members, .. }) => {
625                        let full_name = format!("{}.{}", ns.name, name);
626                        let config = config.objects.get(&full_name);
627                        let mut type_members = HashMap::new();
628                        for member in members.iter() {
629                            let status = config.and_then(|m| {
630                                m.members.matched(&member.name).first().map(|m| m.status)
631                            });
632                            type_members.insert(member.c_identifier.clone(), status);
633                        }
634                        members_to_change.push((type_id, type_members));
635                    }
636                    _ => (),
637                };
638            }
639        }
640
641        for (type_id, item_members) in members_to_change {
642            match self.type_mut(type_id) {
643                Type::Bitfield(Bitfield { members, .. })
644                | Type::Enumeration(Enumeration { members, .. }) => {
645                    for member in members.iter_mut() {
646                        let status = item_members
647                            .get(&member.c_identifier)
648                            .copied()
649                            .flatten()
650                            .unwrap_or(GStatus::Generate);
651                        member.status = status;
652                    }
653                }
654                _ => (),
655            };
656        }
657    }
658}