mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-30 13:51:13 +00:00

state info properties are special and cannot simply be inlined or set (because we need to record the time it was changed and stuff) So disable the optimization for now. In fact, what could be done is to remove the state entirely if the state property is constant. But that change is a bit more involved This patch does: - Don't inline const state property - Don't generate a call to .set in the generated code - Also allowed to debug the expression with a context from the generator (added T generic parameter to the pretty printer) Fix panic reported in https://github.com/slint-ui/slint/issues/1327#issuecomment-1151244049
167 lines
6.9 KiB
Rust
167 lines
6.9 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
|
|
|
//! Try to simplify property bindings by propagating constant expressions
|
|
|
|
use crate::expression_tree::*;
|
|
use crate::langtype::Type;
|
|
use crate::object_tree::*;
|
|
|
|
pub fn const_propagation(component: &Component) {
|
|
visit_all_expressions(component, |expr, ty| {
|
|
if matches!(ty(), Type::Callback { .. }) {
|
|
return;
|
|
}
|
|
simplify_expression(expr);
|
|
});
|
|
}
|
|
|
|
/// Returns false if the expression still contains a reference to an element
|
|
fn simplify_expression(expr: &mut Expression) -> bool {
|
|
match expr {
|
|
Expression::PropertyReference(nr) => {
|
|
if nr.is_constant()
|
|
&& !matches!(nr.ty(), Type::Struct { name: Some(name), .. } if name.ends_with("::StateInfo"))
|
|
{
|
|
// Inline the constant value
|
|
if let Some(result) = extract_constant_property_reference(nr) {
|
|
*expr = result;
|
|
return true;
|
|
}
|
|
}
|
|
false
|
|
}
|
|
Expression::BinaryExpression { lhs, op, rhs } => {
|
|
let mut can_inline = simplify_expression(lhs);
|
|
can_inline &= simplify_expression(rhs);
|
|
|
|
let new = match (*op, &mut **lhs, &mut **rhs) {
|
|
('+', Expression::StringLiteral(a), Expression::StringLiteral(b)) => {
|
|
Some(Expression::StringLiteral(format!("{}{}", a, b)))
|
|
}
|
|
('+', Expression::NumberLiteral(a, un1), Expression::NumberLiteral(b, un2))
|
|
if un1 == un2 =>
|
|
{
|
|
Some(Expression::NumberLiteral(*a + *b, *un1))
|
|
}
|
|
('-', Expression::NumberLiteral(a, un1), Expression::NumberLiteral(b, un2))
|
|
if un1 == un2 =>
|
|
{
|
|
Some(Expression::NumberLiteral(*a - *b, *un1))
|
|
}
|
|
('*', Expression::NumberLiteral(a, un1), Expression::NumberLiteral(b, un2))
|
|
if *un1 == Unit::None || *un2 == Unit::None =>
|
|
{
|
|
let preserved_unit = if *un1 == Unit::None { *un2 } else { *un1 };
|
|
Some(Expression::NumberLiteral(*a * *b, preserved_unit))
|
|
}
|
|
(
|
|
'/',
|
|
Expression::NumberLiteral(a, un1),
|
|
Expression::NumberLiteral(b, Unit::None),
|
|
) => Some(Expression::NumberLiteral(*a / *b, *un1)),
|
|
// TODO: take care of * and / when both numbers have units
|
|
('=' | '!', Expression::NumberLiteral(a, _), Expression::NumberLiteral(b, _)) => {
|
|
Some(Expression::BoolLiteral((a == b) == (*op == '=')))
|
|
}
|
|
('=' | '!', Expression::StringLiteral(a), Expression::StringLiteral(b)) => {
|
|
Some(Expression::BoolLiteral((a == b) == (*op == '=')))
|
|
}
|
|
('=' | '!', Expression::EnumerationValue(a), Expression::EnumerationValue(b)) => {
|
|
Some(Expression::BoolLiteral((a == b) == (*op == '=')))
|
|
}
|
|
// TODO: more types and more comparison operators
|
|
('&', Expression::BoolLiteral(false), _) => {
|
|
can_inline = true;
|
|
Some(Expression::BoolLiteral(false))
|
|
}
|
|
('&', _, Expression::BoolLiteral(false)) => {
|
|
can_inline = true;
|
|
Some(Expression::BoolLiteral(false))
|
|
}
|
|
('&', Expression::BoolLiteral(true), e) => Some(std::mem::take(e)),
|
|
('&', e, Expression::BoolLiteral(true)) => Some(std::mem::take(e)),
|
|
('|', Expression::BoolLiteral(true), _) => {
|
|
can_inline = true;
|
|
Some(Expression::BoolLiteral(true))
|
|
}
|
|
('|', _, Expression::BoolLiteral(true)) => {
|
|
can_inline = true;
|
|
Some(Expression::BoolLiteral(true))
|
|
}
|
|
('|', Expression::BoolLiteral(false), e) => Some(std::mem::take(e)),
|
|
('|', e, Expression::BoolLiteral(false)) => Some(std::mem::take(e)),
|
|
_ => None,
|
|
};
|
|
if let Some(new) = new {
|
|
*expr = new;
|
|
}
|
|
can_inline
|
|
}
|
|
Expression::Cast { from, to } => {
|
|
let can_inline = simplify_expression(from);
|
|
let new = if from.ty() == *to {
|
|
Some(std::mem::take(&mut **from))
|
|
} else {
|
|
match (&**from, to) {
|
|
(Expression::NumberLiteral(x, Unit::None), Type::String) => {
|
|
Some(Expression::StringLiteral((*x).to_string()))
|
|
}
|
|
_ => None,
|
|
}
|
|
};
|
|
if let Some(new) = new {
|
|
*expr = new;
|
|
}
|
|
can_inline
|
|
}
|
|
Expression::CallbackReference { .. } => false,
|
|
Expression::ElementReference { .. } => false,
|
|
// FIXME
|
|
Expression::LayoutCacheAccess { .. } => false,
|
|
Expression::SolveLayout { .. } => false,
|
|
Expression::ComputeLayoutInfo { .. } => false,
|
|
_ => {
|
|
let mut result = true;
|
|
expr.visit_mut(|expr| result &= simplify_expression(expr));
|
|
result
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Will extract the property binding from the given named reference
|
|
/// and propagate constant expression within it. If that's possible,
|
|
/// return the new expression
|
|
fn extract_constant_property_reference(nr: &NamedReference) -> Option<Expression> {
|
|
debug_assert!(nr.is_constant());
|
|
// find the binding.
|
|
let mut element = nr.element();
|
|
let mut expression = loop {
|
|
if let Some(binding) = element.borrow().bindings.get(nr.name()) {
|
|
let binding = binding.borrow();
|
|
if !binding.two_way_bindings.is_empty() {
|
|
// TODO: In practice, we should still find out what the real binding is
|
|
// and solve that.
|
|
return None;
|
|
}
|
|
if !matches!(binding.expression, Expression::Invalid) {
|
|
break binding.expression.clone();
|
|
}
|
|
};
|
|
if let Some(decl) = element.clone().borrow().property_declarations.get(nr.name()) {
|
|
if let Some(alias) = &decl.is_alias {
|
|
return extract_constant_property_reference(alias);
|
|
}
|
|
} else if let Type::Component(c) = &element.clone().borrow().base_type {
|
|
element = c.root_element.clone();
|
|
continue;
|
|
}
|
|
|
|
// There is no binding for this property, return the default value
|
|
return Some(Expression::default_value_for_type(&nr.ty()));
|
|
};
|
|
if !(simplify_expression(&mut expression)) {
|
|
return None;
|
|
}
|
|
Some(expression)
|
|
}
|