| 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 | |
| 6 | use crate::expression_tree::Expression; |
| 7 | use crate::langtype::Type; |
| 8 | use crate::object_tree::*; |
| 9 | use smol_str::SmolStr; |
| 10 | use std::collections::BTreeMap; |
| 11 | use std::rc::Rc; |
| 12 | |
| 13 | /// Fill the root_component's used_types.structs |
| 14 | pub 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 | |
| 35 | fn 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 | |
| 41 | fn 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 |
| 62 | fn 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 |
| 81 | fn 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 | |