mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-21 15:52:19 +00:00
188 lines
6.4 KiB
Rust
188 lines
6.4 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
|
|
|
|
//! Do not read twice the same property, store in a local variable instead
|
|
|
|
use crate::expression_tree::*;
|
|
use crate::langtype::Type;
|
|
use crate::object_tree::*;
|
|
use smol_str::{format_smolstr, SmolStr};
|
|
use std::cell::RefCell;
|
|
use std::collections::{BTreeMap, HashMap};
|
|
|
|
pub fn deduplicate_property_read(component: &Component) {
|
|
visit_all_expressions(component, |expr, ty| {
|
|
if matches!(ty(), Type::Callback { .. }) {
|
|
// Callback handler can't be optimizes because they can have side effect.
|
|
// But that's fine as they also do not register dependencies
|
|
return;
|
|
}
|
|
process_expression(expr, &DedupPropState::default());
|
|
});
|
|
}
|
|
|
|
struct ReadCount {
|
|
count: usize,
|
|
has_been_mapped: bool,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct PropertyReadCounts {
|
|
counts: HashMap<NamedReference, ReadCount>,
|
|
/// If at least one element of the map has duplicates
|
|
has_duplicate: bool,
|
|
/// if there is an assignment of a property we currently disable this optimization
|
|
has_set: bool,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct DedupPropState<'a> {
|
|
parent_state: Option<&'a DedupPropState<'a>>,
|
|
counts: RefCell<PropertyReadCounts>,
|
|
}
|
|
|
|
impl DedupPropState<'_> {
|
|
fn add(&self, nr: &NamedReference) {
|
|
if self.parent_state.is_some_and(|pc| pc.add_from_children(nr)) {
|
|
return;
|
|
}
|
|
let mut use_counts = self.counts.borrow_mut();
|
|
let use_counts = &mut *use_counts;
|
|
let has_duplicate = &mut use_counts.has_duplicate;
|
|
use_counts
|
|
.counts
|
|
.entry(nr.clone())
|
|
.and_modify(|c| {
|
|
if c.count == 1 {
|
|
*has_duplicate = true;
|
|
}
|
|
c.count += 1;
|
|
})
|
|
.or_insert(ReadCount { count: 1, has_been_mapped: false });
|
|
}
|
|
|
|
fn add_from_children(&self, nr: &NamedReference) -> bool {
|
|
if self.parent_state.is_some_and(|pc| pc.add_from_children(nr)) {
|
|
return true;
|
|
}
|
|
let mut use_counts = self.counts.borrow_mut();
|
|
let use_counts = &mut *use_counts;
|
|
if let Some(c) = use_counts.counts.get_mut(nr) {
|
|
if c.count == 1 {
|
|
use_counts.has_duplicate = true;
|
|
}
|
|
c.count += 1;
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
fn get_mapping(&self, nr: &NamedReference) -> Option<SmolStr> {
|
|
self.parent_state.and_then(|pr| pr.get_mapping(nr)).or_else(|| {
|
|
self.counts.borrow_mut().counts.get_mut(nr).filter(|c| c.count > 1).map(|c| {
|
|
c.has_been_mapped = true;
|
|
map_nr(nr)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
fn map_nr(nr: &NamedReference) -> SmolStr {
|
|
format_smolstr!("tmp_{}_{}", nr.element().borrow().id, nr.name())
|
|
}
|
|
|
|
fn process_expression(expr: &mut Expression, old_state: &DedupPropState) {
|
|
if old_state.counts.borrow().has_set {
|
|
return;
|
|
}
|
|
let new_state = DedupPropState { parent_state: Some(old_state), ..DedupPropState::default() };
|
|
collect_unconditional_read_count(expr, &new_state);
|
|
process_conditional_expressions(expr, &new_state);
|
|
if new_state.counts.borrow().has_set {
|
|
old_state.counts.borrow_mut().has_set = true;
|
|
} else {
|
|
do_replacements(expr, &new_state);
|
|
}
|
|
|
|
if new_state.counts.borrow().has_duplicate {
|
|
let mut stores = BTreeMap::<SmolStr, NamedReference>::new();
|
|
for (nr, c) in &new_state.counts.borrow().counts {
|
|
if c.has_been_mapped {
|
|
stores.insert(map_nr(nr), nr.clone());
|
|
}
|
|
}
|
|
let mut exprs = stores
|
|
.into_iter()
|
|
.map(|(name, nr)| Expression::StoreLocalVariable {
|
|
name,
|
|
value: Box::new(Expression::PropertyReference(nr)),
|
|
})
|
|
.collect::<Vec<_>>();
|
|
exprs.push(std::mem::take(expr));
|
|
*expr = Expression::CodeBlock(exprs);
|
|
}
|
|
}
|
|
|
|
// Collect all use of variable and their count, only in non conditional expression
|
|
fn collect_unconditional_read_count(expr: &Expression, result: &DedupPropState) {
|
|
if result.counts.borrow().has_set {
|
|
return;
|
|
}
|
|
match expr {
|
|
Expression::PropertyReference(nr) => {
|
|
result.add(nr);
|
|
}
|
|
//Expression::RepeaterIndexReference { element } => {}
|
|
//Expression::RepeaterModelReference { element } => {}
|
|
Expression::BinaryExpression { lhs, rhs: _, op: '|' | '&' } => {
|
|
lhs.visit(|sub| collect_unconditional_read_count(sub, result))
|
|
}
|
|
Expression::Condition { condition, .. } => {
|
|
condition.visit(|sub| collect_unconditional_read_count(sub, result))
|
|
}
|
|
Expression::SelfAssignment { .. } => {
|
|
result.counts.borrow_mut().has_set = true;
|
|
}
|
|
_ => expr.visit(|sub| collect_unconditional_read_count(sub, result)),
|
|
}
|
|
}
|
|
|
|
fn process_conditional_expressions(expr: &mut Expression, state: &DedupPropState) {
|
|
if state.counts.borrow().has_set {
|
|
return;
|
|
}
|
|
match expr {
|
|
Expression::BinaryExpression { lhs, rhs, op: '|' | '&' } => {
|
|
lhs.visit_mut(|sub| process_conditional_expressions(sub, state));
|
|
process_expression(rhs, state);
|
|
}
|
|
Expression::Condition { condition, true_expr, false_expr } => {
|
|
condition.visit_mut(|sub| process_conditional_expressions(sub, state));
|
|
process_expression(true_expr, state);
|
|
process_expression(false_expr, state);
|
|
}
|
|
Expression::SelfAssignment { .. } => {
|
|
state.counts.borrow_mut().has_set = true;
|
|
}
|
|
_ => expr.visit_mut(|sub| process_conditional_expressions(sub, state)),
|
|
}
|
|
}
|
|
|
|
fn do_replacements(expr: &mut Expression, state: &DedupPropState) {
|
|
match expr {
|
|
Expression::PropertyReference(nr) => {
|
|
if let Some(name) = state.get_mapping(nr) {
|
|
let ty = expr.ty();
|
|
*expr = Expression::ReadLocalVariable { name, ty };
|
|
}
|
|
}
|
|
Expression::BinaryExpression { lhs, rhs: _, op: '|' | '&' } => {
|
|
lhs.visit_mut(|sub| do_replacements(sub, state));
|
|
}
|
|
Expression::Condition { condition, .. } => {
|
|
condition.visit_mut(|sub| do_replacements(sub, state));
|
|
}
|
|
_ => expr.visit_mut(|sub| do_replacements(sub, state)),
|
|
}
|
|
}
|