Avoid crash when adjusting a node tree that is not in the tree

When node tree `A` is not in the tree, `remove_child(B)` will not
automatically clean up the owners of `B` and `B`'s child nodes.
This is convenient for implementing operations like `replace_by()`,
but may have hidden dangers when manipulating the rest of the tree
`A`.

This commit makes it safe to manipulate the rest of `A` after freeing
`B`.
This commit is contained in:
Rindbee 2023-02-27 16:16:51 +08:00
parent c80a2b4fe9
commit 1eb250e17d
2 changed files with 21 additions and 11 deletions

View File

@ -202,6 +202,10 @@ void Node::_notification(int p_notification) {
return;
}
if (data.owner) {
_clean_up_owner();
}
if (data.parent) {
data.parent->remove_child(this);
}
@ -301,11 +305,7 @@ void Node::_propagate_after_exit_tree() {
}
if (!found) {
if (data.unique_name_in_owner) {
_release_unique_name_in_owner();
}
data.owner->data.owned.erase(data.OW);
data.owner = nullptr;
_clean_up_owner();
}
}
@ -1876,12 +1876,7 @@ bool Node::is_unique_name_in_owner() const {
void Node::set_owner(Node *p_owner) {
ERR_MAIN_THREAD_GUARD
if (data.owner) {
if (data.unique_name_in_owner) {
_release_unique_name_in_owner();
}
data.owner->data.owned.erase(data.OW);
data.OW = nullptr;
data.owner = nullptr;
_clean_up_owner();
}
ERR_FAIL_COND(p_owner == this);
@ -1915,6 +1910,17 @@ Node *Node::get_owner() const {
return data.owner;
}
void Node::_clean_up_owner() {
ERR_FAIL_NULL(data.owner); // Sanity check.
if (data.unique_name_in_owner) {
_release_unique_name_in_owner();
}
data.owner->data.owned.erase(data.OW);
data.owner = nullptr;
data.OW = nullptr;
}
Node *Node::find_common_parent_with(const Node *p_node) const {
if (this == p_node) {
return const_cast<Node *>(p_node);
@ -2747,6 +2753,8 @@ void Node::replace_by(Node *p_node, bool p_keep_groups) {
for (int i = 0; i < get_child_count(); i++) {
find_owned_by(data.owner, get_child(i), &owned_by_owner);
}
_clean_up_owner();
}
Node *parent = data.parent;

View File

@ -236,6 +236,8 @@ private:
void _release_unique_name_in_owner();
void _acquire_unique_name_in_owner();
void _clean_up_owner();
_FORCE_INLINE_ void _update_children_cache() const {
if (unlikely(data.children_cache_dirty)) {
_update_children_cache_impl();