i_slint_compiler/
passes.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
4mod apply_default_properties_from_style;
5mod binding_analysis;
6mod border_radius;
7mod check_expressions;
8mod check_public_api;
9mod check_rotation;
10mod clip;
11mod collect_custom_fonts;
12mod collect_globals;
13mod collect_init_code;
14mod collect_structs_and_enums;
15mod collect_subcomponents;
16mod compile_paths;
17mod const_propagation;
18mod deduplicate_property_read;
19mod default_geometry;
20#[cfg(feature = "software-renderer")]
21mod embed_glyphs;
22mod embed_images;
23mod ensure_window;
24mod flickable;
25mod focus_handling;
26pub mod generate_item_indices;
27pub mod infer_aliases_types;
28mod inject_debug_hooks;
29mod inlining;
30mod lower_absolute_coordinates;
31mod lower_accessibility;
32mod lower_component_container;
33mod lower_layout;
34mod lower_menus;
35mod lower_platform;
36mod lower_popups;
37mod lower_property_to_element;
38mod lower_shadows;
39mod lower_states;
40mod lower_tabwidget;
41mod lower_text_input_interface;
42mod lower_timers;
43pub mod materialize_fake_properties;
44pub mod move_declarations;
45mod optimize_useless_rectangles;
46mod purity_check;
47mod remove_aliases;
48mod remove_return;
49mod remove_unused_properties;
50mod repeater_component;
51pub mod resolve_native_classes;
52pub mod resolving;
53mod unique_id;
54mod visible;
55mod z_order;
56
57use crate::expression_tree::Expression;
58use crate::namedreference::NamedReference;
59use smol_str::SmolStr;
60
61pub fn ignore_debug_hooks(expr: &Expression) -> &Expression {
62    let mut expr = expr;
63    loop {
64        match expr {
65            Expression::DebugHook { expression, .. } => expr = expression.as_ref(),
66            _ => return expr,
67        }
68    }
69}
70
71pub async fn run_passes(
72    doc: &mut crate::object_tree::Document,
73    type_loader: &mut crate::typeloader::TypeLoader,
74    keep_raw: bool,
75    diag: &mut crate::diagnostics::BuildDiagnostics,
76) -> Option<crate::typeloader::TypeLoader> {
77    let style_metrics = {
78        // Ignore import errors
79        let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default();
80        type_loader
81            .import_component("std-widgets.slint", "StyleMetrics", &mut build_diags_to_ignore)
82            .await
83            .unwrap_or_else(|| panic!("can't load style metrics"))
84    };
85
86    let palette = {
87        // Ignore import errors
88        let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default();
89        type_loader
90            .import_component("std-widgets.slint", "Palette", &mut build_diags_to_ignore)
91            .await
92            .unwrap_or_else(|| panic!("can't load palette"))
93    };
94
95    let global_type_registry = type_loader.global_type_registry.clone();
96
97    run_import_passes(doc, type_loader, diag);
98    check_public_api::check_public_api(doc, &type_loader.compiler_config, diag);
99
100    let raw_type_loader =
101        keep_raw.then(|| crate::typeloader::snapshot_with_extra_doc(type_loader, doc).unwrap());
102
103    collect_subcomponents::collect_subcomponents(doc);
104    doc.visit_all_used_components(|component| {
105        compile_paths::compile_paths(
106            component,
107            &doc.local_registry,
108            type_loader.compiler_config.embed_resources,
109            diag,
110        );
111    });
112    lower_tabwidget::lower_tabwidget(doc, type_loader, diag).await;
113    lower_menus::lower_menus(doc, type_loader, diag).await;
114    lower_component_container::lower_component_container(doc, type_loader, diag);
115    collect_subcomponents::collect_subcomponents(doc);
116
117    doc.visit_all_used_components(|component| {
118        apply_default_properties_from_style::apply_default_properties_from_style(
119            component,
120            &style_metrics,
121            &palette,
122            diag,
123        );
124        lower_states::lower_states(component, &doc.local_registry, diag);
125        lower_text_input_interface::lower_text_input_interface(component);
126        repeater_component::process_repeater_components(component);
127        lower_popups::lower_popups(component, &doc.local_registry, diag);
128        collect_init_code::collect_init_code(component);
129        lower_timers::lower_timers(component, diag);
130    });
131
132    inlining::inline(doc, inlining::InlineSelection::InlineOnlyRequiredComponents, diag);
133    collect_subcomponents::collect_subcomponents(doc);
134
135    for root_component in doc.exported_roots() {
136        focus_handling::call_focus_on_init(&root_component);
137        ensure_window::ensure_window(&root_component, &doc.local_registry, &style_metrics, diag);
138    }
139    if let Some(popup_menu_impl) = &doc.popup_menu_impl {
140        focus_handling::call_focus_on_init(popup_menu_impl);
141    }
142
143    doc.visit_all_used_components(|component| {
144        border_radius::handle_border_radius(component, diag);
145        flickable::handle_flickable(component, &global_type_registry.borrow());
146        lower_layout::lower_layouts(component, type_loader, &style_metrics, diag);
147        default_geometry::default_geometry(component, diag);
148        lower_absolute_coordinates::lower_absolute_coordinates(component);
149        z_order::reorder_by_z_order(component, diag);
150        lower_property_to_element::lower_property_to_element(
151            component,
152            "opacity",
153            core::iter::empty(),
154            None,
155            &SmolStr::new_static("Opacity"),
156            &global_type_registry.borrow(),
157            diag,
158        );
159        lower_property_to_element::lower_property_to_element(
160            component,
161            "cache-rendering-hint",
162            core::iter::empty(),
163            None,
164            &SmolStr::new_static("Layer"),
165            &global_type_registry.borrow(),
166            diag,
167        );
168        visible::handle_visible(component, &global_type_registry.borrow(), diag);
169        lower_shadows::lower_shadow_properties(component, &doc.local_registry, diag);
170        lower_property_to_element::lower_property_to_element(
171            component,
172            crate::typeregister::RESERVED_ROTATION_PROPERTIES[0].0,
173            crate::typeregister::RESERVED_ROTATION_PROPERTIES[1..]
174                .iter()
175                .map(|(prop_name, _)| *prop_name),
176            Some(&|e, prop| Expression::BinaryExpression {
177                lhs: Expression::PropertyReference(NamedReference::new(
178                    e,
179                    match prop {
180                        "rotation-origin-x" => SmolStr::new_static("width"),
181                        "rotation-origin-y" => SmolStr::new_static("height"),
182                        "rotation-angle" => return Expression::Invalid,
183                        _ => unreachable!(),
184                    },
185                ))
186                .into(),
187                op: '/',
188                rhs: Expression::NumberLiteral(2., Default::default()).into(),
189            }),
190            &SmolStr::new_static("Rotate"),
191            &global_type_registry.borrow(),
192            diag,
193        );
194        clip::handle_clip(component, &global_type_registry.borrow(), diag);
195        if type_loader.compiler_config.accessibility {
196            lower_accessibility::lower_accessibility_properties(component, diag);
197        }
198        materialize_fake_properties::materialize_fake_properties(component);
199    });
200    for root_component in doc.exported_roots() {
201        lower_layout::check_window_layout(&root_component);
202    }
203    collect_globals::collect_globals(doc, diag);
204
205    if type_loader.compiler_config.inline_all_elements {
206        inlining::inline(doc, inlining::InlineSelection::InlineAllComponents, diag);
207        doc.used_types.borrow_mut().sub_components.clear();
208    }
209
210    binding_analysis::binding_analysis(doc, &type_loader.compiler_config, diag);
211    unique_id::assign_unique_id(doc);
212
213    doc.visit_all_used_components(|component| {
214        lower_platform::lower_platform(component, type_loader);
215
216        // Don't perform the empty rectangle removal when debug info is requested, because the resulting
217        // item tree ends up with a hierarchy where certain items have children that aren't child elements
218        // but siblings or sibling children. We need a new data structure to perform a correct element tree
219        // traversal.
220        if !type_loader.compiler_config.debug_info {
221            optimize_useless_rectangles::optimize_useless_rectangles(component);
222        }
223        move_declarations::move_declarations(component);
224    });
225
226    remove_aliases::remove_aliases(doc, diag);
227    remove_return::remove_return(doc);
228
229    doc.visit_all_used_components(|component| {
230        if !diag.has_errors() {
231            // binding loop causes panics in const_propagation
232            const_propagation::const_propagation(component);
233        }
234        deduplicate_property_read::deduplicate_property_read(component);
235        if !component.is_global() {
236            resolve_native_classes::resolve_native_classes(component);
237        }
238    });
239
240    remove_unused_properties::remove_unused_properties(doc);
241    // collect globals once more: After optimizations we might have less globals
242    collect_globals::collect_globals(doc, diag);
243    collect_structs_and_enums::collect_structs_and_enums(doc);
244
245    doc.visit_all_used_components(|component| {
246        if !component.is_global() {
247            generate_item_indices::generate_item_indices(component);
248        }
249    });
250
251    embed_images::embed_images(
252        doc,
253        type_loader.compiler_config.embed_resources,
254        type_loader.compiler_config.const_scale_factor,
255        &type_loader.compiler_config.resource_url_mapper,
256        diag,
257    )
258    .await;
259
260    #[cfg(feature = "bundle-translations")]
261    if let Some(path) = &type_loader.compiler_config.translation_path_bundle {
262        match crate::translations::TranslationsBuilder::load_translations(
263            path,
264            type_loader.compiler_config.translation_domain.as_deref().unwrap_or(""),
265        ) {
266            Ok(builder) => {
267                doc.translation_builder = Some(builder);
268            }
269            Err(err) => {
270                diag.push_error(
271                    format!("Cannot load bundled translation: {err}"),
272                    doc.node.as_ref().expect("Unexpected empty document"),
273                );
274            }
275        }
276    }
277
278    match type_loader.compiler_config.embed_resources {
279        #[cfg(feature = "software-renderer")]
280        crate::EmbedResourcesKind::EmbedTextures => {
281            let mut characters_seen = std::collections::HashSet::new();
282
283            // Include at least the default font sizes used in the MCU backend
284            let mut font_pixel_sizes =
285                vec![(12. * type_loader.compiler_config.const_scale_factor) as i16];
286            doc.visit_all_used_components(|component| {
287                embed_glyphs::collect_font_sizes_used(
288                    component,
289                    type_loader.compiler_config.const_scale_factor,
290                    &mut font_pixel_sizes,
291                );
292                embed_glyphs::scan_string_literals(component, &mut characters_seen);
293            });
294
295            // This is not perfect, as this includes translations that may not be used.
296            #[cfg(feature = "bundle-translations")]
297            if let Some(translation_builder) = doc.translation_builder.as_ref() {
298                translation_builder.collect_characters_seen(&mut characters_seen);
299            }
300
301            embed_glyphs::embed_glyphs(
302                doc,
303                &type_loader.compiler_config,
304                font_pixel_sizes,
305                characters_seen,
306                std::iter::once(&*doc).chain(type_loader.all_documents()),
307                diag,
308            );
309        }
310        _ => {
311            // Create font registration calls for custom fonts, unless we're embedding pre-rendered glyphs
312            collect_custom_fonts::collect_custom_fonts(
313                doc,
314                std::iter::once(&*doc).chain(type_loader.all_documents()),
315                type_loader.compiler_config.embed_resources
316                    == crate::EmbedResourcesKind::EmbedAllResources,
317            );
318        }
319    };
320
321    raw_type_loader
322}
323
324/// Run the passes on imported documents
325pub fn run_import_passes(
326    doc: &crate::object_tree::Document,
327    type_loader: &crate::typeloader::TypeLoader,
328    diag: &mut crate::diagnostics::BuildDiagnostics,
329) {
330    inject_debug_hooks::inject_debug_hooks(doc, type_loader);
331    infer_aliases_types::resolve_aliases(doc, diag);
332    resolving::resolve_expressions(doc, type_loader, diag);
333    purity_check::purity_check(doc, diag);
334    focus_handling::replace_forward_focus_bindings_with_focus_functions(doc, diag);
335    check_expressions::check_expressions(doc, diag);
336    check_rotation::check_rotation(doc, diag);
337    unique_id::check_unique_id(doc, diag);
338}