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