117 lines
4.7 KiB
Python
117 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Guard C89 declarations and ownership/order invariants in interpreteridobject.c."""
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import NoReturn
|
|
|
|
|
|
def fail(message: str) -> NoReturn:
|
|
print(message, file=sys.stderr)
|
|
raise SystemExit(1)
|
|
|
|
|
|
def extract_function(source: str, signature: str) -> str:
|
|
start = source.find(signature)
|
|
if start < 0:
|
|
fail(f"missing function signature: {signature}")
|
|
brace = source.find("{", start + len(signature))
|
|
if brace < 0:
|
|
fail("missing function body")
|
|
depth = 0
|
|
for end in range(brace, len(source)):
|
|
if source[end] == "{":
|
|
depth += 1
|
|
elif source[end] == "}":
|
|
depth -= 1
|
|
if depth == 0:
|
|
return source[brace + 1:end]
|
|
fail("unterminated function body")
|
|
|
|
|
|
def ordered(body: str, tokens: tuple[str, ...], message: str) -> None:
|
|
positions = [body.find(token) for token in tokens]
|
|
if any(position < 0 for position in positions) or positions != sorted(positions):
|
|
fail(message)
|
|
|
|
|
|
source = Path(sys.argv[1]).read_text(encoding="utf-8")
|
|
|
|
newinterpid = extract_function(source, "newinterpid(PyTypeObject *cls, int64_t id, int force)")
|
|
for declaration in ("PyInterpreterState *interp;", "interpid *self;"):
|
|
if declaration not in newinterpid:
|
|
fail("newinterpid local must be declared at function entry: " + declaration)
|
|
if "PyInterpreterState *interp =" in newinterpid or "interpid *self =" in newinterpid:
|
|
fail("newinterpid must not initialize locals in late declarations")
|
|
ordered(newinterpid, (
|
|
"interp = _PyInterpreterState_LookUpID(id);",
|
|
"if (interp == NULL)",
|
|
"if (interp != NULL)",
|
|
"_PyInterpreterState_IDIncref(interp)",
|
|
"self = PyObject_New(interpid, cls);",
|
|
"if (self == NULL)",
|
|
"_PyInterpreterState_IDDecref(interp);",
|
|
"self->id = id;",
|
|
"return self;",
|
|
), "newinterpid must preserve lookup, incref, allocation, rollback, and assignment order")
|
|
|
|
dealloc = extract_function(source, "interpid_dealloc(PyObject *v)")
|
|
for declaration in ("int64_t id;", "PyInterpreterState *interp;"):
|
|
if declaration not in dealloc:
|
|
fail("interpid_dealloc local must be declared at function entry: " + declaration)
|
|
ordered(dealloc, (
|
|
"id = ((interpid *)v)->id;",
|
|
"interp = _PyInterpreterState_LookUpID(id);",
|
|
"_PyInterpreterState_IDDecref(interp);",
|
|
"PyErr_Clear();",
|
|
"Py_TYPE(v)->tp_free(v);",
|
|
), "interpid_dealloc must preserve ID lookup, reference release, and free order")
|
|
|
|
hash_body = extract_function(source, "interpid_hash(PyObject *self)")
|
|
if "Py_hash_t hash;" not in hash_body or "Py_hash_t hash =" in hash_body:
|
|
fail("interpid_hash hash result must be declared before object hashing")
|
|
ordered(hash_body, (
|
|
"PyObject *obj = PyLong_FromLongLong(id->id);",
|
|
"if (obj == NULL)",
|
|
"hash = PyObject_Hash(obj);",
|
|
"Py_DECREF(obj);",
|
|
"return hash;",
|
|
), "interpid_hash must preserve allocation, hashing, decref, and return order")
|
|
|
|
richcompare = extract_function(source, "interpid_richcompare(PyObject *self, PyObject *other, int op)")
|
|
for declaration in ("interpid *id;", "int equal;"):
|
|
if declaration not in richcompare:
|
|
fail("interpid_richcompare local must be declared at function entry: " + declaration)
|
|
ordered(richcompare, (
|
|
"if (op != Py_EQ && op != Py_NE)",
|
|
"if (!PyObject_TypeCheck(self, &_PyInterpreterID_Type))",
|
|
"id = (interpid *)self;",
|
|
"if (PyObject_TypeCheck(other, &_PyInterpreterID_Type))",
|
|
"interpid *otherid = (interpid *)other;",
|
|
"equal = (id->id == otherid->id);",
|
|
"else if (PyLong_CheckExact(other))",
|
|
"int overflow;",
|
|
"PY_LONG_LONG otherid;",
|
|
"otherid = PyLong_AsLongLongAndOverflow(other, &overflow);",
|
|
"equal = !overflow && (otherid >= 0) && (id->id == otherid);",
|
|
"else if (PyNumber_Check(other))",
|
|
"pyid = PyLong_FromLongLong(id->id);",
|
|
"res = PyObject_RichCompare(pyid, other, op);",
|
|
"Py_DECREF(pyid);",
|
|
"return res;",
|
|
"if ((op == Py_EQ && equal) || (op == Py_NE && !equal))",
|
|
"Py_RETURN_TRUE;",
|
|
"Py_RETURN_FALSE;",
|
|
), "interpid_richcompare must preserve dispatch, overflow, and reference ordering")
|
|
|
|
get_id = extract_function(source, "_PyInterpreterState_GetIDObject(PyInterpreterState *interp)")
|
|
if "int64_t id;" not in get_id or "int64_t id =" in get_id:
|
|
fail("_PyInterpreterState_GetIDObject ID local must be declared at function entry")
|
|
ordered(get_id, (
|
|
"if (_PyInterpreterState_IDInitref(interp) != 0)",
|
|
"id = PyInterpreterState_GetID(interp);",
|
|
"if (id < 0)",
|
|
"return (PyObject *)newinterpid(&_PyInterpreterID_Type, id, 0);",
|
|
), "_PyInterpreterState_GetIDObject must preserve initref and ID validation order")
|
|
|
|
print("interpreteridobject C89 declarations and ownership order: PASS")
|