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