Skip to main content

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