Files
Python3-Win9x/scripts/vc6-funcobject-c89.test.py

164 lines
6.4 KiB
Python

#!/usr/bin/env python3
"""Guard C89 declarations and cleanup order in function construction."""
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")
source = Path(sys.argv[1]).read_text(encoding="utf-8")
body = extract_function(
source,
"PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname)",
)
declarations = (
"PyThreadState *tstate;",
"PyCodeObject *code_obj;",
"PyObject *name;",
"PyObject *consts;",
"PyObject *doc;",
"PyObject *module;",
"PyObject *builtins;",
"PyFunctionObject *op;",
)
remaining = body.lstrip()
for declaration in declarations:
if not remaining.startswith(declaration + "\n"):
fail("PyFunction_NewWithQualName entry declaration missing or out of order: " + declaration)
remaining = remaining[len(declaration):].lstrip()
for late_declaration in (
"PyThreadState *tstate =",
"PyCodeObject *code_obj =",
"PyObject *name =",
"PyObject *consts =",
"PyObject *doc;\n if",
"PyObject *module =",
"PyObject *builtins =",
"PyFunctionObject *op =",
):
if late_declaration in body:
fail("PyFunction_NewWithQualName contains a late declaration: " + late_declaration)
ordered = (
"assert(globals != NULL);",
"assert(PyDict_Check(globals));",
"Py_INCREF(globals);",
"tstate = _PyThreadState_GET();",
"code_obj = (PyCodeObject *)code;",
"Py_INCREF(code_obj);",
"name = code_obj->co_name;",
"assert(name != NULL);",
"Py_INCREF(name);",
"if (!qualname)",
"assert(qualname != NULL);",
"Py_INCREF(qualname);",
"consts = code_obj->co_consts;",
"assert(PyTuple_Check(consts));",
"if (PyTuple_Size(consts) >= 1)",
"doc = PyTuple_GetItem(consts, 0);",
"Py_INCREF(doc);",
"module = PyDict_GetItemWithError(globals, &_Py_ID(__name__));",
"builtins = NULL;",
"if (module == NULL && _PyErr_Occurred(tstate))",
"Py_XINCREF(module);",
"builtins = _PyEval_BuiltinsFromGlobals(tstate, globals);",
"Py_INCREF(builtins);",
"op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type);",
"if (op == NULL)",
"op->func_globals = globals;",
"op->func_qualname = qualname;",
"op->func_doc = doc;",
"_PyObject_GC_TRACK(op);",
"return (PyObject *)op;",
"error:",
"Py_DECREF(globals);",
"Py_DECREF(code_obj);",
"Py_DECREF(doc);",
"Py_XDECREF(module);",
"Py_XDECREF(builtins);",
"return NULL;",
)
positions = [body.find(token) for token in ordered]
if any(position < 0 for position in positions) or positions != sorted(positions):
fail("PyFunction_NewWithQualName must preserve acquisition, failure cleanup, and ownership order")
version = extract_function(source, "_PyFunction_GetVersionForCurrentState(PyFunctionObject *func)")
if not version.lstrip().startswith("uint32_t v;") or "uint32_t v =" in version:
fail("_PyFunction_GetVersionForCurrentState local must be declared at function entry")
ordered_version = (
"if (func->func_version != 0)",
"if (next_func_version == 0)",
"v = next_func_version++;",
"func->func_version = v;",
"return v;",
)
positions = [version.find(token) for token in ordered_version]
if any(position < 0 for position in positions) or positions != sorted(positions):
fail("function-version allocation must preserve guard and update order")
annotation_dict = extract_function(source, "func_get_annotation_dict(PyFunctionObject *op)")
if "if (PyTuple_CheckExact(op->func_annotations)) {\n Py_ssize_t i;" not in annotation_dict:
fail("func_get_annotation_dict loop index must be declared at the owning block entry")
if "for (Py_ssize_t i =" in annotation_dict:
fail("func_get_annotation_dict must not declare the loop index in its for initializer")
if not annotation_dict.find("assert(PyTuple_GET_SIZE(ann_tuple) % 2 == 0);") < annotation_dict.find("for (i = 0;") < annotation_dict.find("Py_SETREF(op->func_annotations, ann_dict);"):
fail("func_get_annotation_dict must preserve tuple validation, iteration, and replacement order")
get_annotations = extract_function(source, "func_get_annotations(PyFunctionObject *op, void *Py_UNUSED(ignored))")
if not get_annotations.lstrip().startswith("PyObject *d;") or "PyObject *d =" in get_annotations:
fail("func_get_annotations result local must be declared at function entry")
ordered_get_annotations = (
"if (op->func_annotations == NULL)",
"op->func_annotations = PyDict_New();",
"d = func_get_annotation_dict(op);",
"if (d)",
"Py_INCREF(d);",
"return d;",
)
positions = [get_annotations.find(token) for token in ordered_get_annotations]
if any(position < 0 for position in positions) or positions != sorted(positions):
fail("func_get_annotations must preserve lazy allocation and incref order")
copy_attr = extract_function(source, "functools_copy_attr(PyObject *wrapper, PyObject *wrapped, PyObject *name)")
remaining = copy_attr.lstrip()
for declaration in ("PyObject *value;", "int res;"):
if not remaining.startswith(declaration + "\n"):
fail("functools_copy_attr entry declaration missing or out of order: " + declaration)
remaining = remaining[len(declaration):].lstrip()
if "PyObject *value =" in copy_attr or "int res =" in copy_attr:
fail("functools_copy_attr must not declare initialized locals after statements")
ordered_copy_attr = (
"value = PyObject_GetAttr(wrapped, name);",
"if (value == NULL)",
"res = PyObject_SetAttr(wrapper, name, value);",
"Py_DECREF(value);",
"return res;",
)
positions = [copy_attr.find(token) for token in ordered_copy_attr]
if any(position < 0 for position in positions) or positions != sorted(positions):
fail("functools_copy_attr must preserve attribute lookup, error handling, and decref order")
print("funcobject C89 declarations and function-construction ownership order: PASS")