diff --git a/source/blender/makesrna/RNA_access.hh b/source/blender/makesrna/RNA_access.hh index 402e54df156..93dd180a313 100644 --- a/source/blender/makesrna/RNA_access.hh +++ b/source/blender/makesrna/RNA_access.hh @@ -214,8 +214,12 @@ PropertyRNA *RNA_struct_type_find_property(StructRNA *srna, const char *identifi FunctionRNA *RNA_struct_find_function(StructRNA *srna, const char *identifier); const ListBase *RNA_struct_type_functions(StructRNA *srna); -char *RNA_struct_name_get_alloc(PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len) - ATTR_WARN_UNUSED_RESULT; +[[nodiscard]] char *RNA_struct_name_get_alloc_ex( + PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len, PropertyRNA **r_nameprop); +[[nodiscard]] char *RNA_struct_name_get_alloc(PointerRNA *ptr, + char *fixedbuf, + int fixedlen, + int *r_len); /** * Use when registering structs with the #STRUCT_PUBLIC_NAMESPACE flag. diff --git a/source/blender/makesrna/intern/rna_access.cc b/source/blender/makesrna/intern/rna_access.cc index 68e139be78b..7442dffe020 100644 --- a/source/blender/makesrna/intern/rna_access.cc +++ b/source/blender/makesrna/intern/rna_access.cc @@ -1116,14 +1116,25 @@ void RNA_struct_blender_type_set(StructRNA *srna, void *blender_type) srna->blender_type = blender_type; } +char *RNA_struct_name_get_alloc_ex( + PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len, PropertyRNA **r_nameprop) +{ + if (ptr->data) { + if (PropertyRNA *nameprop = RNA_struct_name_property(ptr->type)) { + *r_nameprop = nameprop; + return RNA_property_string_get_alloc(ptr, nameprop, fixedbuf, fixedlen, r_len); + } + } + return nullptr; +} + char *RNA_struct_name_get_alloc(PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len) { - PropertyRNA *nameprop; - - if (ptr->data && (nameprop = RNA_struct_name_property(ptr->type))) { - return RNA_property_string_get_alloc(ptr, nameprop, fixedbuf, fixedlen, r_len); + if (ptr->data) { + if (PropertyRNA *nameprop = RNA_struct_name_property(ptr->type)) { + return RNA_property_string_get_alloc(ptr, nameprop, fixedbuf, fixedlen, r_len); + } } - return nullptr; } diff --git a/source/blender/python/intern/bpy_rna.cc b/source/blender/python/intern/bpy_rna.cc index da1c184fd1c..57ca8cc068a 100644 --- a/source/blender/python/intern/bpy_rna.cc +++ b/source/blender/python/intern/bpy_rna.cc @@ -341,6 +341,46 @@ static void id_release_weakref(struct ID *id) #endif /* USE_PYRNA_INVALIDATE_WEAKREF */ +struct BPy_NamePropAsPyObject_Cache { + PyObject *(*nameprop_as_py_object_fn)(const char *, Py_ssize_t); + PropertyRNA *nameprop; +}; + +/** + * Wrapper for #RNA_struct_name_get_alloc_ex that handles non UTF8 names, see #142909. + */ +static PyObject *pyrna_struct_get_nameprop_as_pyobject( + PointerRNA *ptr, BPy_NamePropAsPyObject_Cache &nameprop_cache) +{ + char fixedbuf[256]; + int name_len; + PropertyRNA *nameprop; + char *name_ptr = RNA_struct_name_get_alloc_ex( + ptr, fixedbuf, sizeof(fixedbuf), &name_len, &nameprop); + if (LIKELY(name_ptr)) { + /* In most cases this only runs once. */ + if (UNLIKELY(nameprop != nameprop_cache.nameprop)) { + nameprop_cache.nameprop = nameprop; + const PropertySubType subtype = RNA_property_subtype(nameprop); + if (ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_FILENAME)) { + nameprop_cache.nameprop_as_py_object_fn = PyC_UnicodeFromBytesAndSize; + } + else { + nameprop_cache.nameprop_as_py_object_fn = PyUnicode_FromStringAndSize; + } + } + PyObject *result = nameprop_cache.nameprop_as_py_object_fn(name_ptr, name_len); + /* The string data may be corrupt if this asserts, + * or not using a file-path sub-type when it should.. */ + BLI_assert(result != nullptr); + if (name_ptr != fixedbuf) { + MEM_freeN(name_ptr); + } + return result; + } + return nullptr; +} + void BPY_id_release(ID *id) { #ifdef USE_PYRNA_INVALIDATE_GC @@ -4308,24 +4348,16 @@ static void pyrna_dir_members_rna(PyObject *list, PointerRNA *ptr) /* * Collect RNA attributes */ - char name[256], *name_ptr; - int name_len; - iterprop = RNA_struct_iterator_property(ptr->type); + BPy_NamePropAsPyObject_Cache nameprop_cache = {nullptr}; RNA_PROP_BEGIN (ptr, itemptr, iterprop) { /* Custom-properties are exposed using `__getitem__`, exclude from `__dir__`. */ if (RNA_property_is_idprop(static_cast(itemptr.data))) { continue; } - name_ptr = RNA_struct_name_get_alloc(&itemptr, name, sizeof(name), &name_len); - - if (name_ptr) { - PyList_APPEND(list, PyUnicode_FromStringAndSize(name_ptr, name_len)); - - if (name != name_ptr) { - MEM_freeN(name_ptr); - } + if (PyObject *name_py = pyrna_struct_get_nameprop_as_pyobject(&itemptr, nameprop_cache)) { + PyList_APPEND(list, name_py); } } RNA_PROP_END; @@ -5179,18 +5211,11 @@ PyDoc_STRVAR( static PyObject *pyrna_prop_collection_keys(BPy_PropertyRNA *self) { PyObject *ret = PyList_New(0); - char name[256], *name_ptr; - int name_len; + BPy_NamePropAsPyObject_Cache nameprop_cache = {nullptr}; RNA_PROP_BEGIN (&self->ptr.value(), itemptr, self->prop) { - name_ptr = RNA_struct_name_get_alloc(&itemptr, name, sizeof(name), &name_len); - - if (name_ptr) { - PyList_APPEND(ret, PyUnicode_FromStringAndSize(name_ptr, name_len)); - - if (name != name_ptr) { - MEM_freeN(name_ptr); - } + if (PyObject *name_py = pyrna_struct_get_nameprop_as_pyobject(&itemptr, nameprop_cache)) { + PyList_APPEND(ret, name_py); } } RNA_PROP_END; @@ -5211,32 +5236,24 @@ PyDoc_STRVAR( static PyObject *pyrna_prop_collection_items(BPy_PropertyRNA *self) { PyObject *ret = PyList_New(0); - PyObject *item; - char name[256], *name_ptr; - int name_len; int i = 0; + BPy_NamePropAsPyObject_Cache nameprop_cache = {nullptr}; RNA_PROP_BEGIN (&self->ptr.value(), itemptr, self->prop) { - if (itemptr.data) { - /* Add to Python list. */ - item = PyTuple_New(2); - name_ptr = RNA_struct_name_get_alloc(&itemptr, name, sizeof(name), &name_len); - if (name_ptr) { - PyTuple_SET_ITEM(item, 0, PyUnicode_FromStringAndSize(name_ptr, name_len)); - if (name != name_ptr) { - MEM_freeN(name_ptr); - } - } - else { - /* A bit strange, but better than returning an empty list. */ - PyTuple_SET_ITEM(item, 0, PyLong_FromLong(i)); - } - PyTuple_SET_ITEM(item, 1, pyrna_struct_CreatePyObject(&itemptr)); - - PyList_APPEND(ret, item); - - i++; + if (UNLIKELY(itemptr.data == nullptr)) { + continue; } + /* Add to Python list. */ + PyObject *item = PyTuple_New(2); + PyObject *name_py = pyrna_struct_get_nameprop_as_pyobject(&itemptr, nameprop_cache); + /* Strange to use an index `i` when `name_py` is null, better than excluding it's value. + * In practice this should not happen. */ + PyTuple_SET_ITEM(item, 0, name_py ? name_py : PyLong_FromLong(i)); + PyTuple_SET_ITEM(item, 1, pyrna_struct_CreatePyObject(&itemptr)); + + PyList_APPEND(ret, item); + + i++; } RNA_PROP_END;