diff options
| author | Yuri Sizov <yuris@humnom.net> | 2023-07-14 21:27:58 +0200 |
|---|---|---|
| committer | Yuri Sizov <yuris@humnom.net> | 2023-07-14 21:27:58 +0200 |
| commit | 4dc26bffeb059eafb5aab4c32d1abe8c7ba2cff6 (patch) | |
| tree | cbd596276cfb2548548f72c519924b045937aa2b | |
| parent | 5f23b8b91669173664595b32996f736ac30df9cb (diff) | |
| parent | 058604f5b845812a8b75a8014a1b243115ad65c7 (diff) | |
| download | redot-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.cpp | 3 | ||||
| -rw-r--r-- | scene/resources/resource_format_text.cpp | 5 | ||||
| -rw-r--r-- | tests/core/io/test_resource.h | 52 |
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 |
