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
229                let (name, type_struct, c_class_type) = match type_ {
230                    Type::Class(klass) => (&klass.name, &klass.type_struct, &klass.c_class_type),
231
232                    Type::Interface(iface) => {
233                        (&iface.name, &iface.type_struct, &iface.c_class_type)
234                    }
235
236                    _ => {
237                        continue;
238                    }
239                };
240
241                if let Some(type_struct) = type_struct {
242                    let type_struct_tid = self.find_type(ns_id as u16, type_struct);
243                    assert!(
244                        type_struct_tid.is_some(),
245                        "\"{name}\" has glib:type-struct=\"{type_struct}\" but there is no such record"
246                    );
247
248                    let type_struct_type = self.type_(type_struct_tid.unwrap());
249
250                    if let Type::Record(r) = type_struct_type {
251                        if r.gtype_struct_for.as_ref() != Some(name) {
252                            if let Some(ref gtype_struct_for) = r.gtype_struct_for {
253                                panic!(
254                                    "\"{}\" has glib:type-struct=\"{}\" but the corresponding record \"{}\" has glib:is-gtype-struct-for={:?}",
255                                    name, type_struct, r.name, gtype_struct_for
256                                );
257                            } else {
258                                panic!(
259                                    "\"{}\" has glib:type-struct=\"{}\" but the corresponding record \"{}\" has no glib:is-gtype-struct-for attribute",
260                                    name, type_struct, r.name
261                                );
262                            }
263                        }
264                    } else {
265                        panic!(
266                            "Element with name=\"{type_struct}\" should be a record but it isn't"
267                        );
268                    }
269                } else if let Some(c) = c_class_type {
270                    panic!(
271                        "\"{name}\" has no glib:type-struct but there is an element with glib:is-gtype-struct-for=\"{c}\""
272                    );
273                }
274                // else both type_struct and c_class_type are None,
275                // and that's fine because they don't reference each
276                // other.
277            }
278        }
279    }
280
281    fn fix_fields(&mut self) {
282        enum Action {
283            SetCType(String),
284            SetName(String),
285        }
286        let mut actions: Vec<(TypeId, usize, Action)> = Vec::new();
287        for (ns_id, ns) in self.namespaces.iter().enumerate() {
288            for (id, type_) in ns.types.iter().enumerate() {
289                let type_ = type_.as_ref().unwrap(); // Always contains something
290                let tid = TypeId {
291                    ns_id: ns_id as u16,
292                    id: id as u32,
293                };
294                match type_ {
295                    Type::Class(Class { name, fields, .. })
296                    | Type::Record(Record { name, fields, .. })
297                    | Type::Union(Union { name, fields, .. }) => {
298                        for (fid, field) in fields.iter().enumerate() {
299                            if nameutil::needs_mangling(&field.name) {
300                                let new_name = nameutil::mangle_keywords(&*field.name).into_owned();
301                                actions.push((tid, fid, Action::SetName(new_name)));
302                            }
303                            if field.c_type.is_some() {
304                                continue;
305                            }
306                            let field_type = self.type_(field.typ);
307                            if field_type.maybe_ref_as::<Function>().is_some() {
308                                // Function pointers generally don't have c_type.
309                                continue;
310                            }
311                            if let Some(c_type) = field_type.get_glib_name() {
312                                actions.push((tid, fid, Action::SetCType(c_type.to_owned())));
313                                continue;
314                            }
315                            if let Type::Basic(Basic::Pointer) = field_type {
316                                // For example SoupBuffer is missing c:type for data field.
317                                actions.push((tid, fid, Action::SetCType("void*".to_owned())));
318                                continue;
319                            }
320                            if let Type::FixedArray(..) = field_type {
321                                // Fixed-size arrays may provide only the inner c:type.
322                                // We still set a non-pointer sentinel here because field-level
323                                // c:type is required by sys field codegen to proceed.
324                                let array_c_type = "fixed_array".to_owned();
325                                actions.push((tid, fid, Action::SetCType(array_c_type)));
326                                continue;
327                            }
328                            if let Type::CArray(..) = field_type {
329                                // Flexible arrays / VLA-like fields may not carry a field-level
330                                // c:type in GIR even when the inner type is known.
331                                // We set a non-pointer sentinel to satisfy the field c:type
332                                // requirement in sys field codegen.
333                                let array_c_type = "c_array".to_owned();
334                                actions.push((tid, fid, Action::SetCType(array_c_type)));
335                                continue;
336                            }
337                            error!("Field `{}::{}` is missing c:type", name, field.name);
338                        }
339                    }
340                    _ => {}
341                }
342            }
343        }
344        let ignore_missing_ctype = ["padding", "reserved", "_padding", "_reserved"];
345        for (tid, fid, action) in actions {
346            match self.type_mut(tid) {
347                Type::Class(Class { name, fields, .. })
348                | Type::Record(Record { name, fields, .. })
349                | Type::Union(Union { name, fields, .. }) => match action {
350                    Action::SetCType(c_type) => {
351                        // Don't be verbose when internal fields such as padding don't provide a
352                        // c-type
353                        if !ignore_missing_ctype.contains(&fields[fid].name.as_str()) {
354                            warn_main!(
355                                tid,
356                                "Field `{}::{}` missing c:type assumed to be `{}`",
357                                name,
358                                &fields[fid].name,
359                                c_type
360                            );
361                        }
362                        fields[fid].c_type = Some(c_type);
363                    }
364                    Action::SetName(name) => fields[fid].name = name,
365                },
366                _ => unreachable!("Expected class, record or union"),
367            }
368        }
369    }
370
371    fn make_unrepresentable_types_opaque(&mut self) {
372        // Unions with non-`Copy` fields are unstable (see issue #32836).
373        // It would seem that this shouldn't be cause for concern as one can
374        // always make all types in the union copyable.
375        //
376        // Unfortunately, this is not that simple, as some types are currently
377        // unrepresentable in Rust, and they do occur inside the unions.
378        // Thus to avoid the problem, we mark all unions with such unrepresentable
379        // types as opaque, and don't generate their definitions.
380        let mut unrepresentable: Vec<TypeId> = Vec::new();
381        for (ns_id, ns) in self.namespaces.iter().enumerate() {
382            for (id, type_) in ns.types.iter().enumerate() {
383                let type_ = type_.as_ref().unwrap();
384                let tid = TypeId {
385                    ns_id: ns_id as u16,
386                    id: id as u32,
387                };
388                match type_ {
389                    Type::Union(Union { fields, .. }) if fields.as_slice().is_incomplete(self) => {
390                        unrepresentable.push(tid);
391                    }
392                    _ => {}
393                }
394            }
395        }
396        for tid in unrepresentable {
397            match self.type_mut(tid) {
398                Type::Union(Union { name, fields, .. }) => {
399                    info!("Type `{name}` is not representable.");
400                    fields.clear();
401                }
402                _ => unreachable!("Expected a union"),
403            }
404        }
405    }
406
407    fn has_subtypes(&self, parent_tid: TypeId) -> bool {
408        for (tid, _) in self.types() {
409            if let Type::Class(class) = self.type_(tid)
410                && class.parent == Some(parent_tid)
411            {
412                return true;
413            }
414        }
415
416        false
417    }
418
419    fn mark_final_types(&mut self, config: &Config) {
420        // Here we mark all class types as final types if configured so in the config or
421        // otherwise if there is no public class struct for the type or the instance
422        // struct has no fields (i.e. is not known!), and there are no known
423        // subtypes.
424        //
425        // Final types can't have any subclasses and we handle them slightly different
426        // for that reason.
427        // FIXME: without class_hierarchy this function O(n2) due inner loop in
428        // `has_subtypes`
429        let mut overridden_final_types: Vec<(TypeId, bool)> = Vec::new();
430
431        for (ns_id, ns) in self.namespaces.iter().enumerate() {
432            for (id, type_) in ns.types.iter().enumerate() {
433                let type_ = type_.as_ref().unwrap(); // Always contains something
434
435                if let Type::Class(klass) = type_ {
436                    let tid = TypeId {
437                        ns_id: ns_id as u16,
438                        id: id as u32,
439                    };
440
441                    let full_name = tid.full_name(self);
442                    let obj = config.objects.get(&*full_name);
443
444                    if let Some(GObject {
445                        final_type: Some(final_type),
446                        ..
447                    }) = obj
448                    {
449                        // The config might also be used to override a type that is wrongly
450                        // detected as final type otherwise
451                        overridden_final_types.push((tid, *final_type));
452                    } else if klass.final_type {
453                        continue;
454                    } else if klass.type_struct.is_none() {
455                        let is_final = !self.has_subtypes(tid);
456                        if is_final {
457                            overridden_final_types.push((tid, true));
458                        }
459                    } else {
460                        let has_subtypes = self.has_subtypes(tid);
461                        let instance_struct_known = !klass.fields.is_empty();
462
463                        let class_struct_known = if let Some(class_record_tid) =
464                            self.find_type(ns_id as u16, klass.type_struct.as_ref().unwrap())
465                        {
466                            if let Type::Record(record) = self.type_(class_record_tid) {
467                                !record.disguised && !record.pointer
468                            } else {
469                                unreachable!("Type {} with non-record class", full_name);
470                            }
471                        } else {
472                            unreachable!("Can't find class for {}", full_name);
473                        };
474
475                        let is_final =
476                            !has_subtypes && (!instance_struct_known || !class_struct_known);
477                        if is_final {
478                            overridden_final_types.push((tid, true));
479                        }
480                    };
481                }
482            }
483        }
484
485        for (tid, new_is_final) in overridden_final_types {
486            if let Type::Class(Class { final_type, .. }) = self.type_mut(tid) {
487                *final_type = new_is_final;
488            } else {
489                unreachable!();
490            }
491        }
492    }
493
494    fn update_error_domain_functions(&mut self, config: &Config) {
495        // Find find all error domains that have corresponding functions
496        let mut error_domains = vec![];
497        for (ns_id, ns) in self.namespaces.iter().enumerate() {
498            'next_enum: for (id, type_) in ns.types.iter().enumerate() {
499                let type_ = type_.as_ref().unwrap(); // Always contains something
500                let enum_tid = TypeId {
501                    ns_id: ns_id as u16,
502                    id: id as u32,
503                };
504
505                if let Type::Enumeration(enum_) = type_
506                    && let Some(ErrorDomain::Quark(ref domain)) = enum_.error_domain
507                {
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
530                        .functions
531                        .iter()
532                        .find(|f| function_candidates.iter().any(|c| &f.c_identifier == c))
533                    {
534                        error_domains.push((ns_id, enum_tid, None, func.c_identifier.clone()));
535                        continue 'next_enum;
536                    }
537
538                    // Quadratic in number of types...
539                    for (id, type_) in ns.types.iter().enumerate() {
540                        let type_ = type_.as_ref().unwrap(); // Always contains something
541                        let domain_tid = TypeId {
542                            ns_id: ns_id as u16,
543                            id: id as u32,
544                        };
545
546                        let functions = match type_ {
547                            Type::Enumeration(Enumeration { functions, .. })
548                            | Type::Class(Class { functions, .. })
549                            | Type::Record(Record { functions, .. })
550                            | Type::Interface(Interface { functions, .. }) => functions,
551                            _ => continue,
552                        };
553
554                        if let Some(func) = functions
555                            .iter()
556                            .find(|f| function_candidates.iter().any(|c| &f.c_identifier == c))
557                        {
558                            error_domains.push((
559                                ns_id,
560                                enum_tid,
561                                Some(domain_tid),
562                                func.c_identifier.clone(),
563                            ));
564                            continue 'next_enum;
565                        }
566                    }
567                }
568            }
569        }
570
571        for (ns_id, enum_tid, domain_tid, function_name) in error_domains {
572            if config.work_mode != WorkMode::Sys {
573                if let Some(domain_tid) = domain_tid {
574                    match self.type_mut(domain_tid) {
575                        Type::Enumeration(Enumeration { functions, .. })
576                        | Type::Class(Class { functions, .. })
577                        | Type::Record(Record { functions, .. })
578                        | Type::Interface(Interface { functions, .. }) => {
579                            let pos = functions
580                                .iter()
581                                .position(|f| f.c_identifier == function_name)
582                                .unwrap();
583                            functions.remove(pos);
584                        }
585                        _ => unreachable!(),
586                    }
587                } else {
588                    let pos = self.namespaces[ns_id]
589                        .functions
590                        .iter()
591                        .position(|f| f.c_identifier == function_name)
592                        .unwrap();
593                    self.namespaces[ns_id].functions.remove(pos);
594                }
595            }
596
597            if let Type::Enumeration(enum_) = self.type_mut(enum_tid) {
598                assert!(enum_.error_domain.is_some());
599                enum_.error_domain = Some(ErrorDomain::Function(function_name));
600            } else {
601                unreachable!();
602            }
603        }
604    }
605
606    fn mark_ignored_enum_members(&mut self, config: &Config) {
607        let mut members_to_change = vec![];
608        for (ns_id, ns) in self.namespaces.iter().enumerate() {
609            for (id, _type_) in ns.types.iter().enumerate() {
610                let type_id = TypeId {
611                    ns_id: ns_id as u16,
612                    id: id as u32,
613                };
614
615                match self.type_(type_id) {
616                    Type::Bitfield(Bitfield { name, members, .. })
617                    | Type::Enumeration(Enumeration { name, members, .. }) => {
618                        let full_name = format!("{}.{}", ns.name, name);
619                        let config = config.objects.get(&full_name);
620                        let mut type_members = HashMap::new();
621                        for member in members.iter() {
622                            let status = config.and_then(|m| {
623                                m.members.matched(&member.name).first().map(|m| m.status)
624                            });
625                            type_members.insert(member.c_identifier.clone(), status);
626                        }
627                        members_to_change.push((type_id, type_members));
628                    }
629                    _ => (),
630                };
631            }
632        }
633
634        for (type_id, item_members) in members_to_change {
635            match self.type_mut(type_id) {
636                Type::Bitfield(Bitfield { members, .. })
637                | Type::Enumeration(Enumeration { members, .. }) => {
638                    for member in members.iter_mut() {
639                        let status = item_members
640                            .get(&member.c_identifier)
641                            .copied()
642                            .flatten()
643                            .unwrap_or(GStatus::Generate);
644                        member.status = status;
645                    }
646                }
647                _ => (),
648            };
649        }
650    }
651}