slint/internal/compiler/llr/optim_passes/count_property_use.rs
Olivier Goffart 1ab228c628 Implement calling public functions from native code
This doesn't implement any support for the interpreter yet

CC: #2012
2022-12-22 04:28:32 -08:00

144 lines
5.5 KiB
Rust

// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
//! Passes that fills the Property::use_count
//!
//! This pass assume that use_count of all properties is zero
use crate::llr::{EvaluationContext, Expression, ParentCtx, PropertyReference, PublicComponent};
pub fn count_property_use(root: &PublicComponent) {
// Visit the root properties that are used.
// 1. the public properties
let root_ctx = EvaluationContext::new_sub_component(root, &root.item_tree.root, (), None);
for p in root.public_properties.iter().filter(|p| {
!matches!(
p.prop,
PropertyReference::Function { .. } | PropertyReference::GlobalFunction { .. }
)
}) {
visit_property(&p.prop, &root_ctx);
}
for g in root.globals.iter().filter(|g| g.exported) {
let ctx = EvaluationContext::new_global(root, g, ());
for p in g.public_properties.iter().filter(|p| {
!matches!(
p.prop,
PropertyReference::Function { .. } | PropertyReference::GlobalFunction { .. }
)
}) {
visit_property(&p.prop, &ctx);
}
}
root.for_each_sub_components(&mut |sc, ctx| {
// 2. the native items and bindings of used properties
for (pr, expr) in &sc.property_init {
let c = expr.use_count.get();
if c > 0 {
continue;
}
match pr {
PropertyReference::Local { sub_component_path, property_index } => {
let mut sc = sc;
for i in sub_component_path {
sc = &sc.sub_components[*i].ty;
}
if sc.properties[*property_index].use_count.get() == 0 {
continue;
}
}
PropertyReference::InNativeItem { .. } => {}
_ => unreachable!(),
}
expr.use_count.set(c + 1);
expr.expression.borrow().visit_recursive(&mut |e| visit_expression(e, ctx));
}
// 3. the init code
for expr in &sc.init_code {
expr.borrow().visit_recursive(&mut |e| visit_expression(e, ctx));
}
// 4. the models
for (idx, r) in sc.repeated.iter().enumerate() {
r.model.borrow().visit_recursive(&mut |e| visit_expression(e, ctx));
if let Some(lv) = &r.listview {
visit_property(&lv.viewport_y, ctx);
visit_property(&lv.viewport_width, ctx);
visit_property(&lv.viewport_height, ctx);
visit_property(&lv.listview_width, ctx);
visit_property(&lv.listview_height, ctx);
let rep_ctx = EvaluationContext::new_sub_component(
root,
&r.sub_tree.root,
(),
Some(ParentCtx::new(ctx, Some(idx))),
);
visit_property(&lv.prop_y, &rep_ctx);
visit_property(&lv.prop_width, &rep_ctx);
visit_property(&lv.prop_height, &rep_ctx);
}
for idx in r.data_prop.iter().chain(r.index_prop.iter()) {
// prevent optimizing model properties
let p = &r.sub_tree.root.properties[*idx];
p.use_count.set(2);
}
}
// 5. the layout info
sc.layout_info_h.borrow().visit_recursive(&mut |e| visit_expression(e, ctx));
sc.layout_info_v.borrow().visit_recursive(&mut |e| visit_expression(e, ctx));
// 6. accessibility props
for (_, b) in &sc.accessible_prop {
b.borrow().visit_recursive(&mut |e| visit_expression(e, ctx))
}
// 7. aliases (if they were not optimize, they are probably used)
for (a, b) in &sc.two_way_bindings {
visit_property(a, ctx);
visit_property(b, ctx);
}
// 8.functions (TODO: only visit used function)
for f in &sc.functions {
f.code.visit_recursive(&mut |e| visit_expression(e, &ctx));
}
});
// TODO: only visit used function
for g in root.globals.iter() {
let ctx = EvaluationContext::new_global(root, g, ());
for f in &g.functions {
f.code.visit_recursive(&mut |e| visit_expression(e, &ctx));
}
}
}
fn visit_property(pr: &PropertyReference, ctx: &EvaluationContext) {
let p_info = super::inline_expressions::property_binding_and_analysis(ctx, pr);
if let Some(p) = &p_info.property_decl {
p.use_count.set(p.use_count.get() + 1);
}
if let Some((binding, map)) = &p_info.binding {
let c = binding.use_count.get();
binding.use_count.set(c + 1);
if c == 0 {
let ctx2 = map.map_context(ctx);
binding.expression.borrow().visit_recursive(&mut |e| visit_expression(e, &ctx2))
}
}
}
fn visit_expression(expr: &Expression, ctx: &EvaluationContext) {
let p = match expr {
Expression::PropertyReference(p) => p,
Expression::CallBackCall { callback, .. } => callback,
Expression::PropertyAssignment { property, .. } => property,
// FIXME (should be fine anyway because we mark these as not optimizable)
Expression::ModelDataAssignment { .. } => return,
Expression::LayoutCacheAccess { layout_cache_prop, .. } => layout_cache_prop,
_ => return,
};
visit_property(p, ctx)
}