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