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