51 lines
2.1 KiB
Python
51 lines
2.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Keep type_add_method locals C89-compatible without moving API calls."""
|
|
import re
|
|
from pathlib import Path
|
|
|
|
source = Path("cpython/Objects/typeobject.c").read_text()
|
|
match = re.search(r"\nstatic int\ntype_add_method\(PyTypeObject \*type, PyMethodDef \*meth\)\n\{(.*?)\n\}", source, re.S)
|
|
assert match, "type_add_method definition missing"
|
|
body = re.sub(r"/\*.*?\*/|//[^\n]*", "", match.group(1), flags=re.S)
|
|
assert re.match(r"\s*PyObject \*descr;\s*PyObject \*name;\s*int err;\s*int isdescr = 1;\s*if", body), (
|
|
"type_add_method: name and err must be in the opening C89 declaration block"
|
|
)
|
|
for declaration in (r"\bPyObject\s*\*\s*name\b", r"\bint\s+err\b"):
|
|
assert len(re.findall(declaration, body)) == 1, "declare each moved local exactly once"
|
|
|
|
# Search in execution order: duplicate cleanup calls occur on error paths.
|
|
ordered = [
|
|
"if (meth->ml_flags & METH_CLASS)",
|
|
"if (meth->ml_flags & METH_STATIC)",
|
|
"descr = PyDescr_NewClassMethod(type, meth);",
|
|
"else if (meth->ml_flags & METH_STATIC)",
|
|
"PyObject *cfunc = PyCFunction_NewEx(meth, (PyObject*)type, NULL);",
|
|
"descr = PyStaticMethod_New(cfunc);",
|
|
"isdescr = 0;",
|
|
"Py_DECREF(cfunc);",
|
|
"descr = PyDescr_NewMethod(type, meth);",
|
|
"if (descr == NULL)",
|
|
"if (isdescr)",
|
|
"name = PyDescr_NAME(descr);",
|
|
"name = PyUnicode_FromString(meth->ml_name);",
|
|
"if (name == NULL)",
|
|
"Py_DECREF(descr);",
|
|
"if (!(meth->ml_flags & METH_COEXIST))",
|
|
"err = PyDict_SetDefault(type->tp_dict, name, descr) == NULL;",
|
|
"err = PyDict_SetItem(type->tp_dict, name, descr) < 0;",
|
|
"if (!isdescr)",
|
|
"Py_DECREF(name);",
|
|
"Py_DECREF(descr);",
|
|
"if (err)",
|
|
"return -1;",
|
|
"return 0;",
|
|
]
|
|
cursor = 0
|
|
for token in ordered:
|
|
position = body.find(token, cursor)
|
|
assert position >= 0, f"type_add_method: missing or reordered operation: {token}"
|
|
cursor = position + len(token)
|
|
assert len(re.findall(r"\bname\s*=(?!=)", body)) == 2, "retain both name assignment branches"
|
|
assert len(re.findall(r"\berr\s*=(?!=)", body)) == 2, "retain both dictionary insertion branches"
|
|
print("type_add_method C89 locals, branch ordering and reference cleanup: PASS")
|