summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuri Sizov <yuris@humnom.net>2023-07-14 21:27:58 +0200
committerYuri Sizov <yuris@humnom.net>2023-07-14 21:27:58 +0200
commit4dc26bffeb059eafb5aab4c32d1abe8c7ba2cff6 (patch)
treecbd596276cfb2548548f72c519924b045937aa2b
parent5f23b8b91669173664595b32996f736ac30df9cb (diff)
parent058604f5b845812a8b75a8014a1b243115ad65c7 (diff)
downloadredot-engine-4dc26bffeb059eafb5aab4c32d1abe8c7ba2cff6.tar.gz
Merge pull request #68281 from maximkulkin/resource-circular-references
Fix crash when saving resources with circular references
-rw-r--r--core/io/resource_format_binary.cpp3
-rw-r--r--scene/resources/resource_format_text.cpp5
-rw-r--r--tests/core/io/test_resource.h52
3 files changed, 57 insertions, 3 deletions
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 3037f603c4..adae468d93 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -1960,6 +1960,8 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
return;
}
+ resource_set.insert(res);
+
List<PropertyInfo> property_list;
res->get_property_list(&property_list);
@@ -1983,7 +1985,6 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
}
}
- resource_set.insert(res);
saved_resources.push_back(res);
} break;
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index dc0240859e..c02431efa8 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -1877,6 +1877,8 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
return;
}
+ resource_set.insert(res);
+
List<PropertyInfo> property_list;
res->get_property_list(&property_list);
@@ -1908,8 +1910,7 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
I = I->next();
}
- resource_set.insert(res); //saved after, so the children it needs are available when loaded
- saved_resources.push_back(res);
+ saved_resources.push_back(res); // Saved after, so the children it needs are available when loaded
} break;
case Variant::ARRAY: {
diff --git a/tests/core/io/test_resource.h b/tests/core/io/test_resource.h
index 20d76c8894..8fc2a2f040 100644
--- a/tests/core/io/test_resource.h
+++ b/tests/core/io/test_resource.h
@@ -109,6 +109,58 @@ TEST_CASE("[Resource] Saving and loading") {
loaded_child_resource_text->get_name() == "I'm a child resource",
"The loaded child resource name should be equal to the expected value.");
}
+
+TEST_CASE("[Resource] Breaking circular references on save") {
+ Ref<Resource> resource_a = memnew(Resource);
+ resource_a->set_name("A");
+ Ref<Resource> resource_b = memnew(Resource);
+ resource_b->set_name("B");
+ Ref<Resource> resource_c = memnew(Resource);
+ resource_c->set_name("C");
+ resource_a->set_meta("next", resource_b);
+ resource_b->set_meta("next", resource_c);
+ resource_c->set_meta("next", resource_b);
+
+ const String save_path_binary = OS::get_singleton()->get_cache_path().path_join("resource.res");
+ const String save_path_text = OS::get_singleton()->get_cache_path().path_join("resource.tres");
+ ResourceSaver::save(resource_a, save_path_binary);
+ ResourceSaver::save(resource_a, save_path_text);
+
+ const Ref<Resource> &loaded_resource_a_binary = ResourceLoader::load(save_path_binary);
+ CHECK_MESSAGE(
+ loaded_resource_a_binary->get_name() == "A",
+ "The loaded resource name should be equal to the expected value.");
+ const Ref<Resource> &loaded_resource_b_binary = loaded_resource_a_binary->get_meta("next");
+ CHECK_MESSAGE(
+ loaded_resource_b_binary->get_name() == "B",
+ "The loaded child resource name should be equal to the expected value.");
+ const Ref<Resource> &loaded_resource_c_binary = loaded_resource_b_binary->get_meta("next");
+ CHECK_MESSAGE(
+ loaded_resource_c_binary->get_name() == "C",
+ "The loaded child resource name should be equal to the expected value.");
+ CHECK_MESSAGE(
+ !loaded_resource_c_binary->get_meta("next"),
+ "The loaded child resource circular reference should be NULL.");
+
+ const Ref<Resource> &loaded_resource_a_text = ResourceLoader::load(save_path_text);
+ CHECK_MESSAGE(
+ loaded_resource_a_text->get_name() == "A",
+ "The loaded resource name should be equal to the expected value.");
+ const Ref<Resource> &loaded_resource_b_text = loaded_resource_a_text->get_meta("next");
+ CHECK_MESSAGE(
+ loaded_resource_b_text->get_name() == "B",
+ "The loaded child resource name should be equal to the expected value.");
+ const Ref<Resource> &loaded_resource_c_text = loaded_resource_b_text->get_meta("next");
+ CHECK_MESSAGE(
+ loaded_resource_c_text->get_name() == "C",
+ "The loaded child resource name should be equal to the expected value.");
+ CHECK_MESSAGE(
+ !loaded_resource_c_text->get_meta("next"),
+ "The loaded child resource circular reference should be NULL.");
+
+ // Break circular reference to avoid memory leak
+ resource_c->remove_meta("next");
+}
} // namespace TestResource
#endif // TEST_RESOURCE_H