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//! Passes that fills the root component used_types.structs
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 = BTreeMap::new();
16
17 for (_, exp) in doc.exports.iter() {
18 if let Some(ty: &Type) = exp.as_ref().right() {
19 maybe_collect_struct(ty, &mut hash);
20 }
21 }
22
23 doc.visit_all_used_components(|component: &Rc| collect_types_in_component(component, &mut hash));
24
25 let mut used_types: RefMut<'_, UsedSubTypes> = doc.used_types.borrow_mut();
26 let used_struct_and_enums: &mut Vec = &mut used_types.structs_and_enums;
27 *used_struct_and_enums = Vec::with_capacity(hash.len());
28 while let Some(next: (&SmolStr, &Type)) = hash.iter().next() {
29 // Here, using BTreeMap::pop_first would be great when it is stable
30 let key: SmolStr = next.0.clone();
31 sort_types(&mut hash, vec:used_struct_and_enums, &key);
32 }
33}
34
35fn maybe_collect_struct(ty: &Type, hash: &mut BTreeMap<SmolStr, Type>) {
36 visit_declared_type(ty, &mut |name: &SmolStr, sub_ty: &Type| {
37 hash.entry(name.clone()).or_insert_with(|| sub_ty.clone());
38 });
39}
40
41fn collect_types_in_component(root_component: &Rc<Component>, hash: &mut BTreeMap<SmolStr, Type>) {
42 recurse_elem_including_sub_components_no_borrow(root_component, &(), &mut |elem: &Rc>, _| {
43 for x: &PropertyDeclaration in elem.borrow().property_declarations.values() {
44 maybe_collect_struct(&x.property_type, hash);
45 }
46 });
47
48 visit_all_expressions(root_component, |expr: &mut Expression, _| {
49 expr.visit_recursive(&mut |expr: &Expression| match expr {
50 Expression::Struct { ty: &Rc, .. } => maybe_collect_struct(&Type::Struct(ty.clone()), hash),
51 Expression::Array { element_ty: &Type, .. } => maybe_collect_struct(element_ty, hash),
52 Expression::EnumerationValue(ev: &EnumerationValue) => {
53 maybe_collect_struct(&Type::Enumeration(ev.enumeration.clone()), hash)
54 }
55 _ => (),
56 })
57 });
58}
59
60/// Move the object named `key` from hash to vector, making sure that all object used by
61/// it are placed before in the vector
62fn sort_types(hash: &mut BTreeMap<SmolStr, Type>, vec: &mut Vec<Type>, key: &str) {
63 let ty: Type = if let Some(ty: Type) = hash.remove(key) { ty } else { return };
64 if let Type::Struct(s: &Rc) = &ty {
65 if let Some(name: &SmolStr) = &s.name {
66 if name.contains("::") {
67 // This is a builtin type.
68 // FIXME! there should be a better way to handle builtin struct
69 return;
70 }
71
72 for sub_ty: &Type in s.fields.values() {
73 visit_declared_type(sub_ty, &mut |name: &SmolStr, _| sort_types(hash, vec, key:name));
74 }
75 }
76 }
77 vec.push(ty)
78}
79
80/// Will call the `visitor` for every named struct or enum that is not builtin
81fn visit_declared_type(ty: &Type, visitor: &mut impl FnMut(&SmolStr, &Type)) {
82 match ty {
83 Type::Struct(s) => {
84 if s.node.is_some() {
85 if let Some(struct_name) = s.name.as_ref() {
86 visitor(struct_name, ty);
87 }
88 }
89 for sub_ty in s.fields.values() {
90 visit_declared_type(sub_ty, visitor);
91 }
92 }
93 Type::Array(x) => visit_declared_type(x, visitor),
94 Type::Function(function) | Type::Callback(function) => {
95 visit_declared_type(&function.return_type, visitor);
96 for a in &function.args {
97 visit_declared_type(a, visitor);
98 }
99 }
100 Type::Enumeration(en) => {
101 if en.node.is_some() {
102 visitor(&en.name, ty)
103 }
104 }
105 _ => {}
106 }
107}
108