1use 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 StringLiteral(SmolStr),
28 NumberLiteral(f64),
30 BoolLiteral(bool),
32
33 KeysLiteral(Keys),
35
36 PropertyReference(MemberReference),
38
39 FunctionParameterReference {
41 index: usize,
42 },
44
45 StoreLocalVariable {
47 name: SmolStr,
48 value: Box<Expression>,
49 },
50
51 ReadLocalVariable {
54 name: SmolStr,
55 ty: Type,
56 },
57
58 StructFieldAccess {
60 base: Box<Expression>,
62 name: SmolStr,
63 },
64
65 ArrayIndex {
67 array: Box<Expression>,
69 index: Box<Expression>,
70 },
71
72 Cast {
74 from: Box<Expression>,
75 to: Type,
76 },
77
78 CodeBlock(Vec<Expression>),
80
81 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 ExtraBuiltinFunctionCall {
101 return_ty: Type,
102 function: String,
103 arguments: Vec<Expression>,
104 },
105
106 PropertyAssignment {
108 property: MemberReference,
109 value: Box<Expression>,
110 },
111 ModelDataAssignment {
113 level: usize,
115 value: Box<Expression>,
116 },
117 ArrayIndexAssignment {
119 array: Box<Expression>,
120 index: Box<Expression>,
121 value: Box<Expression>,
122 },
123 SliceIndexAssignment {
126 slice_name: SmolStr,
128 index: usize,
129 value: Box<Expression>,
130 },
131
132 BinaryExpression {
133 lhs: Box<Expression>,
134 rhs: Box<Expression>,
135 op: char,
137 },
138
139 UnaryOp {
140 sub: Box<Expression>,
141 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 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 stops: Vec<(Expression, Expression)>,
173 },
174
175 RadialGradient {
176 stops: Vec<(Expression, Expression)>,
178 },
179
180 ConicGradient {
181 from_angle: Box<Expression>,
183 stops: Vec<(Expression, Expression)>,
185 },
186
187 EnumerationValue(crate::langtype::EnumerationValue),
188
189 LayoutCacheAccess {
192 layout_cache_prop: MemberReference,
193 index: usize,
194 repeater_index: Option<Box<Expression>>,
195 entries_per_item: usize,
196 },
197 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 WithLayoutItemInfo {
211 cells_variable: String,
213 repeater_indices_var_name: Option<SmolStr>,
215 repeater_steps_var_name: Option<SmolStr>,
217 elements: Vec<Either<Expression, LayoutRepeatedElement>>,
219 orientation: Orientation,
220 sub_expression: Box<Expression>,
221 },
222 WithFlexboxLayoutItemInfo {
225 cells_h_variable: String,
227 cells_v_variable: String,
229 repeater_indices_var_name: Option<SmolStr>,
231 elements: Vec<Either<(Expression, Expression), LayoutRepeatedElement>>,
233 sub_expression: Box<Expression>,
234 },
235 WithGridInputData {
238 cells_variable: String,
240 repeater_indices_var_name: SmolStr,
242 repeater_steps_var_name: SmolStr,
244 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 TranslationReference {
260 format_args: Box<Expression>,
262 string_index: usize,
263 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 pub fn visit(&self, mut visitor: impl FnMut(&Self)) {
513 visit_impl!(self, visitor, as_ref, iter, values)
514 }
515
516 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 pub fn visit_recursive(&self, visitor: &mut dyn FnMut(&Self)) {
523 visitor(self);
524 self.visit(|e| e.visit_recursive(visitor));
525 }
526
527 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 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 fn property_ty(&self, _: &MemberReference) -> &Type;
565
566 fn arg_type(&self, _index: usize) -> &Type {
568 unimplemented!()
569 }
570}
571
572#[derive(Clone, Copy)]
574pub struct ParentScope<'a> {
575 pub sub_component: SubComponentIdx,
577 pub repeater_index: Option<RepeatedElementIdx>,
579 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 SubComponent(SubComponentIdx, Option<&'a ParentScope<'a>>),
599 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 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 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#[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}