Skip to main content

i_slint_compiler/llr/optim_passes/
count_property_use.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 Property::use_count
5//!
6//! It assumes that use_count of all properties is zero initially
7
8use crate::llr::{
9    Animation, BindingExpression, CompilationUnit, EvaluationContext, Expression,
10    LocalMemberReference, MemberReference, ParentScope,
11};
12
13pub fn count_property_use(root: &CompilationUnit) {
14    // Visit the root properties that are used.
15    // 1. the public properties
16    for c in &root.public_components {
17        let root_ctx = EvaluationContext::new_sub_component(root, c.item_tree.root, (), None);
18        for p in c.public_properties.iter().filter(|p| !p.prop.is_function()) {
19            visit_property(&p.prop, &root_ctx);
20        }
21    }
22    for (idx, g) in root.globals.iter_enumerated().filter(|(_, g)| g.exported) {
23        let ctx = EvaluationContext::new_global(root, idx, ());
24        for p in g.public_properties.iter().filter(|p| !p.prop.is_function()) {
25            visit_property(&p.prop, &ctx);
26        }
27    }
28
29    root.for_each_sub_components(&mut |sc, ctx| {
30        // 2. the native items and bindings of properties
31        for (_, expr) in &sc.property_init {
32            let c = expr.use_count.get();
33            expr.use_count.set(c + 1);
34            if c == 0 {
35                visit_binding_expression(expr, ctx)
36            }
37        }
38        // 3. the init code
39        for expr in &sc.init_code {
40            expr.borrow().visit_property_references(ctx, &mut visit_property);
41        }
42        // 4. the models
43        for (idx, r) in sc.repeated.iter_enumerated() {
44            r.model.borrow().visit_property_references(ctx, &mut visit_property);
45            if let Some(lv) = &r.listview {
46                visit_property(&lv.viewport_y, ctx);
47                visit_property(&lv.viewport_width, ctx);
48                visit_property(&lv.viewport_height, ctx);
49                visit_property(&lv.listview_width, ctx);
50                visit_property(&lv.listview_height, ctx);
51
52                let parent_ctx = ParentScope::new(ctx, Some(idx));
53                let rep_ctx = EvaluationContext::new_sub_component(
54                    root,
55                    r.sub_tree.root,
56                    (),
57                    Some(&parent_ctx),
58                );
59                visit_property(&lv.prop_y, &rep_ctx);
60                visit_property(&lv.prop_height, &rep_ctx);
61            }
62            for idx in r.data_prop.iter().chain(r.index_prop.iter()) {
63                // prevent optimizing model properties
64                let p = &root.sub_components[r.sub_tree.root].properties[*idx];
65                p.use_count.set(2);
66            }
67        }
68
69        // 5. the layout info
70        sc.layout_info_h.borrow().visit_property_references(ctx, &mut visit_property);
71        sc.layout_info_v.borrow().visit_property_references(ctx, &mut visit_property);
72        if let Some(e) = &sc.grid_layout_input_for_repeated {
73            e.borrow().visit_property_references(ctx, &mut visit_property);
74        }
75        if let Some(e) = &sc.flexbox_layout_item_info_for_repeated {
76            e.borrow().visit_property_references(ctx, &mut visit_property);
77        }
78        for child in &sc.grid_layout_children {
79            child.layout_info_h.borrow().visit_property_references(ctx, &mut visit_property);
80            child.layout_info_v.borrow().visit_property_references(ctx, &mut visit_property);
81        }
82
83        // 6. accessibility props and geometries
84        for b in sc.accessible_prop.values() {
85            b.borrow().visit_property_references(ctx, &mut visit_property)
86        }
87        for i in sc.geometries.iter().filter_map(Option::as_ref) {
88            i.borrow().visit_property_references(ctx, &mut visit_property)
89        }
90
91        // 7. aliases (if they were not optimize, they are probably used)
92        for (a, b, _) in &sc.two_way_bindings {
93            visit_property(&a.clone(), ctx);
94            visit_property(&b.clone(), ctx);
95        }
96
97        // 8.functions (TODO: only visit used function)
98        for f in &sc.functions {
99            f.code.visit_property_references(ctx, &mut visit_property);
100        }
101
102        // 9. change callbacks
103        for (p, e) in &sc.change_callbacks {
104            visit_property(&p.clone(), ctx);
105            e.borrow().visit_property_references(ctx, &mut visit_property);
106        }
107
108        // 10. popup x/y coordinates
109        for popup in &sc.popup_windows {
110            let parent_ctx = ParentScope::new(ctx, None);
111            let popup_ctx = EvaluationContext::new_sub_component(
112                root,
113                popup.item_tree.root,
114                (),
115                Some(&parent_ctx),
116            );
117            popup.position.borrow().visit_property_references(&popup_ctx, &mut visit_property)
118        }
119        // 11. timer
120        for timer in &sc.timers {
121            timer.interval.borrow().visit_property_references(ctx, &mut visit_property);
122            timer.running.borrow().visit_property_references(ctx, &mut visit_property);
123            timer.triggered.borrow().visit_property_references(ctx, &mut visit_property);
124        }
125    });
126
127    for (idx, g) in root.globals.iter_enumerated() {
128        let ctx = EvaluationContext::new_global(root, idx, ());
129        // TODO: only visit used function
130        for f in &g.functions {
131            f.code.visit_property_references(&ctx, &mut visit_property);
132        }
133
134        for (p, e) in &g.change_callbacks {
135            visit_property(&LocalMemberReference::from(*p).into(), &ctx);
136            e.borrow().visit_property_references(&ctx, &mut visit_property);
137        }
138    }
139
140    if let Some(p) = &root.popup_menu {
141        let ctx = EvaluationContext::new_sub_component(root, p.item_tree.root, (), None);
142        visit_property(&p.entries, &ctx);
143        visit_property(&p.sub_menu, &ctx);
144        visit_property(&p.activated, &ctx);
145    }
146
147    clean_unused_bindings(root);
148}
149
150fn visit_property(pr: &MemberReference, ctx: &EvaluationContext) {
151    let p_info = ctx.property_info(pr);
152    if let Some(use_count) = &p_info.use_count {
153        use_count.set(use_count.get() + 1);
154    }
155    if let Some((binding, map)) = &p_info.binding {
156        let c = binding.use_count.get();
157        binding.use_count.set(c + 1);
158        if c == 0 {
159            let ctx2 = map.map_context(ctx);
160            visit_binding_expression(binding, &ctx2);
161        }
162    }
163}
164
165fn visit_binding_expression(binding: &BindingExpression, ctx: &EvaluationContext) {
166    binding.expression.borrow().visit_property_references(ctx, &mut visit_property);
167    match &binding.animation {
168        Some(Animation::Static(e) | Animation::Transition(e)) => {
169            e.visit_property_references(ctx, &mut visit_property);
170        }
171        None => (),
172    }
173}
174
175/// Bindings which have a use_count of zero can be cleared so that we won't ever visit them later.
176fn clean_unused_bindings(root: &CompilationUnit) {
177    root.for_each_sub_components(&mut |sc, _| {
178        for (_, e) in &sc.property_init {
179            if e.use_count.get() == 0 {
180                e.expression.replace(Expression::CodeBlock(Vec::new()));
181            }
182        }
183    });
184    for g in &root.globals {
185        for e in g.init_values.values() {
186            if e.use_count.get() == 0 {
187                e.expression.replace(Expression::CodeBlock(Vec::new()));
188            }
189        }
190    }
191}