1use std::collections::HashMap;
7use std::collections::HashSet;
8use std::rc::Rc;
9
10use by_address::ByAddress;
11
12use crate::diagnostics::{BuildDiagnostics, Spanned};
13use crate::expression_tree::{BindingExpression, BuiltinFunction, Expression};
14use crate::langtype::ElementType;
15use crate::layout::{LayoutItem, Orientation};
16use crate::namedreference::NamedReference;
17use crate::object_tree::{find_parent_element, Document, ElementRc, PropertyAnimation};
18use derive_more as dm;
19
20use crate::expression_tree::Callable;
21use crate::CompilerConfiguration;
22use smol_str::{SmolStr, ToSmolStr};
23
24type ReverseAliases = HashMap<NamedReference, Vec<NamedReference>>;
28
29pub fn binding_analysis(
30 doc: &Document,
31 compiler_config: &CompilerConfiguration,
32 diag: &mut BuildDiagnostics,
33) {
34 let mut reverse_aliases = Default::default();
35 mark_used_base_properties(doc);
36 propagate_is_set_on_aliases(doc, &mut reverse_aliases);
37 perform_binding_analysis(
38 doc,
39 &reverse_aliases,
40 compiler_config.error_on_binding_loop_with_window_layout,
41 diag,
42 );
43}
44
45#[derive(Hash, PartialEq, Eq, Clone)]
48struct PropertyPath {
49 elements: Vec<ByAddress<ElementRc>>,
50 prop: NamedReference,
51}
52
53impl std::fmt::Debug for PropertyPath {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 for e in &self.elements {
56 write!(f, "{}.", e.borrow().id)?;
57 }
58 self.prop.fmt(f)
59 }
60}
61
62impl PropertyPath {
63 fn relative(&self, second: &PropertyPath) -> Self {
67 let mut element =
68 second.elements.first().map_or_else(|| second.prop.element(), |f| f.0.clone());
69 if element.borrow().enclosing_component.upgrade().unwrap().is_global() {
70 return second.clone();
71 }
72 let mut elements = self.elements.clone();
73 loop {
74 let enclosing = element.borrow().enclosing_component.upgrade().unwrap();
75 if enclosing.parent_element.upgrade().is_some()
76 || !Rc::ptr_eq(&element, &enclosing.root_element)
77 {
78 break;
79 }
80
81 if let Some(last) = elements.pop() {
82 #[cfg(debug_assertions)]
83 fn check_that_element_is_in_the_component(
84 e: &ElementRc,
85 c: &Rc<crate::object_tree::Component>,
86 ) -> bool {
87 let enclosing = e.borrow().enclosing_component.upgrade().unwrap();
88 Rc::ptr_eq(c, &enclosing)
89 || enclosing
90 .parent_element
91 .upgrade()
92 .is_some_and(|e| check_that_element_is_in_the_component(&e, c))
93 }
94 #[cfg(debug_assertions)]
95 debug_assert!(
96 check_that_element_is_in_the_component(
97 &element,
98 last.borrow().base_type.as_component()
99 ),
100 "The element is not in the component pointed at by the path ({self:?} / {second:?})"
101 );
102 element = last.0;
103 } else {
104 break;
105 }
106 }
107 if second.elements.is_empty() {
108 debug_assert!(elements.last().map_or(true, |x| *x != ByAddress(second.prop.element())));
109 Self { elements, prop: NamedReference::new(&element, second.prop.name().clone()) }
110 } else {
111 elements.push(ByAddress(element));
112 elements.extend(second.elements.iter().skip(1).cloned());
113 Self { elements, prop: second.prop.clone() }
114 }
115 }
116}
117
118impl From<NamedReference> for PropertyPath {
119 fn from(prop: NamedReference) -> Self {
120 Self { elements: vec![], prop }
121 }
122}
123
124#[derive(Default)]
125struct AnalysisContext {
126 visited: HashSet<PropertyPath>,
127 currently_analyzing: linked_hash_set::LinkedHashSet<PropertyPath>,
129 window_layout_property: Option<PropertyPath>,
132 error_on_binding_loop_with_window_layout: bool,
133}
134
135fn perform_binding_analysis(
136 doc: &Document,
137 reverse_aliases: &ReverseAliases,
138 error_on_binding_loop_with_window_layout: bool,
139 diag: &mut BuildDiagnostics,
140) {
141 let mut context =
142 AnalysisContext { error_on_binding_loop_with_window_layout, ..Default::default() };
143 doc.visit_all_used_components(|component| {
144 crate::object_tree::recurse_elem_including_sub_components_no_borrow(
145 component,
146 &(),
147 &mut |e, _| analyze_element(e, &mut context, reverse_aliases, diag),
148 )
149 });
150}
151
152fn analyze_element(
153 elem: &ElementRc,
154 context: &mut AnalysisContext,
155 reverse_aliases: &ReverseAliases,
156 diag: &mut BuildDiagnostics,
157) {
158 for (name, binding) in &elem.borrow().bindings {
159 if binding.borrow().analysis.is_some() {
160 continue;
161 }
162 analyze_binding(
163 &PropertyPath::from(NamedReference::new(elem, name.clone())),
164 context,
165 reverse_aliases,
166 diag,
167 );
168 }
169 for cb in elem.borrow().change_callbacks.values() {
170 for e in cb.borrow().iter() {
171 recurse_expression(e, &mut |prop, r| {
172 process_property(prop, r, context, reverse_aliases, diag);
173 });
174 }
175 }
176 const P: ReadType = ReadType::PropertyRead;
177 for nr in elem.borrow().accessibility_props.0.values() {
178 process_property(&PropertyPath::from(nr.clone()), P, context, reverse_aliases, diag);
179 }
180 if let Some(g) = elem.borrow().geometry_props.as_ref() {
181 process_property(&g.x.clone().into(), P, context, reverse_aliases, diag);
182 process_property(&g.y.clone().into(), P, context, reverse_aliases, diag);
183 process_property(&g.width.clone().into(), P, context, reverse_aliases, diag);
184 process_property(&g.height.clone().into(), P, context, reverse_aliases, diag);
185 }
186
187 if let Some(component) = elem.borrow().enclosing_component.upgrade() {
188 if Rc::ptr_eq(&component.root_element, elem) {
189 for e in component.init_code.borrow().iter() {
190 recurse_expression(e, &mut |prop, r| {
191 process_property(prop, r, context, reverse_aliases, diag);
192 });
193 }
194 component.root_constraints.borrow_mut().visit_named_references(&mut |nr| {
195 process_property(&nr.clone().into(), P, context, reverse_aliases, diag);
196 });
197 component.popup_windows.borrow().iter().for_each(|p| {
198 process_property(&p.x.clone().into(), P, context, reverse_aliases, diag);
199 process_property(&p.y.clone().into(), P, context, reverse_aliases, diag);
200 });
201 component.timers.borrow().iter().for_each(|t| {
202 process_property(&t.interval.clone().into(), P, context, reverse_aliases, diag);
203 process_property(&t.running.clone().into(), P, context, reverse_aliases, diag);
204 process_property(&t.triggered.clone().into(), P, context, reverse_aliases, diag);
205 });
206 }
207 }
208
209 if let Some(repeated) = &elem.borrow().repeated {
210 recurse_expression(&repeated.model, &mut |prop, r| {
211 process_property(prop, r, context, reverse_aliases, diag);
212 });
213 if let Some(lv) = &repeated.is_listview {
214 process_property(&lv.viewport_y.clone().into(), P, context, reverse_aliases, diag);
215 process_property(&lv.viewport_height.clone().into(), P, context, reverse_aliases, diag);
216 process_property(&lv.viewport_width.clone().into(), P, context, reverse_aliases, diag);
217 process_property(&lv.listview_height.clone().into(), P, context, reverse_aliases, diag);
218 process_property(&lv.listview_width.clone().into(), P, context, reverse_aliases, diag);
219 }
220 }
221 if let Some((h, v)) = &elem.borrow().layout_info_prop {
222 process_property(&h.clone().into(), P, context, reverse_aliases, diag);
223 process_property(&v.clone().into(), P, context, reverse_aliases, diag);
224 }
225}
226
227#[derive(Copy, Clone, dm::BitAnd, dm::BitOr, dm::BitAndAssign, dm::BitOrAssign)]
228struct DependsOnExternal(bool);
229
230fn analyze_binding(
231 current: &PropertyPath,
232 context: &mut AnalysisContext,
233 reverse_aliases: &ReverseAliases,
234 diag: &mut BuildDiagnostics,
235) -> DependsOnExternal {
236 let mut depends_on_external = DependsOnExternal(false);
237 let element = current.prop.element();
238 let name = current.prop.name();
239 if (context.currently_analyzing.back() == Some(current))
240 && !element.borrow().bindings[name].borrow().two_way_bindings.is_empty()
241 {
242 let span = element.borrow().bindings[name]
243 .borrow()
244 .span
245 .clone()
246 .unwrap_or_else(|| element.borrow().to_source_location());
247 diag.push_error(format!("Property '{name}' cannot refer to itself"), &span);
248 return depends_on_external;
249 }
250
251 if context.currently_analyzing.contains(current) {
252 let mut loop_description = String::new();
253 let mut has_window_layout = false;
254 for it in context.currently_analyzing.iter().rev() {
255 if context.window_layout_property.as_ref().is_some_and(|p| p == it) {
256 has_window_layout = true;
257 }
258 if !loop_description.is_empty() {
259 loop_description.push_str(" -> ");
260 }
261 match it.prop.element().borrow().id.as_str() {
262 "" => loop_description.push_str(it.prop.name()),
263 id => {
264 loop_description.push_str(id);
265 loop_description.push_str(".");
266 loop_description.push_str(it.prop.name());
267 }
268 }
269 if it == current {
270 break;
271 }
272 }
273
274 for it in context.currently_analyzing.iter().rev() {
275 let p = &it.prop;
276 let elem = p.element();
277 let elem = elem.borrow();
278 let binding = elem.bindings[p.name()].borrow();
279 if binding.analysis.as_ref().unwrap().is_in_binding_loop.replace(true) {
280 break;
281 }
282
283 let span = binding.span.clone().unwrap_or_else(|| elem.to_source_location());
284 if !context.error_on_binding_loop_with_window_layout && has_window_layout {
285 diag.push_warning(format!("The binding for the property '{}' is part of a binding loop ({loop_description}).\nThis was allowed in previous version of Slint, but is deprecated and may cause panic at runtime", p.name()), &span);
286 } else {
287 diag.push_error(format!("The binding for the property '{}' is part of a binding loop ({loop_description})", p.name()), &span);
288 }
289 if it == current {
290 break;
291 }
292 }
293 return depends_on_external;
294 }
295
296 let binding = &element.borrow().bindings[name];
297 if binding.borrow().analysis.as_ref().is_some_and(|a| a.no_external_dependencies) {
298 return depends_on_external;
299 } else if !context.visited.insert(current.clone()) {
300 return DependsOnExternal(true);
301 }
302
303 if let Ok(mut b) = binding.try_borrow_mut() {
304 b.analysis = Some(Default::default());
305 };
306 context.currently_analyzing.insert(current.clone());
307
308 let b = binding.borrow();
309 for nr in &b.two_way_bindings {
310 if nr != ¤t.prop {
311 depends_on_external |= process_property(
312 ¤t.relative(&nr.clone().into()),
313 ReadType::PropertyRead,
314 context,
315 reverse_aliases,
316 diag,
317 );
318 }
319 }
320
321 let mut process_prop = |prop: &PropertyPath, r| {
322 depends_on_external |=
323 process_property(¤t.relative(prop), r, context, reverse_aliases, diag);
324 for x in reverse_aliases.get(&prop.prop).unwrap_or(&Default::default()) {
325 if x != ¤t.prop && x != &prop.prop {
326 depends_on_external |= process_property(
327 ¤t.relative(&x.clone().into()),
328 ReadType::PropertyRead,
329 context,
330 reverse_aliases,
331 diag,
332 );
333 }
334 }
335 };
336
337 recurse_expression(&b.expression, &mut process_prop);
338
339 let mut is_const =
340 b.expression.is_constant() && b.two_way_bindings.iter().all(|n| n.is_constant());
341
342 if is_const && matches!(b.expression, Expression::Invalid) {
343 if let Some(base) = element.borrow().sub_component() {
345 is_const = NamedReference::new(&base.root_element, name.clone()).is_constant();
346 }
347 }
348 drop(b);
349
350 if let Ok(mut b) = binding.try_borrow_mut() {
351 b.analysis.as_mut().unwrap().is_const = is_const;
353 }
354
355 match &binding.borrow().animation {
356 Some(PropertyAnimation::Static(e)) => analyze_element(e, context, reverse_aliases, diag),
357 Some(PropertyAnimation::Transition { animations, state_ref }) => {
358 recurse_expression(state_ref, &mut process_prop);
359 for a in animations {
360 analyze_element(&a.animation, context, reverse_aliases, diag);
361 }
362 }
363 None => (),
364 }
365
366 let o = context.currently_analyzing.pop_back();
367 assert_eq!(&o.unwrap(), current);
368
369 depends_on_external
370}
371
372#[derive(Copy, Clone, Eq, PartialEq)]
373enum ReadType {
374 NativeRead,
376 PropertyRead,
378}
379
380fn process_property(
384 prop: &PropertyPath,
385 read_type: ReadType,
386 context: &mut AnalysisContext,
387 reverse_aliases: &ReverseAliases,
388 diag: &mut BuildDiagnostics,
389) -> DependsOnExternal {
390 let depends_on_external = match prop
391 .prop
392 .element()
393 .borrow()
394 .property_analysis
395 .borrow_mut()
396 .entry(prop.prop.name().clone())
397 .or_default()
398 {
399 a => {
400 if read_type == ReadType::PropertyRead {
401 a.is_read = true;
402 }
403 DependsOnExternal(prop.elements.is_empty() && a.is_set_externally)
404 }
405 };
406
407 let mut prop = prop.clone();
408
409 loop {
410 let element = prop.prop.element();
411 if element.borrow().bindings.contains_key(prop.prop.name()) {
412 analyze_binding(&prop, context, reverse_aliases, diag);
413 }
414 let next = match &element.borrow().base_type {
415 ElementType::Component(base) => {
416 if element.borrow().property_declarations.contains_key(prop.prop.name()) {
417 break;
418 }
419 base.root_element.clone()
420 }
421 ElementType::Builtin(builtin) => {
422 if builtin.properties.contains_key(prop.prop.name()) {
423 visit_builtin_property(builtin, &prop, context, reverse_aliases, diag);
424 }
425 break;
426 }
427 _ => break,
428 };
429 next.borrow()
430 .property_analysis
431 .borrow_mut()
432 .entry(prop.prop.name().clone())
433 .or_default()
434 .is_read_externally = true;
435 prop.elements.push(element.into());
436 prop.prop = NamedReference::new(&next, prop.prop.name().clone());
437 }
438 depends_on_external
439}
440
441fn recurse_expression(expr: &Expression, vis: &mut impl FnMut(&PropertyPath, ReadType)) {
443 const P: ReadType = ReadType::PropertyRead;
444 expr.visit(|sub| recurse_expression(sub, vis));
445 match expr {
446 Expression::PropertyReference(r) => vis(&r.clone().into(), P),
447 Expression::LayoutCacheAccess { layout_cache_prop, .. } => {
448 vis(&layout_cache_prop.clone().into(), P)
449 }
450 Expression::SolveLayout(l, o) | Expression::ComputeLayoutInfo(l, o) => {
451 if matches!(expr, Expression::SolveLayout(..)) {
453 if let Some(nr) = l.rect().size_reference(*o) {
454 vis(&nr.clone().into(), P);
455 }
456 }
457 match l {
458 crate::layout::Layout::GridLayout(l) => {
459 visit_layout_items_dependencies(l.elems.iter().map(|it| &it.item), *o, vis)
460 }
461 crate::layout::Layout::BoxLayout(l) => {
462 visit_layout_items_dependencies(l.elems.iter(), *o, vis)
463 }
464 }
465
466 let mut g = l.geometry().clone();
467 g.rect = Default::default(); g.visit_named_references(&mut |nr| vis(&nr.clone().into(), P))
469 }
470 Expression::FunctionCall {
471 function: Callable::Callback(nr) | Callable::Function(nr),
472 ..
473 } => vis(&nr.clone().into(), P),
474 Expression::FunctionCall { function: Callable::Builtin(b), arguments, .. } => match b {
475 BuiltinFunction::ImplicitLayoutInfo(orientation) => {
476 if let [Expression::ElementReference(item)] = arguments.as_slice() {
477 visit_implicit_layout_info_dependencies(
478 *orientation,
479 &item.upgrade().unwrap(),
480 vis,
481 );
482 }
483 }
484 BuiltinFunction::ItemAbsolutePosition => {
485 if let Some(Expression::ElementReference(item)) = arguments.first() {
486 let mut item = item.upgrade().unwrap();
487 while let Some(parent) = find_parent_element(&item) {
488 item = parent;
489 vis(
490 &NamedReference::new(&item, SmolStr::new_static("x")).into(),
491 ReadType::NativeRead,
492 );
493 vis(
494 &NamedReference::new(&item, SmolStr::new_static("y")).into(),
495 ReadType::NativeRead,
496 );
497 }
498 }
499 }
500 BuiltinFunction::ItemFontMetrics => {
501 if let Some(Expression::ElementReference(item)) = arguments.first() {
502 let item = item.upgrade().unwrap();
503 vis(
504 &NamedReference::new(&item, SmolStr::new_static("font-size")).into(),
505 ReadType::NativeRead,
506 );
507 vis(
508 &NamedReference::new(&item, SmolStr::new_static("font-weight")).into(),
509 ReadType::NativeRead,
510 );
511 vis(
512 &NamedReference::new(&item, SmolStr::new_static("font-family")).into(),
513 ReadType::NativeRead,
514 );
515 vis(
516 &NamedReference::new(&item, SmolStr::new_static("font-italic")).into(),
517 ReadType::NativeRead,
518 );
519 }
520 }
521 _ => {}
522 },
523 _ => {}
524 }
525}
526
527fn visit_layout_items_dependencies<'a>(
528 items: impl Iterator<Item = &'a LayoutItem>,
529 orientation: Orientation,
530 vis: &mut impl FnMut(&PropertyPath, ReadType),
531) {
532 for it in items {
533 let mut element = it.element.clone();
534 if element.borrow().repeated.as_ref().map(|r| recurse_expression(&r.model, vis)).is_some() {
535 element = it.element.borrow().base_type.as_component().root_element.clone();
536 }
537
538 if let Some(nr) = element.borrow().layout_info_prop(orientation) {
539 vis(&nr.clone().into(), ReadType::PropertyRead);
540 } else {
541 if let ElementType::Component(base) = &element.borrow().base_type {
542 if let Some(nr) = base.root_element.borrow().layout_info_prop(orientation) {
543 vis(
544 &PropertyPath {
545 elements: vec![ByAddress(element.clone())],
546 prop: nr.clone(),
547 },
548 ReadType::PropertyRead,
549 );
550 }
551 }
552 visit_implicit_layout_info_dependencies(orientation, &element, vis);
553 }
554
555 for (nr, _) in it.constraints.for_each_restrictions(orientation) {
556 vis(&nr.clone().into(), ReadType::PropertyRead)
557 }
558 }
559}
560
561fn visit_implicit_layout_info_dependencies(
563 orientation: crate::layout::Orientation,
564 item: &ElementRc,
565 vis: &mut impl FnMut(&PropertyPath, ReadType),
566) {
567 let base_type = item.borrow().base_type.to_smolstr();
568 const N: ReadType = ReadType::NativeRead;
569 match base_type.as_str() {
570 "Image" => {
571 vis(&NamedReference::new(item, SmolStr::new_static("source")).into(), N);
572 if orientation == Orientation::Vertical {
573 vis(&NamedReference::new(item, SmolStr::new_static("width")).into(), N);
574 }
575 }
576 "Text" | "TextInput" => {
577 vis(&NamedReference::new(item, SmolStr::new_static("text")).into(), N);
578 vis(&NamedReference::new(item, SmolStr::new_static("font-family")).into(), N);
579 vis(&NamedReference::new(item, SmolStr::new_static("font-size")).into(), N);
580 vis(&NamedReference::new(item, SmolStr::new_static("font-weight")).into(), N);
581 vis(&NamedReference::new(item, SmolStr::new_static("letter-spacing")).into(), N);
582 vis(&NamedReference::new(item, SmolStr::new_static("wrap")).into(), N);
583 let wrap_set = item.borrow().is_binding_set("wrap", false)
584 || item
585 .borrow()
586 .property_analysis
587 .borrow()
588 .get("wrap")
589 .is_some_and(|a| a.is_set || a.is_set_externally);
590 if wrap_set && orientation == Orientation::Vertical {
591 vis(&NamedReference::new(item, SmolStr::new_static("width")).into(), N);
592 }
593 if base_type.as_str() == "TextInput" {
594 vis(&NamedReference::new(item, SmolStr::new_static("single-line")).into(), N);
595 } else {
596 vis(&NamedReference::new(item, SmolStr::new_static("overflow")).into(), N);
597 }
598 }
599
600 _ => (),
601 }
602}
603
604fn visit_builtin_property(
605 builtin: &crate::langtype::BuiltinElement,
606 prop: &PropertyPath,
607 context: &mut AnalysisContext,
608 reverse_aliases: &ReverseAliases,
609 diag: &mut BuildDiagnostics,
610) {
611 let name = prop.prop.name();
612 if builtin.name == "Window" {
613 for (p, orientation) in
614 [("width", Orientation::Horizontal), ("height", Orientation::Vertical)]
615 {
616 if name == p {
617 let is_root = |e: &ElementRc| -> bool {
619 ElementRc::ptr_eq(
620 e,
621 &e.borrow().enclosing_component.upgrade().unwrap().root_element,
622 )
623 };
624 let mut root = prop.prop.element();
625 if !is_root(&root) {
626 return;
627 };
628 for e in prop.elements.iter().rev() {
629 if !is_root(&e.0) {
630 return;
631 }
632 root = e.0.clone();
633 }
634 if let Some(p) = root.borrow().layout_info_prop(orientation) {
635 let path = PropertyPath::from(p.clone());
636 let old_layout = context.window_layout_property.replace(path.clone());
637 process_property(&path, ReadType::NativeRead, context, reverse_aliases, diag);
638 context.window_layout_property = old_layout;
639 };
640 }
641 }
642 }
643}
644
645fn propagate_is_set_on_aliases(doc: &Document, reverse_aliases: &mut ReverseAliases) {
657 doc.visit_all_used_components(|component| {
658 crate::object_tree::recurse_elem_including_sub_components_no_borrow(
659 component,
660 &(),
661 &mut |e, _| visit_element(e, reverse_aliases),
662 );
663 });
664
665 fn visit_element(e: &ElementRc, reverse_aliases: &mut ReverseAliases) {
666 for (name, binding) in &e.borrow().bindings {
667 if !binding.borrow().two_way_bindings.is_empty() {
668 check_alias(e, name, &binding.borrow());
669
670 let nr = NamedReference::new(e, name.clone());
671 for a in &binding.borrow().two_way_bindings {
672 if a != &nr
673 && !a.element().borrow().enclosing_component.upgrade().unwrap().is_global()
674 {
675 reverse_aliases.entry(a.clone()).or_default().push(nr.clone())
676 }
677 }
678 }
679 }
680 for decl in e.borrow().property_declarations.values() {
681 if let Some(alias) = &decl.is_alias {
682 mark_alias(alias)
683 }
684 }
685 }
686
687 fn check_alias(e: &ElementRc, name: &SmolStr, binding: &BindingExpression) {
688 let is_binding_constant =
690 binding.is_constant() && binding.two_way_bindings.iter().all(|n| n.is_constant());
691 if is_binding_constant && !NamedReference::new(e, name.clone()).is_externally_modified() {
692 for alias in &binding.two_way_bindings {
693 crate::namedreference::mark_property_set_derived_in_base(
694 alias.element(),
695 alias.name(),
696 );
697 }
698 return;
699 }
700
701 propagate_alias(binding);
702 }
703
704 fn propagate_alias(binding: &BindingExpression) {
705 for alias in &binding.two_way_bindings {
706 mark_alias(alias);
707 }
708 }
709
710 fn mark_alias(alias: &NamedReference) {
711 alias.mark_as_set();
712 if !alias.is_externally_modified() {
713 if let Some(bind) = alias.element().borrow().bindings.get(alias.name()) {
714 propagate_alias(&bind.borrow())
715 }
716 }
717 }
718}
719
720fn mark_used_base_properties(doc: &Document) {
723 doc.visit_all_used_components(|component| {
724 crate::object_tree::recurse_elem_including_sub_components_no_borrow(
725 component,
726 &(),
727 &mut |element, _| {
728 if !matches!(element.borrow().base_type, ElementType::Component(_)) {
729 return;
730 }
731 for (name, binding) in &element.borrow().bindings {
732 if binding.borrow().has_binding() {
733 crate::namedreference::mark_property_set_derived_in_base(
734 element.clone(),
735 name,
736 );
737 }
738 }
739 for name in element.borrow().change_callbacks.keys() {
740 crate::namedreference::mark_property_read_derived_in_base(
741 element.clone(),
742 name,
743 );
744 }
745 },
746 );
747 });
748}