Skip to content
Draft
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
3 changes: 3 additions & 0 deletions Doc/includes/typestruct.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,7 @@ typedef struct _typeobject {
* Otherwise, limited to MAX_VERSIONS_PER_CLASS (defined elsewhere).
*/
uint16_t tp_versions_used;

/* call function for all referenced objects (includes non-cyclic refs) */
traverseproc tp_reachable;
} PyTypeObject;
3 changes: 3 additions & 0 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,9 @@ struct _typeobject {
* Otherwise, limited to MAX_VERSIONS_PER_CLASS (defined elsewhere).
*/
uint16_t tp_versions_used;

/* call function for all referenced objects (includes non-cyclic refs) */
traverseproc tp_reachable;
};

#define _Py_ATTR_CACHE_UNUSED (30000) // (see tp_versions_used)
Expand Down
4 changes: 4 additions & 0 deletions Include/typeslots.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,7 @@
/* New in 3.14 */
#define Py_tp_token 83
#endif
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030F0000
/* New in 3.15 */
#define Py_tp_reachable 84
#endif
49 changes: 49 additions & 0 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -4748,6 +4748,54 @@ dict_traverse(PyObject *op, visitproc visit, void *arg)
return 0;
}

static int
dict_reachable(PyObject *op, visitproc visit, void *arg)
{
PyDictObject *mp = (PyDictObject *)op;
PyDictKeysObject *keys = mp->ma_keys;
Py_ssize_t n = keys->dk_nentries;

Py_VISIT(_PyObject_CAST(Py_TYPE(op)));

if (DK_IS_UNICODE(keys)) {
PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys);
if (_PyDict_HasSplitTable(mp)) {
PyObject **values = mp->ma_values->values;
for (Py_ssize_t i = 0; i < n; i++) {
PyObject *value = values[i];
if (value == NULL) {
continue;
}
Py_VISIT(entries[i].me_key);
Py_VISIT(value);
}
}
else {
for (Py_ssize_t i = 0; i < n; i++) {
PyObject *value = entries[i].me_value;
if (value == NULL) {
continue;
}
Py_VISIT(entries[i].me_key);
Py_VISIT(value);
}
}
}
else {
PyDictKeyEntry *entries = DK_ENTRIES(keys);
for (Py_ssize_t i = 0; i < n; i++) {
PyObject *value = entries[i].me_value;
if (value == NULL) {
continue;
}
Py_VISIT(entries[i].me_key);
Py_VISIT(value);
}
}

return 0;
}

static int
dict_tp_clear(PyObject *op)
{
Expand Down Expand Up @@ -5072,6 +5120,7 @@ PyTypeObject PyDict_Type = {
PyObject_GC_Del, /* tp_free */
.tp_vectorcall = dict_vectorcall,
.tp_version_tag = _Py_TYPE_VERSION_DICT,
.tp_reachable = dict_reachable,
};

/* For backward compatibility with old dictionary interface */
Expand Down
8 changes: 8 additions & 0 deletions Objects/listobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3463,6 +3463,13 @@ list_traverse(PyObject *self, visitproc visit, void *arg)
return 0;
}

static int
list_reachable(PyObject *self, visitproc visit, void *arg)
{
Py_VISIT(_PyObject_CAST(Py_TYPE(self)));
return list_traverse(self, visit, arg);
}

static PyObject *
list_richcompare_impl(PyObject *v, PyObject *w, int op)
{
Expand Down Expand Up @@ -3985,6 +3992,7 @@ PyTypeObject PyList_Type = {
PyObject_GC_Del, /* tp_free */
.tp_vectorcall = list_vectorcall,
.tp_version_tag = _Py_TYPE_VERSION_LIST,
.tp_reachable = list_reachable,
};

/*********************** List Iterator **************************/
Expand Down
8 changes: 8 additions & 0 deletions Objects/tupleobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,13 @@ tuple_traverse(PyObject *self, visitproc visit, void *arg)
return 0;
}

static int
tuple_reachable(PyObject *self, visitproc visit, void *arg)
{
Py_VISIT(_PyObject_CAST(Py_TYPE(self)));
return tuple_traverse(self, visit, arg);
}

static PyObject *
tuple_richcompare(PyObject *v, PyObject *w, int op)
{
Expand Down Expand Up @@ -911,6 +918,7 @@ PyTypeObject PyTuple_Type = {
PyObject_GC_Del, /* tp_free */
.tp_vectorcall = tuple_vectorcall,
.tp_version_tag = _Py_TYPE_VERSION_TUPLE,
.tp_reachable = tuple_reachable,
};

/* The following function breaks the notion that tuples are immutable:
Expand Down
49 changes: 49 additions & 0 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value);
static PyObject *
slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds);

static int
type_reachable(PyObject *self, visitproc visit, void *arg);

static inline PyTypeObject *
type_from_ref(PyObject *ref)
{
Expand Down Expand Up @@ -6944,6 +6947,9 @@ PyDoc_STRVAR(type_doc,
"type(object) -> the object's type\n"
"type(name, bases, dict, **kwds) -> a new type");

static int
object_reachable(PyObject *self, visitproc visit, void *arg);

static int
type_traverse(PyObject *self, visitproc visit, void *arg)
{
Expand Down Expand Up @@ -6975,6 +6981,30 @@ type_traverse(PyObject *self, visitproc visit, void *arg)
return 0;
}

static int
type_reachable(PyObject *self, visitproc visit, void *arg)
{
PyTypeObject *type = PyTypeObject_CAST(self);

Py_VISIT(lookup_tp_dict(type));
Py_VISIT(type->tp_cache);
Py_VISIT(lookup_tp_mro(type));
Py_VISIT(lookup_tp_bases(type));
Py_VISIT(type->tp_base);
Py_VISIT(lookup_tp_subclasses(type));
Py_VISIT(type->tp_weaklist);

if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) {
PyHeapTypeObject *ht = (PyHeapTypeObject *)type;
Py_VISIT(ht->ht_module);
Py_VISIT(ht->ht_name);
Py_VISIT(ht->ht_qualname);
Py_VISIT(ht->ht_slots);
}

return 0;
}

static int
type_clear(PyObject *self)
{
Expand Down Expand Up @@ -7079,6 +7109,7 @@ PyTypeObject PyType_Type = {
PyObject_GC_Del, /* tp_free */
type_is_gc, /* tp_is_gc */
.tp_vectorcall = type_vectorcall,
.tp_reachable = type_reachable,
};


Expand Down Expand Up @@ -8338,8 +8369,22 @@ PyTypeObject PyBaseObject_Type = {
PyType_GenericAlloc, /* tp_alloc */
object_new, /* tp_new */
PyObject_Free, /* tp_free */
.tp_reachable = object_reachable,
};

static int
object_reachable(PyObject *self, visitproc visit, void *arg)
{
Py_VISIT(_PyObject_CAST(Py_TYPE(self)));

traverseproc traverse = Py_TYPE(self)->tp_traverse;
if (traverse != NULL) {
return traverse(self, visit, arg);
}

return 0;
}


static int
type_add_method(PyTypeObject *type, PyMethodDef *meth)
Expand Down Expand Up @@ -8503,6 +8548,10 @@ inherit_special(PyTypeObject *type, PyTypeObject *base)

#undef COPYVAL

if (type->tp_reachable == NULL) {
type->tp_reachable = base->tp_reachable;
}

/* Setup fast subclass flags */
PyObject *mro = lookup_tp_mro(base);
unsigned long flags = 0;
Expand Down
1 change: 1 addition & 0 deletions Objects/typeslots.inc

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions Objects/unicodeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -14629,6 +14629,15 @@ errors defaults to 'strict'.");

static PyObject *unicode_iter(PyObject *seq);

static int
unicode_reachable(PyObject *self, visitproc visit, void *arg)
{
// Strings do not own references to other PyObjects, but we still
// report reachability to the type object.
Py_VISIT(_PyObject_CAST(Py_TYPE(self)));
return 0;
}

PyTypeObject PyUnicode_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"str", /* tp_name */
Expand Down Expand Up @@ -14673,6 +14682,7 @@ PyTypeObject PyUnicode_Type = {
unicode_new, /* tp_new */
PyObject_Free, /* tp_free */
.tp_vectorcall = unicode_vectorcall,
.tp_reachable = unicode_reachable,
};

/* Initialize the Unicode implementation */
Expand Down
18 changes: 7 additions & 11 deletions Python/immutability.c
Original file line number Diff line number Diff line change
Expand Up @@ -1526,9 +1526,12 @@ int traverse_freeze(PyObject* obj, struct FreezeState* freeze_state)
}
else
{
traverseproc traverse = Py_TYPE(obj)->tp_traverse;
if(traverse != NULL){
SUCCEEDS(traverse(obj, (visitproc)freeze_visit, freeze_state));
traverseproc references = Py_TYPE(obj)->tp_reachable;
if (references == NULL) {
references = Py_TYPE(obj)->tp_traverse;
}
if(references != NULL){
SUCCEEDS(references(obj, (visitproc)freeze_visit, freeze_state));
}
}

Expand All @@ -1550,13 +1553,6 @@ int traverse_freeze(PyObject* obj, struct FreezeState* freeze_state)
}
}

// The default tp_traverse will not visit the type object if it is
// not heap allocated, so we need to do that manually here to freeze
// the statically allocated types that are reachable.
if (!(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
SUCCEEDS(freeze_visit(_PyObject_CAST(Py_TYPE(obj)), freeze_state));
}

return 0;

error:
Expand Down Expand Up @@ -1678,4 +1674,4 @@ int _PyImmutability_Freeze(PyObject* obj)
deallocate_FreezeState(&freeze_state);
TRACE_MERMAID_END();
return result;
}
}