104 lines
3.4 KiB
Python
104 lines
3.4 KiB
Python
#!/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")
|