i_slint_compiler/passes/
remove_aliases.rs1use crate::diagnostics::BuildDiagnostics;
7use crate::expression_tree::{BindingExpression, Expression, NamedReference, TwoWayBinding};
8use crate::langtype::Type;
9use crate::object_tree::*;
10use std::cell::RefCell;
11use std::collections::{HashMap, HashSet, btree_map::Entry};
12use std::rc::Rc;
13
14type Mapping = HashMap<NamedReference, NamedReference>;
16
17#[derive(Default, Debug)]
18struct PropertySets {
19 map: HashMap<NamedReference, Rc<RefCell<HashSet<NamedReference>>>>,
20 all_sets: Vec<Rc<RefCell<HashSet<NamedReference>>>>,
21}
22
23impl PropertySets {
24 fn add_link(&mut self, p1: NamedReference, p2: NamedReference) {
25 let (e1, e2) = (p1.element(), p2.element());
26 let same_component = std::rc::Weak::ptr_eq(
27 &e1.borrow().enclosing_component,
28 &e2.borrow().enclosing_component,
29 );
30 if !same_component {
31 let can_merge_across_components =
32 (e1.borrow().enclosing_component.upgrade().unwrap().is_global()
33 && !e2.borrow().change_callbacks.contains_key(p2.name()))
34 || (e2.borrow().enclosing_component.upgrade().unwrap().is_global()
35 && !e1.borrow().change_callbacks.contains_key(p1.name()));
36 if !can_merge_across_components {
37 return;
41 }
42 }
43
44 if let Some(s1) = self.map.get(&p1).cloned() {
45 if let Some(s2) = self.map.get(&p2).cloned() {
46 if Rc::ptr_eq(&s1, &s2) {
47 return;
48 }
49 for x in s1.borrow().iter() {
50 self.map.insert(x.clone(), s2.clone());
51 s2.borrow_mut().insert(x.clone());
52 }
53 *s1.borrow_mut() = HashSet::new();
54 } else {
55 s1.borrow_mut().insert(p2.clone());
56 self.map.insert(p2, s1);
57 }
58 } else if let Some(s2) = self.map.get(&p2).cloned() {
59 s2.borrow_mut().insert(p1.clone());
60 self.map.insert(p1, s2);
61 } else {
62 let mut set = HashSet::new();
63 set.insert(p1.clone());
64 set.insert(p2.clone());
65 let set = Rc::new(RefCell::new(set));
66 self.map.insert(p1, set.clone());
67 self.map.insert(p2, set.clone());
68 self.all_sets.push(set)
69 }
70 }
71}
72
73pub fn remove_aliases(doc: &Document, diag: &mut BuildDiagnostics) {
74 let mut property_sets = PropertySets::default();
76
77 let mut process_element = |e: &ElementRc| {
78 'bindings: for (name, binding) in &e.borrow().bindings {
79 for twb in &binding.borrow().two_way_bindings {
80 if let TwoWayBinding::Property { property, field_access } = twb {
81 if !field_access.is_empty() {
82 continue;
84 }
85 let other_e = property.element();
86 if name == property.name() && Rc::ptr_eq(e, &other_e) {
87 diag.push_error(
88 "Property cannot alias to itself".into(),
89 &*binding.borrow(),
90 );
91 continue 'bindings;
92 }
93 property_sets.add_link(NamedReference::new(e, name.clone()), property.clone());
94 }
95 }
96 }
97 };
98
99 doc.visit_all_used_components(|component| {
100 recurse_elem_including_sub_components(component, &(), &mut |e, &()| process_element(e))
101 });
102
103 let mut aliases_to_remove = Mapping::new();
105
106 for set in property_sets.all_sets {
109 let set = set.borrow();
110
111 let implementers: Vec<NamedReference> = set
116 .iter()
117 .filter(|nr| {
118 if !matches!(nr.ty(), Type::Callback(..)) {
119 return false;
120 }
121 let elem = nr.element();
122 let elem = elem.borrow();
123 elem.enclosing_component.upgrade().is_some_and(|c| c.is_global())
124 && elem.bindings.get(nr.name()).is_some_and(|b| {
125 !matches!(
126 super::ignore_debug_hooks(&b.borrow().expression),
127 Expression::Invalid
128 )
129 })
130 })
131 .cloned()
132 .collect();
133 if implementers.len() > 1 {
134 for nr in &implementers {
135 let elem = nr.element();
136 let elem = elem.borrow();
137 if let Some(b) = elem.bindings.get(nr.name()) {
138 diag.push_error(
139 format!("Callback '{}' is implemented in more than one global", nr.name()),
140 &*b.borrow(),
141 );
142 }
143 }
144 }
145
146 let mut set_iter = set.iter();
147 if let Some(mut best) = set_iter.next().cloned() {
148 for candidate in set_iter {
149 best = best_property(best.clone(), candidate.clone());
150 }
151 for x in set.iter() {
152 if *x != best {
153 aliases_to_remove.insert(x.clone(), best.clone());
154 }
155 }
156 }
157 }
158
159 doc.visit_all_used_components(|component| {
160 visit_all_named_references(component, &mut |nr: &mut NamedReference| {
162 if let Some(new) = aliases_to_remove.get(nr) {
163 *nr = new.clone();
164 }
165 })
166 });
167
168 for (remove, to) in aliases_to_remove {
170 let elem = remove.element();
171 let to_elem = to.element();
172
173 let old_binding = elem.borrow_mut().bindings.remove(remove.name());
175 let mut old_binding = old_binding.map(RefCell::into_inner).unwrap_or_else(|| {
176 let mut b = BindingExpression::from(Expression::default_value_for_type(&to.ty()));
179 b.priority = to_elem
180 .borrow_mut()
181 .bindings
182 .get(to.name())
183 .map_or(i32::MAX, |x| x.borrow().priority.saturating_add(1));
184 b
185 });
186
187 remove_from_binding_expression(&mut old_binding, &to);
188
189 let same_component = std::rc::Weak::ptr_eq(
190 &elem.borrow().enclosing_component,
191 &to_elem.borrow().enclosing_component,
192 );
193 let both_global =
196 elem.borrow().enclosing_component.upgrade().is_some_and(|c| c.is_global())
197 && to_elem.borrow().enclosing_component.upgrade().is_some_and(|c| c.is_global());
198 match to_elem.borrow_mut().bindings.entry(to.name().clone()) {
199 Entry::Occupied(mut e) => {
200 let b = e.get_mut().get_mut();
201 remove_from_binding_expression(b, &to);
202 if !same_component || b.priority < old_binding.priority || !b.has_binding() {
203 b.merge_with(&old_binding);
204 } else {
205 old_binding.merge_with(b);
206 *b = old_binding;
207 }
208 }
209 Entry::Vacant(e) => {
210 if (same_component || both_global) && old_binding.has_binding() {
211 e.insert(old_binding.into());
212 }
213 }
214 };
215
216 {
218 let mut elem = elem.borrow_mut();
219 if let Some(old_change_callback) = elem.change_callbacks.remove(remove.name()) {
220 drop(elem);
221 let mut old_change_callback = old_change_callback.into_inner();
222 to_elem
223 .borrow_mut()
224 .change_callbacks
225 .entry(to.name().clone())
226 .or_default()
227 .borrow_mut()
228 .append(&mut old_change_callback);
229 }
230 }
231
232 {
234 let mut elem = elem.borrow_mut();
235 let used_externally = elem
236 .property_analysis
237 .borrow()
238 .get(remove.name())
239 .is_some_and(|v| v.is_read_externally || v.is_set_externally);
240 if let Some(d) = elem.property_declarations.get_mut(remove.name()) {
241 if d.expose_in_public_api || used_externally {
242 d.is_alias = Some(to.clone());
243 drop(elem);
244 to.mark_as_set();
246 } else {
247 elem.property_declarations.remove(remove.name());
248 let analysis = elem.property_analysis.borrow().get(remove.name()).cloned();
249 if let Some(analysis) = analysis {
250 drop(elem);
251 to.element()
252 .borrow()
253 .property_analysis
254 .borrow_mut()
255 .entry(to.name().clone())
256 .or_default()
257 .merge(&analysis);
258 };
259 }
260 } else {
261 elem.bindings.insert(
263 remove.name().clone(),
264 BindingExpression::new_two_way(to.clone().into()).into(),
265 );
266 drop(elem);
267 if remove.is_externally_modified() {
268 to.mark_as_set();
269 }
270 }
271 }
272 }
273}
274
275fn is_declaration(x: &NamedReference) -> bool {
276 x.element().borrow().property_declarations.contains_key(x.name())
277}
278
279fn best_property(p1: NamedReference, p2: NamedReference) -> NamedReference {
281 macro_rules! canonical_order {
283 ($x: expr) => {{
284 (
285 !$x.element().borrow().enclosing_component.upgrade().unwrap().is_global(),
286 is_declaration(&$x),
287 $x.element().borrow().id.clone(),
288 $x.name(),
289 )
290 }};
291 }
292
293 if canonical_order!(p1) < canonical_order!(p2) { p1 } else { p2 }
294}
295
296fn remove_from_binding_expression(expression: &mut BindingExpression, to: &NamedReference) {
298 expression.two_way_bindings.retain(|x| x.property() != Some(to));
299}