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        /// Explicit gradient center in the element's local coordinate space (`at <x> <y>`).
177        /// `None` means use the element's bbox centre.
178        center: Option<(Box<Expression>, Box<Expression>)>,
179        /// Explicit radius in the element's local coordinate space (`circle <radius>`).
180        /// `None` means use the element's bbox half-diagonal.
181        radius: Option<Box<Expression>>,
182        /// First expression in the tuple is a color, second expression is the stop position
183        stops: Vec<(Expression, Expression)>,
184    },
185
186    ConicGradient {
187        /// The starting angle (rotation) of the gradient, corresponding to CSS `from <angle>`
188        from_angle: Box<Expression>,
189        /// Explicit gradient center in the element's local coordinate space (`at <x> <y>`).
190        /// `None` means use the element's bbox centre.
191        center: Option<(Box<Expression>, Box<Expression>)>,
192        /// First expression in the tuple is a color, second expression is the stop position (normalized angle 0-1)
193        stops: Vec<(Expression, Expression)>,
194    },
195
196    EnumerationValue(crate::langtype::EnumerationValue),
197
198    /// Standard cache access (box layouts and static grid cells).
199    /// See LayoutCacheAccess in expression_tree.rs
200    LayoutCacheAccess {
201        layout_cache_prop: MemberReference,
202        index: usize,
203        repeater_index: Option<Box<Expression>>,
204        entries_per_item: usize,
205    },
206    /// Two-level indirection cache access for grid layouts with repeaters.
207    /// See GridRepeaterCacheAccess in expression_tree.rs
208    GridRepeaterCacheAccess {
209        layout_cache_prop: MemberReference,
210        index: usize,
211        repeater_index: Box<Expression>,
212        stride: Box<Expression>,
213        child_offset: usize,
214        inner_repeater_index: Option<Box<Expression>>,
215        entries_per_item: usize,
216    },
217    /// Will call the sub_expression, with the cells variable set to the
218    /// array of LayoutItemInfo from the elements
219    WithLayoutItemInfo {
220        /// The local variable (as read with [`Self::ReadLocalVariable`]) that contains the cells
221        cells_variable: String,
222        /// The name for the local variable that contains the repeater indices
223        repeater_indices_var_name: Option<SmolStr>,
224        /// The name for the local variable that contains the repeater steps
225        repeater_steps_var_name: Option<SmolStr>,
226        /// Either an expression of type LayoutItemInfo, or information about the repeater
227        elements: Vec<Either<Expression, LayoutRepeatedElement>>,
228        orientation: Orientation,
229        sub_expression: Box<Expression>,
230    },
231    /// Will call the sub_expression, with two cells variables (horizontal and vertical)
232    /// set to the arrays of LayoutItemInfo from the elements for FlexboxLayout
233    WithFlexboxLayoutItemInfo {
234        /// The local variable for horizontal cells
235        cells_h_variable: String,
236        /// The local variable for vertical cells
237        cells_v_variable: String,
238        /// The name for the local variable that contains the repeater indices
239        repeater_indices_var_name: Option<SmolStr>,
240        /// Either an expression pair of type (LayoutItemInfo, LayoutItemInfo), or information about the repeater
241        elements: Vec<Either<(Expression, Expression), LayoutRepeatedElement>>,
242        sub_expression: Box<Expression>,
243    },
244    /// Calls `solve_flexbox_layout_with_measure` with a generated measure
245    /// callback so the cross-axis size of height-for-width cells is recomputed
246    /// at the width/height taffy actually assigns (rather than the cell's
247    /// preferred size). `data` is the `FlexboxLayoutData`. For each static cell,
248    /// `measure_cells[i]` is `(h_info_given_known_h, v_info_given_known_w)`,
249    /// each a `LayoutInfo`-typed expression that reads
250    /// `ReadLocalVariable("measure_known_w" / "measure_known_h")` (a `Float32`)
251    /// as its cross-axis constraint. `default_cells[i]` is the cell's
252    /// `(h_info, v_info)` at the default constraint (matching `data`'s cells);
253    /// it provides the preferred size returned when taffy asks for a dimension
254    /// without a known cross-axis size (mirroring the plain `solve_flexbox_layout`
255    /// measure). Repeater cells (the `Right` case) are not routed through the
256    /// callback yet.
257    SolveFlexboxLayoutWithMeasure {
258        /// The `FlexboxLayoutData` (built inline with the cell arrays, so its
259        /// temporaries live for the duration of the solve call).
260        data: Box<Expression>,
261        repeater_indices: Box<Expression>,
262        measure_cells: Vec<Either<(Expression, Expression), LayoutRepeatedElement>>,
263        default_cells: Vec<Either<(Expression, Expression), LayoutRepeatedElement>>,
264    },
265    /// Will call the sub_expression, with the cells variable set to the
266    /// array of GridLayoutInputData from the elements
267    WithGridInputData {
268        /// The local variable (as read with [`Self::ReadLocalVariable`]) that contains the cells
269        cells_variable: String,
270        /// The name for the local variable that contains the repeater indices
271        repeater_indices_var_name: SmolStr,
272        /// The name for the local variable that contains the repeater steps
273        repeater_steps_var_name: SmolStr,
274        /// Either an expression of type GridLayoutInputData, or information about the repeated element
275        elements: Vec<Either<Expression, GridLayoutRepeatedElement>>,
276        sub_expression: Box<Expression>,
277    },
278
279    MinMax {
280        ty: Type,
281        op: MinMaxOp,
282        lhs: Box<Expression>,
283        rhs: Box<Expression>,
284    },
285
286    EmptyComponentFactory,
287
288    EmptyDataTransfer,
289
290    /// A reference to bundled translated string
291    TranslationReference {
292        /// An expression of type array of strings
293        format_args: Box<Expression>,
294        string_index: usize,
295        /// The `n` value to use for the plural form if it is a plural form
296        plural: Option<Box<Expression>>,
297    },
298}
299
300impl Expression {
301    pub fn default_value_for_type(ty: &Type) -> Option<Self> {
302        Some(match ty {
303            Type::Invalid
304            | Type::Callback { .. }
305            | Type::Function { .. }
306            | Type::Void
307            | Type::InferredProperty
308            | Type::InferredCallback
309            | Type::ElementReference
310            | Type::LayoutCache
311            | Type::ArrayOfU16 => return None,
312            Type::Float32
313            | Type::Duration
314            | Type::Int32
315            | Type::Angle
316            | Type::PhysicalLength
317            | Type::LogicalLength
318            | Type::Rem
319            | Type::UnitProduct(_) => Expression::NumberLiteral(0.),
320            Type::Percent => Expression::NumberLiteral(1.),
321            Type::String => Expression::StringLiteral(SmolStr::default()),
322            Type::Color => {
323                Expression::Cast { from: Box::new(Expression::NumberLiteral(0.)), to: ty.clone() }
324            }
325            Type::Image => Expression::ImageReference {
326                resource_ref: crate::expression_tree::ImageReference::None,
327                nine_slice: None,
328            },
329            Type::Bool => Expression::BoolLiteral(false),
330            Type::Model => return None,
331            Type::PathData => return None,
332            Type::Array(element_ty) => Expression::Array {
333                element_ty: (**element_ty).clone(),
334                values: Vec::new(),
335                output: ArrayOutput::Model,
336            },
337            Type::Struct(s) => Expression::Struct {
338                ty: s.clone(),
339                values: s
340                    .fields
341                    .iter()
342                    .map(|(k, v)| Some((k.clone(), Expression::default_value_for_type(v)?)))
343                    .collect::<Option<_>>()?,
344            },
345            Type::Easing => Expression::EasingCurve(crate::expression_tree::EasingCurve::default()),
346            Type::Brush => Expression::Cast {
347                from: Box::new(Expression::default_value_for_type(&Type::Color)?),
348                to: Type::Brush,
349            },
350            Type::Enumeration(enumeration) => {
351                Expression::EnumerationValue(enumeration.clone().default_value())
352            }
353            Type::Keys => Expression::KeysLiteral(Keys::default()),
354            Type::DataTransfer => Expression::EmptyDataTransfer,
355            Type::ComponentFactory => Expression::EmptyComponentFactory,
356            Type::StyledText => Expression::BuiltinFunctionCall {
357                function: BuiltinFunction::StringToStyledText,
358                arguments: vec![Expression::StringLiteral(SmolStr::default())],
359            },
360        })
361    }
362
363    pub fn ty(&self, ctx: &dyn TypeResolutionContext) -> Type {
364        match self {
365            Self::StringLiteral(_) => Type::String,
366            Self::NumberLiteral(_) => Type::Float32,
367            Self::BoolLiteral(_) => Type::Bool,
368            Self::PropertyReference(prop) => ctx.property_ty(prop).clone(),
369            Self::FunctionParameterReference { index } => ctx.arg_type(*index).clone(),
370            Self::StoreLocalVariable { .. } => Type::Void,
371            Self::ReadLocalVariable { ty, .. } => ty.clone(),
372            Self::StructFieldAccess { base, name } => match base.ty(ctx) {
373                Type::Struct(s) => s.fields[name].clone(),
374                _ => unreachable!(),
375            },
376            Self::ArrayIndex { array, .. } => match array.ty(ctx) {
377                Type::Array(ty) => (*ty).clone(),
378                _ => unreachable!(),
379            },
380            Self::Cast { to, .. } => to.clone(),
381            Self::CodeBlock(sub) => sub.last().map_or(Type::Void, |e| e.ty(ctx)),
382            Self::BuiltinFunctionCall { function, .. } => function.ty().return_type.clone(),
383            Self::CallBackCall { callback, .. } => match ctx.property_ty(callback) {
384                Type::Callback(callback) => callback.return_type.clone(),
385                _ => Type::Invalid,
386            },
387            Self::FunctionCall { function, .. } => ctx.property_ty(function).clone(),
388            Self::ItemMemberFunctionCall { function } => match ctx.property_ty(function) {
389                Type::Function(function) => function.return_type.clone(),
390                _ => Type::Invalid,
391            },
392            Self::ExtraBuiltinFunctionCall { return_ty, .. } => return_ty.clone(),
393            Self::PropertyAssignment { .. } => Type::Void,
394            Self::ModelDataAssignment { .. } => Type::Void,
395            Self::ArrayIndexAssignment { .. } => Type::Void,
396            Self::SliceIndexAssignment { .. } => Type::Void,
397            Self::BinaryExpression { lhs, rhs: _, op } => {
398                if crate::expression_tree::operator_class(*op) != OperatorClass::ArithmeticOp {
399                    Type::Bool
400                } else {
401                    lhs.ty(ctx)
402                }
403            }
404            Self::UnaryOp { sub, .. } => sub.ty(ctx),
405            Self::ImageReference { .. } => Type::Image,
406            Self::Condition { false_expr, .. } => false_expr.ty(ctx),
407            Self::Array { element_ty, .. } => Type::Array(element_ty.clone().into()),
408            Self::Struct { ty, .. } => ty.clone().into(),
409            Self::EasingCurve(_) => Type::Easing,
410            Self::LinearGradient { .. } => Type::Brush,
411            Self::RadialGradient { .. } => Type::Brush,
412            Self::ConicGradient { .. } => Type::Brush,
413            Self::EnumerationValue(e) => Type::Enumeration(e.enumeration.clone()),
414            Self::KeysLiteral(_) => Type::Keys,
415            Self::LayoutCacheAccess { .. } => Type::LogicalLength,
416            Self::GridRepeaterCacheAccess { .. } => Type::LogicalLength,
417            Self::WithLayoutItemInfo { sub_expression, .. } => sub_expression.ty(ctx),
418            Self::WithFlexboxLayoutItemInfo { sub_expression, .. } => sub_expression.ty(ctx),
419            Self::SolveFlexboxLayoutWithMeasure { .. } => Type::LayoutCache,
420            Self::WithGridInputData { sub_expression, .. } => sub_expression.ty(ctx),
421            Self::MinMax { ty, .. } => ty.clone(),
422            Self::EmptyComponentFactory => Type::ComponentFactory,
423            Self::EmptyDataTransfer => Type::DataTransfer,
424            Self::TranslationReference { .. } => Type::String,
425        }
426    }
427}
428
429macro_rules! visit_impl {
430    ($self:ident, $visitor:ident, $as_ref:ident, $iter:ident, $values:ident) => {
431        match $self {
432            Expression::StringLiteral(_) => {}
433            Expression::NumberLiteral(_) => {}
434            Expression::BoolLiteral(_) => {}
435            Expression::PropertyReference(_) => {}
436            Expression::FunctionParameterReference { .. } => {}
437            Expression::StoreLocalVariable { value, .. } => $visitor(value),
438            Expression::ReadLocalVariable { .. } => {}
439            Expression::StructFieldAccess { base, .. } => $visitor(base),
440            Expression::ArrayIndex { array, index } => {
441                $visitor(array);
442                $visitor(index);
443            }
444            Expression::Cast { from, .. } => $visitor(from),
445            Expression::CodeBlock(b) => b.$iter().for_each($visitor),
446            Expression::BuiltinFunctionCall { arguments, .. }
447            | Expression::CallBackCall { arguments, .. }
448            | Expression::FunctionCall { arguments, .. } => arguments.$iter().for_each($visitor),
449            Expression::ItemMemberFunctionCall { function: _ } => {}
450            Expression::ExtraBuiltinFunctionCall { arguments, .. } => {
451                arguments.$iter().for_each($visitor)
452            }
453            Expression::PropertyAssignment { value, .. } => $visitor(value),
454            Expression::ModelDataAssignment { value, .. } => $visitor(value),
455            Expression::ArrayIndexAssignment { array, index, value } => {
456                $visitor(array);
457                $visitor(index);
458                $visitor(value);
459            }
460            Expression::SliceIndexAssignment { value, .. } => {
461                $visitor(value);
462            }
463            Expression::BinaryExpression { lhs, rhs, .. } => {
464                $visitor(lhs);
465                $visitor(rhs);
466            }
467            Expression::UnaryOp { sub, .. } => {
468                $visitor(sub);
469            }
470            Expression::ImageReference { .. } => {}
471            Expression::Condition { condition, true_expr, false_expr } => {
472                $visitor(condition);
473                $visitor(true_expr);
474                $visitor(false_expr);
475            }
476            Expression::Array { values, .. } => values.$iter().for_each($visitor),
477            Expression::Struct { values, .. } => values.$values().for_each($visitor),
478            Expression::EasingCurve(_) => {}
479            Expression::LinearGradient { angle, stops } => {
480                $visitor(angle);
481                for (a, b) in stops {
482                    $visitor(a);
483                    $visitor(b);
484                }
485            }
486            Expression::RadialGradient { center, radius, stops } => {
487                if let Some((cx, cy)) = center {
488                    $visitor(cx);
489                    $visitor(cy);
490                }
491                if let Some(r) = radius {
492                    $visitor(r);
493                }
494                for (a, b) in stops {
495                    $visitor(a);
496                    $visitor(b);
497                }
498            }
499            Expression::ConicGradient { from_angle, center, stops } => {
500                $visitor(from_angle);
501                if let Some((cx, cy)) = center {
502                    $visitor(cx);
503                    $visitor(cy);
504                }
505                for (a, b) in stops {
506                    $visitor(a);
507                    $visitor(b);
508                }
509            }
510            Expression::EnumerationValue(_) => {}
511            Expression::KeysLiteral(_) => {}
512            Expression::LayoutCacheAccess { repeater_index, .. } => {
513                if let Some(repeater_index) = repeater_index {
514                    $visitor(repeater_index);
515                }
516            }
517            Expression::GridRepeaterCacheAccess {
518                repeater_index,
519                stride,
520                inner_repeater_index,
521                ..
522            } => {
523                $visitor(repeater_index);
524                $visitor(stride);
525                if let Some(inner_repeater_index) = inner_repeater_index {
526                    $visitor(inner_repeater_index);
527                }
528            }
529            Expression::WithLayoutItemInfo { elements, sub_expression, .. } => {
530                $visitor(sub_expression);
531                elements.$iter().filter_map(|x| x.$as_ref().left()).for_each($visitor);
532            }
533            Expression::WithFlexboxLayoutItemInfo { elements, sub_expression, .. } => {
534                $visitor(sub_expression);
535                elements.$iter().filter_map(|x| x.$as_ref().left()).for_each(|(h, v)| {
536                    $visitor(h);
537                    $visitor(v);
538                });
539            }
540            Expression::SolveFlexboxLayoutWithMeasure {
541                data,
542                repeater_indices,
543                measure_cells,
544                default_cells,
545            } => {
546                $visitor(data);
547                $visitor(repeater_indices);
548                measure_cells.$iter().filter_map(|x| x.$as_ref().left()).for_each(|(h, v)| {
549                    $visitor(h);
550                    $visitor(v);
551                });
552                default_cells.$iter().filter_map(|x| x.$as_ref().left()).for_each(|(h, v)| {
553                    $visitor(h);
554                    $visitor(v);
555                });
556            }
557            Expression::WithGridInputData { elements, sub_expression, .. } => {
558                $visitor(sub_expression);
559                elements.$iter().filter_map(|x| x.$as_ref().left()).for_each($visitor);
560            }
561            Expression::MinMax { ty: _, op: _, lhs, rhs } => {
562                $visitor(lhs);
563                $visitor(rhs);
564            }
565            Expression::EmptyComponentFactory => {}
566            Expression::EmptyDataTransfer => {}
567            Expression::TranslationReference { format_args, plural, string_index: _ } => {
568                $visitor(format_args);
569                if let Some(plural) = plural {
570                    $visitor(plural);
571                }
572            }
573        }
574    };
575}
576
577impl Expression {
578    /// Call the visitor for each sub-expression (not recursive)
579    pub fn visit(&self, mut visitor: impl FnMut(&Self)) {
580        visit_impl!(self, visitor, as_ref, iter, values)
581    }
582
583    /// Call the visitor for each sub-expression (not recursive)
584    pub fn visit_mut(&mut self, mut visitor: impl FnMut(&mut Self)) {
585        visit_impl!(self, visitor, as_mut, iter_mut, values_mut)
586    }
587
588    /// Visit itself and each sub expression recursively
589    pub fn visit_recursive(&self, visitor: &mut dyn FnMut(&Self)) {
590        visitor(self);
591        self.visit(|e| e.visit_recursive(visitor));
592    }
593
594    /// Visit itself and each sub expression recursively
595    pub fn visit_recursive_mut(&mut self, visitor: &mut dyn FnMut(&mut Self)) {
596        visitor(self);
597        self.visit_mut(|e| e.visit_recursive_mut(visitor));
598    }
599
600    pub fn visit_property_references(
601        &self,
602        ctx: &EvaluationContext,
603        visitor: &mut dyn FnMut(&MemberReference, &EvaluationContext),
604    ) {
605        self.visit_recursive(&mut |expr| {
606            let p = match expr {
607                Expression::PropertyReference(p) => p,
608                Expression::CallBackCall { callback, .. } => callback,
609                Expression::PropertyAssignment { property, .. } => {
610                    if let Some((a, map)) = &ctx.property_info(property).animation {
611                        let ctx2 = map.map_context(ctx);
612                        a.visit_property_references(&ctx2, visitor);
613                    }
614                    property
615                }
616                // FIXME  (should be fine anyway because we mark these as not optimizable)
617                Expression::ModelDataAssignment { .. } => return,
618                Expression::LayoutCacheAccess { layout_cache_prop, .. } => layout_cache_prop,
619                Expression::GridRepeaterCacheAccess { layout_cache_prop, .. } => layout_cache_prop,
620                _ => return,
621            };
622            visitor(p, ctx)
623        });
624    }
625}
626
627pub trait TypeResolutionContext {
628    /// The type of the property.
629    ///
630    /// For reference to function, this is the return type
631    fn property_ty(&self, _: &MemberReference) -> &Type;
632
633    // The type of the specified argument when evaluating a callback
634    fn arg_type(&self, _index: usize) -> &Type {
635        unimplemented!()
636    }
637}
638
639/// The parent context of the current context when the current context is repeated
640#[derive(Clone, Copy)]
641pub struct ParentScope<'a> {
642    /// The parent sub component
643    pub sub_component: SubComponentIdx,
644    /// Index of the repeater within the ctx.current_sub_component
645    pub repeater_index: Option<RepeatedElementIdx>,
646    /// A further parent context when the parent context is itself in a repeater
647    pub parent: Option<&'a ParentScope<'a>>,
648}
649
650impl<'a> ParentScope<'a> {
651    pub fn new<T>(
652        ctx: &'a EvaluationContext<'a, T>,
653        repeater_index: Option<RepeatedElementIdx>,
654    ) -> Self {
655        let EvaluationScope::SubComponent(sub_component, parent) = ctx.current_scope else {
656            unreachable!()
657        };
658        Self { sub_component, repeater_index, parent }
659    }
660}
661
662#[derive(Clone, Copy)]
663pub enum EvaluationScope<'a> {
664    /// The evaluation context is in a sub component, optionally with information about the repeater parent
665    SubComponent(SubComponentIdx, Option<&'a ParentScope<'a>>),
666    /// The evaluation context is in a global
667    Global(GlobalIdx),
668}
669
670#[derive(Clone)]
671pub struct EvaluationContext<'a, T = ()> {
672    pub compilation_unit: &'a super::CompilationUnit,
673    pub current_scope: EvaluationScope<'a>,
674    pub generator_state: T,
675
676    /// The callback argument types
677    pub argument_types: &'a [Type],
678}
679
680impl<'a, T> EvaluationContext<'a, T> {
681    pub fn new_sub_component(
682        compilation_unit: &'a super::CompilationUnit,
683        sub_component: SubComponentIdx,
684        generator_state: T,
685        parent: Option<&'a ParentScope<'a>>,
686    ) -> Self {
687        Self {
688            compilation_unit,
689            current_scope: EvaluationScope::SubComponent(sub_component, parent),
690            generator_state,
691            argument_types: &[],
692        }
693    }
694
695    pub fn new_global(
696        compilation_unit: &'a super::CompilationUnit,
697        global: GlobalIdx,
698        generator_state: T,
699    ) -> Self {
700        Self {
701            compilation_unit,
702            current_scope: EvaluationScope::Global(global),
703            generator_state,
704            argument_types: &[],
705        }
706    }
707
708    pub(crate) fn property_info<'b>(&'b self, prop: &MemberReference) -> PropertyInfoResult<'b> {
709        fn match_in_sub_component<'b>(
710            cu: &'b super::CompilationUnit,
711            sc: &'b super::SubComponent,
712            prop: &LocalMemberReference,
713            map: ContextMap,
714        ) -> PropertyInfoResult<'b> {
715            let use_count_and_ty = || {
716                let mut sc = sc;
717                for i in &prop.sub_component_path {
718                    sc = &cu.sub_components[sc.sub_components[*i].ty];
719                }
720                match &prop.reference {
721                    LocalMemberIndex::Property(property_index) => {
722                        sc.properties.get(*property_index).map(|x| (&x.use_count, &x.ty))
723                    }
724                    LocalMemberIndex::Callback(callback_index) => {
725                        sc.callbacks.get(*callback_index).map(|x| (&x.use_count, &x.ty))
726                    }
727                    _ => None,
728                }
729            };
730
731            let animation = sc.animations.get(prop).map(|a| (a, map.clone()));
732            let analysis = sc.prop_analysis.get(&prop.clone().into());
733            if let Some(a) = &analysis
734                && let Some(init) = a.property_init
735            {
736                let u = use_count_and_ty();
737                return PropertyInfoResult {
738                    analysis: Some(&a.analysis),
739                    binding: Some((&sc.property_init[init].1, map)),
740                    animation,
741                    ty: u.map_or(Type::Invalid, |x| x.1.clone()),
742                    use_count: u.map(|x| x.0),
743                };
744            }
745            let mut r = if let &[idx, ref rest @ ..] = prop.sub_component_path.as_slice() {
746                let prop2 = LocalMemberReference {
747                    sub_component_path: rest.to_vec(),
748                    reference: prop.reference.clone(),
749                };
750                match_in_sub_component(
751                    cu,
752                    &cu.sub_components[sc.sub_components[idx].ty],
753                    &prop2,
754                    map.deeper_in_sub_component(idx),
755                )
756            } else {
757                let u = use_count_and_ty();
758                PropertyInfoResult {
759                    ty: u.map_or(Type::Invalid, |x| x.1.clone()),
760                    use_count: u.map(|x| x.0),
761                    ..Default::default()
762                }
763            };
764
765            if animation.is_some() {
766                r.animation = animation
767            };
768            if let Some(a) = analysis {
769                r.analysis = Some(&a.analysis);
770            }
771            r
772        }
773
774        fn in_global<'a>(
775            g: &'a super::GlobalComponent,
776            r: &'_ LocalMemberIndex,
777            map: ContextMap,
778        ) -> PropertyInfoResult<'a> {
779            let binding = g.init_values.get(r).map(|b| (b, map));
780            match r {
781                LocalMemberIndex::Property(index) => {
782                    let property_decl = &g.properties[*index];
783                    PropertyInfoResult {
784                        analysis: Some(&g.prop_analysis[*index]),
785                        binding,
786                        animation: None,
787                        ty: property_decl.ty.clone(),
788                        use_count: Some(&property_decl.use_count),
789                    }
790                }
791                LocalMemberIndex::Callback(index) => {
792                    let callback_decl = &g.callbacks[*index];
793                    PropertyInfoResult {
794                        analysis: None,
795                        binding,
796                        animation: None,
797                        ty: callback_decl.ty.clone(),
798                        use_count: Some(&callback_decl.use_count),
799                    }
800                }
801                _ => PropertyInfoResult::default(),
802            }
803        }
804
805        match prop {
806            MemberReference::Relative { parent_level, local_reference } => {
807                match self.current_scope {
808                    EvaluationScope::Global(g) => {
809                        let g = &self.compilation_unit.globals[g];
810                        in_global(g, &local_reference.reference, ContextMap::Identity)
811                    }
812                    EvaluationScope::SubComponent(mut sc, mut parent) => {
813                        for _ in 0..*parent_level {
814                            let p = parent.unwrap();
815                            sc = p.sub_component;
816                            parent = p.parent;
817                        }
818                        match_in_sub_component(
819                            self.compilation_unit,
820                            &self.compilation_unit.sub_components[sc],
821                            local_reference,
822                            ContextMap::from_parent_level(*parent_level),
823                        )
824                    }
825                }
826            }
827            MemberReference::Global { global_index, member } => {
828                let g = &self.compilation_unit.globals[*global_index];
829                in_global(g, member, ContextMap::InGlobal(*global_index))
830            }
831        }
832    }
833
834    pub fn current_sub_component(&self) -> Option<&super::SubComponent> {
835        let EvaluationScope::SubComponent(i, _) = self.current_scope else { return None };
836        self.compilation_unit.sub_components.get(i)
837    }
838
839    pub fn current_global(&self) -> Option<&super::GlobalComponent> {
840        let EvaluationScope::Global(i) = self.current_scope else { return None };
841        self.compilation_unit.globals.get(i)
842    }
843
844    pub fn parent_sub_component_idx(&self, parent: usize) -> Option<SubComponentIdx> {
845        let EvaluationScope::SubComponent(mut sc, mut par) = self.current_scope else {
846            return None;
847        };
848        for _ in 0..parent {
849            let p = par?;
850            sc = p.sub_component;
851            par = p.parent;
852        }
853        Some(sc)
854    }
855
856    pub fn relative_property_ty(
857        &self,
858        local_reference: &LocalMemberReference,
859        parent_level: usize,
860    ) -> &Type {
861        if let Some(g) = self.current_global() {
862            return match &local_reference.reference {
863                LocalMemberIndex::Property(property_idx) => &g.properties[*property_idx].ty,
864                LocalMemberIndex::Function(function_idx) => &g.functions[*function_idx].ret_ty,
865                LocalMemberIndex::Callback(callback_idx) => &g.callbacks[*callback_idx].ty,
866                LocalMemberIndex::Native { .. } => unreachable!(),
867            };
868        }
869
870        let mut sc = &self.compilation_unit.sub_components
871            [self.parent_sub_component_idx(parent_level).unwrap()];
872        for i in &local_reference.sub_component_path {
873            sc = &self.compilation_unit.sub_components[sc.sub_components[*i].ty];
874        }
875        match &local_reference.reference {
876            LocalMemberIndex::Property(property_index) => &sc.properties[*property_index].ty,
877            LocalMemberIndex::Function(function_index) => &sc.functions[*function_index].ret_ty,
878            LocalMemberIndex::Callback(callback_index) => &sc.callbacks[*callback_index].ty,
879            LocalMemberIndex::Native { item_index, prop_name } => {
880                if prop_name == "elements" {
881                    // The `Path::elements` property is not in the NativeClass
882                    return &Type::PathData;
883                }
884                let item = &sc.items[*item_index];
885                item.ty.lookup_property(prop_name).unwrap_or_else(|| {
886                    panic!("Failed to lookup property {prop_name} for {}", item.name)
887                })
888            }
889        }
890    }
891}
892
893impl<T> TypeResolutionContext for EvaluationContext<'_, T> {
894    fn property_ty(&self, prop: &MemberReference) -> &Type {
895        match prop {
896            MemberReference::Relative { parent_level, local_reference } => {
897                self.relative_property_ty(local_reference, *parent_level)
898            }
899            MemberReference::Global { global_index, member } => {
900                let g = &self.compilation_unit.globals[*global_index];
901                match member {
902                    LocalMemberIndex::Property(property_idx) => &g.properties[*property_idx].ty,
903                    LocalMemberIndex::Function(function_idx) => &g.functions[*function_idx].ret_ty,
904                    LocalMemberIndex::Callback(callback_idx) => &g.callbacks[*callback_idx].ty,
905                    LocalMemberIndex::Native { .. } => unreachable!(),
906                }
907            }
908        }
909    }
910
911    fn arg_type(&self, index: usize) -> &Type {
912        &self.argument_types[index]
913    }
914}
915
916#[derive(Default, Debug)]
917pub(crate) struct PropertyInfoResult<'a> {
918    pub analysis: Option<&'a crate::object_tree::PropertyAnalysis>,
919    pub binding: Option<(&'a super::BindingExpression, ContextMap)>,
920    pub animation: Option<(&'a Expression, ContextMap)>,
921    pub ty: Type,
922    pub use_count: Option<&'a std::cell::Cell<usize>>,
923}
924
925/// Maps between two evaluation context.
926/// This allows to go from the current subcomponents context, to the context
927/// relative to the binding we want to inline
928#[derive(Debug, Clone)]
929pub(crate) enum ContextMap {
930    Identity,
931    InSubElement { path: Vec<SubComponentInstanceIdx>, parent: usize },
932    InGlobal(GlobalIdx),
933}
934
935impl ContextMap {
936    fn from_parent_level(parent_level: usize) -> Self {
937        if parent_level == 0 {
938            ContextMap::Identity
939        } else {
940            ContextMap::InSubElement { parent: parent_level, path: Vec::new() }
941        }
942    }
943
944    fn deeper_in_sub_component(self, sub: SubComponentInstanceIdx) -> Self {
945        match self {
946            ContextMap::Identity => ContextMap::InSubElement { parent: 0, path: vec![sub] },
947            ContextMap::InSubElement { mut path, parent } => {
948                path.push(sub);
949                ContextMap::InSubElement { path, parent }
950            }
951            ContextMap::InGlobal(_) => panic!(),
952        }
953    }
954
955    pub fn map_property_reference(&self, p: &MemberReference) -> MemberReference {
956        match self {
957            ContextMap::Identity => p.clone(),
958            ContextMap::InSubElement { path, parent } => match p {
959                MemberReference::Relative { parent_level, local_reference } => {
960                    MemberReference::Relative {
961                        parent_level: *parent_level + *parent,
962                        local_reference: LocalMemberReference {
963                            sub_component_path: path
964                                .iter()
965                                .chain(local_reference.sub_component_path.iter())
966                                .copied()
967                                .collect(),
968                            reference: local_reference.reference.clone(),
969                        },
970                    }
971                }
972                MemberReference::Global { .. } => p.clone(),
973            },
974            ContextMap::InGlobal(global_index) => match p {
975                MemberReference::Relative { parent_level, local_reference } => {
976                    assert!(local_reference.sub_component_path.is_empty());
977                    assert_eq!(*parent_level, 0);
978                    MemberReference::Global {
979                        global_index: *global_index,
980                        member: local_reference.reference.clone(),
981                    }
982                }
983                g @ MemberReference::Global { .. } => g.clone(),
984            },
985        }
986    }
987
988    pub fn map_expression(&self, e: &mut Expression) {
989        match e {
990            Expression::PropertyReference(p)
991            | Expression::CallBackCall { callback: p, .. }
992            | Expression::PropertyAssignment { property: p, .. }
993            | Expression::LayoutCacheAccess { layout_cache_prop: p, .. }
994            | Expression::GridRepeaterCacheAccess { layout_cache_prop: p, .. } => {
995                *p = self.map_property_reference(p);
996            }
997            _ => (),
998        }
999        e.visit_mut(|e| self.map_expression(e))
1000    }
1001
1002    pub fn map_context<'a>(&self, ctx: &EvaluationContext<'a>) -> EvaluationContext<'a> {
1003        match self {
1004            ContextMap::Identity => ctx.clone(),
1005            ContextMap::InSubElement { path, parent } => {
1006                let mut sc = ctx.parent_sub_component_idx(*parent).unwrap();
1007                for i in path {
1008                    sc = ctx.compilation_unit.sub_components[sc].sub_components[*i].ty;
1009                }
1010                EvaluationContext::new_sub_component(ctx.compilation_unit, sc, (), None)
1011            }
1012            ContextMap::InGlobal(g) => EvaluationContext::new_global(ctx.compilation_unit, *g, ()),
1013        }
1014    }
1015}