i_slint_compiler/passes/
collect_structs_and_enums.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4//! This pass fills the root component's used_types.structs_and_enums
5
6use crate::expression_tree::Expression;
7use crate::langtype::Type;
8use crate::object_tree::*;
9use smol_str::SmolStr;
10use std::collections::BTreeMap;
11use std::rc::Rc;
12
13/// Fill the root_component's used_types.structs
14pub fn collect_structs_and_enums(doc: &Document) {
15    let mut hash = BTreeMap::new();
16
17    for (_, exp) in doc.exports.iter() {
18        if let Some(ty) = exp.as_ref().right() {
19            maybe_collect_struct(ty, &mut hash);
20        }
21    }
22
23    doc.visit_all_used_components(|component| collect_types_in_component(component, &mut hash));
24
25    let mut used_types = doc.used_types.borrow_mut();
26    used_types.structs_and_enums = Vec::with_capacity(hash.len());
27    while let Some(next) = hash.iter().next() {
28        let key = next.0.clone();
29        if let Some(library_info) = doc.library_exports.get(key.as_str()) {
30            // This is a type imported from an external library, just skip it for code generation
31            hash.remove(&key);
32            used_types.library_types_imports.push((key, library_info.clone()));
33            continue;
34        }
35
36        // Here, using BTreeMap::pop_first would be great when it is stable
37        let used_struct_and_enums = &mut used_types.structs_and_enums;
38        sort_types(&mut hash, used_struct_and_enums, &key);
39    }
40}
41
42fn maybe_collect_struct(ty: &Type, hash: &mut BTreeMap<SmolStr, Type>) {
43    visit_declared_type(ty, &mut |name, sub_ty| {
44        hash.entry(name.clone()).or_insert_with(|| sub_ty.clone());
45    });
46}
47
48fn collect_types_in_component(root_component: &Rc<Component>, hash: &mut BTreeMap<SmolStr, Type>) {
49    recurse_elem_including_sub_components_no_borrow(root_component, &(), &mut |elem, _| {
50        for x in elem.borrow().property_declarations.values() {
51            maybe_collect_struct(&x.property_type, hash);
52        }
53    });
54
55    visit_all_expressions(root_component, |expr, _| {
56        expr.visit_recursive(&mut |expr| match expr {
57            Expression::Struct { ty, .. } => maybe_collect_struct(&Type::Struct(ty.clone()), hash),
58            Expression::Array { element_ty, .. } => maybe_collect_struct(element_ty, hash),
59            Expression::EnumerationValue(ev) => {
60                maybe_collect_struct(&Type::Enumeration(ev.enumeration.clone()), hash)
61            }
62            _ => (),
63        })
64    });
65}
66
67/// Move the object named `key` from hash to vector, making sure that all object used by
68/// it are placed before in the vector
69fn sort_types(hash: &mut BTreeMap<SmolStr, Type>, vec: &mut Vec<Type>, key: &str) {
70    let ty = if let Some(ty) = hash.remove(key) { ty } else { return };
71    if let Type::Struct(s) = &ty {
72        if let Some(name) = &s.name {
73            if name.contains("::") {
74                // This is a builtin type.
75                // FIXME! there should be a better way to handle builtin struct
76                return;
77            }
78
79            for sub_ty in s.fields.values() {
80                visit_declared_type(sub_ty, &mut |name, _| sort_types(hash, vec, name));
81            }
82        }
83    }
84    vec.push(ty)
85}
86
87/// Will call the `visitor` for every named struct or enum that is not builtin
88fn visit_declared_type(ty: &Type, visitor: &mut impl FnMut(&SmolStr, &Type)) {
89    match ty {
90        Type::Struct(s) => {
91            if s.node.is_some() {
92                if let Some(struct_name) = s.name.as_ref() {
93                    visitor(struct_name, ty);
94                }
95            }
96            for sub_ty in s.fields.values() {
97                visit_declared_type(sub_ty, visitor);
98            }
99        }
100        Type::Array(x) => visit_declared_type(x, visitor),
101        Type::Function(function) | Type::Callback(function) => {
102            visit_declared_type(&function.return_type, visitor);
103            for a in &function.args {
104                visit_declared_type(a, visitor);
105            }
106        }
107        Type::Enumeration(en) => {
108            if en.node.is_some() {
109                visitor(&en.name, ty)
110            }
111        }
112        _ => {}
113    }
114}