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 Property::use_count
5//!
6//! This pass assume that use_count of all properties is zero
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 // TODO: only visit used function
126 for (idx, g) in root.globals.iter_enumerated() {
127 let ctx = EvaluationContext::new_global(root, idx, ());
128 for f in &g.functions {
129 f.code.visit_property_references(&ctx, &mut visit_property);
130 }
131 }
132
133 if let Some(p) = &root.popup_menu {
134 let ctx = EvaluationContext::new_sub_component(root, p.item_tree.root, (), None);
135 visit_property(&p.entries, &ctx);
136 visit_property(&p.sub_menu, &ctx);
137 visit_property(&p.activated, &ctx);
138 }
139
140 clean_unused_bindings(root);
141}
142
143fn visit_property(pr: &PropertyReference, ctx: &EvaluationContext) {
144 let p_info: PropertyInfoResult<'_> = ctx.property_info(prop:pr);
145 if let Some(p: &&Property) = &p_info.property_decl {
146 p.use_count.set(val:p.use_count.get() + 1);
147 }
148 if let Some((binding: &&BindingExpression, map: &ContextMap)) = &p_info.binding {
149 let c: usize = binding.use_count.get();
150 binding.use_count.set(val:c + 1);
151 if c == 0 {
152 let ctx2: EvaluationContext<'_> = map.map_context(ctx);
153 visit_binding_expression(binding, &ctx2);
154 }
155 }
156}
157
158fn visit_binding_expression(binding: &BindingExpression, ctx: &EvaluationContext) {
159 binding.expression.borrow().visit_property_references(ctx, &mut visit_property);
160 match &binding.animation {
161 Some(Animation::Static(e: &Expression) | Animation::Transition(e: &Expression)) => {
162 e.visit_property_references(ctx, &mut visit_property)
163 }
164 None => (),
165 }
166}
167
168/// Bindings which have a use_count of zero can be cleared so that we won't ever visit them later.
169fn clean_unused_bindings(root: &CompilationUnit) {
170 root.for_each_sub_components(&mut |sc: &SubComponent, _| {
171 for (_, e: &BindingExpression) in &sc.property_init {
172 if e.use_count.get() == 0 {
173 e.expression.replace(Expression::CodeBlock(vec![]));
174 }
175 }
176 });
177 for g: &GlobalComponent in &root.globals {
178 for e: &BindingExpression in g.init_values.iter().flatten() {
179 if e.use_count.get() == 0 {
180 e.expression.replace(Expression::CodeBlock(vec![]));
181 }
182 }
183 }
184}
185