mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-28 21:04:47 +00:00

This removes a lot of allocations and speeds up the compiler step a bit. Sadly, this patch is very invasive as it touches a lot of files. That said, each individual hunk is pretty trivial. For a non-trivial real-world example, the impact is significant, we get rid of ~29% of all allocations and improve the runtime by about 4.8% (measured until the viewer loop would start). Before: ``` Benchmark 1: ./target/release/slint-viewer ../slint-perf/app.slint Time (mean ± σ): 664.2 ms ± 6.7 ms [User: 589.2 ms, System: 74.0 ms] Range (min … max): 659.0 ms … 682.4 ms 10 runs allocations: 4886888 temporary allocations: 857508 ``` After: ``` Benchmark 1: ./target/release/slint-viewer ../slint-perf/app.slint Time (mean ± σ): 639.5 ms ± 17.8 ms [User: 556.9 ms, System: 76.2 ms] Range (min … max): 621.4 ms … 666.5 ms 10 runs allocations: 3544318 temporary allocations: 495685 ```
119 lines
5.2 KiB
Rust
119 lines
5.2 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
|
|
|
//! Pass that check that the public api is ok and mark the property as exposed
|
|
|
|
use std::rc::Rc;
|
|
|
|
use crate::diagnostics::{BuildDiagnostics, DiagnosticLevel};
|
|
use crate::langtype::ElementType;
|
|
use crate::object_tree::{Component, Document, ExportedName, PropertyVisibility};
|
|
use crate::{CompilerConfiguration, ComponentSelection};
|
|
use itertools::Either;
|
|
|
|
pub fn check_public_api(
|
|
doc: &mut Document,
|
|
config: &CompilerConfiguration,
|
|
diag: &mut BuildDiagnostics,
|
|
) {
|
|
let last = doc.last_exported_component();
|
|
|
|
if last.is_none() && !matches!(&config.components_to_generate, ComponentSelection::Named(_)) {
|
|
let last_imported = doc
|
|
.node
|
|
.as_ref()
|
|
.and_then(|n| {
|
|
let import_node = n.ImportSpecifier().last()?;
|
|
let import = crate::typeloader::ImportedName::extract_imported_names(&import_node).last()?;
|
|
let ElementType::Component(c) = doc.local_registry.lookup_element(&import.internal_name).ok()? else { return None };
|
|
diag.push_warning(format!("No component is exported. The last imported component '{}' will be used. This is deprecated", import.internal_name), &import_node);
|
|
let exported_name = ExportedName{ name: import.internal_name, name_ident: import_node.into() };
|
|
Some((exported_name, Either::Left(c)))
|
|
});
|
|
doc.exports.add_reexports(last_imported, diag);
|
|
}
|
|
|
|
match &config.components_to_generate {
|
|
ComponentSelection::ExportedWindows => doc.exports.retain(|export| {
|
|
// Warn about exported non-window (and remove them from the export unless it's the last for compatibility)
|
|
if let Either::Left(c) = &export.1 {
|
|
if !c.is_global() && !super::ensure_window::inherits_window(c) {
|
|
let is_last = last.as_ref().is_some_and(|last| !Rc::ptr_eq(last, c));
|
|
if is_last {
|
|
diag.push_warning(format!("Exported component '{}' doesn't inherit Window. No code will be generated for it", export.0.name), &export.0.name_ident);
|
|
return false;
|
|
} else {
|
|
diag.push_warning(format!("Exported component '{}' doesn't inherit Window. This is deprecated", export.0.name), &export.0.name_ident);
|
|
}
|
|
}
|
|
}
|
|
true
|
|
}),
|
|
// Only keep the last component if there is one
|
|
ComponentSelection::LastExported => doc.exports.retain(|export| {
|
|
if let Either::Left(c) = &export.1 {
|
|
c.is_global() || last.as_ref().map_or(true, |last| Rc::ptr_eq(last, c))
|
|
} else {
|
|
true
|
|
}
|
|
}),
|
|
// Only keep the component with the given name
|
|
ComponentSelection::Named(name) => {
|
|
doc.exports.retain(|export| {
|
|
if let Either::Left(c) = &export.1 {
|
|
c.is_global() || &c.id == name
|
|
} else {
|
|
true
|
|
}
|
|
});
|
|
if doc.last_exported_component().is_none() {
|
|
// We maybe requested to preview a non-exported component.
|
|
if let Ok(ElementType::Component(c)) = doc.local_registry.lookup_element(name) {
|
|
if let Some(name_ident) = c.node.clone() {
|
|
doc.exports.add_reexports(
|
|
[(ExportedName{ name: name.into(), name_ident }, Either::Left(c))],
|
|
diag,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
for c in doc.exported_roots() {
|
|
check_public_api_component(&c, diag);
|
|
}
|
|
for (export_name, e) in &*doc.exports {
|
|
if let Some(c) = e.as_ref().left() {
|
|
if c.is_global() {
|
|
// This global will become part of the public API.
|
|
c.exported_global_names.borrow_mut().push(export_name.clone());
|
|
check_public_api_component(c, diag)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn check_public_api_component(root_component: &Rc<Component>, diag: &mut BuildDiagnostics) {
|
|
let mut root_elem = root_component.root_element.borrow_mut();
|
|
let root_elem = &mut *root_elem;
|
|
let mut pa = root_elem.property_analysis.borrow_mut();
|
|
root_elem.property_declarations.iter_mut().for_each(|(n, d)| {
|
|
if d.property_type.ok_for_public_api() {
|
|
if d.visibility == PropertyVisibility::Private {
|
|
root_component.private_properties.borrow_mut().push((n.clone(), d.property_type.clone()));
|
|
} else {
|
|
d.expose_in_public_api = true;
|
|
if d.visibility != PropertyVisibility::Output {
|
|
pa.entry(n.clone()).or_default().is_set = true;
|
|
}
|
|
}
|
|
} else {
|
|
diag.push_diagnostic(
|
|
format!("Properties of type {} are not supported yet for public API. The property will not be exposed", d.property_type),
|
|
&d.type_node(),
|
|
DiagnosticLevel::Warning
|
|
);
|
|
}
|
|
});
|
|
}
|