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