1use crate::llr::*;
5use typed_index_collections::TiVec;
6
7struct Mapping {
8 prop_mapping: TiVec<PropertyIdx, Option<PropertyIdx>>,
9 callback_mapping: TiVec<CallbackIdx, Option<CallbackIdx>>,
10}
11
12impl Mapping {
13 fn keep(&self, member: &LocalMemberIndex) -> bool {
14 match member {
15 LocalMemberIndex::Property(p) => self.prop_mapping[*p].is_some(),
16 LocalMemberIndex::Callback(c) => self.callback_mapping[*c].is_some(),
17 _ => true,
18 }
19 }
20}
21
22type ScMappings = TiVec<SubComponentIdx, Mapping>;
23type GlobMappings = TiVec<GlobalIdx, Mapping>;
24
25pub fn remove_unused(root: &mut CompilationUnit) {
26 struct RemoveUnusedMappings {
27 sc_mappings: ScMappings,
28 glob_mappings: GlobMappings,
29 }
30 let mappings = RemoveUnusedMappings {
31 sc_mappings: root
32 .sub_components
33 .iter_mut()
34 .map(|sc| create_mapping(&mut sc.properties, &mut sc.callbacks))
35 .collect(),
36 glob_mappings: root
37 .globals
38 .iter_mut()
39 .map(|g| {
40 clean_vec(&mut g.const_properties, &g.properties);
41 clean_vec(&mut g.prop_analysis, &g.properties);
42 create_mapping(&mut g.properties, &mut g.callbacks)
43 })
44 .collect(),
45 };
46
47 let state = visitor::VisitorState::new(root);
48
49 for (idx, sc) in root.sub_components.iter_mut_enumerated() {
50 let keep = |refer: &MemberReference| match refer {
51 MemberReference::Relative { parent_level, local_reference } => {
52 assert_eq!(*parent_level, 0);
53 let idx = state.follow_sub_components(idx, &local_reference.sub_component_path);
54 mappings.sc_mappings[idx].keep(&local_reference.reference)
55 }
56 MemberReference::Global { global_index, member } => {
57 mappings.glob_mappings[*global_index].keep(member)
58 }
59 };
60
61 let mut property_init_mapping = Vec::new();
62 let mut i = 0;
63 sc.property_init.retain(|(x, v)| {
64 if keep(x) && v.use_count.get() > 0 {
65 property_init_mapping.push(Some(i));
66 i += 1;
67 true
68 } else {
69 property_init_mapping.push(None);
70 false
71 }
72 });
73 sc.change_callbacks.retain(|(x, _)| keep(x));
74 sc.const_properties.retain(|x| {
75 let idx = state.follow_sub_components(idx, &x.sub_component_path);
76 mappings.sc_mappings[idx].keep(&x.reference)
77 });
78 sc.prop_analysis.retain(|x, v| {
79 v.property_init = v.property_init.and_then(|x| property_init_mapping[x]);
80 keep(x)
81 });
82 sc.animations.retain(|x, _| keep(&x.clone().into()));
83 }
84 for (idx, g) in root.globals.iter_mut_enumerated() {
85 g.init_values.retain(|x, _| mappings.glob_mappings[idx].keep(x));
86 }
87
88 impl visitor::Visitor for &RemoveUnusedMappings {
89 fn visit_property_idx(
90 &mut self,
91 p: &mut PropertyIdx,
92 scope: &EvaluationScope,
93 _state: &visitor::VisitorState,
94 ) {
95 match scope {
96 EvaluationScope::SubComponent(sub_component_idx, _) => {
97 *p = self.sc_mappings[*sub_component_idx].prop_mapping[*p].unwrap();
100 }
101 EvaluationScope::Global(global_idx) => {
102 *p = self.glob_mappings[*global_idx].prop_mapping[*p].unwrap();
103 }
104 }
105 }
106
107 fn visit_callback_idx(
108 &mut self,
109 p: &mut CallbackIdx,
110 scope: &EvaluationScope,
111 _state: &visitor::VisitorState,
112 ) {
113 match scope {
114 EvaluationScope::SubComponent(sub_component_idx, _) => {
115 *p = self.sc_mappings[*sub_component_idx].callback_mapping[*p].unwrap();
116 }
117 EvaluationScope::Global(global_idx) => {
118 *p = self.glob_mappings[*global_idx].callback_mapping[*p].unwrap();
119 }
120 }
121 }
122 }
123 let mut visitor = &mappings;
124 visitor::visit_compilation_unit(root, &state, &mut visitor);
125}
126
127fn create_mapping(
128 properties: &mut TiVec<PropertyIdx, Property>,
129 callbacks: &mut TiVec<CallbackIdx, Callback>,
130) -> Mapping {
131 Mapping {
132 prop_mapping: create_vec_mapping(properties, |p| p.use_count.get() > 0),
133 callback_mapping: create_vec_mapping(callbacks, |c| c.use_count.get() > 0),
134 }
135}
136
137fn create_vec_mapping<Idx: From<usize>, T>(
138 vec: &mut TiVec<Idx, T>,
139 mut retain: impl FnMut(&T) -> bool,
140) -> TiVec<Idx, Option<Idx>> {
141 let mut map = TiVec::with_capacity(vec.len());
142 let mut i = 0;
143 vec.retain(|t| {
144 if retain(t) {
145 map.push(Some(Idx::from(i)));
146 i += 1;
147 true
148 } else {
149 map.push(None);
150 false
151 }
152 });
153 map
154}
155
156fn clean_vec<T>(vec: &mut TiVec<PropertyIdx, T>, properties: &TiVec<PropertyIdx, Property>) {
157 let mut idx = 0;
158 vec.retain(|_| {
159 idx += 1;
160 properties[PropertyIdx::from(idx - 1)].use_count.get() >= 1
161 });
162}
163
164mod visitor {
165
166 use super::*;
167
168 pub trait Visitor {
169 fn visit_property_idx(
170 &mut self,
171 _p: &mut PropertyIdx,
172 _scope: &EvaluationScope,
173 _state: &VisitorState,
174 ) {
175 }
176 fn visit_function_idx(
177 &mut self,
178 _p: &mut FunctionIdx,
179 _scope: &EvaluationScope,
180 _state: &VisitorState,
181 ) {
182 }
183
184 fn visit_callback_idx(
185 &mut self,
186 _p: &mut CallbackIdx,
187 _scope: &EvaluationScope,
188 _state: &VisitorState,
189 ) {
190 }
191 }
192
193 pub struct VisitorState {
194 sub_component_maps: TiVec<SubComponentIdx, TiVec<SubComponentInstanceIdx, SubComponentIdx>>,
196 parent_mapping: TiVec<SubComponentIdx, Option<SubComponentIdx>>,
198 }
199
200 impl VisitorState {
201 pub fn new(cu: &CompilationUnit) -> Self {
202 let mut parent_mapping = TiVec::new();
203 parent_mapping.resize(cu.sub_components.len(), None);
204 for (idx, sc) in cu.sub_components.iter_enumerated() {
205 for r in &sc.repeated {
206 parent_mapping[r.sub_tree.root] = Some(idx);
207 }
208 for p in &sc.popup_windows {
209 parent_mapping[p.item_tree.root] = Some(idx);
210 }
211 for m in &sc.menu_item_trees {
212 parent_mapping[m.root] = Some(idx);
213 }
214 }
215 Self {
216 sub_component_maps: cu
217 .sub_components
218 .iter()
219 .map(|sc| sc.sub_components.iter().map(|x| x.ty).collect())
220 .collect(),
221 parent_mapping,
222 }
223 }
224
225 pub fn follow_sub_components(
226 &self,
227 mut sc: SubComponentIdx,
228 sub_component_path: &[SubComponentInstanceIdx],
229 ) -> SubComponentIdx {
230 for i in sub_component_path {
231 sc = self.sub_component_maps[sc][*i];
232 }
233 sc
234 }
235 }
236
237 pub fn visit_compilation_unit(
238 CompilationUnit {
239 public_components,
240 sub_components,
241 used_sub_components: _,
242 globals,
243 popup_menu,
244 has_debug_info: _,
245 #[cfg(feature = "bundle-translations")]
246 translations: _,
247 }: &mut crate::llr::CompilationUnit,
248 state: &VisitorState,
249 visitor: &mut (impl Visitor + ?Sized),
250 ) {
251 for c in public_components {
252 visit_public_component(c, state, visitor);
253 }
254 for (idx, sc) in sub_components.iter_mut_enumerated() {
255 visit_sub_component(idx, sc, state, visitor);
256 }
257 for (idx, g) in globals.iter_mut_enumerated() {
258 visit_global(idx, g, state, visitor);
259 }
260 if let Some(p) = popup_menu {
261 visit_popup_menu(p, state, visitor);
262 }
263 }
264
265 pub fn visit_public_component(
266 PublicComponent { public_properties, private_properties: _, item_tree, name:_ }: &mut PublicComponent,
267 state: &VisitorState,
268 visitor: &mut (impl Visitor + ?Sized),
269 ) {
270 let scope = EvaluationScope::SubComponent(item_tree.root, None);
271 for p in public_properties {
272 visit_public_property(p, &scope, state, visitor);
273 }
274 }
275
276 pub fn visit_sub_component(
277 idx: SubComponentIdx,
278 SubComponent {
279 name: _,
280 properties: _,
281 callbacks: _,
282 functions,
283 items: _,
284 repeated,
285 component_containers: _,
286 popup_windows,
287 menu_item_trees: _,
288 timers,
289 sub_components: _,
290 property_init,
291 change_callbacks,
292 animations,
293 two_way_bindings,
294 const_properties,
295 init_code,
296 geometries,
297 layout_info_h,
298 layout_info_v,
299 child_of_layout: _,
300 grid_layout_input_for_repeated,
301 is_repeated_row: _,
302 grid_layout_children,
303 accessible_prop,
304 element_infos: _,
305 prop_analysis,
306 }: &mut SubComponent,
307 state: &VisitorState,
308 visitor: &mut (impl Visitor + ?Sized),
309 ) {
310 let scope = EvaluationScope::SubComponent(idx, None);
311 for f in functions {
312 visit_function(f, &scope, state, visitor);
313 }
314 for RepeatedElement {
315 model,
316 index_prop,
317 data_prop,
318 sub_tree,
319 index_in_tree: _,
320 listview,
321 container_item_index: _,
322 } in repeated
323 {
324 visit_expression(model.get_mut(), &scope, state, visitor);
325 let inner_scope = EvaluationScope::SubComponent(sub_tree.root, None);
326 if let Some(index_prop) = index_prop {
327 visitor.visit_property_idx(index_prop, &inner_scope, state);
328 }
329 if let Some(data_prop) = data_prop {
330 visitor.visit_property_idx(data_prop, &inner_scope, state);
331 }
332
333 if let Some(listview) = listview {
334 visit_local_member_reference(&mut listview.viewport_y, &scope, state, visitor);
335 visit_local_member_reference(&mut listview.viewport_height, &scope, state, visitor);
336 visit_local_member_reference(&mut listview.viewport_width, &scope, state, visitor);
337 visit_local_member_reference(&mut listview.listview_width, &scope, state, visitor);
338 visit_local_member_reference(&mut listview.listview_height, &scope, state, visitor);
339
340 visit_member_reference(&mut listview.prop_y, &inner_scope, state, visitor);
341 visit_member_reference(&mut listview.prop_height, &inner_scope, state, visitor);
342 }
343 }
344
345 for p in popup_windows {
346 let popup_scope = EvaluationScope::SubComponent(p.item_tree.root, None);
347 visit_expression(p.position.get_mut(), &popup_scope, state, visitor);
348 }
349 for t in timers {
350 visit_expression(t.interval.get_mut(), &scope, state, visitor);
351 visit_expression(t.triggered.get_mut(), &scope, state, visitor);
352 visit_expression(t.running.get_mut(), &scope, state, visitor);
353 }
354 for (idx, init) in property_init {
355 visit_member_reference(idx, &scope, state, visitor);
356 visit_binding_expression(init, &scope, state, visitor);
357 }
358 for (idx, e) in change_callbacks {
359 visit_member_reference(idx, &scope, state, visitor);
360 visit_expression(e.get_mut(), &scope, state, visitor);
361 }
362 *animations = std::mem::take(animations)
363 .into_iter()
364 .map(|(mut k, mut v)| {
365 visit_local_member_reference(&mut k, &scope, state, visitor);
366 visit_expression(&mut v, &scope, state, visitor);
367 (k, v)
368 })
369 .collect();
370
371 for (a, b, _) in two_way_bindings {
372 visit_member_reference(a, &scope, state, visitor);
373 visit_member_reference(b, &scope, state, visitor);
374 }
375 for c in const_properties {
376 visit_local_member_reference(c, &scope, state, visitor);
377 }
378 for i in init_code {
379 visit_expression(i.get_mut(), &scope, state, visitor);
380 }
381 for g in geometries.iter_mut().flatten() {
382 visit_expression(g.get_mut(), &scope, state, visitor);
383 }
384 visit_expression(layout_info_h.get_mut(), &scope, state, visitor);
385 visit_expression(layout_info_v.get_mut(), &scope, state, visitor);
386 if let Some(e) = grid_layout_input_for_repeated {
387 visit_expression(e.get_mut(), &scope, state, visitor);
388 }
389 for child in grid_layout_children {
390 visit_expression(child.layout_info_h.get_mut(), &scope, state, visitor);
391 visit_expression(child.layout_info_v.get_mut(), &scope, state, visitor);
392 }
393
394 for a in accessible_prop.values_mut() {
395 visit_expression(a.get_mut(), &scope, state, visitor);
396 }
397
398 *prop_analysis = std::mem::take(prop_analysis)
399 .into_iter()
400 .map(|(mut k, v)| {
401 visit_member_reference(&mut k, &scope, state, visitor);
402 (k, v)
403 })
404 .collect();
405 }
406
407 fn visit_global(
408 global_idx: GlobalIdx,
409 GlobalComponent {
410 name: _,
411 properties: _,
412 callbacks: _,
413 functions,
414 init_values,
415 change_callbacks,
416 const_properties: _,
417 public_properties,
418 private_properties: _,
419 exported: _,
420 aliases: _,
421 is_builtin: _,
422 from_library: _,
423 prop_analysis: _,
424 }: &mut GlobalComponent,
425 state: &VisitorState,
426 visitor: &mut (impl Visitor + ?Sized),
427 ) {
428 let scope = EvaluationScope::Global(global_idx);
429 for f in functions {
430 visit_function(f, &scope, state, visitor);
431 }
432
433 *init_values = std::mem::take(init_values)
434 .into_iter()
435 .map(|(mut k, mut v)| {
436 visit_member_index(&mut k, &scope, state, visitor);
437 visit_binding_expression(&mut v, &scope, state, visitor);
438 (k, v)
439 })
440 .collect();
441
442 *change_callbacks = std::mem::take(change_callbacks)
443 .into_iter()
444 .map(|(mut k, mut v)| {
445 visitor.visit_property_idx(&mut k, &scope, state);
446 visit_expression(v.get_mut(), &scope, state, visitor);
447 (k, v)
448 })
449 .collect();
450
451 for p in public_properties {
452 visit_public_property(p, &scope, state, visitor);
453 }
454 }
455
456 pub fn visit_popup_menu(
457 PopupMenu { item_tree, sub_menu, activated, close, entries }: &mut PopupMenu,
458 state: &VisitorState,
459 visitor: &mut (impl Visitor + ?Sized),
460 ) {
461 let scope = EvaluationScope::SubComponent(item_tree.root, None);
462 visit_member_reference(sub_menu, &scope, state, visitor);
463 visit_member_reference(activated, &scope, state, visitor);
464 visit_member_reference(close, &scope, state, visitor);
465 visit_member_reference(entries, &scope, state, visitor);
466 }
467
468 pub fn visit_public_property(
469 PublicProperty { name: _, ty: _, prop, read_only: _ }: &mut PublicProperty,
470 scope: &EvaluationScope,
471 state: &VisitorState,
472 visitor: &mut (impl Visitor + ?Sized),
473 ) {
474 visit_member_reference(prop, scope, state, visitor);
475 }
476
477 pub fn visit_function(
478 Function { name: _, ret_ty: _, args: _, code }: &mut Function,
479 scope: &EvaluationScope,
480 state: &VisitorState,
481 visitor: &mut (impl Visitor + ?Sized),
482 ) {
483 visit_expression(code, scope, state, visitor);
484 }
485
486 pub fn visit_expression(
487 expr: &mut Expression,
488 scope: &EvaluationScope,
489 state: &VisitorState,
490 visitor: &mut (impl Visitor + ?Sized),
491 ) {
492 expr.visit_recursive_mut(&mut |expr| {
493 let p = match expr {
494 Expression::PropertyReference(p) => p,
495 Expression::CallBackCall { callback, .. } => callback,
496 Expression::PropertyAssignment { property, .. } => property,
497 Expression::LayoutCacheAccess { layout_cache_prop, .. } => layout_cache_prop,
498 _ => return,
499 };
500 visit_member_reference(p, scope, state, visitor);
501 });
502 }
503
504 pub fn visit_binding_expression(
505 BindingExpression { expression, animation, is_constant: _, is_state_info: _, use_count: _ }: &mut BindingExpression,
506 scope: &EvaluationScope,
507 state: &VisitorState,
508 visitor: &mut (impl Visitor + ?Sized),
509 ) {
510 visit_expression(expression.get_mut(), scope, state, visitor);
511 match animation {
512 Some(Animation::Static(anim) | Animation::Transition(anim)) => {
513 visit_expression(anim, scope, state, visitor)
514 }
515 None => (),
516 }
517 }
518
519 pub fn visit_member_reference(
520 member: &mut MemberReference,
521 scope: &EvaluationScope,
522 state: &VisitorState,
523 visitor: &mut (impl Visitor + ?Sized),
524 ) {
525 match member {
526 MemberReference::Relative { parent_level, local_reference } => {
527 let &EvaluationScope::SubComponent(mut sc, _) = scope else { unreachable!() };
528 for _ in 0..*parent_level {
529 sc = state.parent_mapping[sc].unwrap();
530 }
531 let scope = EvaluationScope::SubComponent(sc, None);
532 visit_local_member_reference(local_reference, &scope, state, visitor);
533 }
534 MemberReference::Global { global_index, member } => {
535 let scope = EvaluationScope::Global(*global_index);
536 visit_member_index(member, &scope, state, visitor);
537 }
538 }
539 }
540
541 pub fn visit_local_member_reference(
542 local_reference: &mut LocalMemberReference,
543 scope: &EvaluationScope,
544 state: &VisitorState,
545 visitor: &mut (impl Visitor + ?Sized),
546 ) {
547 let scope = match scope {
548 EvaluationScope::SubComponent(sub_component_idx, _) => EvaluationScope::SubComponent(
549 state
550 .follow_sub_components(*sub_component_idx, &local_reference.sub_component_path),
551 None,
552 ),
553 scope => *scope,
554 };
555 visit_member_index(&mut local_reference.reference, &scope, state, visitor);
556 }
557
558 pub fn visit_member_index(
559 member: &mut LocalMemberIndex,
560 scope: &EvaluationScope,
561 state: &VisitorState,
562 visitor: &mut (impl Visitor + ?Sized),
563 ) {
564 match member {
565 LocalMemberIndex::Property(p) => {
566 visitor.visit_property_idx(p, scope, state);
567 }
568 LocalMemberIndex::Function(f) => {
569 visitor.visit_function_idx(f, scope, state);
570 }
571 LocalMemberIndex::Callback(c) => {
572 visitor.visit_callback_idx(c, scope, state);
573 }
574 LocalMemberIndex::Native { .. } => {}
575 }
576 }
577}