libgir/
library_postprocessing.rs

1use std::collections::HashMap;
2
3use log::{error, info};
4
5use crate::{
6    analysis::types::IsIncomplete,
7    config::{
8        Config, WorkMode,
9        gobjects::{GObject, GStatus},
10        matchable::Matchable,
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            && let Some(detected_c_type) = self.c_type_by_type_id(tid)
164        {
165            entry.insert(detected_c_type);
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                    && let Some(ref struct_for) = record.gtype_struct_for
200                    && let Some(struct_for_tid) = self.find_type(ns_id as u16, struct_for)
201                {
202                    structs_and_types.push((record.c_type.clone(), struct_for_tid));
203                }
204            }
205        }
206
207        for (gtype_struct_c_type, struct_for_tid) in structs_and_types {
208            match self.type_mut(struct_for_tid) {
209                Type::Class(klass) => {
210                    klass.c_class_type = Some(gtype_struct_c_type);
211                }
212
213                Type::Interface(iface) => {
214                    iface.c_class_type = Some(gtype_struct_c_type);
215                }
216
217                x => unreachable!(
218                    "Something other than a class or interface has a class struct: {:?}",
219                    x
220                ),
221            }
222        }
223    }
224
225    fn correlate_class_structs(&self) {
226        for (ns_id, ns) in self.namespaces.iter().enumerate() {
227            for type_ in &ns.types {
228                let type_ = type_.as_ref().unwrap(); // Always contains something
229                let name;
230                let type_struct;
231                let c_class_type;
232
233                match type_ {
234                    Type::Class(klass) => {
235                        name = &klass.name;
236                        type_struct = &klass.type_struct;
237                        c_class_type = &klass.c_class_type;
238                    }
239
240                    Type::Interface(iface) => {
241                        name = &iface.name;
242                        type_struct = &iface.type_struct;
243                        c_class_type = &iface.c_class_type;
244                    }
245
246                    _ => {
247                        continue;
248                    }
249                }
250
251                if let Some(type_struct) = type_struct {
252                    let type_struct_tid = self.find_type(ns_id as u16, type_struct);
253                    assert!(
254                        type_struct_tid.is_some(),
255                        "\"{name}\" has glib:type-struct=\"{type_struct}\" but there is no such record"
256                    );
257
258                    let type_struct_type = self.type_(type_struct_tid.unwrap());
259
260                    if let Type::Record(r) = type_struct_type {
261                        if r.gtype_struct_for.as_ref() != Some(name) {
262                            if let Some(ref gtype_struct_for) = r.gtype_struct_for {
263                                panic!(
264                                    "\"{}\" has glib:type-struct=\"{}\" but the corresponding record \"{}\" has glib:is-gtype-struct-for={:?}",
265                                    name, type_struct, r.name, gtype_struct_for
266                                );
267                            } else {
268                                panic!(
269                                    "\"{}\" has glib:type-struct=\"{}\" but the corresponding record \"{}\" has no glib:is-gtype-struct-for attribute",
270                                    name, type_struct, r.name
271                                );
272                            }
273                        }
274                    } else {
275                        panic!(
276                            "Element with name=\"{type_struct}\" should be a record but it isn't"
277                        );
278                    }
279                } else if let Some(c) = c_class_type {
280                    panic!(
281                        "\"{name}\" has no glib:type-struct but there is an element with glib:is-gtype-struct-for=\"{c}\""
282                    );
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 may provide only the inner c:type.
332                                // We still set a non-pointer sentinel here because field-level
333                                // c:type is required by sys field codegen to proceed.
334                                let array_c_type = "fixed_array".to_owned();
335                                actions.push((tid, fid, Action::SetCType(array_c_type)));
336                                continue;
337                            }
338                            if let Type::CArray(..) = field_type {
339                                // Flexible arrays / VLA-like fields may not carry a field-level
340                                // c:type in GIR even when the inner type is known.
341                                // We set a non-pointer sentinel to satisfy the field c:type
342                                // requirement in sys field codegen.
343                                let array_c_type = "c_array".to_owned();
344                                actions.push((tid, fid, Action::SetCType(array_c_type)));
345                                continue;
346                            }
347                            error!("Field `{}::{}` is missing c:type", name, &field.name);
348                        }
349                    }
350                    _ => {}
351                }
352            }
353        }
354        let ignore_missing_ctype = ["padding", "reserved", "_padding", "_reserved"];
355        for (tid, fid, action) in actions {
356            match self.type_mut(tid) {
357                Type::Class(Class { name, fields, .. })
358                | Type::Record(Record { name, fields, .. })
359                | Type::Union(Union { name, fields, .. }) => match action {
360                    Action::SetCType(c_type) => {
361                        // Don't be verbose when internal fields such as padding don't provide a
362                        // c-type
363                        if !ignore_missing_ctype.contains(&fields[fid].name.as_str()) {
364                            warn_main!(
365                                tid,
366                                "Field `{}::{}` missing c:type assumed to be `{}`",
367                                name,
368                                &fields[fid].name,
369                                c_type
370                            );
371                        }
372                        fields[fid].c_type = Some(c_type);
373                    }
374                    Action::SetName(name) => fields[fid].name = name,
375                },
376                _ => unreachable!("Expected class, record or union"),
377            }
378        }
379    }
380
381    fn make_unrepresentable_types_opaque(&mut self) {
382        // Unions with non-`Copy` fields are unstable (see issue #32836).
383        // It would seem that this shouldn't be cause for concern as one can
384        // always make all types in the union copyable.
385        //
386        // Unfortunately, this is not that simple, as some types are currently
387        // unrepresentable in Rust, and they do occur inside the unions.
388        // Thus to avoid the problem, we mark all unions with such unrepresentable
389        // types as opaque, and don't generate their definitions.
390        let mut unrepresentable: Vec<TypeId> = Vec::new();
391        for (ns_id, ns) in self.namespaces.iter().enumerate() {
392            for (id, type_) in ns.types.iter().enumerate() {
393                let type_ = type_.as_ref().unwrap();
394                let tid = TypeId {
395                    ns_id: ns_id as u16,
396                    id: id as u32,
397                };
398                match type_ {
399                    Type::Union(Union { fields, .. }) if fields.as_slice().is_incomplete(self) => {
400                        unrepresentable.push(tid);
401                    }
402                    _ => {}
403                }
404            }
405        }
406        for tid in unrepresentable {
407            match self.type_mut(tid) {
408                Type::Union(Union { name, fields, .. }) => {
409                    info!("Type `{name}` is not representable.");
410                    fields.clear();
411                }
412                _ => unreachable!("Expected a union"),
413            }
414        }
415    }
416
417    fn has_subtypes(&self, parent_tid: TypeId) -> bool {
418        for (tid, _) in self.types() {
419            if let Type::Class(class) = self.type_(tid)
420                && class.parent == Some(parent_tid)
421            {
422                return true;
423            }
424        }
425
426        false
427    }
428
429    fn mark_final_types(&mut self, config: &Config) {
430        // Here we mark all class types as final types if configured so in the config or
431        // otherwise if there is no public class struct for the type or the instance
432        // struct has no fields (i.e. is not known!), and there are no known
433        // subtypes.
434        //
435        // Final types can't have any subclasses and we handle them slightly different
436        // for that reason.
437        // FIXME: without class_hierarchy this function O(n2) due inner loop in
438        // `has_subtypes`
439        let mut overridden_final_types: Vec<(TypeId, bool)> = Vec::new();
440
441        for (ns_id, ns) in self.namespaces.iter().enumerate() {
442            for (id, type_) in ns.types.iter().enumerate() {
443                let type_ = type_.as_ref().unwrap(); // Always contains something
444
445                if let Type::Class(klass) = type_ {
446                    let tid = TypeId {
447                        ns_id: ns_id as u16,
448                        id: id as u32,
449                    };
450
451                    let full_name = tid.full_name(self);
452                    let obj = config.objects.get(&*full_name);
453
454                    if let Some(GObject {
455                        final_type: Some(final_type),
456                        ..
457                    }) = obj
458                    {
459                        // The config might also be used to override a type that is wrongly
460                        // detected as final type otherwise
461                        overridden_final_types.push((tid, *final_type));
462                    } else if klass.final_type {
463                        continue;
464                    } else if klass.type_struct.is_none() {
465                        let is_final = !self.has_subtypes(tid);
466                        if is_final {
467                            overridden_final_types.push((tid, true));
468                        }
469                    } else {
470                        let has_subtypes = self.has_subtypes(tid);
471                        let instance_struct_known = !klass.fields.is_empty();
472
473                        let class_struct_known = if let Some(class_record_tid) =
474                            self.find_type(ns_id as u16, klass.type_struct.as_ref().unwrap())
475                        {
476                            if let Type::Record(record) = self.type_(class_record_tid) {
477                                !record.disguised && !record.pointer
478                            } else {
479                                unreachable!("Type {} with non-record class", full_name);
480                            }
481                        } else {
482                            unreachable!("Can't find class for {}", full_name);
483                        };
484
485                        let is_final =
486                            !has_subtypes && (!instance_struct_known || !class_struct_known);
487                        if is_final {
488                            overridden_final_types.push((tid, true));
489                        }
490                    };
491                }
492            }
493        }
494
495        for (tid, new_is_final) in overridden_final_types {
496            if let Type::Class(Class { final_type, .. }) = self.type_mut(tid) {
497                *final_type = new_is_final;
498            } else {
499                unreachable!();
500            }
501        }
502    }
503
504    fn update_error_domain_functions(&mut self, config: &Config) {
505        // Find find all error domains that have corresponding functions
506        let mut error_domains = vec![];
507        for (ns_id, ns) in self.namespaces.iter().enumerate() {
508            'next_enum: for (id, type_) in ns.types.iter().enumerate() {
509                let type_ = type_.as_ref().unwrap(); // Always contains something
510                let enum_tid = TypeId {
511                    ns_id: ns_id as u16,
512                    id: id as u32,
513                };
514
515                if let Type::Enumeration(enum_) = type_
516                    && let Some(ErrorDomain::Quark(ref domain)) = enum_.error_domain
517                {
518                    let domain = domain.replace('-', "_");
519
520                    let mut function_candidates = vec![domain.clone()];
521                    if !domain.ends_with("_quark") {
522                        function_candidates.push(format!("{domain}_quark"));
523                    }
524                    if !domain.ends_with("_error_quark") {
525                        if domain.ends_with("_quark") {
526                            function_candidates
527                                .push(format!("{}_error_quark", &domain[..(domain.len() - 6)]));
528                        } else {
529                            function_candidates.push(format!("{domain}_error_quark"));
530                        }
531                    }
532                    if let Some(domain) = domain.strip_suffix("_error_quark") {
533                        function_candidates.push(domain.to_owned());
534                    }
535                    if let Some(domain) = domain.strip_suffix("_quark") {
536                        function_candidates.push(domain.to_owned());
537                    }
538
539                    if let Some(func) = ns.functions.iter().find(|f| {
540                        function_candidates
541                            .iter()
542                            .any(|c| f.c_identifier.as_ref() == Some(c))
543                    }) {
544                        error_domains.push((
545                            ns_id,
546                            enum_tid,
547                            None,
548                            func.c_identifier.as_ref().unwrap().clone(),
549                        ));
550                        continue 'next_enum;
551                    }
552
553                    // Quadratic in number of types...
554                    for (id, type_) in ns.types.iter().enumerate() {
555                        let type_ = type_.as_ref().unwrap(); // Always contains something
556                        let domain_tid = TypeId {
557                            ns_id: ns_id as u16,
558                            id: id as u32,
559                        };
560
561                        let functions = match type_ {
562                            Type::Enumeration(Enumeration { functions, .. })
563                            | Type::Class(Class { functions, .. })
564                            | Type::Record(Record { functions, .. })
565                            | Type::Interface(Interface { functions, .. }) => functions,
566                            _ => continue,
567                        };
568
569                        if let Some(func) = functions.iter().find(|f| {
570                            function_candidates
571                                .iter()
572                                .any(|c| f.c_identifier.as_ref() == Some(c))
573                        }) {
574                            error_domains.push((
575                                ns_id,
576                                enum_tid,
577                                Some(domain_tid),
578                                func.c_identifier.as_ref().unwrap().clone(),
579                            ));
580                            continue 'next_enum;
581                        }
582                    }
583                }
584            }
585        }
586
587        for (ns_id, enum_tid, domain_tid, function_name) in error_domains {
588            if config.work_mode != WorkMode::Sys {
589                if let Some(domain_tid) = domain_tid {
590                    match self.type_mut(domain_tid) {
591                        Type::Enumeration(Enumeration { functions, .. })
592                        | Type::Class(Class { functions, .. })
593                        | Type::Record(Record { functions, .. })
594                        | Type::Interface(Interface { functions, .. }) => {
595                            let pos = functions
596                                .iter()
597                                .position(|f| f.c_identifier.as_ref() == Some(&function_name))
598                                .unwrap();
599                            functions.remove(pos);
600                        }
601                        _ => unreachable!(),
602                    }
603                } else {
604                    let pos = self.namespaces[ns_id]
605                        .functions
606                        .iter()
607                        .position(|f| f.c_identifier.as_ref() == Some(&function_name))
608                        .unwrap();
609                    self.namespaces[ns_id].functions.remove(pos);
610                }
611            }
612
613            if let Type::Enumeration(enum_) = self.type_mut(enum_tid) {
614                assert!(enum_.error_domain.is_some());
615                enum_.error_domain = Some(ErrorDomain::Function(function_name));
616            } else {
617                unreachable!();
618            }
619        }
620    }
621
622    fn mark_ignored_enum_members(&mut self, config: &Config) {
623        let mut members_to_change = vec![];
624        for (ns_id, ns) in self.namespaces.iter().enumerate() {
625            for (id, _type_) in ns.types.iter().enumerate() {
626                let type_id = TypeId {
627                    ns_id: ns_id as u16,
628                    id: id as u32,
629                };
630
631                match self.type_(type_id) {
632                    Type::Bitfield(Bitfield { name, members, .. })
633                    | Type::Enumeration(Enumeration { name, members, .. }) => {
634                        let full_name = format!("{}.{}", ns.name, name);
635                        let config = config.objects.get(&full_name);
636                        let mut type_members = HashMap::new();
637                        for member in members.iter() {
638                            let status = config.and_then(|m| {
639                                m.members.matched(&member.name).first().map(|m| m.status)
640                            });
641                            type_members.insert(member.c_identifier.clone(), status);
642                        }
643                        members_to_change.push((type_id, type_members));
644                    }
645                    _ => (),
646                };
647            }
648        }
649
650        for (type_id, item_members) in members_to_change {
651            match self.type_mut(type_id) {
652                Type::Bitfield(Bitfield { members, .. })
653                | Type::Enumeration(Enumeration { members, .. }) => {
654                    for member in members.iter_mut() {
655                        let status = item_members
656                            .get(&member.c_identifier)
657                            .copied()
658                            .flatten()
659                            .unwrap_or(GStatus::Generate);
660                        member.status = status;
661                    }
662                }
663                _ => (),
664            };
665        }
666    }
667}