i_slint_compiler/llr/
expression.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use super::{
5    GlobalIdx, PropertyReference, RepeatedElementIdx, SubComponentIdx, SubComponentInstanceIdx,
6};
7use crate::expression_tree::{BuiltinFunction, MinMaxOp, OperatorClass};
8use crate::langtype::Type;
9use crate::layout::Orientation;
10use core::num::NonZeroUsize;
11use itertools::Either;
12use smol_str::SmolStr;
13use std::collections::BTreeMap;
14use std::rc::Rc;
15
16#[derive(Debug, Clone)]
17pub enum Expression {
18    /// A string literal. The .0 is the content of the string, without the quotes
19    StringLiteral(SmolStr),
20    /// Number
21    NumberLiteral(f64),
22    /// Bool
23    BoolLiteral(bool),
24
25    /// Reference to a property (which can also be a callback) or an element (property name is empty then).
26    PropertyReference(PropertyReference),
27
28    /// Reference the parameter at the given index of the current function.
29    FunctionParameterReference {
30        index: usize,
31        //ty: Type,
32    },
33
34    /// Should be directly within a CodeBlock expression, and store the value of the expression in a local variable
35    StoreLocalVariable {
36        name: SmolStr,
37        value: Box<Expression>,
38    },
39
40    /// a reference to the local variable with the given name. The type system should ensure that a variable has been stored
41    /// with this name and this type before in one of the statement of an enclosing codeblock
42    ReadLocalVariable {
43        name: SmolStr,
44        ty: Type,
45    },
46
47    /// Access to a field of the given name within a struct.
48    StructFieldAccess {
49        /// This expression should have [`Type::Struct`] type
50        base: Box<Expression>,
51        name: SmolStr,
52    },
53
54    /// Access to a index within an array.
55    ArrayIndex {
56        /// This expression should have [`Type::Array`] type
57        array: Box<Expression>,
58        index: Box<Expression>,
59    },
60
61    /// Cast an expression to the given type
62    Cast {
63        from: Box<Expression>,
64        to: Type,
65    },
66
67    /// a code block with different expression
68    CodeBlock(Vec<Expression>),
69
70    /// A function call
71    BuiltinFunctionCall {
72        function: BuiltinFunction,
73        arguments: Vec<Expression>,
74    },
75    CallBackCall {
76        callback: PropertyReference,
77        arguments: Vec<Expression>,
78    },
79    FunctionCall {
80        function: PropertyReference,
81        arguments: Vec<Expression>,
82    },
83    ItemMemberFunctionCall {
84        function: PropertyReference,
85    },
86
87    /// A BuiltinFunctionCall, but the function is not yet in the `BuiltinFunction` enum
88    /// TODO: merge in BuiltinFunctionCall
89    ExtraBuiltinFunctionCall {
90        return_ty: Type,
91        function: String,
92        arguments: Vec<Expression>,
93    },
94
95    /// An assignment of a value to a property
96    PropertyAssignment {
97        property: PropertyReference,
98        value: Box<Expression>,
99    },
100    /// an assignment of a value to the model data
101    ModelDataAssignment {
102        // how deep in the parent hierarchy we go
103        level: usize,
104        value: Box<Expression>,
105    },
106    /// An assignment done with the `foo[idx] = ...`
107    ArrayIndexAssignment {
108        array: Box<Expression>,
109        index: Box<Expression>,
110        value: Box<Expression>,
111    },
112
113    BinaryExpression {
114        lhs: Box<Expression>,
115        rhs: Box<Expression>,
116        /// '+', '-', '/', '*', '=', '!', '<', '>', '≤', '≥', '&', '|'
117        op: char,
118    },
119
120    UnaryOp {
121        sub: Box<Expression>,
122        /// '+', '-', '!'
123        op: char,
124    },
125
126    ImageReference {
127        resource_ref: crate::expression_tree::ImageReference,
128        nine_slice: Option<[u16; 4]>,
129    },
130
131    Condition {
132        condition: Box<Expression>,
133        true_expr: Box<Expression>,
134        false_expr: Box<Expression>,
135    },
136
137    Array {
138        element_ty: Type,
139        values: Vec<Expression>,
140        /// When true, this should be converted to a model. When false, this should stay as a slice
141        as_model: bool,
142    },
143    Struct {
144        ty: Rc<crate::langtype::Struct>,
145        values: BTreeMap<SmolStr, Expression>,
146    },
147
148    EasingCurve(crate::expression_tree::EasingCurve),
149
150    LinearGradient {
151        angle: Box<Expression>,
152        /// First expression in the tuple is a color, second expression is the stop position
153        stops: Vec<(Expression, Expression)>,
154    },
155
156    RadialGradient {
157        /// First expression in the tuple is a color, second expression is the stop position
158        stops: Vec<(Expression, Expression)>,
159    },
160
161    EnumerationValue(crate::langtype::EnumerationValue),
162
163    LayoutCacheAccess {
164        layout_cache_prop: PropertyReference,
165        index: usize,
166        /// When set, this is the index within a repeater, and the index is then the location of another offset.
167        /// So this looks like `layout_cache_prop[layout_cache_prop[index] + repeater_index]`
168        repeater_index: Option<Box<Expression>>,
169    },
170    /// Will call the sub_expression, with the cell variable set to the
171    /// array of BoxLayoutCellData from the elements
172    BoxLayoutFunction {
173        /// The local variable (as read with [`Self::ReadLocalVariable`]) that contains the sell
174        cells_variable: String,
175        /// The name for the local variable that contains the repeater indices
176        repeater_indices: Option<SmolStr>,
177        /// Either an expression of type BoxLayoutCellData, or an index to the repeater
178        elements: Vec<Either<Expression, RepeatedElementIdx>>,
179        orientation: Orientation,
180        sub_expression: Box<Expression>,
181    },
182
183    ComputeDialogLayoutCells {
184        /// The local variable where the slice of cells is going to be stored
185        cells_variable: String,
186        roles: Box<Expression>,
187        /// This is an Expression::Array
188        unsorted_cells: Box<Expression>,
189    },
190
191    MinMax {
192        ty: Type,
193        op: MinMaxOp,
194        lhs: Box<Expression>,
195        rhs: Box<Expression>,
196    },
197
198    EmptyComponentFactory,
199
200    /// A reference to bundled translated string
201    TranslationReference {
202        /// An expression of type array of strings
203        format_args: Box<Expression>,
204        string_index: usize,
205        /// The `n` value to use for the plural form if it is a plural form
206        plural: Option<Box<Expression>>,
207    },
208}
209
210impl Expression {
211    pub fn default_value_for_type(ty: &Type) -> Option<Self> {
212        Some(match ty {
213            Type::Invalid
214            | Type::Callback { .. }
215            | Type::Function { .. }
216            | Type::Void
217            | Type::InferredProperty
218            | Type::InferredCallback
219            | Type::ElementReference
220            | Type::LayoutCache => return None,
221            Type::Float32
222            | Type::Duration
223            | Type::Int32
224            | Type::Angle
225            | Type::PhysicalLength
226            | Type::LogicalLength
227            | Type::Rem
228            | Type::UnitProduct(_) => Expression::NumberLiteral(0.),
229            Type::Percent => Expression::NumberLiteral(1.),
230            Type::String => Expression::StringLiteral(SmolStr::default()),
231            Type::Color => {
232                Expression::Cast { from: Box::new(Expression::NumberLiteral(0.)), to: ty.clone() }
233            }
234            Type::Image => Expression::ImageReference {
235                resource_ref: crate::expression_tree::ImageReference::None,
236                nine_slice: None,
237            },
238            Type::Bool => Expression::BoolLiteral(false),
239            Type::Model => return None,
240            Type::PathData => return None,
241            Type::Array(element_ty) => Expression::Array {
242                element_ty: (**element_ty).clone(),
243                values: vec![],
244                as_model: true,
245            },
246            Type::Struct(s) => Expression::Struct {
247                ty: s.clone(),
248                values: s
249                    .fields
250                    .iter()
251                    .map(|(k, v)| Some((k.clone(), Expression::default_value_for_type(v)?)))
252                    .collect::<Option<_>>()?,
253            },
254            Type::Easing => Expression::EasingCurve(crate::expression_tree::EasingCurve::default()),
255            Type::Brush => Expression::Cast {
256                from: Box::new(Expression::default_value_for_type(&Type::Color)?),
257                to: Type::Brush,
258            },
259            Type::Enumeration(enumeration) => {
260                Expression::EnumerationValue(enumeration.clone().default_value())
261            }
262            Type::ComponentFactory => Expression::EmptyComponentFactory,
263        })
264    }
265
266    pub fn ty(&self, ctx: &dyn TypeResolutionContext) -> Type {
267        match self {
268            Self::StringLiteral(_) => Type::String,
269            Self::NumberLiteral(_) => Type::Float32,
270            Self::BoolLiteral(_) => Type::Bool,
271            Self::PropertyReference(prop) => ctx.property_ty(prop).clone(),
272            Self::FunctionParameterReference { index } => ctx.arg_type(*index).clone(),
273            Self::StoreLocalVariable { .. } => Type::Void,
274            Self::ReadLocalVariable { ty, .. } => ty.clone(),
275            Self::StructFieldAccess { base, name } => match base.ty(ctx) {
276                Type::Struct(s) => s.fields[name].clone(),
277                _ => unreachable!(),
278            },
279            Self::ArrayIndex { array, .. } => match array.ty(ctx) {
280                Type::Array(ty) => (*ty).clone(),
281                _ => unreachable!(),
282            },
283            Self::Cast { to, .. } => to.clone(),
284            Self::CodeBlock(sub) => sub.last().map_or(Type::Void, |e| e.ty(ctx)),
285            Self::BuiltinFunctionCall { function, .. } => function.ty().return_type.clone(),
286            Self::CallBackCall { callback, .. } => match ctx.property_ty(callback) {
287                Type::Callback(callback) => callback.return_type.clone(),
288                _ => Type::Invalid,
289            },
290            Self::FunctionCall { function, .. } => ctx.property_ty(function).clone(),
291            Self::ItemMemberFunctionCall { function } => match ctx.property_ty(function) {
292                Type::Function(function) => function.return_type.clone(),
293                _ => Type::Invalid,
294            },
295            Self::ExtraBuiltinFunctionCall { return_ty, .. } => return_ty.clone(),
296            Self::PropertyAssignment { .. } => Type::Void,
297            Self::ModelDataAssignment { .. } => Type::Void,
298            Self::ArrayIndexAssignment { .. } => Type::Void,
299            Self::BinaryExpression { lhs, rhs: _, op } => {
300                if crate::expression_tree::operator_class(*op) != OperatorClass::ArithmeticOp {
301                    Type::Bool
302                } else {
303                    lhs.ty(ctx)
304                }
305            }
306            Self::UnaryOp { sub, .. } => sub.ty(ctx),
307            Self::ImageReference { .. } => Type::Image,
308            Self::Condition { false_expr, .. } => false_expr.ty(ctx),
309            Self::Array { element_ty, .. } => Type::Array(element_ty.clone().into()),
310            Self::Struct { ty, .. } => ty.clone().into(),
311            Self::EasingCurve(_) => Type::Easing,
312            Self::LinearGradient { .. } => Type::Brush,
313            Self::RadialGradient { .. } => Type::Brush,
314            Self::EnumerationValue(e) => Type::Enumeration(e.enumeration.clone()),
315            Self::LayoutCacheAccess { .. } => Type::LogicalLength,
316            Self::BoxLayoutFunction { sub_expression, .. } => sub_expression.ty(ctx),
317            Self::ComputeDialogLayoutCells { .. } => {
318                Type::Array(super::lower_expression::grid_layout_cell_data_ty().into())
319            }
320            Self::MinMax { ty, .. } => ty.clone(),
321            Self::EmptyComponentFactory => Type::ComponentFactory,
322            Self::TranslationReference { .. } => Type::String,
323        }
324    }
325}
326
327macro_rules! visit_impl {
328    ($self:ident, $visitor:ident, $as_ref:ident, $iter:ident, $values:ident) => {
329        match $self {
330            Expression::StringLiteral(_) => {}
331            Expression::NumberLiteral(_) => {}
332            Expression::BoolLiteral(_) => {}
333            Expression::PropertyReference(_) => {}
334            Expression::FunctionParameterReference { .. } => {}
335            Expression::StoreLocalVariable { value, .. } => $visitor(value),
336            Expression::ReadLocalVariable { .. } => {}
337            Expression::StructFieldAccess { base, .. } => $visitor(base),
338            Expression::ArrayIndex { array, index } => {
339                $visitor(array);
340                $visitor(index);
341            }
342            Expression::Cast { from, .. } => $visitor(from),
343            Expression::CodeBlock(b) => b.$iter().for_each($visitor),
344            Expression::BuiltinFunctionCall { arguments, .. }
345            | Expression::CallBackCall { arguments, .. }
346            | Expression::FunctionCall { arguments, .. } => arguments.$iter().for_each($visitor),
347            Expression::ItemMemberFunctionCall { function: _ } => {}
348            Expression::ExtraBuiltinFunctionCall { arguments, .. } => {
349                arguments.$iter().for_each($visitor)
350            }
351            Expression::PropertyAssignment { value, .. } => $visitor(value),
352            Expression::ModelDataAssignment { value, .. } => $visitor(value),
353            Expression::ArrayIndexAssignment { array, index, value } => {
354                $visitor(array);
355                $visitor(index);
356                $visitor(value);
357            }
358            Expression::BinaryExpression { lhs, rhs, .. } => {
359                $visitor(lhs);
360                $visitor(rhs);
361            }
362            Expression::UnaryOp { sub, .. } => {
363                $visitor(sub);
364            }
365            Expression::ImageReference { .. } => {}
366            Expression::Condition { condition, true_expr, false_expr } => {
367                $visitor(condition);
368                $visitor(true_expr);
369                $visitor(false_expr);
370            }
371            Expression::Array { values, .. } => values.$iter().for_each($visitor),
372            Expression::Struct { values, .. } => values.$values().for_each($visitor),
373            Expression::EasingCurve(_) => {}
374            Expression::LinearGradient { angle, stops } => {
375                $visitor(angle);
376                for (a, b) in stops {
377                    $visitor(a);
378                    $visitor(b);
379                }
380            }
381            Expression::RadialGradient { stops } => {
382                for (a, b) in stops {
383                    $visitor(a);
384                    $visitor(b);
385                }
386            }
387            Expression::EnumerationValue(_) => {}
388            Expression::LayoutCacheAccess { repeater_index, .. } => {
389                if let Some(repeater_index) = repeater_index {
390                    $visitor(repeater_index);
391                }
392            }
393            Expression::BoxLayoutFunction { elements, sub_expression, .. } => {
394                $visitor(sub_expression);
395                elements.$iter().filter_map(|x| x.$as_ref().left()).for_each($visitor);
396            }
397            Expression::ComputeDialogLayoutCells { roles, unsorted_cells, .. } => {
398                $visitor(roles);
399                $visitor(unsorted_cells);
400            }
401            Expression::MinMax { ty: _, op: _, lhs, rhs } => {
402                $visitor(lhs);
403                $visitor(rhs);
404            }
405            Expression::EmptyComponentFactory => {}
406            Expression::TranslationReference { format_args, plural, string_index: _ } => {
407                $visitor(format_args);
408                if let Some(plural) = plural {
409                    $visitor(plural);
410                }
411            }
412        }
413    };
414}
415
416impl Expression {
417    /// Call the visitor for each sub-expression (not recursive)
418    pub fn visit(&self, mut visitor: impl FnMut(&Self)) {
419        visit_impl!(self, visitor, as_ref, iter, values)
420    }
421
422    /// Call the visitor for each sub-expression (not recursive)
423    pub fn visit_mut(&mut self, mut visitor: impl FnMut(&mut Self)) {
424        visit_impl!(self, visitor, as_mut, iter_mut, values_mut)
425    }
426
427    /// Visit itself and each sub expression recursively
428    pub fn visit_recursive(&self, visitor: &mut dyn FnMut(&Self)) {
429        visitor(self);
430        self.visit(|e| e.visit_recursive(visitor));
431    }
432
433    pub fn visit_property_references(
434        &self,
435        ctx: &EvaluationContext,
436        visitor: &mut dyn FnMut(&PropertyReference, &EvaluationContext),
437    ) {
438        self.visit_recursive(&mut |expr| {
439            let p = match expr {
440                Expression::PropertyReference(p) => p,
441                Expression::CallBackCall { callback, .. } => callback,
442                Expression::PropertyAssignment { property, .. } => {
443                    if let Some((a, map)) = &ctx.property_info(property).animation {
444                        let ctx2 = map.map_context(ctx);
445                        a.visit_property_references(&ctx2, visitor);
446                    }
447                    property
448                }
449                // FIXME  (should be fine anyway because we mark these as not optimizable)
450                Expression::ModelDataAssignment { .. } => return,
451                Expression::LayoutCacheAccess { layout_cache_prop, .. } => layout_cache_prop,
452                _ => return,
453            };
454            visitor(p, ctx)
455        });
456    }
457}
458
459pub trait TypeResolutionContext {
460    /// The type of the property.
461    ///
462    /// For reference to function, this is the return type
463    fn property_ty(&self, _: &PropertyReference) -> &Type;
464    // The type of the specified argument when evaluating a callback
465    fn arg_type(&self, _index: usize) -> &Type {
466        unimplemented!()
467    }
468}
469
470pub struct ParentCtx<'a, T = ()> {
471    pub ctx: &'a EvaluationContext<'a, T>,
472    // Index of the repeater within the ctx.current_sub_component
473    pub repeater_index: Option<RepeatedElementIdx>,
474}
475
476impl<T> Clone for ParentCtx<'_, T> {
477    fn clone(&self) -> Self {
478        *self
479    }
480}
481impl<T> Copy for ParentCtx<'_, T> {}
482
483impl<'a, T> ParentCtx<'a, T> {
484    pub fn new(
485        ctx: &'a EvaluationContext<'a, T>,
486        repeater_index: Option<RepeatedElementIdx>,
487    ) -> Self {
488        Self { ctx, repeater_index }
489    }
490}
491
492#[derive(Clone)]
493pub struct EvaluationContext<'a, T = ()> {
494    pub compilation_unit: &'a super::CompilationUnit,
495    pub current_sub_component: Option<SubComponentIdx>,
496    pub current_global: Option<GlobalIdx>,
497    pub generator_state: T,
498    /// The repeater parent
499    pub parent: Option<ParentCtx<'a, T>>,
500
501    /// The callback argument types
502    pub argument_types: &'a [Type],
503}
504
505impl<'a, T> EvaluationContext<'a, T> {
506    pub fn new_sub_component(
507        compilation_unit: &'a super::CompilationUnit,
508        sub_component: SubComponentIdx,
509        generator_state: T,
510        parent: Option<ParentCtx<'a, T>>,
511    ) -> Self {
512        Self {
513            compilation_unit,
514            current_sub_component: Some(sub_component),
515            current_global: None,
516            generator_state,
517            parent,
518            argument_types: &[],
519        }
520    }
521
522    pub fn new_global(
523        compilation_unit: &'a super::CompilationUnit,
524        global: GlobalIdx,
525        generator_state: T,
526    ) -> Self {
527        Self {
528            compilation_unit,
529            current_sub_component: None,
530            current_global: Some(global),
531            generator_state,
532            parent: None,
533            argument_types: &[],
534        }
535    }
536
537    pub(crate) fn property_info<'b>(&'b self, prop: &PropertyReference) -> PropertyInfoResult<'b> {
538        fn match_in_sub_component<'b>(
539            cu: &'b super::CompilationUnit,
540            sc: &'b super::SubComponent,
541            prop: &PropertyReference,
542            map: ContextMap,
543        ) -> PropertyInfoResult<'b> {
544            let property_decl = || {
545                if let PropertyReference::Local { property_index, sub_component_path } = &prop {
546                    let mut sc = sc;
547                    for i in sub_component_path {
548                        sc = &cu.sub_components[sc.sub_components[*i].ty];
549                    }
550                    Some(&sc.properties[*property_index])
551                } else {
552                    None
553                }
554            };
555            let animation = sc.animations.get(prop).map(|a| (a, map.clone()));
556            let analysis = sc.prop_analysis.get(prop);
557            if let Some(a) = &analysis {
558                if let Some(init) = a.property_init {
559                    return PropertyInfoResult {
560                        analysis: Some(&a.analysis),
561                        binding: Some((&sc.property_init[init].1, map)),
562                        animation,
563                        property_decl: property_decl(),
564                    };
565                }
566            }
567            let apply_analysis = |mut r: PropertyInfoResult<'b>| -> PropertyInfoResult<'b> {
568                if animation.is_some() {
569                    r.animation = animation
570                };
571                if let Some(a) = analysis {
572                    r.analysis = Some(&a.analysis);
573                }
574                r
575            };
576            match prop {
577                PropertyReference::Local { sub_component_path, property_index } => {
578                    if !sub_component_path.is_empty() {
579                        let prop2 = PropertyReference::Local {
580                            sub_component_path: sub_component_path[1..].to_vec(),
581                            property_index: *property_index,
582                        };
583                        let idx = sub_component_path[0];
584                        return apply_analysis(match_in_sub_component(
585                            cu,
586                            &cu.sub_components[sc.sub_components[idx].ty],
587                            &prop2,
588                            map.deeper_in_sub_component(idx),
589                        ));
590                    }
591                }
592                PropertyReference::InNativeItem { item_index, sub_component_path, prop_name } => {
593                    if !sub_component_path.is_empty() {
594                        let prop2 = PropertyReference::InNativeItem {
595                            sub_component_path: sub_component_path[1..].to_vec(),
596                            prop_name: prop_name.clone(),
597                            item_index: *item_index,
598                        };
599                        let idx = sub_component_path[0];
600                        return apply_analysis(match_in_sub_component(
601                            cu,
602                            &cu.sub_components[sc.sub_components[idx].ty],
603                            &prop2,
604                            map.deeper_in_sub_component(idx),
605                        ));
606                    }
607                }
608                _ => unreachable!(),
609            }
610            apply_analysis(PropertyInfoResult {
611                property_decl: property_decl(),
612                ..Default::default()
613            })
614        }
615
616        match prop {
617            PropertyReference::Local { property_index, .. } => {
618                if let Some(g) = self.current_global() {
619                    PropertyInfoResult {
620                        analysis: Some(&g.prop_analysis[*property_index]),
621                        binding: g.init_values[*property_index]
622                            .as_ref()
623                            .map(|b| (b, ContextMap::Identity)),
624                        animation: None,
625                        property_decl: Some(&g.properties[*property_index]),
626                    }
627                } else if let Some(sc) = self.current_sub_component() {
628                    match_in_sub_component(self.compilation_unit, sc, prop, ContextMap::Identity)
629                } else {
630                    unreachable!()
631                }
632            }
633            PropertyReference::InNativeItem { .. } => match_in_sub_component(
634                self.compilation_unit,
635                self.current_sub_component().unwrap(),
636                prop,
637                ContextMap::Identity,
638            ),
639            PropertyReference::Global { global_index, property_index } => {
640                let g = &self.compilation_unit.globals[*global_index];
641                PropertyInfoResult {
642                    analysis: Some(&g.prop_analysis[*property_index]),
643                    animation: None,
644                    binding: g
645                        .init_values
646                        .get(*property_index)
647                        .and_then(Option::as_ref)
648                        .map(|b| (b, ContextMap::InGlobal(*global_index))),
649                    property_decl: Some(&g.properties[*property_index]),
650                }
651            }
652            PropertyReference::InParent { level, parent_reference } => {
653                let mut ctx = self;
654                for _ in 0..level.get() {
655                    ctx = ctx.parent.as_ref().unwrap().ctx;
656                }
657                let mut ret = ctx.property_info(parent_reference);
658                match &mut ret.binding {
659                    Some((_, m @ ContextMap::Identity)) => {
660                        *m = ContextMap::InSubElement {
661                            path: Default::default(),
662                            parent: level.get(),
663                        };
664                    }
665                    Some((_, ContextMap::InSubElement { parent, .. })) => {
666                        *parent += level.get();
667                    }
668                    _ => {}
669                }
670                ret
671            }
672            PropertyReference::Function { .. } | PropertyReference::GlobalFunction { .. } => {
673                unreachable!()
674            }
675        }
676    }
677
678    pub fn current_sub_component(&self) -> Option<&super::SubComponent> {
679        self.current_sub_component.and_then(|i| self.compilation_unit.sub_components.get(i))
680    }
681
682    pub fn current_global(&self) -> Option<&super::GlobalComponent> {
683        self.current_global.and_then(|i| self.compilation_unit.globals.get(i))
684    }
685}
686
687impl<T> TypeResolutionContext for EvaluationContext<'_, T> {
688    fn property_ty(&self, prop: &PropertyReference) -> &Type {
689        match prop {
690            PropertyReference::Local { sub_component_path, property_index } => {
691                if let Some(mut sub_component) = self.current_sub_component() {
692                    for i in sub_component_path {
693                        sub_component = &self.compilation_unit.sub_components
694                            [sub_component.sub_components[*i].ty];
695                    }
696                    &sub_component.properties[*property_index].ty
697                } else if let Some(current_global) = self.current_global() {
698                    &current_global.properties[*property_index].ty
699                } else {
700                    unreachable!()
701                }
702            }
703            PropertyReference::InNativeItem { sub_component_path, item_index, prop_name } => {
704                if prop_name == "elements" {
705                    // The `Path::elements` property is not in the NativeClass
706                    return &Type::PathData;
707                }
708
709                let mut sub_component = self.current_sub_component().unwrap();
710                for i in sub_component_path {
711                    sub_component =
712                        &self.compilation_unit.sub_components[sub_component.sub_components[*i].ty];
713                }
714
715                sub_component.items[*item_index].ty.lookup_property(prop_name).unwrap()
716            }
717            PropertyReference::InParent { level, parent_reference } => {
718                let mut ctx = self;
719                for _ in 0..level.get() {
720                    ctx = ctx.parent.as_ref().unwrap().ctx;
721                }
722                ctx.property_ty(parent_reference)
723            }
724            PropertyReference::Global { global_index, property_index } => {
725                &self.compilation_unit.globals[*global_index].properties[*property_index].ty
726            }
727            PropertyReference::Function { sub_component_path, function_index } => {
728                if let Some(mut sub_component) = self.current_sub_component() {
729                    for i in sub_component_path {
730                        sub_component = &self.compilation_unit.sub_components
731                            [sub_component.sub_components[*i].ty];
732                    }
733                    &sub_component.functions[*function_index].ret_ty
734                } else if let Some(current_global) = self.current_global() {
735                    &current_global.functions[*function_index].ret_ty
736                } else {
737                    unreachable!()
738                }
739            }
740            PropertyReference::GlobalFunction { global_index, function_index } => {
741                &self.compilation_unit.globals[*global_index].functions[*function_index].ret_ty
742            }
743        }
744    }
745
746    fn arg_type(&self, index: usize) -> &Type {
747        &self.argument_types[index]
748    }
749}
750
751#[derive(Default, Debug)]
752pub(crate) struct PropertyInfoResult<'a> {
753    pub analysis: Option<&'a crate::object_tree::PropertyAnalysis>,
754    pub binding: Option<(&'a super::BindingExpression, ContextMap)>,
755    pub animation: Option<(&'a Expression, ContextMap)>,
756    pub property_decl: Option<&'a super::Property>,
757}
758
759/// Maps between two evaluation context.
760/// This allows to go from the current subcomponent's context, to the context
761/// relative to the binding we want to inline
762#[derive(Debug, Clone)]
763pub(crate) enum ContextMap {
764    Identity,
765    InSubElement { path: Vec<SubComponentInstanceIdx>, parent: usize },
766    InGlobal(GlobalIdx),
767}
768
769impl ContextMap {
770    fn deeper_in_sub_component(self, sub: SubComponentInstanceIdx) -> Self {
771        match self {
772            ContextMap::Identity => ContextMap::InSubElement { parent: 0, path: vec![sub] },
773            ContextMap::InSubElement { mut path, parent } => {
774                path.push(sub);
775                ContextMap::InSubElement { path, parent }
776            }
777            ContextMap::InGlobal(_) => panic!(),
778        }
779    }
780
781    pub fn map_property_reference(&self, p: &PropertyReference) -> PropertyReference {
782        match self {
783            ContextMap::Identity => p.clone(),
784            ContextMap::InSubElement { path, parent } => {
785                let map_sub_path = |sub_component_path: &[SubComponentInstanceIdx]| -> Vec<SubComponentInstanceIdx> {
786                    path.iter().chain(sub_component_path.iter()).copied().collect()
787                };
788
789                let p2 = match p {
790                    PropertyReference::Local { sub_component_path, property_index } => {
791                        PropertyReference::Local {
792                            sub_component_path: map_sub_path(sub_component_path),
793                            property_index: *property_index,
794                        }
795                    }
796                    PropertyReference::Function { sub_component_path, function_index } => {
797                        PropertyReference::Function {
798                            sub_component_path: map_sub_path(sub_component_path),
799                            function_index: *function_index,
800                        }
801                    }
802                    PropertyReference::InNativeItem {
803                        sub_component_path,
804                        item_index,
805                        prop_name,
806                    } => PropertyReference::InNativeItem {
807                        item_index: *item_index,
808                        prop_name: prop_name.clone(),
809                        sub_component_path: map_sub_path(sub_component_path),
810                    },
811                    PropertyReference::InParent { level, parent_reference } => {
812                        return PropertyReference::InParent {
813                            level: (parent + level.get()).try_into().unwrap(),
814                            parent_reference: parent_reference.clone(),
815                        }
816                    }
817                    PropertyReference::Global { .. } | PropertyReference::GlobalFunction { .. } => {
818                        return p.clone()
819                    }
820                };
821                if let Some(level) = NonZeroUsize::new(*parent) {
822                    PropertyReference::InParent { level, parent_reference: p2.into() }
823                } else {
824                    p2
825                }
826            }
827            ContextMap::InGlobal(global_index) => match p {
828                PropertyReference::Local { sub_component_path, property_index } => {
829                    assert!(sub_component_path.is_empty());
830                    PropertyReference::Global {
831                        global_index: *global_index,
832                        property_index: *property_index,
833                    }
834                }
835                g @ PropertyReference::Global { .. } => g.clone(),
836                _ => unreachable!(),
837            },
838        }
839    }
840
841    pub fn map_expression(&self, e: &mut Expression) {
842        match e {
843            Expression::PropertyReference(p)
844            | Expression::CallBackCall { callback: p, .. }
845            | Expression::PropertyAssignment { property: p, .. }
846            | Expression::LayoutCacheAccess { layout_cache_prop: p, .. } => {
847                *p = self.map_property_reference(p);
848            }
849            _ => (),
850        }
851        e.visit_mut(|e| self.map_expression(e))
852    }
853
854    pub fn map_context<'a>(&self, ctx: &EvaluationContext<'a>) -> EvaluationContext<'a> {
855        match self {
856            ContextMap::Identity => ctx.clone(),
857            ContextMap::InSubElement { path, parent } => {
858                let mut ctx = ctx;
859                for _ in 0..*parent {
860                    ctx = ctx.parent.unwrap().ctx;
861                }
862                if path.is_empty() {
863                    ctx.clone()
864                } else {
865                    let mut e = ctx.current_sub_component.unwrap();
866                    for i in path {
867                        e = ctx.compilation_unit.sub_components[e].sub_components[*i].ty;
868                    }
869                    EvaluationContext::new_sub_component(ctx.compilation_unit, e, (), None)
870                }
871            }
872            ContextMap::InGlobal(g) => EvaluationContext::new_global(ctx.compilation_unit, *g, ()),
873        }
874    }
875}