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