Files
Python3-Win9x/scripts/vc6-interpreteridobject-c89.test.py
T

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")