Skip to main content

presolve_compiler/
computed_instance_slots.rs

1//! J1-C compiler-owned instance qualification for E12 computed runtime slots.
2
3use crate::{
4    build_runtime_computed_registry, ApplicationSemanticModel, ComponentInstanceId,
5    ComponentInstanceStatus, ComputedInstanceCacheSlotId, ComputedInstanceDirtySlotId,
6    IntermediateRepresentation, SemanticId,
7};
8
9pub const COMPUTED_INSTANCE_SLOT_REGISTRY_VERSION: u32 = 1;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct ComputedInstanceSlotRecord {
13    pub component_instance_id: ComponentInstanceId,
14    pub component_id: SemanticId,
15    pub computed_id: SemanticId,
16    pub cache_slot_id: ComputedInstanceCacheSlotId,
17    pub dirty_slot_id: ComputedInstanceDirtySlotId,
18    pub dirty_initial_value: bool,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct ComputedInstanceSlotRegistry {
23    pub version: u32,
24    pub records: Vec<ComputedInstanceSlotRecord>,
25}
26
27#[must_use]
28pub fn build_computed_instance_slot_registry(
29    model: &ApplicationSemanticModel,
30    ir: &IntermediateRepresentation,
31) -> ComputedInstanceSlotRegistry {
32    let declaration_records = build_runtime_computed_registry(model, ir);
33    let mut records = Vec::new();
34    for instance in model.component_instance_plan.instances.values() {
35        if instance.status != ComponentInstanceStatus::Planned {
36            continue;
37        }
38        for computed in model
39            .computed_values
40            .values()
41            .filter(|computed| computed.owner.entity_id() == Some(&instance.component))
42        {
43            let Some(declaration) = declaration_records.record(&computed.id) else {
44                continue;
45            };
46            records.push(ComputedInstanceSlotRecord {
47                component_instance_id: instance.id.clone(),
48                component_id: instance.component.clone(),
49                computed_id: computed.id.clone(),
50                cache_slot_id: ComputedInstanceCacheSlotId::for_component_instance_cache_slot(
51                    instance.id.clone(),
52                    declaration.cache_slot.clone(),
53                ),
54                dirty_slot_id: ComputedInstanceDirtySlotId::for_component_instance_dirty_flag(
55                    instance.id.clone(),
56                    declaration.dirty_flag.id.clone(),
57                ),
58                dirty_initial_value: declaration.dirty_flag.initial_value,
59            });
60        }
61    }
62    records.sort_by(|left, right| {
63        (&left.component_instance_id, &left.computed_id)
64            .cmp(&(&right.component_instance_id, &right.computed_id))
65    });
66    ComputedInstanceSlotRegistry {
67        version: COMPUTED_INSTANCE_SLOT_REGISTRY_VERSION,
68        records,
69    }
70}
71
72/// # Errors
73///
74/// Returns an error when a registry is not an exact immutable projection.
75pub fn validate_computed_instance_slot_registry(
76    model: &ApplicationSemanticModel,
77    ir: &IntermediateRepresentation,
78    registry: &ComputedInstanceSlotRegistry,
79) -> Result<(), String> {
80    if registry.version != COMPUTED_INSTANCE_SLOT_REGISTRY_VERSION {
81        return Err("unsupported computed instance slot registry version".to_string());
82    }
83    if registry != &build_computed_instance_slot_registry(model, ir) {
84        return Err("computed instance slot registry drifted from canonical products".to_string());
85    }
86    Ok(())
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn projects_distinct_cache_and_dirty_slots_for_repeated_instances() {
95        let model = crate::build_application_semantic_model(&presolve_parser::parse_file(
96            "src/RepeatedComputed.tsx",
97            r#"@component("x-child") class Child { count = state(1); @computed() get doubled() { return this.count * 2; } render() { return <span>{this.doubled}</span>; } }
98@component("x-parent") class Parent { render() { return <><Child /><Child /></>; } }"#,
99        ));
100        let ir = crate::lower_components_to_ir(&model);
101        let registry = build_computed_instance_slot_registry(&model, &ir);
102        assert_eq!(registry.records.len(), 2);
103        assert_ne!(
104            registry.records[0].cache_slot_id,
105            registry.records[1].cache_slot_id
106        );
107        assert_ne!(
108            registry.records[0].dirty_slot_id,
109            registry.records[1].dirty_slot_id
110        );
111        assert_eq!(
112            registry.records[0].computed_id,
113            registry.records[1].computed_id
114        );
115        assert!(registry
116            .records
117            .iter()
118            .all(|record| record.dirty_initial_value));
119        assert!(validate_computed_instance_slot_registry(&model, &ir, &registry).is_ok());
120    }
121}