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 flexbox_layout_item_info_for_repeated,
302 is_repeated_row: _,
303 grid_layout_children,
304 accessible_prop,
305 element_infos: _,
306 row_child_templates: _,
307 prop_analysis,
308 }: &mut SubComponent,
309 state: &VisitorState,
310 visitor: &mut (impl Visitor + ?Sized),
311 ) {
312 let scope = EvaluationScope::SubComponent(idx, None);
313 for f in functions {
314 visit_function(f, &scope, state, visitor);
315 }
316 for RepeatedElement {
317 model,
318 index_prop,
319 data_prop,
320 sub_tree,
321 index_in_tree: _,
322 listview,
323 container_item_index: _,
324 } in repeated
325 {
326 visit_expression(model.get_mut(), &scope, state, visitor);
327 let inner_scope = EvaluationScope::SubComponent(sub_tree.root, None);
328 if let Some(index_prop) = index_prop {
329 visitor.visit_property_idx(index_prop, &inner_scope, state);
330 }
331 if let Some(data_prop) = data_prop {
332 visitor.visit_property_idx(data_prop, &inner_scope, state);
333 }
334
335 if let Some(listview) = listview {
336 visit_member_reference(&mut listview.viewport_y, &scope, state, visitor);
337 visit_member_reference(&mut listview.viewport_height, &scope, state, visitor);
338 visit_member_reference(&mut listview.viewport_width, &scope, state, visitor);
339 visit_member_reference(&mut listview.listview_width, &scope, state, visitor);
340 visit_member_reference(&mut listview.listview_height, &scope, state, visitor);
341
342 visit_member_reference(&mut listview.prop_y, &inner_scope, state, visitor);
343 visit_member_reference(&mut listview.prop_height, &inner_scope, state, visitor);
344 }
345 }
346
347 for p in popup_windows {
348 let popup_scope = EvaluationScope::SubComponent(p.item_tree.root, None);
349 visit_expression(p.position.get_mut(), &popup_scope, state, visitor);
350 }
351 for t in timers {
352 visit_expression(t.interval.get_mut(), &scope, state, visitor);
353 visit_expression(t.triggered.get_mut(), &scope, state, visitor);
354 visit_expression(t.running.get_mut(), &scope, state, visitor);
355 }
356 for (idx, init) in property_init {
357 visit_member_reference(idx, &scope, state, visitor);
358 visit_binding_expression(init, &scope, state, visitor);
359 }
360 for (idx, e) in change_callbacks {
361 visit_member_reference(idx, &scope, state, visitor);
362 visit_expression(e.get_mut(), &scope, state, visitor);
363 }
364 *animations = std::mem::take(animations)
365 .into_iter()
366 .map(|(mut k, mut v)| {
367 visit_local_member_reference(&mut k, &scope, state, visitor);
368 visit_expression(&mut v, &scope, state, visitor);
369 (k, v)
370 })
371 .collect();
372
373 for (a, b, _) in two_way_bindings {
374 visit_member_reference(a, &scope, state, visitor);
375 visit_member_reference(b, &scope, state, visitor);
376 }
377 for c in const_properties {
378 visit_local_member_reference(c, &scope, state, visitor);
379 }
380 for i in init_code {
381 visit_expression(i.get_mut(), &scope, state, visitor);
382 }
383 for g in geometries.iter_mut().flatten() {
384 visit_expression(g.get_mut(), &scope, state, visitor);
385 }
386 visit_expression(layout_info_h.get_mut(), &scope, state, visitor);
387 visit_expression(layout_info_v.get_mut(), &scope, state, visitor);
388 if let Some(e) = grid_layout_input_for_repeated {
389 visit_expression(e.get_mut(), &scope, state, visitor);
390 }
391 if let Some(e) = flexbox_layout_item_info_for_repeated {
392 visit_expression(e.get_mut(), &scope, state, visitor);
393 }
394 for child in grid_layout_children {
395 visit_expression(child.layout_info_h.get_mut(), &scope, state, visitor);
396 visit_expression(child.layout_info_v.get_mut(), &scope, state, visitor);
397 }
398
399 for a in accessible_prop.values_mut() {
400 visit_expression(a.get_mut(), &scope, state, visitor);
401 }
402
403 *prop_analysis = std::mem::take(prop_analysis)
404 .into_iter()
405 .map(|(mut k, v)| {
406 visit_member_reference(&mut k, &scope, state, visitor);
407 (k, v)
408 })
409 .collect();
410 }
411
412 fn visit_global(
413 global_idx: GlobalIdx,
414 GlobalComponent {
415 name: _,
416 properties: _,
417 callbacks: _,
418 functions,
419 init_values,
420 change_callbacks,
421 const_properties: _,
422 public_properties,
423 private_properties: _,
424 exported: _,
425 aliases: _,
426 is_builtin: _,
427 from_library: _,
428 prop_analysis: _,
429 }: &mut GlobalComponent,
430 state: &VisitorState,
431 visitor: &mut (impl Visitor + ?Sized),
432 ) {
433 let scope = EvaluationScope::Global(global_idx);
434 for f in functions {
435 visit_function(f, &scope, state, visitor);
436 }
437
438 *init_values = std::mem::take(init_values)
439 .into_iter()
440 .map(|(mut k, mut v)| {
441 visit_member_index(&mut k, &scope, state, visitor);
442 visit_binding_expression(&mut v, &scope, state, visitor);
443 (k, v)
444 })
445 .collect();
446
447 *change_callbacks = std::mem::take(change_callbacks)
448 .into_iter()
449 .map(|(mut k, mut v)| {
450 visitor.visit_property_idx(&mut k, &scope, state);
451 visit_expression(v.get_mut(), &scope, state, visitor);
452 (k, v)
453 })
454 .collect();
455
456 for p in public_properties {
457 visit_public_property(p, &scope, state, visitor);
458 }
459 }
460
461 pub fn visit_popup_menu(
462 PopupMenu { item_tree, sub_menu, activated, close, entries }: &mut PopupMenu,
463 state: &VisitorState,
464 visitor: &mut (impl Visitor + ?Sized),
465 ) {
466 let scope = EvaluationScope::SubComponent(item_tree.root, None);
467 visit_member_reference(sub_menu, &scope, state, visitor);
468 visit_member_reference(activated, &scope, state, visitor);
469 visit_member_reference(close, &scope, state, visitor);
470 visit_member_reference(entries, &scope, state, visitor);
471 }
472
473 pub fn visit_public_property(
474 PublicProperty { name: _, ty: _, prop, read_only: _ }: &mut PublicProperty,
475 scope: &EvaluationScope,
476 state: &VisitorState,
477 visitor: &mut (impl Visitor + ?Sized),
478 ) {
479 visit_member_reference(prop, scope, state, visitor);
480 }
481
482 pub fn visit_function(
483 Function { name: _, ret_ty: _, args: _, code }: &mut Function,
484 scope: &EvaluationScope,
485 state: &VisitorState,
486 visitor: &mut (impl Visitor + ?Sized),
487 ) {
488 visit_expression(code, scope, state, visitor);
489 }
490
491 pub fn visit_expression(
492 expr: &mut Expression,
493 scope: &EvaluationScope,
494 state: &VisitorState,
495 visitor: &mut (impl Visitor + ?Sized),
496 ) {
497 expr.visit_recursive_mut(&mut |expr| {
498 let p = match expr {
499 Expression::PropertyReference(p) => p,
500 Expression::CallBackCall { callback, .. } => callback,
501 Expression::PropertyAssignment { property, .. } => property,
502 Expression::LayoutCacheAccess { layout_cache_prop, .. } => layout_cache_prop,
503 Expression::GridRepeaterCacheAccess { layout_cache_prop, .. } => layout_cache_prop,
504 _ => return,
505 };
506 visit_member_reference(p, scope, state, visitor);
507 });
508 }
509
510 pub fn visit_binding_expression(
511 BindingExpression { expression, animation, is_constant: _, is_state_info: _, use_count: _ }: &mut BindingExpression,
512 scope: &EvaluationScope,
513 state: &VisitorState,
514 visitor: &mut (impl Visitor + ?Sized),
515 ) {
516 visit_expression(expression.get_mut(), scope, state, visitor);
517 match animation {
518 Some(Animation::Static(anim) | Animation::Transition(anim)) => {
519 visit_expression(anim, scope, state, visitor)
520 }
521 None => (),
522 }
523 }
524
525 pub fn visit_member_reference(
526 member: &mut MemberReference,
527 scope: &EvaluationScope,
528 state: &VisitorState,
529 visitor: &mut (impl Visitor + ?Sized),
530 ) {
531 match member {
532 MemberReference::Relative { parent_level, local_reference } => {
533 let &EvaluationScope::SubComponent(mut sc, _) = scope else { unreachable!() };
534 for _ in 0..*parent_level {
535 sc = state.parent_mapping[sc].unwrap();
536 }
537 let scope = EvaluationScope::SubComponent(sc, None);
538 visit_local_member_reference(local_reference, &scope, state, visitor);
539 }
540 MemberReference::Global { global_index, member } => {
541 let scope = EvaluationScope::Global(*global_index);
542 visit_member_index(member, &scope, state, visitor);
543 }
544 }
545 }
546
547 pub fn visit_local_member_reference(
548 local_reference: &mut LocalMemberReference,
549 scope: &EvaluationScope,
550 state: &VisitorState,
551 visitor: &mut (impl Visitor + ?Sized),
552 ) {
553 let scope = match scope {
554 EvaluationScope::SubComponent(sub_component_idx, _) => EvaluationScope::SubComponent(
555 state
556 .follow_sub_components(*sub_component_idx, &local_reference.sub_component_path),
557 None,
558 ),
559 scope => *scope,
560 };
561 visit_member_index(&mut local_reference.reference, &scope, state, visitor);
562 }
563
564 pub fn visit_member_index(
565 member: &mut LocalMemberIndex,
566 scope: &EvaluationScope,
567 state: &VisitorState,
568 visitor: &mut (impl Visitor + ?Sized),
569 ) {
570 match member {
571 LocalMemberIndex::Property(p) => {
572 visitor.visit_property_idx(p, scope, state);
573 }
574 LocalMemberIndex::Function(f) => {
575 visitor.visit_function_idx(f, scope, state);
576 }
577 LocalMemberIndex::Callback(c) => {
578 visitor.visit_callback_idx(c, scope, state);
579 }
580 LocalMemberIndex::Native { .. } => {}
581 }
582 }
583}