Skip to content

[3.13] gh-132070: Use _PyObject_IsUniquelyReferenced in unicodeobject (gh-133039) #133126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,23 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
extern void _Py_SetImmortal(PyObject *op);
extern void _Py_SetImmortalUntracked(PyObject *op);

// Checks if an object has a single, unique reference. If the caller holds a
// unique reference, it may be able to safely modify the object in-place.
static inline int
_PyObject_IsUniquelyReferenced(PyObject *ob)
{
#if !defined(Py_GIL_DISABLED)
return Py_REFCNT(ob) == 1;
#else
// NOTE: the entire ob_ref_shared field must be zero, including flags, to
// ensure that other threads cannot concurrently create new references to
// this object.
return (_Py_IsOwnedByCurrentThread(ob) &&
_Py_atomic_load_uint32_relaxed(&ob->ob_ref_local) == 1 &&
_Py_atomic_load_ssize_relaxed(&ob->ob_ref_shared) == 0);
#endif
}

// Makes an immortal object mortal again with the specified refcnt. Should only
// be used during runtime finalization.
static inline void _Py_SetMortal(PyObject *op, Py_ssize_t refcnt)
Expand Down
43 changes: 25 additions & 18 deletions Objects/unicodeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,21 @@ unicode_fill_invalid(PyObject *unicode, Py_ssize_t old_length)
}
#endif

static PyObject*
resize_copy(PyObject *unicode, Py_ssize_t length)
{
Py_ssize_t copy_length;
PyObject *copy;

copy = PyUnicode_New(length, PyUnicode_MAX_CHAR_VALUE(unicode));
if (copy == NULL)
return NULL;

copy_length = Py_MIN(length, PyUnicode_GET_LENGTH(unicode));
_PyUnicode_FastCopyCharacters(copy, 0, unicode, 0, copy_length);
return copy;
}

static PyObject*
resize_compact(PyObject *unicode, Py_ssize_t length)
{
Expand All @@ -1132,7 +1147,14 @@ resize_compact(PyObject *unicode, Py_ssize_t length)
Py_ssize_t old_length = _PyUnicode_LENGTH(unicode);
#endif

assert(unicode_modifiable(unicode));
if (!unicode_modifiable(unicode)) {
PyObject *copy = resize_copy(unicode, length);
if (copy == NULL) {
return NULL;
}
Py_DECREF(unicode);
return copy;
}
assert(PyUnicode_IS_COMPACT(unicode));

char_size = PyUnicode_KIND(unicode);
Expand Down Expand Up @@ -1232,21 +1254,6 @@ resize_inplace(PyObject *unicode, Py_ssize_t length)
return 0;
}

static PyObject*
resize_copy(PyObject *unicode, Py_ssize_t length)
{
Py_ssize_t copy_length;
PyObject *copy;

copy = PyUnicode_New(length, PyUnicode_MAX_CHAR_VALUE(unicode));
if (copy == NULL)
return NULL;

copy_length = Py_MIN(length, PyUnicode_GET_LENGTH(unicode));
_PyUnicode_FastCopyCharacters(copy, 0, unicode, 0, copy_length);
return copy;
}

static const char*
unicode_kind_name(PyObject *unicode)
{
Expand Down Expand Up @@ -1836,7 +1843,7 @@ static int
unicode_modifiable(PyObject *unicode)
{
assert(_PyUnicode_CHECK(unicode));
if (Py_REFCNT(unicode) != 1)
if (!_PyObject_IsUniquelyReferenced(unicode))
return 0;
if (PyUnicode_HASH(unicode) != -1)
return 0;
Expand Down Expand Up @@ -14025,7 +14032,7 @@ _PyUnicode_FormatLong(PyObject *val, int alt, int prec, int type)
assert(PyUnicode_IS_ASCII(result));

/* To modify the string in-place, there can only be one reference. */
if (Py_REFCNT(result) != 1) {
if (!_PyObject_IsUniquelyReferenced(result)) {
Py_DECREF(result);
PyErr_BadInternalCall();
return NULL;
Expand Down
Loading