Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash when source element of callback gets deleted #6426

Open
ubruhin opened this issue Oct 1, 2024 · 3 comments
Open

Crash when source element of callback gets deleted #6426

ubruhin opened this issue Oct 1, 2024 · 3 comments
Labels
a:language-interpreted Interpreter implementation and Rust API for it (slint-interpreter crate) (mO,bT) bug Something isn't working

Comments

@ubruhin
Copy link
Contributor

ubruhin commented Oct 1, 2024

This code:

import { Button } from "std-widgets.slint";

export component Example inherits Window {
    width: 200px;
    height: 200px;

    popup := PopupWindow {
        width: 100px;
        height: 100px;
        Rectangle {
            background: gray;
            Text {
                text: "Hello World";
            }
        }
    }

    touch := TouchArea {
        if touch.has-hover: Button {
            text: "Click me";
            clicked => {
                popup.show();
            }
        }
    }
}

Causes the LSP to crash when clicking on the button:

thread 'main' panicked at internal/interpreter/eval.rs:1527:26:
called `Option::unwrap()` on a `None` value
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
[Info  - 5:35:31 PM] Connection to server got closed. Server will restart.
true
The Slint Language Server crashed. This is a bug. Please open an issue on https://github.com/slint-ui/slint/issues
The Slint Language Server crashed. This is a bug. Please open an issue on https://github.com/slint-ui/slint/issues
The Slint Language Server crashed. This is a bug. Please open an issue on https://github.com/slint-ui/slint/issues

I think the problem is somehow related to the conditional if touch.has-hover: which causes the button being destroyed at the moment the popup shows up. Somehow it also seems to be related to the Text{} element - if I remove it, it does not crash.

A workaround is to remove the conditional and use visible: touch.has-hover; instead.

I didn't experience the crash when compiling & running my C++ application (works as intended) but as we know from C++, this doesn't mean the absence of an illegal memory access...


Slint 1.8.0
Linux x64
Also reproducible in https://releases.slint.dev/1.8.0/editor/ (freeze of preview)

@ogoffart ogoffart added need triaging Issue that the owner of the area still need to triage a:language-interpreted Interpreter implementation and Rust API for it (slint-interpreter crate) (mO,bT) bug Something isn't working and removed need triaging Issue that the owner of the area still need to triage labels Oct 1, 2024
@ogoffart
Copy link
Member

ogoffart commented Oct 2, 2024

Thanks for the bug report.
I can reproduce it easily.
The crash happens when a binding within the popup try to access a global (for example the popup's Text's text binding trying to access the color from the Palette global)
As far as my investigation goes, it seems to be two problem

  1. The popup has the wrong parent: it appears that the popup is parented to the destroyed item instead of the root)
  2. Although even if the parent get destroyed, then it should still not panic.

ogoffart added a commit that referenced this issue Oct 4, 2024
When calling `popup.show()` the parent itemtree of the popup should be
the one from the parent in which the PopupWindow is declared in the
source, and not the one of the context in which the `popup.show()` code
appears.

Part of issue #6426.
This happens to fix the issue as presented there.

But there is another issue in which we still crash when trying to access
global from a popup who's parent has been deleted (second part of the
test) because the interpreter needs access to the root item tree to
access the globals
ogoffart added a commit that referenced this issue Oct 4, 2024
When calling `popup.show()` the parent itemtree of the popup should be
the one from the parent in which the PopupWindow is declared in the
source, and not the one of the context in which the `popup.show()` code
appears.

Part of issue #6426.
This happens to fix the issue as presented there.

But there is another issue in which we still crash when trying to access
global from a popup who's parent has been deleted because the interpreter 
needs access to the root item tree to access the globals
@ogoffart
Copy link
Member

ogoffart commented Oct 4, 2024

#6448 fixes this original test case. But the problem is a bit deeper.
This is a test that do not pass with js currently

export global Foo {
    in-out property <color> color: red;
    in-out property <color> color2: pink;
    in-out property <string> text: "X";
    in-out property <string> global-r;
}

export component TestCase inherits Window {
    width: 200px;
    height: 200px;

    popup := PopupWindow {
        width: 50px;
        height: 50px;
        y: 10px;
        Rectangle {
            background: Foo.color;
            Text {
                text: "Hello World2";
            }
        }
        TouchArea {
            clicked => {
                r += Foo.text + "1";
            }
        }
    }

    property <bool> delete-me: true;
    property <bool> delete-me2: true;

    layout := VerticalLayout {
        if delete-me : touch := TouchArea {
            if true:  TouchArea {
                Rectangle {
                    background: blue;
                }

                clicked => {
                    debug("clicked");
                    popup.show();
                    delete-me = false;
                    debug("end");
                }
            }
        }
        if !delete-me : Rectangle {}
        if delete-me2 : touch2 := TouchArea {
             if touch2.has-hover: inner-touch2 := TouchArea {
                Rectangle {
                    background: green;
                }

                clicked => {
                    popup2.show();
                    delete-me2 = false;
                }

                // This popup's parent should be destroyed when the popup is open.
                popup2 := PopupWindow {
                    width: 50px;
                    height: 50px;
                    y: 10px;
                    Rectangle {
                        background: inner-touch2.has-hover ? Foo.color : Foo.color2;
                        Text {
                            text: Foo.text + (inner-touch2.has-hover ? "a" : "b");
                        }
                    }
                    TouchArea {
                        clicked => {
                            debug("clicked");
                            // this line don't work anymore because access to "r" is no longer possible then the parent is deleted
                            r += Foo.text + "2" + (inner-touch2.has-hover ? "a" : "b");
                            Foo.global-r += "2";
                        }

                    }
                    init => {
                        r += "i" + Foo.text + "2";
                    }
                }


            }
        }
    }

    out property <string> r;
    // force a traversal of the item tree;
    out property <length> pref: layout.preferred-height;
}

/*

```cpp
auto handle = TestCase::create();
const TestCase &instance = *handle;
slint_testing::send_mouse_click(&instance, 50., 50.);
instance.get_pref();
slint_testing::send_mouse_click(&instance, 45., 45.);
assert_eq(instance.get_r(), "X1");
slint_testing::send_mouse_click(&instance, 150., 150.);
assert_eq(instance.get_r(), "X1iX2");
instance.get_pref();
slint_testing::send_mouse_click(&instance, 45., 130.);
assert_eq(instance.get_r(), "X1iX2X2b");
assert_eq(instance.global<Foo>().get_global_r(), "2");
```

```rust
let instance = TestCase::new().unwrap();
slint_testing::send_mouse_click(&instance, 50., 50.);
instance.get_pref();
slint_testing::send_mouse_click(&instance, 45., 45.);
assert_eq!(instance.get_r(), "X1");
slint_testing::send_mouse_click(&instance, 150., 150.);
assert_eq!(instance.get_r(), "X1iX2");
instance.get_pref();
slint_testing::send_mouse_click(&instance, 45., 130.);
assert_eq!(instance.get_r(), "X1iX2");
assert_eq!(instance.global::<Foo<'_>>().get_global_r(), "2");
```

```js
var instance = new slint.TestCase();
slintlib.private_api.send_mouse_click(instance, 50., 50.);
var xxx1 = instance.pref;
slintlib.private_api.send_mouse_click(instance, 45., 45.);
assert.equal(instance.r, "X1");
slintlib.private_api.send_mouse_click(instance, 150., 150.);
assert.equal(instance.r, "X1iX2");
var xxx2 = instance.pref;
slintlib.private_api.send_mouse_click(instance, 45., 130.);
assert.equal(instance.r, "X1iX2");
assert.equal(instance.Foo.global_r, "2");
```
*/

@ubruhin

This comment was marked as off-topic.

NigelBreslaw pushed a commit that referenced this issue Oct 15, 2024
When calling `popup.show()` the parent itemtree of the popup should be
the one from the parent in which the PopupWindow is declared in the
source, and not the one of the context in which the `popup.show()` code
appears.

Part of issue #6426.
This happens to fix the issue as presented there.

But there is another issue in which we still crash when trying to access
global from a popup who's parent has been deleted because the interpreter 
needs access to the root item tree to access the globals
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a:language-interpreted Interpreter implementation and Rust API for it (slint-interpreter crate) (mO,bT) bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants