sixtyfps_compilerlib/
object_tree.rs

1// Copyright © SixtyFPS GmbH <info@sixtyfps.io>
2// SPDX-License-Identifier: (GPL-3.0-only OR LicenseRef-SixtyFPS-commercial)
3
4/*!
5 This module contains the intermediate representation of the code in the form of an object tree
6*/
7
8use itertools::Either;
9
10use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
11use crate::expression_tree::{self, BindingExpression, Expression, Unit};
12use crate::langtype::PropertyLookupResult;
13use crate::langtype::{BuiltinElement, NativeClass, Type};
14use crate::layout::{LayoutConstraints, Orientation};
15use crate::namedreference::NamedReference;
16use crate::parser;
17use crate::parser::{syntax_nodes, SyntaxKind, SyntaxNode};
18use crate::typeloader::ImportedTypes;
19use crate::typeregister::TypeRegister;
20use std::cell::{Cell, RefCell};
21use std::collections::btree_map::Entry;
22use std::collections::{BTreeMap, HashMap};
23use std::rc::{Rc, Weak};
24
25macro_rules! unwrap_or_continue {
26    ($e:expr ; $diag:expr) => {
27        match $e {
28            Some(x) => x,
29            None => {
30                debug_assert!($diag.has_error()); // error should have been reported at parsing time
31                continue;
32            }
33        }
34    };
35}
36
37/// The full document (a complete file)
38#[derive(Default, Debug)]
39pub struct Document {
40    pub node: Option<syntax_nodes::Document>,
41    pub inner_components: Vec<Rc<Component>>,
42    pub inner_structs: Vec<Type>,
43    pub root_component: Rc<Component>,
44    pub local_registry: TypeRegister,
45    /// A list of paths to .ttf/.ttc files that are supposed to be registered on
46    /// startup for custom font use.
47    pub custom_fonts: Vec<String>,
48    exports: Exports,
49}
50
51impl Document {
52    pub fn from_node(
53        node: syntax_nodes::Document,
54        foreign_imports: Vec<ImportedTypes>,
55        diag: &mut BuildDiagnostics,
56        parent_registry: &Rc<RefCell<TypeRegister>>,
57    ) -> Self {
58        debug_assert_eq!(node.kind(), SyntaxKind::Document);
59
60        let mut local_registry = TypeRegister::new(parent_registry);
61        let mut inner_components = vec![];
62        let mut inner_structs = vec![];
63
64        let mut process_component =
65            |n: syntax_nodes::Component,
66             diag: &mut BuildDiagnostics,
67             local_registry: &mut TypeRegister| {
68                let compo = Component::from_node(n, diag, local_registry);
69                local_registry.add(compo.clone());
70                inner_components.push(compo);
71            };
72        let mut process_struct =
73            |n: syntax_nodes::StructDeclaration,
74             diag: &mut BuildDiagnostics,
75             local_registry: &mut TypeRegister| {
76                let mut ty = type_struct_from_node(n.ObjectType(), diag, local_registry);
77                if let Type::Struct { name, .. } = &mut ty {
78                    *name = parser::identifier_text(&n.DeclaredIdentifier());
79                } else {
80                    assert!(diag.has_error());
81                    return;
82                }
83                local_registry.insert_type(ty.clone());
84                inner_structs.push(ty);
85            };
86
87        for n in node.children() {
88            match n.kind() {
89                SyntaxKind::Component => process_component(n.into(), diag, &mut local_registry),
90                SyntaxKind::StructDeclaration => {
91                    process_struct(n.into(), diag, &mut local_registry)
92                }
93                SyntaxKind::ExportsList => {
94                    for n in n.children() {
95                        match n.kind() {
96                            SyntaxKind::Component => {
97                                process_component(n.into(), diag, &mut local_registry)
98                            }
99                            SyntaxKind::StructDeclaration => {
100                                process_struct(n.into(), diag, &mut local_registry)
101                            }
102                            _ => {}
103                        }
104                    }
105                }
106                _ => {}
107            };
108        }
109        let exports = Exports::from_node(&node, &inner_components, &local_registry, diag);
110
111        let root_component = inner_components
112            .last()
113            .cloned()
114            .or_else(|| {
115                node.ImportSpecifier()
116                    .last()
117                    .and_then(|import| {
118                        crate::typeloader::ImportedName::extract_imported_names(&import)
119                            .and_then(|it| it.last())
120                    })
121                    .and_then(|import| match local_registry.lookup(&import.internal_name) {
122                        Type::Component(c) => Some(c),
123                        _ => None,
124                    })
125            })
126            .unwrap_or_default();
127
128        let custom_fonts = foreign_imports
129            .into_iter()
130            .filter_map(|import| {
131                if import.file.ends_with(".ttc")
132                    || import.file.ends_with(".ttf")
133                    || import.file.ends_with(".otf")
134                {
135                    // Assume remote urls are valid, we need to load them at run-time (which we currently don't). For
136                    // local paths we should try to verify the existence and let the developer know ASAP.
137                    if import.file.starts_with("http://")
138                        || import.file.starts_with("https://")
139                        || crate::fileaccess::load_file(std::path::Path::new(&import.file))
140                            .is_some()
141                    {
142                        Some(import.file)
143                    } else {
144                        diag.push_error(
145                            format!("File \"{}\" not found", import.file),
146                            &import.import_token,
147                        );
148                        None
149                    }
150                } else {
151                    diag.push_error(
152                        format!("Unsupported foreign import \"{}\"", import.file),
153                        &import.import_token,
154                    );
155                    None
156                }
157            })
158            .collect();
159
160        Document {
161            node: Some(node),
162            root_component,
163            inner_components,
164            inner_structs,
165            local_registry,
166            custom_fonts,
167            exports,
168        }
169    }
170
171    pub fn exports(&self) -> &Vec<(ExportedName, Type)> {
172        &self.exports.0
173    }
174}
175
176#[derive(Debug)]
177pub struct PopupWindow {
178    pub component: Rc<Component>,
179    pub x: NamedReference,
180    pub y: NamedReference,
181    pub parent_element: ElementRc,
182}
183
184type ChildrenInsertionPoint = (ElementRc, syntax_nodes::ChildrenPlaceholder);
185
186/// Used sub types for a root component
187#[derive(Debug, Default)]
188pub struct UsedSubTypes {
189    /// All the globals used by the component and its children.
190    pub globals: Vec<Rc<Component>>,
191    /// All the structs used by the component and its children.
192    pub structs: Vec<Type>,
193    /// All the sub components use by this components and its children,
194    /// and the amount of time it is used
195    pub sub_components: Vec<Rc<Component>>,
196}
197
198/// A component is a type in the language which can be instantiated,
199/// Or is materialized for repeated expression.
200#[derive(Default, Debug)]
201pub struct Component {
202    //     node: SyntaxNode,
203    pub id: String,
204    pub root_element: ElementRc,
205
206    /// The parent element within the parent component if this component represents a repeated element
207    pub parent_element: Weak<RefCell<Element>>,
208
209    /// List of elements that are not attached to the root anymore because they have been
210    /// optimized away, but their properties may still be in use
211    pub optimized_elements: RefCell<Vec<ElementRc>>,
212
213    /// Map of resources that should be embedded in the generated code, indexed by their absolute path on
214    /// disk on the build system
215    pub embedded_file_resources:
216        RefCell<HashMap<String, crate::embedded_resources::EmbeddedResources>>,
217
218    /// The layout constraints of the root item
219    pub root_constraints: RefCell<LayoutConstraints>,
220
221    /// When creating this component and inserting "children", append them to the children of
222    /// the element pointer to by this field.
223    pub child_insertion_point: RefCell<Option<ChildrenInsertionPoint>>,
224
225    /// Code to be inserted into the constructor
226    pub setup_code: RefCell<Vec<Expression>>,
227
228    /// The list of used extra types used (recursively) by this root component.
229    /// (This only make sense on the root component)
230    pub used_types: RefCell<UsedSubTypes>,
231    pub popup_windows: RefCell<Vec<PopupWindow>>,
232
233    /// The names under which this component should be accessible
234    /// if it is a global singleton and exported.
235    pub exported_global_names: RefCell<Vec<ExportedName>>,
236
237    /// This is the main entry point for the code generators. Such a component
238    /// should have the full API, etc.
239    pub is_root_component: Cell<bool>,
240}
241
242impl Component {
243    pub fn from_node(
244        node: syntax_nodes::Component,
245        diag: &mut BuildDiagnostics,
246        tr: &TypeRegister,
247    ) -> Rc<Self> {
248        let mut child_insertion_point = None;
249        let c = Component {
250            id: parser::identifier_text(&node.DeclaredIdentifier()).unwrap_or_default(),
251            root_element: Element::from_node(
252                node.Element(),
253                "root".into(),
254                Type::Invalid,
255                &mut child_insertion_point,
256                diag,
257                tr,
258            ),
259            child_insertion_point: RefCell::new(child_insertion_point),
260            ..Default::default()
261        };
262        let c = Rc::new(c);
263        let weak = Rc::downgrade(&c);
264        recurse_elem(&c.root_element, &(), &mut |e, _| {
265            e.borrow_mut().enclosing_component = weak.clone()
266        });
267        c
268    }
269
270    /// This component is a global component introduced with the "global" keyword
271    pub fn is_global(&self) -> bool {
272        match &self.root_element.borrow().base_type {
273            Type::Void => true,
274            Type::Builtin(c) => c.is_global,
275            _ => false,
276        }
277    }
278
279    /// Returns true if use/instantiation of this component requires generating
280    /// code in Rust/C++/etc..
281    pub fn requires_code_generation(&self) -> bool {
282        !matches!(self.root_element.borrow().base_type, Type::Builtin(_))
283    }
284
285    pub fn visible_in_public_api(&self) -> bool {
286        if self.is_global() {
287            !self.exported_global_names.borrow().is_empty()
288        } else {
289            self.parent_element.upgrade().is_none() && self.is_root_component.get()
290        }
291    }
292
293    /// Returns the names of aliases to global singletons, exactly as
294    /// specified in the .60 markup (not normalized).
295    pub fn global_aliases(&self) -> Vec<String> {
296        self.exported_global_names
297            .borrow()
298            .iter()
299            .filter(|name| name.as_str() != self.root_element.borrow().id)
300            .map(|name| name.original_name())
301            .collect()
302    }
303
304    pub fn is_sub_component(&self) -> bool {
305        !self.is_root_component.get()
306            && self.parent_element.upgrade().is_none()
307            && !self.is_global()
308    }
309
310    // Number of repeaters in this component, including sub-components
311    pub fn repeater_count(&self) -> u32 {
312        let mut count = 0;
313        recurse_elem(&self.root_element, &(), &mut |element, _| {
314            let element = element.borrow();
315            if let Some(sub_component) = element.sub_component() {
316                count += sub_component.repeater_count();
317            } else if element.repeated.is_some() {
318                count += 1;
319            }
320        });
321        count
322    }
323}
324
325#[derive(Clone, Debug, Default)]
326pub struct PropertyDeclaration {
327    pub property_type: Type,
328    pub node: Option<Either<syntax_nodes::PropertyDeclaration, syntax_nodes::CallbackDeclaration>>,
329    /// Tells if getter and setter will be added to expose in the native language API
330    pub expose_in_public_api: bool,
331    /// Public API property exposed as an alias: it shouldn't be generated but instead forward to the alias.
332    pub is_alias: Option<NamedReference>,
333}
334
335impl PropertyDeclaration {
336    // For diagnostics: return a node pointing to the type
337    pub fn type_node(&self) -> Option<SyntaxNode> {
338        self.node.as_ref().map(|x| -> crate::parser::SyntaxNode {
339            x.as_ref().either(
340                |x| x.Type().map_or_else(|| x.clone().into(), |x| x.into()),
341                |x| x.clone().into(),
342            )
343        })
344    }
345}
346
347impl From<Type> for PropertyDeclaration {
348    fn from(ty: Type) -> Self {
349        PropertyDeclaration { property_type: ty, ..Self::default() }
350    }
351}
352
353#[derive(Debug, Clone)]
354pub struct TransitionPropertyAnimation {
355    /// The state id as computed in lower_state
356    pub state_id: i32,
357    /// false for 'to', true for 'out'
358    pub is_out: bool,
359    /// The content of the `animation` object
360    pub animation: ElementRc,
361}
362
363impl TransitionPropertyAnimation {
364    /// Return an expression which returns a boolean which is true if the transition is active.
365    /// The state argument is an expression referencing the state property of type StateInfo
366    pub fn condition(&self, state: Expression) -> Expression {
367        Expression::BinaryExpression {
368            lhs: Box::new(Expression::StructFieldAccess {
369                base: Box::new(state),
370                name: (if self.is_out { "previous-state" } else { "current-state" }).into(),
371            }),
372            rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
373            op: '=',
374        }
375    }
376}
377
378#[derive(Debug)]
379pub enum PropertyAnimation {
380    Static(ElementRc),
381    Transition { state_ref: Expression, animations: Vec<TransitionPropertyAnimation> },
382}
383
384impl Clone for PropertyAnimation {
385    fn clone(&self) -> Self {
386        fn deep_clone(e: &ElementRc) -> ElementRc {
387            let e = e.borrow();
388            debug_assert!(e.children.is_empty());
389            debug_assert!(e.property_declarations.is_empty());
390            debug_assert!(e.states.is_empty() && e.transitions.is_empty());
391            Rc::new(RefCell::new(Element {
392                id: e.id.clone(),
393                base_type: e.base_type.clone(),
394                bindings: e.bindings.clone(),
395                property_analysis: e.property_analysis.clone(),
396                enclosing_component: e.enclosing_component.clone(),
397                repeated: None,
398                node: e.node.clone(),
399                ..Default::default()
400            }))
401        }
402        match self {
403            PropertyAnimation::Static(e) => PropertyAnimation::Static(deep_clone(e)),
404            PropertyAnimation::Transition { state_ref, animations } => {
405                PropertyAnimation::Transition {
406                    state_ref: state_ref.clone(),
407                    animations: animations
408                        .iter()
409                        .map(|t| TransitionPropertyAnimation {
410                            state_id: t.state_id,
411                            is_out: t.is_out,
412                            animation: deep_clone(&t.animation),
413                        })
414                        .collect(),
415                }
416            }
417        }
418    }
419}
420
421pub type BindingsMap = BTreeMap<String, RefCell<BindingExpression>>;
422
423/// An Element is an instantiation of a Component
424#[derive(Default)]
425pub struct Element {
426    /// The id as named in the original .60 file.
427    ///
428    /// Note that it can only be used for lookup before inlining.
429    /// After inlining there can be duplicated id in the component.
430    /// The id are then re-assigned unique id in the assign_id pass
431    pub id: String,
432    //pub base: QualifiedTypeName,
433    pub base_type: crate::langtype::Type,
434    /// Currently contains also the callbacks. FIXME: should that be changed?
435    pub bindings: BindingsMap,
436    pub property_analysis: RefCell<HashMap<String, PropertyAnalysis>>,
437
438    pub children: Vec<ElementRc>,
439    /// The component which contains this element.
440    pub enclosing_component: Weak<Component>,
441
442    pub property_declarations: BTreeMap<String, PropertyDeclaration>,
443
444    /// Main owner for a reference to a property.
445    pub named_references: crate::namedreference::NamedReferenceContainer,
446
447    /// Tis element is part of a `for <xxx> in <model>:
448    pub repeated: Option<RepeatedElementInfo>,
449
450    pub states: Vec<State>,
451    pub transitions: Vec<Transition>,
452
453    /// true when this item's geometry is handled by a layout
454    pub child_of_layout: bool,
455    /// The property pointing to the layout info. `(horizontal, vertical)`
456    pub layout_info_prop: Option<(NamedReference, NamedReference)>,
457
458    /// true if this Element is the fake Flickable viewport
459    pub is_flickable_viewport: bool,
460
461    /// This is the component-local index of this item in the item tree array.
462    /// It is generated after the last pass and before the generators run.
463    pub item_index: once_cell::unsync::OnceCell<usize>,
464    /// the index of the first children in the tree, set with item_index
465    pub item_index_of_first_children: once_cell::unsync::OnceCell<usize>,
466
467    /// The AST node, if available
468    pub node: Option<syntax_nodes::Element>,
469}
470
471impl Spanned for Element {
472    fn span(&self) -> crate::diagnostics::Span {
473        self.node.as_ref().map(|n| n.span()).unwrap_or_default()
474    }
475
476    fn source_file(&self) -> Option<&crate::diagnostics::SourceFile> {
477        self.node.as_ref().map(|n| &n.source_file)
478    }
479}
480
481impl core::fmt::Debug for Element {
482    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
483        pretty_print(f, self, 0)
484    }
485}
486
487pub fn pretty_print(
488    f: &mut impl std::fmt::Write,
489    e: &Element,
490    indentation: usize,
491) -> std::fmt::Result {
492    if let Some(repeated) = &e.repeated {
493        write!(f, "for {}[{}] in ", repeated.model_data_id, repeated.index_id)?;
494        expression_tree::pretty_print(f, &repeated.model)?;
495        write!(f, ":")?;
496    }
497    writeln!(f, "{} := {} {{", e.id, e.base_type)?;
498    let mut indentation = indentation + 1;
499    macro_rules! indent {
500        () => {
501            for _ in 0..indentation {
502                write!(f, "   ")?
503            }
504        };
505    }
506    for (name, ty) in &e.property_declarations {
507        indent!();
508        if let Some(alias) = &ty.is_alias {
509            writeln!(f, "alias<{}> {} <=> {:?};", ty.property_type, name, alias)?
510        } else {
511            writeln!(f, "property<{}> {};", ty.property_type, name)?
512        }
513    }
514    for (name, expr) in &e.bindings {
515        let expr = expr.borrow();
516        indent!();
517        write!(f, "{}: ", name)?;
518        expression_tree::pretty_print(f, &expr.expression)?;
519        if expr.analysis.as_ref().map_or(false, |a| a.is_const) {
520            write!(f, "/*const*/")?;
521        }
522        writeln!(f, ";")?;
523        //writeln!(f, "; /*{}*/", expr.priority)?;
524        if let Some(anim) = &expr.animation {
525            indent!();
526            writeln!(f, "animate {} {:?}", name, anim)?;
527        }
528        for nr in &expr.two_way_bindings {
529            indent!();
530            writeln!(f, "{} <=> {:?};", name, nr)?;
531        }
532    }
533    if !e.states.is_empty() {
534        indent!();
535        writeln!(f, "states {:?}", e.states)?;
536    }
537    if !e.transitions.is_empty() {
538        indent!();
539        writeln!(f, "transitions {:?} ", e.transitions)?;
540    }
541    for c in &e.children {
542        indent!();
543        pretty_print(f, &c.borrow(), indentation)?
544    }
545
546    /*if let Type::Component(base) = &e.base_type {
547        pretty_print(f, &c.borrow(), indentation)?
548    }*/
549    indentation -= 1;
550    indent!();
551    writeln!(f, "}}")
552}
553
554#[derive(Clone, Default, Debug)]
555pub struct PropertyAnalysis {
556    /// true if somewhere in the code, there is an expression that changes this property with an assignment
557    pub is_set: bool,
558
559    /// True if this property might be set from a different component.
560    pub is_set_externally: bool,
561
562    /// true if somewhere in the code, an expression is reading this property
563    /// Note: currently this is only set in the binding analysis pass
564    pub is_read: bool,
565
566    /// true if this property is read from another component
567    pub is_read_externally: bool,
568}
569
570impl PropertyAnalysis {
571    /// Merge analysis from base element for inlining
572    ///
573    /// Contrary to `merge`, we don't keep the external uses because
574    /// they should come from us
575    pub fn merge_with_base(&mut self, other: &PropertyAnalysis) {
576        self.is_set |= other.is_set;
577        self.is_read |= other.is_read;
578    }
579
580    /// Merge the analysis
581    pub fn merge(&mut self, other: &PropertyAnalysis) {
582        self.is_set |= other.is_set;
583        self.is_read |= other.is_read;
584        self.is_read_externally |= other.is_read_externally;
585        self.is_set_externally |= other.is_set_externally;
586    }
587
588    /// Return true if it is read or set or used in any way
589    pub fn is_used(&self) -> bool {
590        self.is_read || self.is_read_externally || self.is_set || self.is_set_externally
591    }
592}
593
594#[derive(Debug, Clone)]
595pub struct ListViewInfo {
596    pub viewport_y: NamedReference,
597    pub viewport_height: NamedReference,
598    pub viewport_width: NamedReference,
599    /// The ListView's inner visible height (not counting eventual scrollbar)
600    pub listview_height: NamedReference,
601    /// The ListView's inner visible width (not counting eventual scrollbar)
602    pub listview_width: NamedReference,
603}
604
605#[derive(Debug, Clone)]
606/// If the parent element is a repeated element, this has information about the models
607pub struct RepeatedElementInfo {
608    pub model: Expression,
609    pub model_data_id: String,
610    pub index_id: String,
611    /// A conditional element is just a for whose model is a boolean expression
612    ///
613    /// When this is true, the model is of type boolean instead of Model
614    pub is_conditional_element: bool,
615    /// When the for is the delegate of a ListView
616    pub is_listview: Option<ListViewInfo>,
617}
618
619pub type ElementRc = Rc<RefCell<Element>>;
620
621impl Element {
622    pub fn from_node(
623        node: syntax_nodes::Element,
624        id: String,
625        parent_type: Type,
626        component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
627        diag: &mut BuildDiagnostics,
628        tr: &TypeRegister,
629    ) -> ElementRc {
630        let base_type = if let Some(base_node) = node.QualifiedName() {
631            let base = QualifiedTypeName::from_node(base_node.clone());
632            let base_string = base.to_string();
633            if base_string == "Clip" {
634                diag.push_warning("The 'Clip' element is deprecated, use the 'clip' property on a Rectangle instead".into(), &base_node);
635            }
636            match parent_type.lookup_type_for_child_element(&base_string, tr) {
637                Ok(Type::Component(c)) if c.is_global() => {
638                    diag.push_error(
639                        "Cannot create an instance of a global component".into(),
640                        &base_node,
641                    );
642                    Type::Invalid
643                }
644                Ok(ty @ Type::Component(_)) | Ok(ty @ Type::Builtin(_)) => ty,
645                Ok(ty) => {
646                    diag.push_error(format!("'{}' cannot be used as an element", ty), &base_node);
647                    Type::Invalid
648                }
649                Err(err) => {
650                    diag.push_error(err, &base_node);
651                    Type::Invalid
652                }
653            }
654        } else {
655            if parent_type != Type::Invalid {
656                // This should normally never happen because the parser does not allow for this
657                assert!(diag.has_error());
658                return ElementRc::default();
659            }
660
661            // This must be a global component it can only have properties and callback
662            let mut error_on = |node: &dyn Spanned, what: &str| {
663                diag.push_error(format!("A global component cannot have {}", what), node);
664            };
665            node.SubElement().for_each(|n| error_on(&n, "sub elements"));
666            node.RepeatedElement().for_each(|n| error_on(&n, "sub elements"));
667            if let Some(n) = node.ChildrenPlaceholder() {
668                error_on(&n, "sub elements");
669            }
670            node.PropertyAnimation().for_each(|n| error_on(&n, "animations"));
671            node.States().for_each(|n| error_on(&n, "states"));
672            node.Transitions().for_each(|n| error_on(&n, "transitions"));
673            Type::Void
674        };
675        let mut r = Element { id, base_type, node: Some(node.clone()), ..Default::default() };
676
677        for prop_decl in node.PropertyDeclaration() {
678            let prop_type = prop_decl
679                .Type()
680                .map(|type_node| {
681                    let prop_type = type_from_node(type_node.clone(), diag, tr);
682
683                    if prop_type != Type::Invalid && !prop_type.is_property_type() {
684                        diag.push_error(
685                            format!("'{}' is not a valid property type", prop_type),
686                            &type_node,
687                        );
688                    }
689                    prop_type
690                })
691                // Type::Void is used for two way bindings without type specified
692                .unwrap_or(Type::InferredProperty);
693
694            let unresolved_prop_name =
695                unwrap_or_continue!(parser::identifier_text(&prop_decl.DeclaredIdentifier()); diag);
696            let PropertyLookupResult {
697                resolved_name: prop_name,
698                property_type: maybe_existing_prop_type,
699            } = r.lookup_property(&unresolved_prop_name);
700            if !matches!(maybe_existing_prop_type, Type::Invalid) {
701                diag.push_error(
702                    format!("Cannot override property '{}'", prop_name),
703                    &prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(),
704                )
705            }
706
707            r.property_declarations.insert(
708                prop_name.to_string(),
709                PropertyDeclaration {
710                    property_type: prop_type,
711                    node: Some(Either::Left(prop_decl.clone())),
712                    ..Default::default()
713                },
714            );
715
716            if let Some(csn) = prop_decl.BindingExpression() {
717                if r.bindings
718                    .insert(
719                        prop_name.to_string(),
720                        BindingExpression::new_uncompiled(csn.into()).into(),
721                    )
722                    .is_some()
723                {
724                    diag.push_error(
725                        "Duplicated property binding".into(),
726                        &prop_decl.DeclaredIdentifier(),
727                    );
728                }
729            }
730            if let Some(csn) = prop_decl.TwoWayBinding() {
731                if r.bindings
732                    .insert(prop_name.into(), BindingExpression::new_uncompiled(csn.into()).into())
733                    .is_some()
734                {
735                    diag.push_error(
736                        "Duplicated property binding".into(),
737                        &prop_decl.DeclaredIdentifier(),
738                    );
739                }
740            }
741        }
742
743        r.parse_bindings(
744            node.Binding().filter_map(|b| {
745                Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into()))
746            }),
747            diag,
748        );
749        r.parse_bindings(
750            node.TwoWayBinding()
751                .filter_map(|b| Some((b.child_token(SyntaxKind::Identifier)?, b.into()))),
752            diag,
753        );
754
755        if let Type::Builtin(builtin_base) = &r.base_type {
756            for (prop, info) in &builtin_base.properties {
757                if let Some(expr) = &info.default_value {
758                    r.bindings
759                        .entry(prop.clone())
760                        .or_insert_with(|| RefCell::new(expr.clone().into()));
761                }
762            }
763        }
764
765        for sig_decl in node.CallbackDeclaration() {
766            let name =
767                unwrap_or_continue!(parser::identifier_text(&sig_decl.DeclaredIdentifier()); diag);
768
769            if let Some(csn) = sig_decl.TwoWayBinding() {
770                r.bindings
771                    .insert(name.clone(), BindingExpression::new_uncompiled(csn.into()).into());
772                r.property_declarations.insert(
773                    name,
774                    PropertyDeclaration {
775                        property_type: Type::InferredCallback,
776                        node: Some(Either::Right(sig_decl)),
777                        ..Default::default()
778                    },
779                );
780                continue;
781            }
782
783            let args = sig_decl.Type().map(|node_ty| type_from_node(node_ty, diag, tr)).collect();
784            let return_type = sig_decl
785                .ReturnType()
786                .map(|ret_ty| Box::new(type_from_node(ret_ty.Type(), diag, tr)));
787            r.property_declarations.insert(
788                name,
789                PropertyDeclaration {
790                    property_type: Type::Callback { return_type, args },
791                    node: Some(Either::Right(sig_decl)),
792                    ..Default::default()
793                },
794            );
795        }
796
797        for con_node in node.CallbackConnection() {
798            let unresolved_name = unwrap_or_continue!(parser::identifier_text(&con_node); diag);
799            let PropertyLookupResult { resolved_name, property_type } =
800                r.lookup_property(&unresolved_name);
801            if let Type::Callback { args, .. } = &property_type {
802                let num_arg = con_node.DeclaredIdentifier().count();
803                if num_arg > args.len() {
804                    diag.push_error(
805                        format!(
806                            "'{}' only has {} arguments, but {} were provided",
807                            unresolved_name,
808                            args.len(),
809                            num_arg
810                        ),
811                        &con_node.child_token(SyntaxKind::Identifier).unwrap(),
812                    );
813                }
814            } else if property_type == Type::InferredCallback {
815                // argument matching will happen later
816            } else {
817                diag.push_error(
818                    format!("'{}' is not a callback in {}", unresolved_name, r.base_type),
819                    &con_node.child_token(SyntaxKind::Identifier).unwrap(),
820                );
821                continue;
822            }
823            if r.bindings
824                .insert(
825                    resolved_name.into_owned(),
826                    BindingExpression::new_uncompiled(con_node.clone().into()).into(),
827                )
828                .is_some()
829            {
830                diag.push_error(
831                    "Duplicated callback".into(),
832                    &con_node.child_token(SyntaxKind::Identifier).unwrap(),
833                );
834            }
835        }
836
837        for anim in node.PropertyAnimation() {
838            if let Some(star) = anim.child_token(SyntaxKind::Star) {
839                diag.push_error(
840                    "catch-all property is only allowed within transitions".into(),
841                    &star,
842                )
843            };
844            for prop_name_token in anim.QualifiedName() {
845                match QualifiedTypeName::from_node(prop_name_token.clone()).members.as_slice() {
846                    [unresolved_prop_name] => {
847                        let PropertyLookupResult { resolved_name, property_type } =
848                            r.lookup_property(unresolved_prop_name);
849                        if let Some(anim_element) = animation_element_from_node(
850                            &anim,
851                            &prop_name_token,
852                            property_type,
853                            diag,
854                            tr,
855                        ) {
856                            if unresolved_prop_name != resolved_name.as_ref() {
857                                diag.push_property_deprecation_warning(
858                                    unresolved_prop_name,
859                                    &resolved_name,
860                                    &prop_name_token,
861                                );
862                            }
863
864                            let expr_binding =
865                                r.bindings.entry(resolved_name.to_string()).or_insert_with(|| {
866                                    let mut r = BindingExpression::from(Expression::Invalid);
867                                    r.priority = 1;
868                                    r.span = Some(prop_name_token.to_source_location());
869                                    r.into()
870                                });
871                            if expr_binding
872                                .get_mut()
873                                .animation
874                                .replace(PropertyAnimation::Static(anim_element))
875                                .is_some()
876                            {
877                                diag.push_error("Duplicated animation".into(), &prop_name_token)
878                            }
879                        }
880                    }
881                    _ => diag.push_error(
882                        "Can only refer to property in the current element".into(),
883                        &prop_name_token,
884                    ),
885                }
886            }
887        }
888
889        let mut children_placeholder = None;
890        let r = ElementRc::new(RefCell::new(r));
891
892        for se in node.children() {
893            if se.kind() == SyntaxKind::SubElement {
894                let parent_type = r.borrow().base_type.clone();
895                r.borrow_mut().children.push(Element::from_sub_element_node(
896                    se.into(),
897                    parent_type,
898                    component_child_insertion_point,
899                    diag,
900                    tr,
901                ));
902            } else if se.kind() == SyntaxKind::RepeatedElement {
903                let rep = Element::from_repeated_node(
904                    se.into(),
905                    &r,
906                    component_child_insertion_point,
907                    diag,
908                    tr,
909                );
910                r.borrow_mut().children.push(rep);
911            } else if se.kind() == SyntaxKind::ConditionalElement {
912                let rep = Element::from_conditional_node(
913                    se.into(),
914                    r.borrow().base_type.clone(),
915                    component_child_insertion_point,
916                    diag,
917                    tr,
918                );
919                r.borrow_mut().children.push(rep);
920            } else if se.kind() == SyntaxKind::ChildrenPlaceholder {
921                if children_placeholder.is_some() {
922                    diag.push_error(
923                        "The @children placeholder can only appear once in an element".into(),
924                        &se,
925                    )
926                } else {
927                    children_placeholder = Some(se.clone().into());
928                }
929            }
930        }
931
932        if let Some(children_placeholder) = children_placeholder {
933            if component_child_insertion_point.is_some() {
934                diag.push_error(
935                    "The @children placeholder can only appear once in an element hierarchy".into(),
936                    &children_placeholder,
937                )
938            } else {
939                *component_child_insertion_point = Some((r.clone(), children_placeholder));
940            }
941        }
942
943        for state in node.States().flat_map(|s| s.State()) {
944            let s = State {
945                id: parser::identifier_text(&state.DeclaredIdentifier()).unwrap_or_default(),
946                condition: state.Expression().map(|e| Expression::Uncompiled(e.into())),
947                property_changes: state
948                    .StatePropertyChange()
949                    .filter_map(|s| {
950                        lookup_property_from_qualified_name(s.QualifiedName(), &r, diag).map(
951                            |(ne, _)| (ne, Expression::Uncompiled(s.BindingExpression().into()), s),
952                        )
953                    })
954                    .collect(),
955            };
956            r.borrow_mut().states.push(s);
957        }
958
959        for trs in node.Transitions().flat_map(|s| s.Transition()) {
960            if let Some(star) = trs.child_token(SyntaxKind::Star) {
961                diag.push_error("TODO: catch-all not yet implemented".into(), &star);
962            };
963            let trans = Transition {
964                is_out: parser::identifier_text(&trs).unwrap_or_default() == "out",
965                state_id: parser::identifier_text(&trs.DeclaredIdentifier()).unwrap_or_default(),
966                property_animations: trs
967                    .PropertyAnimation()
968                    .flat_map(|pa| pa.QualifiedName().map(move |qn| (pa.clone(), qn)))
969                    .filter_map(|(pa, qn)| {
970                        lookup_property_from_qualified_name(qn.clone(), &r, diag).and_then(
971                            |(ne, prop_type)| {
972                                animation_element_from_node(&pa, &qn, prop_type, diag, tr)
973                                    .map(|anim_element| (ne, qn.to_source_location(), anim_element))
974                            },
975                        )
976                    })
977                    .collect(),
978                node: trs.DeclaredIdentifier().into(),
979            };
980            r.borrow_mut().transitions.push(trans);
981        }
982
983        r
984    }
985
986    fn from_sub_element_node(
987        node: syntax_nodes::SubElement,
988        parent_type: Type,
989        component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
990        diag: &mut BuildDiagnostics,
991        tr: &TypeRegister,
992    ) -> ElementRc {
993        let id = parser::identifier_text(&node).unwrap_or_default();
994        if matches!(id.as_ref(), "parent" | "self" | "root") {
995            diag.push_error(
996                format!("'{}' is a reserved id", id),
997                &node.child_token(SyntaxKind::Identifier).unwrap(),
998            )
999        }
1000        Element::from_node(
1001            node.Element(),
1002            id,
1003            parent_type,
1004            component_child_insertion_point,
1005            diag,
1006            tr,
1007        )
1008    }
1009
1010    fn from_repeated_node(
1011        node: syntax_nodes::RepeatedElement,
1012        parent: &ElementRc,
1013        component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1014        diag: &mut BuildDiagnostics,
1015        tr: &TypeRegister,
1016    ) -> ElementRc {
1017        let is_listview = if parent.borrow().base_type.to_string() == "ListView" {
1018            Some(ListViewInfo {
1019                viewport_y: NamedReference::new(parent, "viewport-y"),
1020                viewport_height: NamedReference::new(parent, "viewport-height"),
1021                viewport_width: NamedReference::new(parent, "viewport-width"),
1022                listview_height: NamedReference::new(parent, "visible-height"),
1023                listview_width: NamedReference::new(parent, "visible-width"),
1024            })
1025        } else {
1026            None
1027        };
1028        let rei = RepeatedElementInfo {
1029            model: Expression::Uncompiled(node.Expression().into()),
1030            model_data_id: node
1031                .DeclaredIdentifier()
1032                .and_then(|n| parser::identifier_text(&n))
1033                .unwrap_or_default(),
1034            index_id: node
1035                .RepeatedIndex()
1036                .and_then(|r| parser::identifier_text(&r))
1037                .unwrap_or_default(),
1038            is_conditional_element: false,
1039            is_listview,
1040        };
1041        let e = Element::from_sub_element_node(
1042            node.SubElement(),
1043            parent.borrow().base_type.clone(),
1044            component_child_insertion_point,
1045            diag,
1046            tr,
1047        );
1048        e.borrow_mut().repeated = Some(rei);
1049        e
1050    }
1051
1052    fn from_conditional_node(
1053        node: syntax_nodes::ConditionalElement,
1054        parent_type: Type,
1055        component_child_insertion_point: &mut Option<ChildrenInsertionPoint>,
1056        diag: &mut BuildDiagnostics,
1057        tr: &TypeRegister,
1058    ) -> ElementRc {
1059        let rei = RepeatedElementInfo {
1060            model: Expression::Uncompiled(node.Expression().into()),
1061            model_data_id: String::new(),
1062            index_id: String::new(),
1063            is_conditional_element: true,
1064            is_listview: None,
1065        };
1066        let e = Element::from_sub_element_node(
1067            node.SubElement(),
1068            parent_type,
1069            component_child_insertion_point,
1070            diag,
1071            tr,
1072        );
1073        e.borrow_mut().repeated = Some(rei);
1074        e
1075    }
1076
1077    /// Return the type of a property in this element or its base, along with the final name, in case
1078    /// the provided name points towards a property alias. Type::Invalid is returned if the property does
1079    /// not exist.
1080    pub fn lookup_property<'a>(&self, name: &'a str) -> PropertyLookupResult<'a> {
1081        self.property_declarations.get(name).cloned().map(|decl| decl.property_type).map_or_else(
1082            || self.base_type.lookup_property(name),
1083            |property_type| PropertyLookupResult { resolved_name: name.into(), property_type },
1084        )
1085    }
1086
1087    /// Return the Span of this element in the AST for error reporting
1088    pub fn span(&self) -> crate::diagnostics::Span {
1089        self.node.as_ref().map(|n| n.span()).unwrap_or_default()
1090    }
1091
1092    fn parse_bindings(
1093        &mut self,
1094        bindings: impl Iterator<Item = (crate::parser::SyntaxToken, SyntaxNode)>,
1095        diag: &mut BuildDiagnostics,
1096    ) {
1097        for (name_token, b) in bindings {
1098            let unresolved_name = crate::parser::normalize_identifier(name_token.text());
1099            let PropertyLookupResult { resolved_name, property_type } =
1100                self.lookup_property(&unresolved_name);
1101            if !property_type.is_property_type() {
1102                diag.push_error(
1103                    match property_type {
1104                        Type::Invalid => {
1105                            if self.base_type != Type::Invalid {
1106                                format!(
1107                                    "Unknown property {} in {}",
1108                                    unresolved_name, self.base_type
1109                                )
1110                            } else {
1111                                continue;
1112                            }
1113                        }
1114                        Type::Callback { .. } => {
1115                            format!("'{}' is a callback. Use `=>` to connect", unresolved_name)
1116                        }
1117                        _ => format!(
1118                            "Cannot assign to {} in {} because it does not have a valid property type",
1119                            unresolved_name, self.base_type,
1120                        ),
1121                    },
1122                    &name_token,
1123                );
1124            }
1125
1126            if resolved_name != unresolved_name {
1127                diag.push_property_deprecation_warning(
1128                    &unresolved_name,
1129                    &resolved_name,
1130                    &name_token,
1131                );
1132            }
1133
1134            if self
1135                .bindings
1136                .insert(resolved_name.to_string(), BindingExpression::new_uncompiled(b).into())
1137                .is_some()
1138            {
1139                diag.push_error("Duplicated property binding".into(), &name_token);
1140            }
1141        }
1142    }
1143
1144    pub fn native_class(&self) -> Option<Rc<NativeClass>> {
1145        let mut base_type = self.base_type.clone();
1146        loop {
1147            match &base_type {
1148                Type::Component(component) => {
1149                    base_type = component.root_element.clone().borrow().base_type.clone();
1150                }
1151                Type::Builtin(builtin) => break Some(builtin.native_class.clone()),
1152                Type::Native(native) => break Some(native.clone()),
1153                _ => break None,
1154            }
1155        }
1156    }
1157
1158    pub fn builtin_type(&self) -> Option<Rc<BuiltinElement>> {
1159        let mut base_type = self.base_type.clone();
1160        loop {
1161            match &base_type {
1162                Type::Component(component) => {
1163                    base_type = component.root_element.clone().borrow().base_type.clone();
1164                }
1165                Type::Builtin(builtin) => break Some(builtin.clone()),
1166                _ => break None,
1167            }
1168        }
1169    }
1170
1171    pub fn layout_info_prop(&self, orientation: Orientation) -> Option<&NamedReference> {
1172        self.layout_info_prop.as_ref().map(|prop| match orientation {
1173            Orientation::Horizontal => &prop.0,
1174            Orientation::Vertical => &prop.1,
1175        })
1176    }
1177
1178    /// Returns the element's name as specified in the markup, not normalized.
1179    pub fn original_name(&self) -> String {
1180        self.node
1181            .as_ref()
1182            .and_then(|n| n.child_token(parser::SyntaxKind::Identifier))
1183            .map(|n| n.to_string())
1184            .unwrap_or_else(|| self.id.clone())
1185    }
1186
1187    /// Return true if the binding is set, either on this element or in a base
1188    ///
1189    /// If `need_explicit` is true, then only consider binding set in the code, not the ones set
1190    /// by the compiler later.
1191    pub fn is_binding_set(self: &Element, property_name: &str, need_explicit: bool) -> bool {
1192        if self.bindings.get(property_name).map_or(false, |b| {
1193            b.borrow().has_binding() && (!need_explicit || b.borrow().priority > 0)
1194        }) {
1195            true
1196        } else if let Type::Component(base) = &self.base_type {
1197            base.root_element.borrow().is_binding_set(property_name, need_explicit)
1198        } else {
1199            false
1200        }
1201    }
1202
1203    /// Set the property `property_name` of this Element only if it was not set.
1204    /// the `expression_fn` will only be called if it isn't set
1205    pub fn set_binding_if_not_set(
1206        &mut self,
1207        property_name: String,
1208        expression_fn: impl FnOnce() -> Expression,
1209    ) {
1210        if self.is_binding_set(&property_name, false) {
1211            return;
1212        }
1213
1214        match self.bindings.entry(property_name) {
1215            Entry::Vacant(vacant_entry) => {
1216                let mut binding: BindingExpression = expression_fn().into();
1217                binding.priority = i32::MAX;
1218                vacant_entry.insert(binding.into());
1219            }
1220            Entry::Occupied(mut existing_entry) => {
1221                let mut binding: BindingExpression = expression_fn().into();
1222                binding.priority = i32::MAX;
1223                existing_entry.get_mut().get_mut().merge_with(&binding);
1224            }
1225        };
1226    }
1227
1228    pub fn sub_component(&self) -> Option<&Rc<Component>> {
1229        if self.repeated.is_some() {
1230            None
1231        } else if let Type::Component(sub_component) = &self.base_type {
1232            Some(sub_component)
1233        } else {
1234            None
1235        }
1236    }
1237}
1238
1239/// Create a Type for this node
1240pub fn type_from_node(
1241    node: syntax_nodes::Type,
1242    diag: &mut BuildDiagnostics,
1243    tr: &TypeRegister,
1244) -> Type {
1245    if let Some(qualified_type_node) = node.QualifiedName() {
1246        let qualified_type = QualifiedTypeName::from_node(qualified_type_node.clone());
1247
1248        let prop_type = tr.lookup_qualified(&qualified_type.members);
1249
1250        if prop_type == Type::Invalid {
1251            diag.push_error(format!("Unknown type '{}'", qualified_type), &qualified_type_node);
1252        }
1253        prop_type
1254    } else if let Some(object_node) = node.ObjectType() {
1255        type_struct_from_node(object_node, diag, tr)
1256    } else if let Some(array_node) = node.ArrayType() {
1257        Type::Array(Box::new(type_from_node(array_node.Type(), diag, tr)))
1258    } else {
1259        assert!(diag.has_error());
1260        Type::Invalid
1261    }
1262}
1263
1264/// Create a Type::Object from a syntax_nodes::ObjectType
1265pub fn type_struct_from_node(
1266    object_node: syntax_nodes::ObjectType,
1267    diag: &mut BuildDiagnostics,
1268    tr: &TypeRegister,
1269) -> Type {
1270    let fields = object_node
1271        .ObjectTypeMember()
1272        .map(|member| {
1273            (
1274                parser::identifier_text(&member).unwrap_or_default(),
1275                type_from_node(member.Type(), diag, tr),
1276            )
1277        })
1278        .collect();
1279    Type::Struct { fields, name: None, node: Some(object_node) }
1280}
1281
1282fn animation_element_from_node(
1283    anim: &syntax_nodes::PropertyAnimation,
1284    prop_name: &syntax_nodes::QualifiedName,
1285    prop_type: Type,
1286    diag: &mut BuildDiagnostics,
1287    tr: &TypeRegister,
1288) -> Option<ElementRc> {
1289    let anim_type = tr.property_animation_type_for_property(prop_type);
1290    if !matches!(anim_type, Type::Builtin(..)) {
1291        diag.push_error(
1292            format!(
1293                "'{}' is not a property that can be animated",
1294                prop_name.text().to_string().trim()
1295            ),
1296            prop_name,
1297        );
1298        None
1299    } else {
1300        let mut anim_element =
1301            Element { id: "".into(), base_type: anim_type, node: None, ..Default::default() };
1302        anim_element.parse_bindings(
1303            anim.Binding().filter_map(|b| {
1304                Some((b.child_token(SyntaxKind::Identifier)?, b.BindingExpression().into()))
1305            }),
1306            diag,
1307        );
1308        Some(Rc::new(RefCell::new(anim_element)))
1309    }
1310}
1311
1312#[derive(Default, Debug, Clone)]
1313pub struct QualifiedTypeName {
1314    pub members: Vec<String>,
1315}
1316
1317impl QualifiedTypeName {
1318    pub fn from_node(node: syntax_nodes::QualifiedName) -> Self {
1319        debug_assert_eq!(node.kind(), SyntaxKind::QualifiedName);
1320        let members = node
1321            .children_with_tokens()
1322            .filter(|n| n.kind() == SyntaxKind::Identifier)
1323            .filter_map(|x| x.as_token().map(|x| crate::parser::normalize_identifier(x.text())))
1324            .collect();
1325        Self { members }
1326    }
1327}
1328
1329impl std::fmt::Display for QualifiedTypeName {
1330    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1331        write!(f, "{}", self.members.join("."))
1332    }
1333}
1334
1335/// Return a NamedReference, if the reference is invalid, there will be a diagnostic
1336fn lookup_property_from_qualified_name(
1337    node: syntax_nodes::QualifiedName,
1338    r: &Rc<RefCell<Element>>,
1339    diag: &mut BuildDiagnostics,
1340) -> Option<(NamedReference, Type)> {
1341    let qualname = QualifiedTypeName::from_node(node.clone());
1342    match qualname.members.as_slice() {
1343        [unresolved_prop_name] => {
1344            let PropertyLookupResult { resolved_name, property_type } =
1345                r.borrow().lookup_property(unresolved_prop_name.as_ref());
1346            if !property_type.is_property_type() {
1347                diag.push_error(format!("'{}' is not a valid property", qualname), &node);
1348            }
1349            Some((NamedReference::new(r, &resolved_name), property_type))
1350        }
1351        [elem_id, unresolved_prop_name] => {
1352            if let Some(element) = find_element_by_id(r, elem_id.as_ref()) {
1353                let PropertyLookupResult { resolved_name, property_type } =
1354                    element.borrow().lookup_property(unresolved_prop_name.as_ref());
1355                if !property_type.is_property_type() {
1356                    diag.push_error(
1357                        format!("'{}' not found in '{}'", unresolved_prop_name, elem_id),
1358                        &node,
1359                    );
1360                }
1361                Some((NamedReference::new(&element, &resolved_name), property_type))
1362            } else {
1363                diag.push_error(format!("'{}' is not a valid element id", elem_id), &node);
1364                None
1365            }
1366        }
1367        _ => {
1368            diag.push_error(format!("'{}' is not a valid property", qualname), &node);
1369            None
1370        }
1371    }
1372}
1373
1374/// FIXME: this is duplicated the resolving pass. Also, we should use a hash table
1375fn find_element_by_id(e: &ElementRc, name: &str) -> Option<ElementRc> {
1376    if e.borrow().id == name {
1377        return Some(e.clone());
1378    }
1379    for x in &e.borrow().children {
1380        if x.borrow().repeated.is_some() {
1381            continue;
1382        }
1383        if let Some(x) = find_element_by_id(x, name) {
1384            return Some(x);
1385        }
1386    }
1387
1388    None
1389}
1390
1391/// Find the parent element to a given element.
1392/// (since there is no parent mapping we need to fo an exhaustive search)
1393pub fn find_parent_element(e: &ElementRc) -> Option<ElementRc> {
1394    fn recurse(base: &ElementRc, e: &ElementRc) -> Option<ElementRc> {
1395        for child in &base.borrow().children {
1396            if Rc::ptr_eq(child, e) {
1397                return Some(base.clone());
1398            }
1399            if let Some(x) = recurse(child, e) {
1400                return Some(x);
1401            }
1402        }
1403        None
1404    }
1405
1406    let root = e.borrow().enclosing_component.upgrade().unwrap().root_element.clone();
1407    if Rc::ptr_eq(&root, e) {
1408        return None;
1409    }
1410    recurse(&root, e)
1411}
1412
1413/// Call the visitor for each children of the element recursively, starting with the element itself
1414///
1415/// The state returned by the visitor is passed to the children
1416pub fn recurse_elem<State>(
1417    elem: &ElementRc,
1418    state: &State,
1419    vis: &mut impl FnMut(&ElementRc, &State) -> State,
1420) {
1421    let state = vis(elem, state);
1422    for sub in &elem.borrow().children {
1423        recurse_elem(sub, &state, vis);
1424    }
1425}
1426
1427/// Same as [`recurse_elem`] but include the elements form sub_components
1428pub fn recurse_elem_including_sub_components<State>(
1429    component: &Component,
1430    state: &State,
1431    vis: &mut impl FnMut(&ElementRc, &State) -> State,
1432) {
1433    recurse_elem(&component.root_element, state, &mut |elem, state| {
1434        debug_assert!(std::ptr::eq(
1435            component as *const Component,
1436            (&*elem.borrow().enclosing_component.upgrade().unwrap()) as *const Component
1437        ));
1438        if elem.borrow().repeated.is_some() {
1439            if let Type::Component(base) = &elem.borrow().base_type {
1440                if base.parent_element.upgrade().is_some() {
1441                    recurse_elem_including_sub_components(base, state, vis);
1442                }
1443            }
1444        }
1445        vis(elem, state)
1446    });
1447    component
1448        .popup_windows
1449        .borrow()
1450        .iter()
1451        .for_each(|p| recurse_elem_including_sub_components(&p.component, state, vis))
1452}
1453
1454/// Same as recurse_elem, but will take the children from the element as to not keep the element borrow
1455pub fn recurse_elem_no_borrow<State>(
1456    elem: &ElementRc,
1457    state: &State,
1458    vis: &mut impl FnMut(&ElementRc, &State) -> State,
1459) {
1460    let state = vis(elem, state);
1461    let children = elem.borrow().children.clone();
1462    for sub in &children {
1463        recurse_elem_no_borrow(sub, &state, vis);
1464    }
1465}
1466
1467/// Same as [`recurse_elem`] but include the elements form sub_components
1468pub fn recurse_elem_including_sub_components_no_borrow<State>(
1469    component: &Component,
1470    state: &State,
1471    vis: &mut impl FnMut(&ElementRc, &State) -> State,
1472) {
1473    recurse_elem_no_borrow(&component.root_element, state, &mut |elem, state| {
1474        let base = if elem.borrow().repeated.is_some() {
1475            if let Type::Component(base) = &elem.borrow().base_type {
1476                Some(base.clone())
1477            } else {
1478                None
1479            }
1480        } else {
1481            None
1482        };
1483        if let Some(base) = base {
1484            recurse_elem_including_sub_components_no_borrow(&base, state, vis);
1485        }
1486        vis(elem, state)
1487    });
1488    component
1489        .popup_windows
1490        .borrow()
1491        .iter()
1492        .for_each(|p| recurse_elem_including_sub_components_no_borrow(&p.component, state, vis));
1493    component
1494        .used_types
1495        .borrow()
1496        .globals
1497        .iter()
1498        .for_each(|p| recurse_elem_including_sub_components_no_borrow(p, state, vis));
1499}
1500
1501/// This visit the binding attached to this element, but does not recurse in children elements
1502/// Also does not recurse within the expressions.
1503///
1504/// This code will temporarily move the bindings or states member so it can call the visitor without
1505/// maintaining a borrow on the RefCell.
1506pub fn visit_element_expressions(
1507    elem: &ElementRc,
1508    mut vis: impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
1509) {
1510    fn visit_element_expressions_simple(
1511        elem: &ElementRc,
1512        vis: &mut impl FnMut(&mut Expression, Option<&str>, &dyn Fn() -> Type),
1513    ) {
1514        for (name, expr) in &elem.borrow().bindings {
1515            vis(&mut *expr.borrow_mut(), Some(name.as_str()), &|| {
1516                elem.borrow().lookup_property(name).property_type
1517            });
1518
1519            match &mut expr.borrow_mut().animation {
1520                Some(PropertyAnimation::Static(e)) => visit_element_expressions_simple(e, vis),
1521                Some(PropertyAnimation::Transition { animations, state_ref }) => {
1522                    vis(state_ref, None, &|| Type::Int32);
1523                    for a in animations {
1524                        visit_element_expressions_simple(&a.animation, vis)
1525                    }
1526                }
1527                None => (),
1528            }
1529        }
1530    }
1531
1532    let repeated = std::mem::take(&mut elem.borrow_mut().repeated);
1533    if let Some(mut r) = repeated {
1534        let is_conditional_element = r.is_conditional_element;
1535        vis(&mut r.model, None, &|| if is_conditional_element { Type::Bool } else { Type::Model });
1536        elem.borrow_mut().repeated = Some(r)
1537    }
1538    visit_element_expressions_simple(elem, &mut vis);
1539    let mut states = std::mem::take(&mut elem.borrow_mut().states);
1540    for s in &mut states {
1541        if let Some(cond) = s.condition.as_mut() {
1542            vis(cond, None, &|| Type::Bool)
1543        }
1544        for (ne, e, _) in &mut s.property_changes {
1545            vis(e, Some(ne.name()), &|| {
1546                ne.element().borrow().lookup_property(ne.name()).property_type
1547            });
1548        }
1549    }
1550    elem.borrow_mut().states = states;
1551
1552    let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions);
1553    for t in &mut transitions {
1554        for (_, _, a) in &mut t.property_animations {
1555            visit_element_expressions_simple(a, &mut vis);
1556        }
1557    }
1558    elem.borrow_mut().transitions = transitions;
1559}
1560
1561/// Visit all the named reference in an element
1562/// But does not recurse in sub-elements. (unlike [`visit_all_named_references`] which recurse)
1563pub fn visit_all_named_references_in_element(
1564    elem: &ElementRc,
1565    mut vis: impl FnMut(&mut NamedReference),
1566) {
1567    fn recurse_expression(expr: &mut Expression, vis: &mut impl FnMut(&mut NamedReference)) {
1568        expr.visit_mut(|sub| recurse_expression(sub, vis));
1569        match expr {
1570            Expression::PropertyReference(r) | Expression::CallbackReference(r) => vis(r),
1571            Expression::LayoutCacheAccess { layout_cache_prop, .. } => vis(layout_cache_prop),
1572            Expression::SolveLayout(l, _) => l.visit_named_references(vis),
1573            Expression::ComputeLayoutInfo(l, _) => l.visit_named_references(vis),
1574            // This is not really a named reference, but the result is the same, it need to be updated
1575            // FIXME: this should probably be lowered into a PropertyReference
1576            Expression::RepeaterModelReference { element }
1577            | Expression::RepeaterIndexReference { element } => {
1578                // FIXME: this is questionable
1579                let mut nc = NamedReference::new(&element.upgrade().unwrap(), "$model");
1580                vis(&mut nc);
1581                debug_assert!(nc.element().borrow().repeated.is_some());
1582                *element = Rc::downgrade(&nc.element());
1583            }
1584            _ => {}
1585        }
1586    }
1587    visit_element_expressions(elem, |expr, _, _| recurse_expression(expr, &mut vis));
1588    let mut states = std::mem::take(&mut elem.borrow_mut().states);
1589    for s in &mut states {
1590        for (r, _, _) in &mut s.property_changes {
1591            vis(r);
1592        }
1593    }
1594    elem.borrow_mut().states = states;
1595    let mut transitions = std::mem::take(&mut elem.borrow_mut().transitions);
1596    for t in &mut transitions {
1597        for (r, _, _) in &mut t.property_animations {
1598            vis(r)
1599        }
1600    }
1601    elem.borrow_mut().transitions = transitions;
1602    let mut repeated = std::mem::take(&mut elem.borrow_mut().repeated);
1603    if let Some(r) = &mut repeated {
1604        if let Some(lv) = &mut r.is_listview {
1605            vis(&mut lv.viewport_y);
1606            vis(&mut lv.viewport_height);
1607            vis(&mut lv.viewport_width);
1608            vis(&mut lv.listview_height);
1609            vis(&mut lv.listview_width);
1610        }
1611    }
1612    elem.borrow_mut().repeated = repeated;
1613    let mut layout_info_prop = std::mem::take(&mut elem.borrow_mut().layout_info_prop);
1614    layout_info_prop.as_mut().map(|(h, b)| (vis(h), vis(b)));
1615    elem.borrow_mut().layout_info_prop = layout_info_prop;
1616
1617    // visit two way bindings
1618    for expr in elem.borrow().bindings.values() {
1619        for nr in &mut expr.borrow_mut().two_way_bindings {
1620            vis(nr);
1621        }
1622    }
1623
1624    let mut property_declarations = std::mem::take(&mut elem.borrow_mut().property_declarations);
1625    for pd in property_declarations.values_mut() {
1626        pd.is_alias.as_mut().map(&mut vis);
1627    }
1628    elem.borrow_mut().property_declarations = property_declarations;
1629}
1630
1631/// Visit all named reference in this component and sub component
1632pub fn visit_all_named_references(
1633    component: &Component,
1634    vis: &mut impl FnMut(&mut NamedReference),
1635) {
1636    recurse_elem_including_sub_components_no_borrow(
1637        component,
1638        &Weak::new(),
1639        &mut |elem, parent_compo| {
1640            visit_all_named_references_in_element(elem, |nr| vis(nr));
1641            let compo = elem.borrow().enclosing_component.clone();
1642            if !Weak::ptr_eq(parent_compo, &compo) {
1643                let compo = compo.upgrade().unwrap();
1644                compo.root_constraints.borrow_mut().visit_named_references(vis);
1645                compo.popup_windows.borrow_mut().iter_mut().for_each(|p| {
1646                    vis(&mut p.x);
1647                    vis(&mut p.y);
1648                });
1649            }
1650            compo
1651        },
1652    );
1653}
1654
1655/// Visit all expression in this component and sub components
1656///
1657/// Does not recurse in the expression itself
1658pub fn visit_all_expressions(
1659    component: &Component,
1660    mut vis: impl FnMut(&mut Expression, &dyn Fn() -> Type),
1661) {
1662    recurse_elem_including_sub_components(component, &(), &mut |elem, _| {
1663        visit_element_expressions(elem, |expr, _, ty| vis(expr, ty));
1664    })
1665}
1666
1667#[derive(Debug, Clone)]
1668pub struct State {
1669    pub id: String,
1670    pub condition: Option<Expression>,
1671    pub property_changes: Vec<(NamedReference, Expression, syntax_nodes::StatePropertyChange)>,
1672}
1673
1674#[derive(Debug, Clone)]
1675pub struct Transition {
1676    /// false for 'to', true for 'out'
1677    pub is_out: bool,
1678    pub state_id: String,
1679    pub property_animations: Vec<(NamedReference, SourceLocation, ElementRc)>,
1680    /// Node pointing to the state name
1681    pub node: SyntaxNode,
1682}
1683
1684#[derive(Clone, Debug, derive_more::Deref)]
1685pub struct ExportedName {
1686    #[deref]
1687    pub name: String, // normalized
1688    pub name_ident: SyntaxNode,
1689}
1690
1691impl ExportedName {
1692    pub fn original_name(&self) -> String {
1693        self.name_ident
1694            .child_token(parser::SyntaxKind::Identifier)
1695            .map(|n| n.to_string())
1696            .unwrap_or_else(|| self.name.clone())
1697    }
1698}
1699
1700#[derive(Default, Debug, derive_more::Deref)]
1701pub struct Exports(Vec<(ExportedName, Type)>);
1702
1703impl Exports {
1704    pub fn from_node(
1705        doc: &syntax_nodes::Document,
1706        inner_components: &[Rc<Component>],
1707        type_registry: &TypeRegister,
1708        diag: &mut BuildDiagnostics,
1709    ) -> Self {
1710        #[derive(Debug, Clone)]
1711        struct NamedExport {
1712            internal_name_ident: SyntaxNode,
1713            internal_name: String,
1714            external_name_ident: SyntaxNode,
1715            exported_name: String,
1716        }
1717
1718        let exports_it = doc.ExportsList().flat_map(|exports| exports.ExportSpecifier()).map(
1719            |export_specifier| {
1720                let internal_name = parser::identifier_text(&export_specifier.ExportIdentifier())
1721                    .unwrap_or_else(|| {
1722                        debug_assert!(diag.has_error());
1723                        String::new()
1724                    });
1725
1726                let (exported_name, name_location): (String, SyntaxNode) = export_specifier
1727                    .ExportName()
1728                    .and_then(|ident| {
1729                        parser::identifier_text(&ident).map(|text| (text, ident.clone().into()))
1730                    })
1731                    .unwrap_or_else(|| {
1732                        (internal_name.clone(), export_specifier.ExportIdentifier().into())
1733                    });
1734
1735                NamedExport {
1736                    internal_name_ident: export_specifier.ExportIdentifier().into(),
1737                    internal_name,
1738                    external_name_ident: name_location,
1739                    exported_name,
1740                }
1741            },
1742        );
1743
1744        let exports_it = exports_it.chain(
1745            doc.ExportsList().filter_map(|exports| exports.Component()).map(|component| {
1746                let name_location: SyntaxNode = component.DeclaredIdentifier().into();
1747                let name =
1748                    parser::identifier_text(&component.DeclaredIdentifier()).unwrap_or_else(|| {
1749                        debug_assert!(diag.has_error());
1750                        String::new()
1751                    });
1752                NamedExport {
1753                    internal_name_ident: name_location.clone(),
1754                    internal_name: name.clone(),
1755                    external_name_ident: name_location,
1756                    exported_name: name,
1757                }
1758            }),
1759        );
1760        let exports_it = exports_it.chain(
1761            doc.ExportsList().flat_map(|exports| exports.StructDeclaration()).map(|st| {
1762                let name_location: SyntaxNode = st.DeclaredIdentifier().into();
1763                let name = parser::identifier_text(&st.DeclaredIdentifier()).unwrap_or_else(|| {
1764                    debug_assert!(diag.has_error());
1765                    String::new()
1766                });
1767                NamedExport {
1768                    internal_name_ident: name_location.clone(),
1769                    internal_name: name.clone(),
1770                    external_name_ident: name_location,
1771                    exported_name: name,
1772                }
1773            }),
1774        );
1775
1776        struct SeenExport {
1777            name_location: SyntaxNode,
1778            warned: bool,
1779        }
1780        let mut seen_exports: HashMap<String, SeenExport> = HashMap::new();
1781        let mut export_diagnostics = Vec::new();
1782
1783        let mut exports: Vec<_> = exports_it
1784            .filter(|export| {
1785                if let Some(other_loc) = seen_exports.get_mut(&export.exported_name) {
1786                    let message = format!("Duplicated export '{}'", export.exported_name);
1787                    if !other_loc.warned {
1788                        export_diagnostics.push((message.clone(), other_loc.name_location.clone()));
1789                        other_loc.warned = true;
1790                    }
1791                    export_diagnostics.push((message, export.external_name_ident.clone()));
1792                    false
1793                } else {
1794                    seen_exports.insert(
1795                        export.exported_name.clone(),
1796                        SeenExport {
1797                            name_location: export.external_name_ident.clone(),
1798                            warned: false,
1799                        },
1800                    );
1801
1802                    true
1803                }
1804            })
1805            .collect();
1806
1807        for (message, location) in export_diagnostics {
1808            diag.push_error(message, &location);
1809        }
1810
1811        if exports.is_empty() {
1812            if let Some(internal_name) = inner_components.last().as_ref().map(|x| x.id.clone()) {
1813                exports.push(NamedExport {
1814                    internal_name_ident: doc.clone().into(),
1815                    internal_name: internal_name.clone(),
1816                    external_name_ident: doc.clone().into(),
1817                    exported_name: internal_name,
1818                })
1819            }
1820        }
1821
1822        let mut resolve_export_to_inner_component_or_import =
1823            |export: &NamedExport| match type_registry.lookup(export.internal_name.as_str()) {
1824                ty @ Type::Component(_) | ty @ Type::Struct { .. } => Some(ty),
1825                Type::Invalid => {
1826                    diag.push_error(
1827                        format!("'{}' not found", export.internal_name),
1828                        &export.internal_name_ident,
1829                    );
1830                    None
1831                }
1832                _ => {
1833                    diag.push_error(
1834                        format!(
1835                            "Cannot export '{}' because it is not a component",
1836                            export.internal_name,
1837                        ),
1838                        &export.internal_name_ident,
1839                    );
1840                    None
1841                }
1842            };
1843
1844        Self(
1845            exports
1846                .iter()
1847                .filter_map(|export| {
1848                    Some((
1849                        ExportedName {
1850                            name: export.exported_name.clone(),
1851                            name_ident: export.external_name_ident.clone(),
1852                        },
1853                        resolve_export_to_inner_component_or_import(export)?,
1854                    ))
1855                })
1856                .collect(),
1857        )
1858    }
1859}
1860
1861/// This function replace the root element of a repeated element. the previous root becomes the only
1862/// child of the new root element.
1863/// Note that no reference to the base component must exist outside of repeated_element.base_type
1864pub fn inject_element_as_repeated_element(repeated_element: &ElementRc, new_root: ElementRc) {
1865    let component = repeated_element.borrow().base_type.as_component().clone();
1866    // Since we're going to replace the repeated element's component, we need to assert that
1867    // outside this function no strong reference exists to it. Then we can unwrap and
1868    // replace the root element.
1869    debug_assert_eq!(Rc::strong_count(&component), 2);
1870    let old_root = &component.root_element;
1871
1872    // The values for properties that affect the geometry may be supplied in two different ways:
1873    //
1874    //   * When coming from the outside, for example by the repeater being inside a layout, we need
1875    //     the values to apply to the new root element and the old root just needs to follow.
1876    //   * When coming from the inside, for example when the repeater just creates rectangles that
1877    //     calculate their own position, we need to move those bindings as well to the new root.
1878    //
1879    //  Finally, the default geometry pass lowering following the passes that call this function
1880    //  will apply a binding to the width and height of the inner to follow the size of the parent
1881    //  (the new root).
1882    {
1883        let mut old_root = old_root.borrow_mut();
1884        for (binding_to_move, _) in crate::typeregister::RESERVED_GEOMETRY_PROPERTIES.iter() {
1885            let binding_to_move = binding_to_move.to_string();
1886            if let Some(binding) = old_root.bindings.remove(&binding_to_move) {
1887                new_root.borrow_mut().bindings.insert(binding_to_move, binding);
1888            }
1889        }
1890    }
1891
1892    // Any elements with a weak reference to the repeater's component will need fixing later.
1893    let mut elements_with_enclosing_component_reference = Vec::new();
1894    recurse_elem(old_root, &(), &mut |element: &ElementRc, _| {
1895        if let Some(enclosing_component) = element.borrow().enclosing_component.upgrade() {
1896            if Rc::ptr_eq(&enclosing_component, &component) {
1897                elements_with_enclosing_component_reference.push(element.clone());
1898            }
1899        }
1900    });
1901    elements_with_enclosing_component_reference
1902        .extend_from_slice(component.optimized_elements.borrow().as_slice());
1903    elements_with_enclosing_component_reference.push(new_root.clone());
1904
1905    new_root.borrow_mut().child_of_layout =
1906        std::mem::replace(&mut old_root.borrow_mut().child_of_layout, false);
1907    new_root.borrow_mut().layout_info_prop = old_root.borrow().layout_info_prop.clone();
1908    new_root
1909        .borrow_mut()
1910        .bindings
1911        .extend(["x", "y"].iter().filter_map(|x| old_root.borrow_mut().bindings.remove_entry(*x)));
1912
1913    // Replace the repeated component's element with our shadow element. That requires a bit of reference counting
1914    // surgery and relies on nobody having a strong reference left to the component, which we take out of the Rc.
1915    drop(std::mem::take(&mut repeated_element.borrow_mut().base_type));
1916
1917    debug_assert_eq!(Rc::strong_count(&component), 1);
1918
1919    let mut component = Rc::try_unwrap(component).expect("internal compiler error: more than one strong reference left to repeated component when lowering shadow properties");
1920
1921    let old_root = std::mem::replace(&mut component.root_element, new_root.clone());
1922    new_root.borrow_mut().children.push(old_root);
1923
1924    let component = Rc::new(component);
1925    repeated_element.borrow_mut().base_type = Type::Component(component.clone());
1926
1927    for elem in elements_with_enclosing_component_reference {
1928        elem.borrow_mut().enclosing_component = Rc::downgrade(&component);
1929    }
1930}