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