i_slint_compiler/passes/
deduplicate_property_read.rs1use crate::expression_tree::*;
7use crate::langtype::Type;
8use crate::object_tree::*;
9use smol_str::{format_smolstr, SmolStr};
10use std::cell::RefCell;
11use std::collections::{BTreeMap, HashMap};
12
13pub fn deduplicate_property_read(component: &Component) {
14 visit_all_expressions(component, |expr, ty| {
15 if matches!(ty(), Type::Callback { .. }) {
16 return;
19 }
20 process_expression(expr, &DedupPropState::default());
21 });
22}
23
24struct ReadCount {
25 count: usize,
26 has_been_mapped: bool,
27}
28
29#[derive(Default)]
30struct PropertyReadCounts {
31 counts: HashMap<NamedReference, ReadCount>,
32 has_duplicate: bool,
34 has_set: bool,
36}
37
38#[derive(Default)]
39struct DedupPropState<'a> {
40 parent_state: Option<&'a DedupPropState<'a>>,
41 counts: RefCell<PropertyReadCounts>,
42}
43
44impl DedupPropState<'_> {
45 fn add(&self, nr: &NamedReference) {
46 if self.parent_state.is_some_and(|pc| pc.add_from_children(nr)) {
47 return;
48 }
49 let mut use_counts = self.counts.borrow_mut();
50 let use_counts = &mut *use_counts;
51 let has_duplicate = &mut use_counts.has_duplicate;
52 use_counts
53 .counts
54 .entry(nr.clone())
55 .and_modify(|c| {
56 if c.count == 1 {
57 *has_duplicate = true;
58 }
59 c.count += 1;
60 })
61 .or_insert(ReadCount { count: 1, has_been_mapped: false });
62 }
63
64 fn add_from_children(&self, nr: &NamedReference) -> bool {
65 if self.parent_state.is_some_and(|pc| pc.add_from_children(nr)) {
66 return true;
67 }
68 let mut use_counts = self.counts.borrow_mut();
69 let use_counts = &mut *use_counts;
70 if let Some(c) = use_counts.counts.get_mut(nr) {
71 if c.count == 1 {
72 use_counts.has_duplicate = true;
73 }
74 c.count += 1;
75 true
76 } else {
77 false
78 }
79 }
80
81 fn get_mapping(&self, nr: &NamedReference) -> Option<SmolStr> {
82 self.parent_state.and_then(|pr| pr.get_mapping(nr)).or_else(|| {
83 self.counts.borrow_mut().counts.get_mut(nr).filter(|c| c.count > 1).map(|c| {
84 c.has_been_mapped = true;
85 map_nr(nr)
86 })
87 })
88 }
89}
90
91fn map_nr(nr: &NamedReference) -> SmolStr {
92 format_smolstr!("tmp_{}_{}", nr.element().borrow().id, nr.name())
93}
94
95fn process_expression(expr: &mut Expression, old_state: &DedupPropState) {
96 if old_state.counts.borrow().has_set {
97 return;
98 }
99 let new_state = DedupPropState { parent_state: Some(old_state), ..DedupPropState::default() };
100 collect_unconditional_read_count(expr, &new_state);
101 process_conditional_expressions(expr, &new_state);
102 if new_state.counts.borrow().has_set {
103 old_state.counts.borrow_mut().has_set = true;
104 } else {
105 do_replacements(expr, &new_state);
106 }
107
108 if new_state.counts.borrow().has_duplicate {
109 let mut stores = BTreeMap::<SmolStr, NamedReference>::new();
110 for (nr, c) in &new_state.counts.borrow().counts {
111 if c.has_been_mapped {
112 stores.insert(map_nr(nr), nr.clone());
113 }
114 }
115 let mut exprs = stores
116 .into_iter()
117 .map(|(name, nr)| Expression::StoreLocalVariable {
118 name,
119 value: Box::new(Expression::PropertyReference(nr)),
120 })
121 .collect::<Vec<_>>();
122 exprs.push(std::mem::take(expr));
123 *expr = Expression::CodeBlock(exprs);
124 }
125}
126
127fn collect_unconditional_read_count(expr: &Expression, result: &DedupPropState) {
129 if result.counts.borrow().has_set {
130 return;
131 }
132 match expr {
133 Expression::PropertyReference(nr) => {
134 result.add(nr);
135 }
136 Expression::BinaryExpression { lhs, rhs: _, op: '|' | '&' } => {
139 lhs.visit(|sub| collect_unconditional_read_count(sub, result))
140 }
141 Expression::Condition { condition, .. } => {
142 condition.visit(|sub| collect_unconditional_read_count(sub, result))
143 }
144 Expression::SelfAssignment { .. } => {
145 result.counts.borrow_mut().has_set = true;
146 }
147 _ => expr.visit(|sub| collect_unconditional_read_count(sub, result)),
148 }
149}
150
151fn process_conditional_expressions(expr: &mut Expression, state: &DedupPropState) {
152 if state.counts.borrow().has_set {
153 return;
154 }
155 match expr {
156 Expression::BinaryExpression { lhs, rhs, op: '|' | '&' } => {
157 lhs.visit_mut(|sub| process_conditional_expressions(sub, state));
158 process_expression(rhs, state);
159 }
160 Expression::Condition { condition, true_expr, false_expr } => {
161 condition.visit_mut(|sub| process_conditional_expressions(sub, state));
162 process_expression(true_expr, state);
163 process_expression(false_expr, state);
164 }
165 Expression::SelfAssignment { .. } => {
166 state.counts.borrow_mut().has_set = true;
167 }
168 _ => expr.visit_mut(|sub| process_conditional_expressions(sub, state)),
169 }
170}
171
172fn do_replacements(expr: &mut Expression, state: &DedupPropState) {
173 match expr {
174 Expression::PropertyReference(nr) => {
175 if let Some(name) = state.get_mapping(nr) {
176 let ty = expr.ty();
177 *expr = Expression::ReadLocalVariable { name, ty };
178 }
179 }
180 Expression::BinaryExpression { lhs, rhs: _, op: '|' | '&' } => {
181 lhs.visit_mut(|sub| do_replacements(sub, state));
182 }
183 Expression::Condition { condition, .. } => {
184 condition.visit_mut(|sub| do_replacements(sub, state));
185 }
186 _ => expr.visit_mut(|sub| do_replacements(sub, state)),
187 }
188}