#!/usr/bin/env python3 """Keep GC subtype_dealloc's decref decision C89-safe at its evaluation point.""" import re import sys from pathlib import Path source = Path(sys.argv[1] if len(sys.argv) > 1 else "cpython/Objects/typeobject.c").read_text() match = re.search( r"\nstatic void\nsubtype_dealloc\(PyObject \*self\)\n\{(.*?)\n\}", source, re.S, ) assert match, "subtype_dealloc definition missing" body = match.group(1) assert re.match( r"\s*PyTypeObject \*type, \*base;\s*destructor basedealloc;" r"\s*int has_finalizer;\s*int type_needs_decref;", body, ), "GC subtype_dealloc: type_needs_decref must be declared at function entry (C89)" start = body.index(" /* We get here only if the type has GC */") end = body.index(" /* Explanation of the weirdness", start) branch = re.sub(r"/\*.*?\*/|//[^\n]*", "", body[start:end], flags=re.S) expected = r""" PyObject_GC_UnTrack(self); Py_TRASHCAN_BEGIN(self, subtype_dealloc); base = type; while (( base->tp_dealloc) == subtype_dealloc) { base = base->tp_base; assert(base); } has_finalizer = type->tp_finalize || type->tp_del; if (type->tp_finalize) { _PyObject_GC_TRACK(self); if (PyObject_CallFinalizerFromDealloc(self) < 0) { goto endlabel; } _PyObject_GC_UNTRACK(self); } if (type->tp_weaklistoffset && !base->tp_weaklistoffset) { PyObject_ClearWeakRefs(self); } if (type->tp_del) { _PyObject_GC_TRACK(self); type->tp_del(self); if (Py_REFCNT(self) > 0) { goto endlabel; } _PyObject_GC_UNTRACK(self); } if (has_finalizer) { if (type->tp_weaklistoffset && !base->tp_weaklistoffset) { PyWeakReference **list = (PyWeakReference **) \ _PyObject_GET_WEAKREFS_LISTPTR(self); while (*list) _PyWeakref_ClearRef(*list); } } base = type; while ((basedealloc = base->tp_dealloc) == subtype_dealloc) { if (Py_SIZE(base)) clear_slots(base, self); base = base->tp_base; assert(base); } if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { PyObject **dictptr = _PyObject_ManagedDictPointer(self); if (*dictptr != NULL) { assert(*_PyObject_ValuesPointer(self) == NULL); Py_DECREF(*dictptr); *dictptr = NULL; } else { _PyObject_FreeInstanceAttributes(self); } } else if (type->tp_dictoffset && !base->tp_dictoffset) { PyObject **dictptr = _PyObject_DictPointer(self); if (dictptr != NULL) { PyObject *dict = *dictptr; if (dict != NULL) { Py_DECREF(dict); *dictptr = NULL; } } } type = Py_TYPE(self); if (_PyType_IS_GC(base)) { _PyObject_GC_TRACK(self); } type_needs_decref = (type->tp_flags & Py_TPFLAGS_HEAPTYPE && !(base->tp_flags & Py_TPFLAGS_HEAPTYPE)); assert(basedealloc); basedealloc(self); if (type_needs_decref) { Py_DECREF(type); } endlabel: Py_TRASHCAN_END """ assert branch.split() == expected.split(), ( "GC subtype_dealloc: preserve assignment evaluation point and all control flow" ) print("GC subtype_dealloc type_needs_decref C89 declaration and unchanged control flow: PASS")