i_slint_compiler/passes/
remove_aliases.rs1use crate::diagnostics::BuildDiagnostics;
7use crate::expression_tree::{BindingExpression, Expression, NamedReference};
8use crate::object_tree::*;
9use std::cell::RefCell;
10use std::collections::{btree_map::Entry, HashMap, HashSet};
11use std::rc::Rc;
12
13type Mapping = HashMap<NamedReference, NamedReference>;
15
16#[derive(Default, Debug)]
17struct PropertySets {
18 map: HashMap<NamedReference, Rc<RefCell<HashSet<NamedReference>>>>,
19 all_sets: Vec<Rc<RefCell<HashSet<NamedReference>>>>,
20}
21
22impl PropertySets {
23 fn add_link(&mut self, p1: NamedReference, p2: NamedReference) {
24 let (e1, e2) = (p1.element(), p2.element());
25 if !std::rc::Weak::ptr_eq(
26 &e1.borrow().enclosing_component,
27 &e2.borrow().enclosing_component,
28 ) {
29 if !(e1.borrow().enclosing_component.upgrade().unwrap().is_global()
30 && !e2.borrow().change_callbacks.contains_key(p2.name()))
31 && !(e2.borrow().enclosing_component.upgrade().unwrap().is_global()
32 && !e1.borrow().change_callbacks.contains_key(p1.name()))
33 {
34 return;
38 }
39 }
40
41 if let Some(s1) = self.map.get(&p1).cloned() {
42 if let Some(s2) = self.map.get(&p2).cloned() {
43 if Rc::ptr_eq(&s1, &s2) {
44 return;
45 }
46 for x in s1.borrow().iter() {
47 self.map.insert(x.clone(), s2.clone());
48 s2.borrow_mut().insert(x.clone());
49 }
50 *s1.borrow_mut() = HashSet::new();
51 } else {
52 s1.borrow_mut().insert(p2.clone());
53 self.map.insert(p2, s1);
54 }
55 } else if let Some(s2) = self.map.get(&p2).cloned() {
56 s2.borrow_mut().insert(p1.clone());
57 self.map.insert(p1, s2);
58 } else {
59 let mut set = HashSet::new();
60 set.insert(p1.clone());
61 set.insert(p2.clone());
62 let set = Rc::new(RefCell::new(set));
63 self.map.insert(p1, set.clone());
64 self.map.insert(p2, set.clone());
65 self.all_sets.push(set)
66 }
67 }
68}
69
70pub fn remove_aliases(doc: &Document, diag: &mut BuildDiagnostics) {
71 let mut property_sets = PropertySets::default();
73
74 let mut process_element = |e: &ElementRc| {
75 'bindings: for (name, binding) in &e.borrow().bindings {
76 for nr in &binding.borrow().two_way_bindings {
77 let other_e = nr.element();
78 if name == nr.name() && Rc::ptr_eq(e, &other_e) {
79 diag.push_error("Property cannot alias to itself".into(), &*binding.borrow());
80 continue 'bindings;
81 }
82 property_sets.add_link(NamedReference::new(e, name.clone()), nr.clone());
83 }
84 }
85 };
86
87 doc.visit_all_used_components(|component| {
88 recurse_elem_including_sub_components(component, &(), &mut |e, &()| process_element(e))
89 });
90
91 let mut aliases_to_remove = Mapping::new();
93
94 for set in property_sets.all_sets {
97 let set = set.borrow();
98 let mut set_iter = set.iter();
99 if let Some(mut best) = set_iter.next().cloned() {
100 for candidate in set_iter {
101 best = best_property(best.clone(), candidate.clone());
102 }
103 for x in set.iter() {
104 if *x != best {
105 aliases_to_remove.insert(x.clone(), best.clone());
106 }
107 }
108 }
109 }
110
111 doc.visit_all_used_components(|component| {
112 visit_all_named_references(component, &mut |nr: &mut NamedReference| {
114 if let Some(new) = aliases_to_remove.get(nr) {
115 *nr = new.clone();
116 }
117 })
118 });
119
120 for (remove, to) in aliases_to_remove {
122 let elem = remove.element();
123 let to_elem = to.element();
124
125 let old_binding = elem.borrow_mut().bindings.remove(remove.name());
127 let mut old_binding = old_binding.map(RefCell::into_inner).unwrap_or_else(|| {
128 let mut b = BindingExpression::from(Expression::default_value_for_type(&to.ty()));
131 b.priority = to_elem
132 .borrow_mut()
133 .bindings
134 .get(to.name())
135 .map_or(i32::MAX, |x| x.borrow().priority.saturating_add(1));
136 b
137 });
138
139 remove_from_binding_expression(&mut old_binding, &to);
140
141 let same_component = std::rc::Weak::ptr_eq(
142 &elem.borrow().enclosing_component,
143 &to_elem.borrow().enclosing_component,
144 );
145 match to_elem.borrow_mut().bindings.entry(to.name().clone()) {
146 Entry::Occupied(mut e) => {
147 let b = e.get_mut().get_mut();
148 remove_from_binding_expression(b, &to);
149 if !same_component || b.priority < old_binding.priority || !b.has_binding() {
150 b.merge_with(&old_binding);
151 } else {
152 old_binding.merge_with(b);
153 *b = old_binding;
154 }
155 }
156 Entry::Vacant(e) => {
157 if same_component && old_binding.has_binding() {
158 e.insert(old_binding.into());
159 }
160 }
161 };
162
163 {
165 let mut elem = elem.borrow_mut();
166 if let Some(old_change_callback) = elem.change_callbacks.remove(remove.name()) {
167 drop(elem);
168 let mut old_change_callback = old_change_callback.into_inner();
169 to_elem
170 .borrow_mut()
171 .change_callbacks
172 .entry(to.name().clone())
173 .or_default()
174 .borrow_mut()
175 .append(&mut old_change_callback);
176 }
177 }
178
179 {
181 let mut elem = elem.borrow_mut();
182 let used_externally = elem
183 .property_analysis
184 .borrow()
185 .get(remove.name())
186 .is_some_and(|v| v.is_read_externally || v.is_set_externally);
187 if let Some(d) = elem.property_declarations.get_mut(remove.name()) {
188 if d.expose_in_public_api || used_externally {
189 d.is_alias = Some(to.clone());
190 drop(elem);
191 to.mark_as_set();
193 } else {
194 elem.property_declarations.remove(remove.name());
195 let analysis = elem.property_analysis.borrow().get(remove.name()).cloned();
196 if let Some(analysis) = analysis {
197 drop(elem);
198 to.element()
199 .borrow()
200 .property_analysis
201 .borrow_mut()
202 .entry(to.name().clone())
203 .or_default()
204 .merge(&analysis);
205 };
206 }
207 } else {
208 elem.bindings.insert(
210 remove.name().clone(),
211 BindingExpression::new_two_way(to.clone()).into(),
212 );
213 drop(elem);
214 if remove.is_externally_modified() {
215 to.mark_as_set();
216 }
217 }
218 }
219 }
220}
221
222fn is_declaration(x: &NamedReference) -> bool {
223 x.element().borrow().property_declarations.contains_key(x.name())
224}
225
226fn best_property(p1: NamedReference, p2: NamedReference) -> NamedReference {
228 macro_rules! canonical_order {
230 ($x: expr) => {{
231 (
232 !$x.element().borrow().enclosing_component.upgrade().unwrap().is_global(),
233 is_declaration(&$x),
234 $x.element().borrow().id.clone(),
235 $x.name(),
236 )
237 }};
238 }
239
240 if canonical_order!(p1) < canonical_order!(p2) {
241 p1
242 } else {
243 p2
244 }
245}
246
247fn remove_from_binding_expression(expression: &mut BindingExpression, to: &NamedReference) {
249 expression.two_way_bindings.retain(|x| x != to);
250}