Skip to content

Commit 83afa76

Browse files
committed
gh-148722: Make code objects support cyclic GC
1 parent 9fdbade commit 83afa76

4 files changed

Lines changed: 19 additions & 22 deletions

File tree

Lib/test/test_code.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,19 @@ def callback(code):
12821282
self.assertFalse(bool(coderef()))
12831283
self.assertTrue(self.called)
12841284

1285+
def test_code_const_reference_cycle_collected(self):
1286+
def f():
1287+
return 1234.5
1288+
1289+
a = []
1290+
code = f.__code__.replace(co_consts=f.__code__.co_consts + (a,))
1291+
coderef = weakref.ref(code)
1292+
a.append(code)
1293+
1294+
del code, a
1295+
gc_collect()
1296+
self.assertIsNone(coderef())
1297+
12851298
# Python implementation of location table parsing algorithm
12861299
def read(it):
12871300
return next(it)

Lib/test/test_marshal.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,7 @@ def f():
357357
code = f.__code__
358358
a = []
359359
code = code.replace(co_consts=code.co_consts + (a,))
360-
# This test creates a reference loop which leads to reference leaks,
361-
# so we need to break the loop manually. See gh-148722.
360+
# Break the cycle without relying on cyclic GC after the test.
362361
self.addCleanup(a.clear)
363362
a.append(code)
364363
for v in range(marshal.version + 1):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a reference leak when a code object's constants contain a reference cycle
2+
back to the code object.

Objects/codeobject.c

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -731,12 +731,7 @@ _PyCode_New(struct _PyCodeConstructor *con)
731731
}
732732

733733
Py_ssize_t size = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT);
734-
PyCodeObject *co;
735-
#ifdef Py_GIL_DISABLED
736-
co = PyObject_GC_NewVar(PyCodeObject, &PyCode_Type, size);
737-
#else
738-
co = PyObject_NewVar(PyCodeObject, &PyCode_Type, size);
739-
#endif
734+
PyCodeObject *co = PyObject_GC_NewVar(PyCodeObject, &PyCode_Type, size);
740735
if (co == NULL) {
741736
Py_XDECREF(replacement_locations);
742737
PyErr_NoMemory();
@@ -750,8 +745,8 @@ _PyCode_New(struct _PyCodeConstructor *con)
750745

751746
#ifdef Py_GIL_DISABLED
752747
co->_co_unique_id = _PyObject_AssignUniqueId((PyObject *)co);
753-
_PyObject_GC_TRACK(co);
754748
#endif
749+
_PyObject_GC_TRACK(co);
755750
Py_XDECREF(replacement_locations);
756751
return co;
757752
}
@@ -2402,9 +2397,7 @@ code_dealloc(PyObject *self)
24022397
return;
24032398
}
24042399

2405-
#ifdef Py_GIL_DISABLED
24062400
PyObject_GC_UnTrack(co);
2407-
#endif
24082401

24092402
_PyFunction_ClearCodeByVersion(co->co_version);
24102403
if (co->co_extra != NULL) {
@@ -2459,18 +2452,16 @@ code_dealloc(PyObject *self)
24592452
}
24602453
PyMem_Free(co->co_tlbc);
24612454
#endif
2462-
PyObject_Free(co);
2455+
PyObject_GC_Del(co);
24632456
}
24642457

2465-
#ifdef Py_GIL_DISABLED
24662458
static int
24672459
code_traverse(PyObject *self, visitproc visit, void *arg)
24682460
{
24692461
PyCodeObject *co = _PyCodeObject_CAST(self);
24702462
Py_VISIT(co->co_consts);
24712463
return 0;
24722464
}
2473-
#endif
24742465

24752466
static PyObject *
24762467
code_repr(PyObject *self)
@@ -2890,17 +2881,9 @@ PyTypeObject PyCode_Type = {
28902881
PyObject_GenericGetAttr, /* tp_getattro */
28912882
0, /* tp_setattro */
28922883
0, /* tp_as_buffer */
2893-
#ifdef Py_GIL_DISABLED
28942884
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
2895-
#else
2896-
Py_TPFLAGS_DEFAULT, /* tp_flags */
2897-
#endif
28982885
code_new__doc__, /* tp_doc */
2899-
#ifdef Py_GIL_DISABLED
29002886
code_traverse, /* tp_traverse */
2901-
#else
2902-
0, /* tp_traverse */
2903-
#endif
29042887
0, /* tp_clear */
29052888
code_richcompare, /* tp_richcompare */
29062889
offsetof(PyCodeObject, co_weakreflist), /* tp_weaklistoffset */

0 commit comments

Comments
 (0)