RNA: Better enforce rules about pointers between datablocks

* Linked datablocks should not point to local datablocks.
* Main datablocks should not point to non-main datablocks.

This is checked now both in the poll function for UI lists, and in the
pointer assignment code used by the Python API.
This commit is contained in:
Brecht Van Lommel 2024-05-17 18:06:41 +02:00 committed by Brecht Van Lommel
parent b5ef5c3aba
commit a1b4d5ecc8
4 changed files with 58 additions and 19 deletions

@ -744,6 +744,13 @@ ID *BKE_id_owner_get(ID *id, const bool debug_relationship_assert = true);
*/
bool BKE_id_is_editable(const Main *bmain, const ID *id);
/**
* Check that a pointer from one ID to another is possible.
*
* Taking into account lib linking and main database membership.
*/
bool BKE_id_can_use_id(const ID &id_from, const ID &id_to);
/**
* Returns ordered list of data-blocks for display in the UI.
*/

@ -2268,6 +2268,20 @@ bool BKE_id_is_editable(const Main *bmain, const ID *id)
return ID_IS_EDITABLE(id) && !BKE_lib_override_library_is_system_defined(bmain, id);
}
bool BKE_id_can_use_id(const ID &id_from, const ID &id_to)
{
/* Can't point from linked to local. */
if (id_from.lib && !id_to.lib) {
return false;
}
/* Can't point from ID in main database to one outside of it. */
if (!(id_from.tag & LIB_TAG_NO_MAIN) && (id_to.tag & LIB_TAG_NO_MAIN)) {
return false;
}
return true;
}
/************************* Datablock order in UI **************************/
static int *id_order_get(ID *id)

@ -1269,14 +1269,28 @@ static char *rna_def_property_set_func(
else {
rna_print_data_get(f, dp);
PointerPropertyRNA *pprop = (PointerPropertyRNA *)dp->prop;
StructRNA *type = (pprop->type) ? rna_find_struct((const char *)pprop->type) : nullptr;
if (prop->flag & PROP_ID_SELF_CHECK) {
/* No pointers to self allowed. */
rna_print_id_get(f, dp);
fprintf(f, " if (id == value.data) {\n");
fprintf(f, " return;\n");
fprintf(f, " }\n");
}
if (type && (type->flag & STRUCT_ID)) {
/* Check if pointers between datablocks are allowed. */
fprintf(f,
" if (value.data && ptr->owner_id && value.owner_id && "
"!BKE_id_can_use_id(*ptr->owner_id, *value.owner_id)) {\n");
fprintf(f, " return;\n");
fprintf(f, " }\n");
}
if (prop->flag & PROP_ID_REFCOUNT) {
/* Perform reference counting. */
fprintf(f, "\n if (data->%s) {\n", dp->dnaname);
fprintf(f, " id_us_min((ID *)data->%s);\n", dp->dnaname);
fprintf(f, " }\n");
@ -1284,14 +1298,11 @@ static char *rna_def_property_set_func(
fprintf(f, " id_us_plus((ID *)value.data);\n");
fprintf(f, " }\n");
}
else {
PointerPropertyRNA *pprop = (PointerPropertyRNA *)dp->prop;
StructRNA *type = (pprop->type) ? rna_find_struct((const char *)pprop->type) : nullptr;
if (type && (type->flag & STRUCT_ID)) {
fprintf(f, " if (value.data) {\n");
fprintf(f, " id_lib_extern((ID *)value.data);\n");
fprintf(f, " }\n");
}
else if (type && (type->flag & STRUCT_ID)) {
/* Still mark linked data as used if not reference counting. */
fprintf(f, " if (value.data) {\n");
fprintf(f, " id_lib_extern((ID *)value.data);\n");
fprintf(f, " }\n");
}
fprintf(f, " *(void **)&data->%s = value.data;\n", dp->dnaname);

@ -40,6 +40,7 @@
#include "BKE_global.hh"
#include "BKE_idprop.hh"
#include "BKE_idtype.hh"
#include "BKE_lib_id.hh"
#include "BKE_lib_override.hh"
#include "BKE_main.hh"
#include "BKE_node.hh"
@ -1580,22 +1581,28 @@ bool RNA_property_pointer_poll(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *v
{
prop = rna_ensure_property(prop);
if (prop->type == PROP_POINTER) {
PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
if (prop->type != PROP_POINTER) {
printf("%s: %s is not a pointer property.\n", __func__, prop->identifier);
return false;
}
if (pprop->poll) {
if (rna_idproperty_check(&prop, ptr)) {
return reinterpret_cast<PropPointerPollFuncPy>(reinterpret_cast<void *>(pprop->poll))(
ptr, *value, prop);
}
return pprop->poll(ptr, *value);
}
PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
/* Can't point from linked to local datablock. */
if (ptr->owner_id && value->owner_id && !BKE_id_can_use_id(*ptr->owner_id, *value->owner_id)) {
return false;
}
/* Check custom poll function. */
if (!pprop->poll) {
return true;
}
printf("%s: %s is not a pointer property.\n", __func__, prop->identifier);
return false;
if (rna_idproperty_check(&prop, ptr)) {
return reinterpret_cast<PropPointerPollFuncPy>(reinterpret_cast<void *>(pprop->poll))(
ptr, *value, prop);
}
return pprop->poll(ptr, *value);
}
void RNA_property_enum_items_ex(bContext *C,