1use by_address::ByAddress;
5
6use super::lower_expression::{ExpressionLoweringCtx, ExpressionLoweringCtxInner};
7use crate::CompilerConfiguration;
8use crate::expression_tree::Expression as tree_Expression;
9use crate::langtype::{
10 BuiltinPrivateStruct, BuiltinPublicStruct, ElementType, Struct, StructName, Type,
11};
12use crate::llr::item_tree::*;
13use crate::namedreference::NamedReference;
14use crate::object_tree::{self, Component, ElementRc, PropertyAnalysis, PropertyVisibility};
15use smol_str::{SmolStr, format_smolstr};
16use std::collections::{BTreeMap, HashMap};
17use std::rc::Rc;
18use typed_index_collections::TiVec;
19
20pub fn lower_to_item_tree(
21 document: &crate::object_tree::Document,
22 compiler_config: &CompilerConfiguration,
23) -> CompilationUnit {
24 let mut state = LoweringState::default();
25
26 #[cfg(feature = "bundle-translations")]
27 {
28 state.translation_builder = document.translation_builder.clone();
29 }
30
31 let mut globals = TiVec::new();
32 for g in &document.used_types.borrow().globals {
33 let count = globals.next_key();
34 globals.push(lower_global(g, count, &mut state));
35 }
36 for (g, l) in document.used_types.borrow().globals.iter().zip(&mut globals) {
37 lower_global_expressions(g, &mut state, l);
38 }
39
40 for c in &document.used_types.borrow().sub_components {
41 let sc = lower_sub_component(c, &mut state, None, compiler_config);
42 let idx = state.push_sub_component(sc);
43 state.sub_component_mapping.insert(ByAddress(c.clone()), idx);
44 }
45
46 let public_components = document
47 .exported_roots()
48 .map(|component| {
49 let mut sc = lower_sub_component(&component, &mut state, None, compiler_config);
50 let public_properties = public_properties(&component, &sc.mapping, &state);
51 sc.sub_component.name = component.id.clone();
52 let item_tree = ItemTree {
53 tree: make_tree(&state, &component.root_element, &sc, &[]),
54 root: state.push_sub_component(sc),
55 };
56 PublicComponent {
58 item_tree,
59 public_properties,
60 private_properties: component.private_properties.borrow().clone(),
61 name: component.id.clone(),
62 }
63 })
64 .collect();
65
66 let popup_menu = document.popup_menu_impl.as_ref().map(|c| {
67 let sc = lower_sub_component(c, &mut state, None, compiler_config);
68 let sub_menu = sc.mapping.map_property_reference(
69 &NamedReference::new(&c.root_element, SmolStr::new_static("sub-menu")),
70 &state,
71 );
72 let activated = sc.mapping.map_property_reference(
73 &NamedReference::new(&c.root_element, SmolStr::new_static("activated")),
74 &state,
75 );
76 let close = sc.mapping.map_property_reference(
77 &NamedReference::new(&c.root_element, SmolStr::new_static("close")),
78 &state,
79 );
80 let entries = sc.mapping.map_property_reference(
81 &NamedReference::new(&c.root_element, SmolStr::new_static("entries")),
82 &state,
83 );
84 let item_tree = ItemTree {
85 tree: make_tree(&state, &c.root_element, &sc, &[]),
86 root: state.push_sub_component(sc),
87 };
88 PopupMenu { item_tree, sub_menu, activated, close, entries }
89 });
90
91 let mut root = CompilationUnit {
92 public_components,
93 globals,
94 sub_components: state.sub_components.into_iter().map(|sc| sc.sub_component).collect(),
95 used_sub_components: document
96 .used_types
97 .borrow()
98 .sub_components
99 .iter()
100 .map(|tree_sub_compo| state.sub_component_mapping[&ByAddress(tree_sub_compo.clone())])
101 .collect(),
102 has_debug_info: compiler_config.debug_info,
103 popup_menu,
104 #[cfg(feature = "bundle-translations")]
105 translations: state.translation_builder.map(|x| x.result()),
106 };
107 super::optim_passes::run_passes(&mut root);
108 root
109}
110
111#[derive(Debug, Clone)]
112pub enum LoweredElement {
113 SubComponent { sub_component_index: SubComponentInstanceIdx },
114 NativeItem { item_index: ItemInstanceIdx },
115 Repeated { repeated_index: RepeatedElementIdx },
116 ComponentPlaceholder { repeated_index: u32 },
117}
118
119#[derive(Default, Debug, Clone)]
120pub struct LoweredSubComponentMapping {
121 pub element_mapping: HashMap<ByAddress<ElementRc>, LoweredElement>,
122 pub property_mapping: HashMap<NamedReference, MemberReference>,
123 pub repeater_count: u32,
124 pub container_count: u32,
125}
126
127impl LoweredSubComponentMapping {
128 pub fn map_property_reference(
129 &self,
130 from: &NamedReference,
131 state: &LoweringState,
132 ) -> MemberReference {
133 if let Some(x) = self.property_mapping.get(from) {
134 return x.clone();
135 }
136 if let Some(x) = state.global_properties.get(from) {
137 return x.clone();
138 }
139 let element = from.element();
140 if let Some(alias) = element
141 .borrow()
142 .property_declarations
143 .get(from.name())
144 .and_then(|x| x.is_alias.as_ref())
145 {
146 return self.map_property_reference(alias, state);
147 }
148 match self.element_mapping.get(&element.clone().into()).unwrap() {
149 LoweredElement::SubComponent { sub_component_index } => {
150 if let ElementType::Component(base) = &element.borrow().base_type {
151 let mut prop_ref = state.map_property_reference(&NamedReference::new(
152 &base.root_element,
153 from.name().clone(),
154 ));
155 if let MemberReference::Relative { parent_level, local_reference } =
156 &mut prop_ref
157 {
158 assert_eq!(*parent_level, 0, "the sub-component had no parents");
159 local_reference.sub_component_path.insert(0, *sub_component_index);
160 }
161 return prop_ref;
162 }
163 unreachable!()
164 }
165 LoweredElement::NativeItem { item_index } => MemberReference::Relative {
166 parent_level: 0,
167 local_reference: LocalMemberReference {
168 sub_component_path: Vec::new(),
169 reference: LocalMemberIndex::Native {
170 item_index: *item_index,
171 prop_name: from.name().clone(),
172 },
173 },
174 },
175 LoweredElement::Repeated { .. } => {
176 panic!(
177 "Trying to map property {from:?} on a repeated element {} of type {:?}",
178 element.borrow().id,
179 element.borrow().base_type
180 );
181 }
182 LoweredElement::ComponentPlaceholder { .. } => unreachable!(),
183 }
184 }
185}
186
187pub struct LoweredSubComponent {
188 sub_component: SubComponent,
189 mapping: LoweredSubComponentMapping,
190}
191
192#[derive(Default)]
193pub struct LoweringState {
194 global_properties: HashMap<NamedReference, MemberReference>,
195 sub_components: TiVec<SubComponentIdx, LoweredSubComponent>,
196 sub_component_mapping: HashMap<ByAddress<Rc<Component>>, SubComponentIdx>,
197 #[cfg(feature = "bundle-translations")]
198 pub translation_builder: Option<crate::translations::TranslationsBuilder>,
199}
200
201impl LoweringState {
202 pub fn map_property_reference(&self, from: &NamedReference) -> MemberReference {
203 if let Some(x) = self.global_properties.get(from) {
204 return x.clone();
205 }
206
207 let element = from.element();
208 let sc = self.sub_component(&element.borrow().enclosing_component.upgrade().unwrap());
209 sc.mapping.map_property_reference(from, self)
210 }
211
212 fn sub_component<'a>(&'a self, component: &Rc<Component>) -> &'a LoweredSubComponent {
213 &self.sub_components[self.sub_component_idx(component)]
214 }
215
216 pub fn row_child_templates(
220 &self,
221 component: &Rc<Component>,
222 ) -> Option<Vec<super::RowChildTemplateInfo>> {
223 self.sub_components[self.sub_component_idx(component)]
224 .sub_component
225 .row_child_templates
226 .clone()
227 }
228
229 fn sub_component_idx(&self, component: &Rc<Component>) -> SubComponentIdx {
230 *self.sub_component_mapping.get(&ByAddress(component.clone())).unwrap_or_else(|| {
231 debug_assert!(
232 false,
233 "no entry found for key: component id='{}', available keys: {:?}",
234 component.id,
235 self.sub_component_mapping.keys().map(|k| k.0.id.clone()).collect::<Vec<_>>()
236 );
237 unreachable!(
238 "component must be registered before querying sub_component_idx: '{}'",
239 component.id
240 )
241 })
242 }
243
244 fn push_sub_component(&mut self, sc: LoweredSubComponent) -> SubComponentIdx {
245 self.sub_components.push_and_get_key(sc)
246 }
247}
248
249fn component_id(component: &Rc<Component>) -> SmolStr {
250 if component.is_global() {
251 component.root_element.borrow().id.clone()
252 } else if component.from_library.get() {
253 component.id.clone()
254 } else if component.id.is_empty() {
255 format_smolstr!("Component_{}", component.root_element.borrow().id)
256 } else {
257 format_smolstr!("{}_{}", component.id, component.root_element.borrow().id)
258 }
259}
260
261fn lower_sub_component(
262 component: &Rc<Component>,
263 state: &mut LoweringState,
264 parent_context: Option<&ExpressionLoweringCtxInner>,
265 compiler_config: &CompilerConfiguration,
266) -> LoweredSubComponent {
267 let mut sub_component = SubComponent {
268 name: component_id(component),
269 properties: Default::default(),
270 callbacks: Default::default(),
271 functions: Default::default(),
272 items: Default::default(),
273 repeated: Default::default(),
274 component_containers: Default::default(),
275 popup_windows: Default::default(),
276 menu_item_trees: Vec::new(),
277 timers: Default::default(),
278 sub_components: Default::default(),
279 property_init: Default::default(),
280 change_callbacks: Default::default(),
281 animations: Default::default(),
282 two_way_bindings: Default::default(),
283 const_properties: Default::default(),
284 init_code: Default::default(),
285 geometries: Default::default(),
286 layout_info_h: super::Expression::BoolLiteral(false).into(),
288 layout_info_v: super::Expression::BoolLiteral(false).into(),
289 child_of_layout: component.root_element.borrow().child_of_layout,
290 grid_layout_input_for_repeated: None,
291 flexbox_layout_item_info_for_repeated: None,
292 is_repeated_row: component
293 .root_element
294 .borrow()
295 .grid_layout_cell
296 .as_ref()
297 .is_some_and(|c| c.borrow().child_items.is_some()),
298 grid_layout_children: Default::default(),
299 row_child_templates: None,
300 accessible_prop: Default::default(),
301 element_infos: Default::default(),
302 prop_analysis: Default::default(),
303 };
304 let mut mapping = LoweredSubComponentMapping::default();
305 let mut repeated = TiVec::new();
306 let mut accessible_prop = Vec::new();
307 let mut change_callbacks = Vec::new();
308
309 if let Some(parent) = component.parent_element() {
310 if parent.borrow().repeated.as_ref().is_some_and(|x| !x.is_conditional_element) {
312 sub_component.properties.push(Property {
313 name: "model_data".into(),
314 ty: crate::expression_tree::Expression::RepeaterModelReference {
315 element: component.parent_element.borrow().clone(),
316 }
317 .ty(),
318 ..Property::default()
319 });
320 sub_component.properties.push(Property {
321 name: "model_index".into(),
322 ty: Type::Int32,
323 ..Property::default()
324 });
325 }
326 };
327
328 let s: Option<ElementRc> = None;
329 let mut repeater_offset = 0;
330 crate::object_tree::recurse_elem(&component.root_element, &s, &mut |element, parent| {
331 let elem = element.borrow();
332 for (p, x) in &elem.property_declarations {
333 if x.is_alias.is_some() {
334 continue;
335 }
336 let reference = if let Type::Function(function) = &x.property_type {
337 let index = sub_component.functions.push_and_get_key(Function {
340 name: p.clone(),
341 ret_ty: function.return_type.clone(),
342 args: function.args.clone(),
343 code: super::Expression::CodeBlock(Vec::new()),
345 });
346 index.into()
347 } else if let Type::Callback(callback) = &x.property_type {
348 let index = sub_component.callbacks.push_and_get_key(Callback {
349 name: format_smolstr!("{}_{}", elem.id, p),
350 ret_ty: callback.return_type.clone(),
351 args: callback.args.clone(),
352 ty: Type::Callback(callback.clone()),
353 use_count: 0.into(),
354 });
355 index.into()
356 } else {
357 let index = sub_component.properties.push_and_get_key(Property {
358 name: format_smolstr!("{}_{}", elem.id, p),
359 ty: x.property_type.clone(),
360 ..Property::default()
361 });
362 index.into()
363 };
364 mapping.property_mapping.insert(
365 NamedReference::new(element, p.clone()),
366 MemberReference::Relative {
367 parent_level: 0,
368 local_reference: LocalMemberReference {
369 sub_component_path: Vec::new(),
370 reference,
371 },
372 },
373 );
374 }
375 if elem.repeated.is_some() {
376 let parent = if elem.is_component_placeholder { parent.clone() } else { None };
377
378 mapping.element_mapping.insert(
379 element.clone().into(),
380 LoweredElement::Repeated {
381 repeated_index: repeated.push_and_get_key((element.clone(), parent)),
382 },
383 );
384 mapping.repeater_count += 1;
385 return None;
386 }
387 match &elem.base_type {
388 ElementType::Component(comp) => {
389 let ty = state.sub_component_idx(comp);
390 let sub_component_index =
391 sub_component.sub_components.push_and_get_key(SubComponentInstance {
392 ty,
393 name: elem.id.clone(),
394 index_in_tree: *elem.item_index.get().unwrap(),
395 index_of_first_child_in_tree: *elem
396 .item_index_of_first_children
397 .get()
398 .unwrap(),
399 repeater_offset,
400 });
401 mapping.element_mapping.insert(
402 element.clone().into(),
403 LoweredElement::SubComponent { sub_component_index },
404 );
405 repeater_offset += comp.repeater_count();
406 }
407
408 ElementType::Native(n) => {
409 let item_index = sub_component.items.push_and_get_key(Item {
410 ty: n.clone(),
411 name: elem.id.clone(),
412 index_in_tree: *elem.item_index.get().unwrap(),
413 });
414 mapping
415 .element_mapping
416 .insert(element.clone().into(), LoweredElement::NativeItem { item_index });
417 }
418 _ => unreachable!(),
419 };
420 for (key, nr) in &elem.accessibility_props.0 {
421 let enum_value =
423 crate::generator::to_pascal_case(key.strip_prefix("accessible-").unwrap());
424 accessible_prop.push((*elem.item_index.get().unwrap(), enum_value, nr.clone()));
425 }
426
427 for (prop, expr) in &elem.change_callbacks {
428 change_callbacks
429 .push((NamedReference::new(element, prop.clone()), expr.borrow().clone()));
430 }
431
432 if compiler_config.debug_info {
433 let element_infos = elem.element_infos();
434 if !element_infos.is_empty() {
435 sub_component.element_infos.insert(*elem.item_index.get().unwrap(), element_infos);
436 }
437 }
438
439 Some(element.clone())
440 });
441
442 let inner = ExpressionLoweringCtxInner { mapping: &mapping, parent: parent_context, component };
443 let mut ctx = ExpressionLoweringCtx { inner, state };
444
445 sub_component.repeated = repeated
448 .into_iter()
449 .map(|(elem, parent)| {
450 lower_repeated_component(&elem, parent, &sub_component, &mut ctx, compiler_config)
451 })
452 .collect();
453 for s in &mut sub_component.sub_components {
454 s.repeater_offset +=
455 (sub_component.repeated.len() + sub_component.component_containers.len()) as u32;
456 }
457
458 crate::generator::handle_property_bindings_init(component, |e, p, binding| {
459 let nr = NamedReference::new(e, p.clone());
460 let prop = ctx.map_property_reference(&nr);
461
462 if let Type::Function { .. } = nr.ty() {
463 let MemberReference::Relative { parent_level, local_reference } = prop else {
464 unreachable!()
465 };
466 assert!(parent_level == 0);
467 assert!(local_reference.sub_component_path.is_empty());
468 let LocalMemberIndex::Function(function_index) = local_reference.reference else {
469 unreachable!()
470 };
471
472 sub_component.functions[function_index].code =
473 super::lower_expression::lower_expression(&binding.expression, &mut ctx);
474
475 return;
476 }
477
478 for tw in &binding.two_way_bindings {
479 sub_component.two_way_bindings.push((
480 prop.clone(),
481 ctx.map_property_reference(&tw.property),
482 tw.field_access.clone(),
483 ));
484 }
485 if !matches!(binding.expression, tree_Expression::Invalid) {
486 let expression =
487 super::lower_expression::lower_expression(&binding.expression, &mut ctx).into();
488
489 let is_constant = binding.analysis.as_ref().is_some_and(|a| a.is_const);
490 let animation = binding
491 .animation
492 .as_ref()
493 .filter(|_| !is_constant)
494 .map(|a| super::lower_expression::lower_animation(a, &mut ctx));
495
496 sub_component.prop_analysis.insert(
497 prop.clone(),
498 PropAnalysis {
499 property_init: Some(sub_component.property_init.len()),
500 analysis: get_property_analysis(e, p),
501 },
502 );
503
504 let is_state_info = matches!(
505 e.borrow().lookup_property(p).property_type,
506 Type::Struct(s) if matches!(s.name, StructName::BuiltinPrivate(BuiltinPrivateStruct::StateInfo))
507 );
508
509 sub_component.property_init.push((
510 prop.clone(),
511 BindingExpression {
512 expression,
513 animation,
514 is_constant,
515 is_state_info,
516 use_count: 0.into(),
517 },
518 ));
519 }
520
521 if e.borrow()
522 .property_analysis
523 .borrow()
524 .get(p)
525 .is_none_or(|a| a.is_set || a.is_set_externally)
526 && let Some(anim) = binding.animation.as_ref()
527 {
528 match super::lower_expression::lower_animation(anim, &mut ctx) {
529 Animation::Static(anim) => {
530 sub_component.animations.insert(prop.local(), anim);
531 }
532 Animation::Transition(_) => {
533 }
535 }
536 }
537 });
538
539 sub_component.popup_windows = component
540 .popup_windows
541 .borrow()
542 .iter()
543 .map(|popup| lower_popup_component(popup, &mut ctx, compiler_config))
544 .collect();
545
546 sub_component.menu_item_trees = component
547 .menu_item_tree
548 .borrow()
549 .iter()
550 .map(|c| {
551 let sc = lower_sub_component(c, ctx.state, Some(&ctx.inner), compiler_config);
552 ItemTree {
553 tree: make_tree(ctx.state, &c.root_element, &sc, &[]),
554 root: ctx.state.push_sub_component(sc),
555 }
556 })
557 .collect();
558
559 sub_component.timers = component.timers.borrow().iter().map(|t| lower_timer(t, &ctx)).collect();
560
561 crate::generator::for_each_const_properties(component, |elem, n| {
562 let x = ctx.map_property_reference(&NamedReference::new(elem, n.clone()));
563 sub_component.prop_analysis.entry(x.clone()).or_insert_with(|| PropAnalysis {
565 property_init: None,
566 analysis: get_property_analysis(elem, n),
567 });
568 sub_component.const_properties.push(x.local());
569 });
570
571 sub_component.init_code = component
572 .init_code
573 .borrow()
574 .iter()
575 .map(|e| super::lower_expression::lower_expression(e, &mut ctx).into())
576 .collect();
577
578 sub_component.layout_info_h = super::lower_layout_expression::get_layout_info(
579 &component.root_element,
580 &mut ctx,
581 &component.root_constraints.borrow(),
582 crate::layout::Orientation::Horizontal,
583 )
584 .into();
585 sub_component.layout_info_v = super::lower_layout_expression::get_layout_info(
586 &component.root_element,
587 &mut ctx,
588 &component.root_constraints.borrow(),
589 crate::layout::Orientation::Vertical,
590 )
591 .into();
592 if sub_component.child_of_layout {
594 let root_elem = &component.root_element;
595 let has_flex_binding =
596 ["flex-grow", "flex-shrink", "flex-basis", "flex-align-self", "flex-order"]
597 .iter()
598 .any(|name| crate::layout::binding_reference(root_elem, name).is_some());
599 if has_flex_binding {
600 sub_component.flexbox_layout_item_info_for_repeated = Some(
601 super::lower_layout_expression::get_flexbox_layout_item_info_for_repeated(
602 &mut ctx, root_elem,
603 )
604 .into(),
605 );
606 }
607 }
608
609 if let Some(grid_layout_cell) = component.root_element.borrow().grid_layout_cell.as_ref() {
610 let grid_cell_ref = grid_layout_cell.borrow();
611 sub_component.grid_layout_input_for_repeated = Some(
612 super::lower_layout_expression::get_grid_layout_input_for_repeated(
613 &mut ctx,
614 &grid_cell_ref,
615 )
616 .into(),
617 );
618
619 if let Some(children_constraints) = grid_cell_ref.child_items.as_ref() {
621 let mut row_child_templates = Vec::new();
622 for child_template in children_constraints.iter() {
623 match child_template {
624 crate::layout::RowChildTemplate::Static(layout_item) => {
625 let layout_info_h = super::lower_layout_expression::get_layout_info(
626 &layout_item.element,
627 &mut ctx,
628 &layout_item.constraints,
629 crate::layout::Orientation::Horizontal,
630 );
631 let layout_info_v = super::lower_layout_expression::get_layout_info(
632 &layout_item.element,
633 &mut ctx,
634 &layout_item.constraints,
635 crate::layout::Orientation::Vertical,
636 );
637 let child_index = sub_component.grid_layout_children.push_and_get_key(
638 super::GridLayoutChildLayoutInfo {
639 layout_info_h: layout_info_h.into(),
640 layout_info_v: layout_info_v.into(),
641 },
642 );
643 row_child_templates
644 .push(super::RowChildTemplateInfo::Static { child_index });
645 }
646 crate::layout::RowChildTemplate::Repeated { repeated_element, .. } => {
647 if let Some(super::lower_to_item_tree::LoweredElement::Repeated {
649 repeated_index,
650 }) = mapping.element_mapping.get(&repeated_element.clone().into())
651 {
652 row_child_templates.push(super::RowChildTemplateInfo::Repeated {
653 repeater_index: *repeated_index,
654 });
655 }
656 }
657 }
658 }
659 sub_component.row_child_templates = Some(row_child_templates);
663 }
664 }
665
666 sub_component.accessible_prop = accessible_prop
667 .into_iter()
668 .map(|(idx, key, nr)| {
669 let prop = ctx.map_property_reference(&nr);
670 let expr = match nr.ty() {
671 Type::Bool => super::Expression::Condition {
672 condition: super::Expression::PropertyReference(prop).into(),
673 true_expr: super::Expression::StringLiteral("true".into()).into(),
674 false_expr: super::Expression::StringLiteral("false".into()).into(),
675 },
676 Type::Int32 | Type::Float32 => super::Expression::Cast {
677 from: super::Expression::PropertyReference(prop).into(),
678 to: Type::String,
679 },
680 Type::String => super::Expression::PropertyReference(prop),
681 Type::Enumeration(e) if e.name == "AccessibleRole" => {
682 super::Expression::PropertyReference(prop)
683 }
684 Type::Callback(callback) => super::Expression::CallBackCall {
685 callback: prop,
686 arguments: (0..callback.args.len())
687 .map(|index| super::Expression::FunctionParameterReference { index })
688 .collect(),
689 },
690 _ => panic!("Invalid type for accessible property"),
691 };
692
693 ((idx, key), expr.into())
694 })
695 .collect();
696
697 sub_component.change_callbacks = change_callbacks
698 .into_iter()
699 .map(|(nr, exprs)| {
700 let prop = ctx.map_property_reference(&nr);
701 let expr = super::lower_expression::lower_expression(
702 &tree_Expression::CodeBlock(exprs),
703 &mut ctx,
704 );
705 (prop, expr.into())
706 })
707 .collect();
708
709 crate::object_tree::recurse_elem(&component.root_element, &(), &mut |element, _| {
710 let elem = element.borrow();
711 if elem.repeated.is_some() {
712 return;
713 };
714 let Some(geom) = &elem.geometry_props else { return };
715 let item_index = *elem.item_index.get().unwrap() as usize;
716 if item_index >= sub_component.geometries.len() {
717 sub_component.geometries.resize(item_index + 1, Default::default());
718 }
719 sub_component.geometries[item_index] = Some(lower_geometry(geom, &ctx).into());
720 });
721
722 LoweredSubComponent { sub_component, mapping }
723}
724
725fn lower_geometry(
726 geom: &crate::object_tree::GeometryProps,
727 ctx: &ExpressionLoweringCtx<'_>,
728) -> super::Expression {
729 let mut fields = BTreeMap::default();
730 let mut values = BTreeMap::default();
731 for (f, v) in [("x", &geom.x), ("y", &geom.y), ("width", &geom.width), ("height", &geom.height)]
732 {
733 fields.insert(f.into(), Type::LogicalLength);
734 values
735 .insert(f.into(), super::Expression::PropertyReference(ctx.map_property_reference(v)));
736 }
737 super::Expression::Struct { ty: Rc::new(Struct { fields, name: StructName::None }), values }
738}
739
740fn get_property_analysis(elem: &ElementRc, p: &str) -> crate::object_tree::PropertyAnalysis {
741 let mut a = elem.borrow().property_analysis.borrow().get(p).cloned().unwrap_or_default();
742 let mut elem = elem.clone();
743 loop {
744 if let Some(d) = elem.borrow().property_declarations.get(p) {
745 if let Some(nr) = &d.is_alias {
746 a.merge(&get_property_analysis(&nr.element(), nr.name()));
747 }
748 return a;
749 }
750 let base = elem.borrow().base_type.clone();
751 match base {
752 ElementType::Native(n) if n.properties.get(p).is_some_and(|p| p.is_native_output()) => {
753 a.is_set = true;
754 }
755 ElementType::Component(c) => {
756 elem = c.root_element.clone();
757 if let Some(a2) = elem.borrow().property_analysis.borrow().get(p) {
758 a.merge_with_base(a2);
759 }
760 continue;
761 }
762 _ => (),
763 };
764 return a;
765 }
766}
767
768fn lower_repeated_component(
769 elem: &ElementRc,
770 parent_component_container: Option<ElementRc>,
771 sub_component: &SubComponent,
772 ctx: &mut ExpressionLoweringCtx,
773 compiler_config: &CompilerConfiguration,
774) -> RepeatedElement {
775 let e = elem.borrow();
776 let component = e.base_type.as_component().clone();
777 let repeated = e.repeated.as_ref().unwrap();
778
779 let sc = lower_sub_component(&component, ctx.state, Some(&ctx.inner), compiler_config);
780
781 let listview = repeated.is_listview.as_ref().map(|lv| {
782 let geom = component.root_element.borrow().geometry_props.clone().unwrap();
783 ListViewInfo {
784 viewport_y: ctx.map_property_reference(&lv.viewport_y),
785 viewport_height: ctx.map_property_reference(&lv.viewport_height),
786 viewport_width: ctx.map_property_reference(&lv.viewport_width),
787 listview_height: ctx.map_property_reference(&lv.listview_height),
788 listview_width: ctx.map_property_reference(&lv.listview_width),
789 prop_y: sc.mapping.map_property_reference(&geom.y, ctx.state),
790 prop_height: sc.mapping.map_property_reference(&geom.height, ctx.state),
791 }
792 });
793
794 let parent_index = parent_component_container.map(|p| *p.borrow().item_index.get().unwrap());
795 let container_item_index =
796 parent_index.and_then(|pii| sub_component.items.position(|i| i.index_in_tree == pii));
797
798 let tree = make_tree(ctx.state, &component.root_element, &sc, &[]);
799 let root = ctx.state.push_sub_component(sc);
800 ctx.state.sub_component_mapping.insert(ByAddress(component.clone()), root);
802
803 RepeatedElement {
804 model: super::lower_expression::lower_expression(&repeated.model, ctx).into(),
805 sub_tree: ItemTree { tree, root },
806 index_prop: (!repeated.is_conditional_element).then_some(PropertyIdx::REPEATER_INDEX),
807 data_prop: (!repeated.is_conditional_element).then_some(PropertyIdx::REPEATER_DATA),
808 index_in_tree: *e.item_index.get().unwrap(),
809 listview,
810 container_item_index,
811 }
812}
813
814fn lower_popup_component(
815 popup: &object_tree::PopupWindow,
816 ctx: &mut ExpressionLoweringCtx,
817 compiler_config: &CompilerConfiguration,
818) -> PopupWindow {
819 let sc = lower_sub_component(&popup.component, ctx.state, Some(&ctx.inner), compiler_config);
820 use super::Expression::PropertyReference as PR;
821 let position = super::lower_expression::make_struct(
822 BuiltinPublicStruct::LogicalPosition,
823 [
824 ("x", Type::LogicalLength, PR(sc.mapping.map_property_reference(&popup.x, ctx.state))),
825 ("y", Type::LogicalLength, PR(sc.mapping.map_property_reference(&popup.y, ctx.state))),
826 ],
827 );
828
829 let item_tree = ItemTree {
830 tree: make_tree(ctx.state, &popup.component.root_element, &sc, &[]),
831 root: ctx.state.push_sub_component(sc),
832 };
833 PopupWindow { item_tree, position: position.into() }
834}
835
836fn lower_timer(timer: &object_tree::Timer, ctx: &ExpressionLoweringCtx) -> Timer {
837 Timer {
838 interval: super::Expression::PropertyReference(ctx.map_property_reference(&timer.interval))
839 .into(),
840 running: super::Expression::PropertyReference(ctx.map_property_reference(&timer.running))
841 .into(),
842 triggered: super::Expression::CallBackCall {
844 callback: ctx.map_property_reference(&timer.triggered),
845 arguments: Vec::new(),
846 }
847 .into(),
848 }
849}
850
851fn lower_global(
853 global: &Rc<Component>,
854 global_index: GlobalIdx,
855 state: &mut LoweringState,
856) -> GlobalComponent {
857 let mut properties = TiVec::new();
858 let mut callbacks = TiVec::new();
859 let mut const_properties = TiVec::new();
860 let mut prop_analysis = TiVec::new();
861 let mut functions = TiVec::new();
862
863 for (p, x) in &global.root_element.borrow().property_declarations {
864 if x.is_alias.is_some() {
865 continue;
866 }
867 let nr = NamedReference::new(&global.root_element, p.clone());
868
869 if let Type::Function(function) = &x.property_type {
870 let function_index: FunctionIdx = functions.push_and_get_key(Function {
872 name: p.clone(),
873 ret_ty: function.return_type.clone(),
874 args: function.args.clone(),
875 code: super::Expression::CodeBlock(Vec::new()),
877 });
878 state.global_properties.insert(
879 nr.clone(),
880 MemberReference::Global { global_index, member: function_index.into() },
881 );
882 continue;
883 } else if let Type::Callback(cb) = &x.property_type {
884 let callback_index: CallbackIdx = callbacks.push_and_get_key(Callback {
885 name: p.clone(),
886 ret_ty: cb.return_type.clone(),
887 args: cb.args.clone(),
888 ty: x.property_type.clone(),
889 use_count: 0.into(),
890 });
891 state.global_properties.insert(
892 nr.clone(),
893 MemberReference::Global { global_index, member: callback_index.into() },
894 );
895 continue;
896 }
897
898 let property_index: PropertyIdx = properties.push_and_get_key(Property {
899 name: p.clone(),
900 ty: x.property_type.clone(),
901 ..Property::default()
902 });
903
904 const_properties.push(nr.is_constant());
905
906 prop_analysis.push(
907 global
908 .root_element
909 .borrow()
910 .property_analysis
911 .borrow()
912 .get(p)
913 .cloned()
914 .unwrap_or_default(),
915 );
916 state.global_properties.insert(
917 nr.clone(),
918 MemberReference::Global { global_index, member: property_index.into() },
919 );
920 }
921
922 let is_builtin = if let Some(builtin) = global.root_element.borrow().native_class() {
923 for (p, x) in &builtin.properties {
925 let property_index = properties.push_and_get_key(Property {
926 name: p.clone(),
927 ty: x.ty.clone(),
928 ..Property::default()
929 });
930 let nr = NamedReference::new(&global.root_element, p.clone());
931 state.global_properties.insert(
932 nr,
933 MemberReference::Global { global_index, member: property_index.into() },
934 );
935 prop_analysis.push(PropertyAnalysis {
936 is_set_externally: true,
938 ..global
939 .root_element
940 .borrow()
941 .property_analysis
942 .borrow()
943 .get(p)
944 .cloned()
945 .unwrap_or_default()
946 });
947 }
948 true
949 } else {
950 false
951 };
952
953 GlobalComponent {
954 name: global.root_element.borrow().id.clone(),
955 init_values: BTreeMap::new(),
956 properties,
957 callbacks,
958 functions,
959 change_callbacks: BTreeMap::new(),
960 const_properties,
961 public_properties: Default::default(),
962 private_properties: global.private_properties.borrow().clone(),
963 exported: !global.exported_global_names.borrow().is_empty(),
964 aliases: global.global_aliases(),
965 is_builtin,
966 from_library: global.from_library.get(),
967 prop_analysis,
968 }
969}
970
971fn lower_global_expressions(
972 global: &Rc<Component>,
973 state: &mut LoweringState,
974 lowered: &mut GlobalComponent,
975) {
976 let mapping = LoweredSubComponentMapping::default();
978 let inner = ExpressionLoweringCtxInner { mapping: &mapping, parent: None, component: global };
979 let mut ctx = ExpressionLoweringCtx { inner, state };
980
981 for (prop, binding) in &global.root_element.borrow().bindings {
982 assert!(binding.borrow().two_way_bindings.is_empty());
983 assert!(binding.borrow().animation.is_none());
984 let expression =
985 super::lower_expression::lower_expression(&binding.borrow().expression, &mut ctx);
986
987 let nr = NamedReference::new(&global.root_element, prop.clone());
988 let member_index = match &ctx.state.global_properties[&nr] {
989 MemberReference::Global {
990 member: LocalMemberIndex::Function(function_index), ..
991 } => {
992 lowered.functions[*function_index].code = expression;
993 continue;
994 }
995 MemberReference::Global { member, .. } => member.clone(),
996 _ => unreachable!(),
997 };
998 let is_constant = binding.borrow().analysis.as_ref().is_some_and(|a| a.is_const);
999 lowered.init_values.insert(
1000 member_index,
1001 BindingExpression {
1002 expression: expression.into(),
1003 animation: None,
1004 is_constant,
1005 is_state_info: false,
1006 use_count: 0.into(),
1007 },
1008 );
1009 }
1010
1011 for (prop, expr) in &global.root_element.borrow().change_callbacks {
1012 let nr = NamedReference::new(&global.root_element, prop.clone());
1013 let MemberReference::Global { member: LocalMemberIndex::Property(property_index), .. } =
1014 ctx.state.global_properties[&nr]
1015 else {
1016 unreachable!()
1017 };
1018 let expression = super::lower_expression::lower_expression(
1019 &tree_Expression::CodeBlock(expr.borrow().clone()),
1020 &mut ctx,
1021 );
1022 lowered.change_callbacks.insert(property_index, expression.into());
1023 }
1024
1025 if let Some(builtin) = global.root_element.borrow().native_class() {
1026 if lowered.exported {
1027 lowered.public_properties = builtin
1028 .properties
1029 .iter()
1030 .map(|(p, c)| {
1031 let property_reference = mapping.map_property_reference(
1032 &NamedReference::new(&global.root_element, p.clone()),
1033 state,
1034 );
1035 PublicProperty {
1036 name: p.clone(),
1037 ty: c.ty.clone(),
1038 prop: property_reference,
1039 read_only: c.property_visibility == PropertyVisibility::Output,
1040 }
1041 })
1042 .collect()
1043 }
1044 } else {
1045 lowered.public_properties = public_properties(global, &mapping, state);
1046 }
1047}
1048
1049fn make_tree(
1050 state: &LoweringState,
1051 element: &ElementRc,
1052 component: &LoweredSubComponent,
1053 sub_component_path: &[SubComponentInstanceIdx],
1054) -> TreeNode {
1055 let e = element.borrow();
1056 let children = e.children.iter().map(|c| make_tree(state, c, component, sub_component_path));
1057 let repeater_count = component.mapping.repeater_count;
1058 match component.mapping.element_mapping.get(&ByAddress(element.clone())).unwrap() {
1059 LoweredElement::SubComponent { sub_component_index } => {
1060 let sub_component = e.sub_component().unwrap();
1061 let new_sub_component_path = sub_component_path
1062 .iter()
1063 .copied()
1064 .chain(std::iter::once(*sub_component_index))
1065 .collect::<Vec<_>>();
1066 let mut tree_node = make_tree(
1067 state,
1068 &sub_component.root_element,
1069 state.sub_component(sub_component),
1070 &new_sub_component_path,
1071 );
1072 tree_node.children.extend(children);
1073 tree_node.is_accessible |= !e.accessibility_props.0.is_empty();
1074 tree_node
1075 }
1076 LoweredElement::NativeItem { item_index } => TreeNode {
1077 is_accessible: !e.accessibility_props.0.is_empty(),
1078 sub_component_path: sub_component_path.into(),
1079 item_index: itertools::Either::Left(*item_index),
1080 children: children.collect(),
1081 },
1082 LoweredElement::Repeated { repeated_index } => TreeNode {
1083 is_accessible: false,
1084 sub_component_path: sub_component_path.into(),
1085 item_index: itertools::Either::Right(usize::from(*repeated_index) as u32),
1086 children: Vec::new(),
1087 },
1088 LoweredElement::ComponentPlaceholder { repeated_index } => TreeNode {
1089 is_accessible: false,
1090 sub_component_path: sub_component_path.into(),
1091 item_index: itertools::Either::Right(*repeated_index + repeater_count),
1092 children: Vec::new(),
1093 },
1094 }
1095}
1096
1097fn public_properties(
1098 component: &Component,
1099 mapping: &LoweredSubComponentMapping,
1100 state: &LoweringState,
1101) -> PublicProperties {
1102 component
1103 .root_element
1104 .borrow()
1105 .property_declarations
1106 .iter()
1107 .filter(|(_, c)| c.expose_in_public_api)
1108 .map(|(p, c)| {
1109 let property_reference = mapping.map_property_reference(
1110 &NamedReference::new(&component.root_element, p.clone()),
1111 state,
1112 );
1113 PublicProperty {
1114 name: p.clone(),
1115 ty: c.property_type.clone(),
1116 prop: property_reference,
1117 read_only: c.visibility == PropertyVisibility::Output,
1118 }
1119 })
1120 .collect()
1121}