slint/internal/compiler/passes/collect_structs_and_enums.rs
Milian Wolff 69c68b22b2 Also wrap langtype::Type::Struct in an Rc
This makes copying such types much cheaper and will allow us to
intern common struct types in the future too. This further
drops the sample cost for langtype.rs from ~6.6% down to 4.0%.

We are now also able to share/intern common struct types.

Before:
```
  Time (mean ± σ):      1.073 s ±  0.021 s    [User: 0.759 s, System: 0.215 s]
  Range (min … max):    1.034 s …  1.105 s    10 runs

        allocations:            3074261
```

After:
```
  Time (mean ± σ):      1.034 s ±  0.026 s    [User: 0.733 s, System: 0.201 s]
  Range (min … max):    1.000 s …  1.078 s    10 runs

        allocations:            2917476
```
2024-10-28 09:39:54 +01:00

115 lines
4 KiB
Rust

// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
//! Passes that fills the root component used_types.structs
use crate::expression_tree::Expression;
use crate::langtype::Type;
use crate::object_tree::*;
use smol_str::SmolStr;
use std::collections::BTreeMap;
use std::rc::Rc;
/// Fill the root_component's used_types.structs
pub fn collect_structs_and_enums(doc: &Document) {
let mut hash = BTreeMap::new();
for (_, exp) in doc.exports.iter() {
if let Some(ty) = exp.as_ref().right() {
maybe_collect_object(ty, &mut hash);
}
}
doc.visit_all_used_components(|component| collect_types_in_component(component, &mut hash));
let mut used_types = doc.used_types.borrow_mut();
let used_struct_and_enums = &mut used_types.structs_and_enums;
*used_struct_and_enums = Vec::with_capacity(hash.len());
while let Some(next) = hash.iter().next() {
// Here, using BTreeMap::pop_first would be great when it is stable
let key = next.0.clone();
sort_types(&mut hash, used_struct_and_enums, &key);
}
}
fn maybe_collect_object(ty: &Type, hash: &mut BTreeMap<SmolStr, Type>) {
visit_declared_type(ty, &mut |name, sub_ty| {
hash.entry(name.clone()).or_insert_with(|| sub_ty.clone());
});
}
fn collect_types_in_component(root_component: &Rc<Component>, hash: &mut BTreeMap<SmolStr, Type>) {
recurse_elem_including_sub_components_no_borrow(root_component, &(), &mut |elem, _| {
for x in elem.borrow().property_declarations.values() {
maybe_collect_object(&x.property_type, hash);
}
});
visit_all_expressions(root_component, |expr, _| {
expr.visit_recursive(&mut |expr| match expr {
Expression::Struct { ty, .. } => maybe_collect_object(ty, hash),
Expression::Array { element_ty, .. } => maybe_collect_object(element_ty, hash),
Expression::EnumerationValue(ev) => {
maybe_collect_object(&Type::Enumeration(ev.enumeration.clone()), hash)
}
_ => (),
})
});
}
/// Move the object named `key` from hash to vector, making sure that all object used by
/// it are placed before in the vector
fn sort_types(hash: &mut BTreeMap<SmolStr, Type>, vec: &mut Vec<Type>, key: &str) {
let ty = if let Some(ty) = hash.remove(key) { ty } else { return };
if let Type::Struct(s) = &ty {
if let Some(name) = &s.name {
if name.contains("::") {
// This is a builtin type.
// FIXME! there should be a better way to handle builtin struct
return;
}
for sub_ty in s.fields.values() {
visit_declared_type(sub_ty, &mut |name, _| sort_types(hash, vec, name));
}
}
}
vec.push(ty)
}
/// Will call the `visitor` for every named struct or enum that is not builtin
fn visit_declared_type(ty: &Type, visitor: &mut impl FnMut(&SmolStr, &Type)) {
match ty {
Type::Struct(s) => {
if s.node.is_some() {
if let Some(struct_name) = s.name.as_ref() {
visitor(struct_name, ty);
}
}
for sub_ty in s.fields.values() {
visit_declared_type(sub_ty, visitor);
}
}
Type::Array(x) => visit_declared_type(x, visitor),
Type::Callback(callback) => {
if let Some(rt) = &callback.return_type {
visit_declared_type(rt, visitor);
}
for a in &callback.args {
visit_declared_type(a, visitor);
}
}
Type::Function(function) => {
visit_declared_type(&function.return_type, visitor);
for a in &function.args {
visit_declared_type(a, visitor);
}
}
Type::Enumeration(en) => {
if en.node.is_some() {
visitor(&en.name, ty)
}
}
_ => {}
}
}