sixtyfps_compilerlib/llr/
lower_to_item_tree.rs

1// Copyright © SixtyFPS GmbH <info@sixtyfps.io>
2// SPDX-License-Identifier: (GPL-3.0-only OR LicenseRef-SixtyFPS-commercial)
3
4use by_address::ByAddress;
5
6use crate::expression_tree::Expression as tree_Expression;
7use crate::langtype::Type;
8use crate::llr::item_tree::*;
9use crate::namedreference::NamedReference;
10use crate::object_tree::{Component, ElementRc};
11use std::collections::HashMap;
12use std::rc::Rc;
13
14use super::lower_expression::ExpressionContext;
15
16pub fn lower_to_item_tree(component: &Rc<Component>) -> PublicComponent {
17    let mut state = LoweringState::default();
18
19    let mut globals = Vec::new();
20    for g in &component.used_types.borrow().globals {
21        let count = globals.len();
22        globals.push(lower_global(g, count, &mut state));
23    }
24    for c in &component.used_types.borrow().sub_components {
25        let sc = lower_sub_component(c, &state, None);
26        state.sub_components.insert(ByAddress(c.clone()), sc);
27    }
28
29    let sc = lower_sub_component(component, &state, None);
30    let public_properties = public_properties(component, &sc.mapping, &state);
31    let item_tree = ItemTree {
32        tree: make_tree(&state, &component.root_element, &sc, &[]),
33        root: Rc::try_unwrap(sc.sub_component).unwrap(),
34        parent_context: None,
35    };
36    PublicComponent {
37        item_tree,
38        globals,
39        sub_components: component
40            .used_types
41            .borrow()
42            .sub_components
43            .iter()
44            .map(|tree_sub_compo| {
45                state.sub_components[&ByAddress(tree_sub_compo.clone())].sub_component.clone()
46            })
47            .collect(),
48        public_properties,
49    }
50}
51
52#[derive(Default)]
53pub struct LoweringState {
54    global_properties: HashMap<NamedReference, PropertyReference>,
55    sub_components: HashMap<ByAddress<Rc<Component>>, LoweredSubComponent>,
56}
57
58#[derive(Debug, Clone)]
59pub enum LoweredElement {
60    SubComponent { sub_component_index: usize },
61    NativeItem { item_index: usize },
62    Repeated { repeated_index: usize },
63}
64
65#[derive(Default, Debug, Clone)]
66pub struct LoweredSubComponentMapping {
67    pub element_mapping: HashMap<ByAddress<ElementRc>, LoweredElement>,
68    pub property_mapping: HashMap<NamedReference, PropertyReference>,
69}
70
71impl LoweredSubComponentMapping {
72    pub fn map_property_reference(
73        &self,
74        from: &NamedReference,
75        state: &LoweringState,
76    ) -> PropertyReference {
77        if let Some(x) = self.property_mapping.get(&from) {
78            return x.clone();
79        }
80        if let Some(x) = state.global_properties.get(&from) {
81            return x.clone();
82        }
83        let element = from.element();
84        if let Some(alias) = element
85            .borrow()
86            .property_declarations
87            .get(from.name())
88            .and_then(|x| x.is_alias.as_ref())
89        {
90            return self.map_property_reference(alias, state);
91        }
92        match self.element_mapping.get(&element.clone().into()).unwrap() {
93            LoweredElement::SubComponent { sub_component_index } => {
94                if let Type::Component(base) = &element.borrow().base_type {
95                    return property_reference_within_sub_component(
96                        state.map_property_reference(&NamedReference::new(
97                            &base.root_element,
98                            from.name(),
99                        )),
100                        *sub_component_index,
101                    );
102                }
103                unreachable!()
104            }
105            LoweredElement::NativeItem { item_index } => {
106                return PropertyReference::InNativeItem {
107                    sub_component_path: vec![],
108                    item_index: *item_index,
109                    prop_name: from.name().into(),
110                };
111            }
112            LoweredElement::Repeated { .. } => unreachable!(),
113        }
114    }
115}
116
117pub struct LoweredSubComponent {
118    sub_component: Rc<SubComponent>,
119    mapping: LoweredSubComponentMapping,
120}
121
122impl LoweringState {
123    pub fn map_property_reference(&self, from: &NamedReference) -> PropertyReference {
124        if let Some(x) = self.global_properties.get(&from) {
125            return x.clone();
126        }
127
128        let element = from.element();
129        let enclosing = self
130            .sub_components
131            .get(&element.borrow().enclosing_component.upgrade().unwrap().into())
132            .unwrap();
133
134        enclosing.mapping.map_property_reference(from, self)
135    }
136}
137
138// Map a PropertyReference within a `sub_component` to a PropertyReference to the component containing it
139fn property_reference_within_sub_component(
140    mut prop_ref: PropertyReference,
141    sub_component: usize,
142) -> PropertyReference {
143    match &mut prop_ref {
144        PropertyReference::Local { sub_component_path, .. }
145        | PropertyReference::InNativeItem { sub_component_path, .. } => {
146            sub_component_path.insert(0, sub_component);
147        }
148        PropertyReference::InParent { .. } => panic!("the sub-component had no parents"),
149        PropertyReference::Global { .. } => (),
150    }
151    prop_ref
152}
153
154impl LoweringState {
155    fn sub_component(&self, component: &Rc<Component>) -> &LoweredSubComponent {
156        &self.sub_components[&ByAddress(component.clone())]
157    }
158}
159
160fn component_id(component: &Rc<Component>) -> String {
161    if component.is_global() {
162        component.root_element.borrow().id.clone()
163    } else if component.id.is_empty() {
164        format!("Component_{}", component.root_element.borrow().id)
165    } else if component.is_sub_component() {
166        format!("{}_{}", component.id, component.root_element.borrow().id)
167    } else {
168        component.id.clone()
169    }
170}
171
172fn lower_sub_component(
173    component: &Rc<Component>,
174    state: &LoweringState,
175    parent_context: Option<&ExpressionContext>,
176) -> LoweredSubComponent {
177    let mut sub_component = SubComponent {
178        name: component_id(component),
179        properties: Default::default(),
180        items: Default::default(),
181        repeated: Default::default(),
182        popup_windows: Default::default(),
183        sub_components: Default::default(),
184        property_init: Default::default(),
185        animations: Default::default(),
186        two_way_bindings: Default::default(),
187        const_properties: Default::default(),
188        init_code: Default::default(),
189        // just initialize to dummy expression right now and it will be set later
190        layout_info_h: super::Expression::BoolLiteral(false),
191        layout_info_v: super::Expression::BoolLiteral(false),
192    };
193    let mut mapping = LoweredSubComponentMapping::default();
194    let mut repeated = vec![];
195
196    if let Some(parent) = component.parent_element.upgrade() {
197        // Add properties for the model data and index
198        if parent.borrow().repeated.as_ref().map_or(false, |x| !x.is_conditional_element) {
199            sub_component.properties.push(Property {
200                name: "model_data".into(),
201                ty: crate::expression_tree::Expression::RepeaterModelReference {
202                    element: component.parent_element.clone(),
203                }
204                .ty(),
205            });
206            sub_component.properties.push(Property { name: "model_index".into(), ty: Type::Int32 });
207        }
208    };
209
210    let s: Option<ElementRc> = None;
211    let mut repeater_offset = 0;
212    crate::object_tree::recurse_elem(&component.root_element, &s, &mut |element, parent| {
213        let elem = element.borrow();
214        for (p, x) in &elem.property_declarations {
215            if x.is_alias.is_some() {
216                continue;
217            }
218            let property_index = sub_component.properties.len();
219            mapping.property_mapping.insert(
220                NamedReference::new(element, &p),
221                PropertyReference::Local { sub_component_path: vec![], property_index },
222            );
223            sub_component
224                .properties
225                .push(Property { name: format!("{}_{}", elem.id, p), ty: x.property_type.clone() });
226        }
227        if elem.repeated.is_some() {
228            mapping.element_mapping.insert(
229                element.clone().into(),
230                LoweredElement::Repeated { repeated_index: repeated.len() },
231            );
232            repeated.push(element.clone());
233            return None;
234        }
235        match &elem.base_type {
236            Type::Component(comp) => {
237                let lc = state.sub_component(comp);
238                let ty = lc.sub_component.clone();
239                let sub_component_index = sub_component.sub_components.len();
240                mapping.element_mapping.insert(
241                    element.clone().into(),
242                    LoweredElement::SubComponent { sub_component_index },
243                );
244                sub_component.sub_components.push(SubComponentInstance {
245                    ty: ty.clone(),
246                    name: elem.id.clone(),
247                    index_in_tree: *elem.item_index.get().unwrap(),
248                    index_of_first_child_in_tree: *elem.item_index_of_first_children.get().unwrap(),
249                    repeater_offset,
250                });
251                repeater_offset += ty.repeater_count();
252            }
253
254            Type::Native(n) => {
255                let item_index = sub_component.items.len();
256                mapping
257                    .element_mapping
258                    .insert(element.clone().into(), LoweredElement::NativeItem { item_index });
259                let is_flickable_viewport = elem.is_flickable_viewport;
260                sub_component.items.push(Item {
261                    ty: n.clone(),
262                    name: if is_flickable_viewport {
263                        parent.as_ref().unwrap().borrow().id.clone()
264                    } else {
265                        elem.id.clone()
266                    },
267                    index_in_tree: *elem.item_index.get().unwrap(),
268                    is_flickable_viewport,
269                })
270            }
271            _ => unreachable!(),
272        };
273        Some(element.clone())
274    });
275    let ctx = ExpressionContext { mapping: &mapping, state, parent: parent_context, component };
276    crate::generator::handle_property_bindings_init(component, |e, p, binding| {
277        let prop = ctx.map_property_reference(&NamedReference::new(e, p));
278        for tw in &binding.two_way_bindings {
279            sub_component.two_way_bindings.push((prop.clone(), ctx.map_property_reference(tw)))
280        }
281        if !matches!(binding.expression, tree_Expression::Invalid) {
282            let expression = super::lower_expression::lower_expression(&binding.expression, &ctx);
283
284            let is_constant = binding.analysis.as_ref().map_or(false, |a| a.is_const);
285            let animation = binding
286                .animation
287                .as_ref()
288                .filter(|_| !is_constant)
289                .map(|a| super::lower_expression::lower_animation(a, &ctx));
290            sub_component
291                .property_init
292                .push((prop.clone(), BindingExpression { expression, animation, is_constant }));
293        }
294
295        if e.borrow()
296            .property_analysis
297            .borrow()
298            .get(p)
299            .map_or(true, |a| a.is_set || a.is_set_externally)
300        {
301            if let Some(anim) = binding.animation.as_ref() {
302                match super::lower_expression::lower_animation(anim, &ctx) {
303                    Animation::Static(anim) => {
304                        sub_component.animations.insert(prop, anim);
305                    }
306                    Animation::Transition(_) => {
307                        // Cannot set a property with a transition anyway
308                    }
309                }
310            }
311        }
312    });
313    sub_component.repeated =
314        repeated.into_iter().map(|elem| lower_repeated_component(&elem, &ctx)).collect();
315    for s in &mut sub_component.sub_components {
316        s.repeater_offset += sub_component.repeated.len();
317    }
318    sub_component.popup_windows = component
319        .popup_windows
320        .borrow()
321        .iter()
322        .map(|popup| lower_popup_component(&popup.component, &ctx))
323        .collect();
324
325    crate::generator::for_each_const_properties(component, |elem, n| {
326        let x = ctx.map_property_reference(&NamedReference::new(elem, n));
327        sub_component.const_properties.push(x);
328    });
329
330    sub_component.init_code = component
331        .setup_code
332        .borrow()
333        .iter()
334        .map(|e| super::lower_expression::lower_expression(e, &ctx))
335        .collect();
336
337    sub_component.layout_info_h = super::lower_expression::get_layout_info(
338        &component.root_element,
339        &ctx,
340        &component.root_constraints.borrow(),
341        crate::layout::Orientation::Horizontal,
342    );
343    sub_component.layout_info_v = super::lower_expression::get_layout_info(
344        &component.root_element,
345        &ctx,
346        &component.root_constraints.borrow(),
347        crate::layout::Orientation::Vertical,
348    );
349
350    LoweredSubComponent { sub_component: Rc::new(sub_component), mapping }
351}
352
353fn lower_repeated_component(elem: &ElementRc, ctx: &ExpressionContext) -> RepeatedElement {
354    let e = elem.borrow();
355    let component = e.base_type.as_component().clone();
356    let repeated = e.repeated.as_ref().unwrap();
357
358    let sc = lower_sub_component(&component, &ctx.state, Some(ctx));
359
360    let map_inner_prop = |p| {
361        sc.mapping
362            .map_property_reference(&NamedReference::new(&component.root_element, p), &ctx.state)
363    };
364
365    let listview = repeated.is_listview.as_ref().map(|lv| ListViewInfo {
366        viewport_y: ctx.map_property_reference(&lv.viewport_y),
367        viewport_height: ctx.map_property_reference(&lv.viewport_height),
368        viewport_width: ctx.map_property_reference(&lv.viewport_width),
369        listview_height: ctx.map_property_reference(&lv.listview_height),
370        listview_width: ctx.map_property_reference(&lv.listview_width),
371
372        prop_y: map_inner_prop("y"),
373        prop_width: map_inner_prop("width"),
374        prop_height: map_inner_prop("height"),
375    });
376
377    RepeatedElement {
378        model: super::lower_expression::lower_expression(&repeated.model, ctx),
379        sub_tree: ItemTree {
380            tree: make_tree(ctx.state, &component.root_element, &sc, &[]),
381            root: Rc::try_unwrap(sc.sub_component).unwrap(),
382            parent_context: Some(e.enclosing_component.upgrade().unwrap().id.clone()),
383        },
384        index_prop: (!repeated.is_conditional_element).then(|| 1),
385        data_prop: (!repeated.is_conditional_element).then(|| 0),
386        index_in_tree: *e.item_index.get().unwrap(),
387        listview,
388    }
389}
390
391fn lower_popup_component(component: &Rc<Component>, ctx: &ExpressionContext) -> ItemTree {
392    let sc = lower_sub_component(component, &ctx.state, Some(ctx));
393    ItemTree {
394        tree: make_tree(ctx.state, &component.root_element, &sc, &[]),
395        root: Rc::try_unwrap(sc.sub_component).unwrap(),
396        parent_context: Some(
397            component
398                .parent_element
399                .upgrade()
400                .unwrap()
401                .borrow()
402                .enclosing_component
403                .upgrade()
404                .unwrap()
405                .id
406                .clone(),
407        ),
408    }
409}
410
411fn lower_global(
412    global: &Rc<Component>,
413    global_index: usize,
414    state: &mut LoweringState,
415) -> GlobalComponent {
416    let mut mapping = LoweredSubComponentMapping::default();
417    let mut properties = vec![];
418    let mut const_properties = vec![];
419
420    for (p, x) in &global.root_element.borrow().property_declarations {
421        let property_index = properties.len();
422        let nr = NamedReference::new(&global.root_element, &p);
423        mapping.property_mapping.insert(
424            nr.clone(),
425            PropertyReference::Local { sub_component_path: vec![], property_index },
426        );
427
428        properties.push(Property { name: p.clone(), ty: x.property_type.clone() });
429        if !matches!(x.property_type, Type::Callback { .. }) {
430            const_properties.push(nr.is_constant());
431        }
432        state
433            .global_properties
434            .insert(nr.clone(), PropertyReference::Global { global_index, property_index });
435    }
436
437    let mut init_values = vec![None; properties.len()];
438
439    let ctx = ExpressionContext { mapping: &mapping, state, parent: None, component: global };
440    for (prop, binding) in &global.root_element.borrow().bindings {
441        assert!(binding.borrow().two_way_bindings.is_empty());
442        assert!(binding.borrow().animation.is_none());
443        let expression =
444            super::lower_expression::lower_expression(&binding.borrow().expression, &ctx);
445
446        let nr = NamedReference::new(&global.root_element, prop);
447        let property_index = match mapping.property_mapping[&nr] {
448            PropertyReference::Local { property_index, .. } => property_index,
449            _ => unreachable!(),
450        };
451        let is_constant = binding.borrow().analysis.as_ref().map_or(false, |a| a.is_const);
452        init_values[property_index] =
453            Some(BindingExpression { expression, animation: None, is_constant });
454    }
455
456    let is_builtin = if let Some(builtin) = global.root_element.borrow().native_class() {
457        // We just generate the property so we know how to address them
458        for (p, x) in &builtin.properties {
459            let property_index = properties.len();
460            properties.push(Property { name: p.clone(), ty: x.ty.clone() });
461            let nr = NamedReference::new(&global.root_element, &p);
462            state
463                .global_properties
464                .insert(nr, PropertyReference::Global { global_index, property_index });
465        }
466        true
467    } else {
468        false
469    };
470
471    let public_properties = public_properties(global, &mapping, &state);
472    GlobalComponent {
473        name: global.root_element.borrow().id.clone(),
474        properties,
475        init_values,
476        const_properties,
477        public_properties,
478        exported: !global.exported_global_names.borrow().is_empty(),
479        aliases: global.global_aliases(),
480        is_builtin,
481    }
482}
483
484fn make_tree(
485    state: &LoweringState,
486    element: &ElementRc,
487    component: &LoweredSubComponent,
488    sub_component_path: &[usize],
489) -> TreeNode {
490    let e = element.borrow();
491    let children = e.children.iter().map(|c| make_tree(state, c, component, sub_component_path));
492    match component.mapping.element_mapping.get(&ByAddress(element.clone())).unwrap() {
493        LoweredElement::SubComponent { sub_component_index } => {
494            let sub_component = e.sub_component().unwrap();
495            let new_sub_component_path = sub_component_path
496                .iter()
497                .copied()
498                .chain(std::iter::once(*sub_component_index))
499                .collect::<Vec<_>>();
500            let mut tree_node = make_tree(
501                state,
502                &sub_component.root_element,
503                state.sub_component(sub_component),
504                &new_sub_component_path,
505            );
506            tree_node.children.extend(children);
507            tree_node
508        }
509        LoweredElement::NativeItem { item_index } => TreeNode {
510            sub_component_path: sub_component_path.into(),
511            item_index: *item_index,
512            children: children.collect(),
513            repeated: false,
514        },
515        LoweredElement::Repeated { repeated_index } => TreeNode {
516            sub_component_path: sub_component_path.into(),
517            item_index: *repeated_index,
518            children: vec![],
519            repeated: true,
520        },
521    }
522}
523
524fn public_properties(
525    component: &Component,
526    mapping: &LoweredSubComponentMapping,
527    state: &LoweringState,
528) -> PublicProperties {
529    component
530        .root_element
531        .borrow()
532        .property_declarations
533        .iter()
534        .filter(|(_, c)| c.expose_in_public_api)
535        .map(|(p, c)| {
536            let property_reference = mapping
537                .map_property_reference(&NamedReference::new(&component.root_element, p), &state);
538            (p.clone(), (c.property_type.clone(), property_reference))
539        })
540        .collect()
541}