Skip to main content

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