Skip to content

Commit

Permalink
Fix panic when code in init callback of conditional/repeater re-trigg…
Browse files Browse the repository at this point in the history
…ers repeater traversal

Don't call init() while mutably borrowing the repeater's inner.

Fixes #3214
  • Loading branch information
tronical committed Mar 8, 2024
1 parent b967ca4 commit c2c2294
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 7 deletions.
17 changes: 10 additions & 7 deletions internal/core/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -896,27 +896,30 @@ impl<C: RepeatedItemTree + 'static> Repeater<C> {
model: &ModelRc<C::Data>,
count: usize,
) -> bool {
let mut indices_to_init = Vec::new();
let mut inner = self.0.inner.borrow_mut();
inner.instances.resize_with(count, || (RepeatedInstanceState::Dirty, None));
let offset = inner.offset;
let mut any_items_created = false;
for (i, c) in inner.instances.iter_mut().enumerate() {
if c.0 == RepeatedInstanceState::Dirty {
let created = if c.1.is_none() {
if c.1.is_none() {
any_items_created = true;
c.1 = Some(init());
true
} else {
false
indices_to_init.push(i);
};
c.1.as_ref().unwrap().update(i + offset, model.row_data(i + offset).unwrap());
if created {
c.1.as_ref().unwrap().init();
}
c.0 = RepeatedInstanceState::Clean;
}
}
self.data().is_dirty.set(false);

drop(inner);
let inner = self.0.inner.borrow();
for item in indices_to_init.into_iter().filter_map(|index| inner.instances.get(index)) {
item.1.as_ref().unwrap().init();
}

any_items_created
}

Expand Down
36 changes: 36 additions & 0 deletions tests/cases/models/init_recursion.slint
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright © SixtyFPS GmbH <[email protected]>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial

TestCase := Rectangle {
property <length> test-height: layout.preferred-height;
property <bool> test: self.test-height != 0;

layout := VerticalLayout {
if true: TextInput {
// Trigger a call into the layout
init => { debug(self.y); }
}
}
}

/*
```cpp
auto handle = TestCase::create();
const TestCase &instance = *handle;
assert(instance.get_test_height() != 0.);
```
```rust
let instance = TestCase::new().unwrap();
assert!(instance.get_test_height() != 0.);
```
```js
var instance = new slint.TestCase();
assert(instance.test_height != 0.);
```
*/

0 comments on commit c2c2294

Please sign in to comment.