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

133 lines
5.0 KiB
Python

#!/usr/bin/env python3
"""Focused Visual C++ 6.0 C89 regression checks for methodobject.c."""
import re
import sys
from pathlib import Path
source = Path(sys.argv[1]).read_text()
match = re.search(
r"PyObject \*\s*PyCMethod_New\(PyMethodDef \*ml, PyObject \*self, PyObject \*module, PyTypeObject \*cls\)\s*\{(?P<body>.*?)\n\}",
source,
re.S,
)
assert match, "PyCMethod_New definition not found"
body = match.group("body")
declarations = ("vectorcallfunc vectorcall;", "PyCFunctionObject *op;", "PyCMethodObject *om;", "switch (ml->ml_flags")
declaration_positions = [body.find(token) for token in declarations]
assert all(position >= 0 for position in declaration_positions) and declaration_positions == sorted(declaration_positions), (
"PyCMethod_New must declare op and om at function entry before its dispatch switch"
)
ordered = (
"switch (ml->ml_flags",
"op = NULL;",
"if (ml->ml_flags & METH_METHOD)",
"om = PyObject_GC_New(PyCMethodObject, &PyCMethod_Type);",
"op = PyObject_GC_New(PyCFunctionObject, &PyCFunction_Type);",
"op->m_weakreflist = NULL;",
)
positions = [body.find(token) for token in ordered]
assert all(position >= 0 for position in positions) and positions == sorted(positions), (
"PyCMethod_New must preserve flag dispatch, null initialization, allocation, and field setup order"
)
assert "PyCFunctionObject *op =" not in body and "PyCMethodObject *om =" not in body, (
"PyCMethod_New must not introduce late C89 declarations"
)
print("methodobject PyCMethod_New C89 declarations and allocation order: PASS")
match = re.search(r"PyTypeObject PyCMethod_Type = \{(?P<body>.*?)\n\};", source, re.S)
assert match, "PyCMethod_Type initializer not found"
body = match.group("body")
assert ".tp_" not in body, "PyCMethod_Type must use positional fields instead of C99 designated initializers"
expected = (
"PyVarObject_HEAD_INIT(&PyType_Type, 0)",
'"builtin_method"',
"sizeof(PyCMethodObject)",
) + ("0,",) * 27 + ("&PyCFunction_Type",)
positions = []
cursor = 0
for token in expected:
position = body.find(token, cursor)
positions.append(position)
if position >= 0:
cursor = position + len(token)
assert all(position >= 0 for position in positions), "PyCMethod_Type positional initializer must preserve the base-type slot"
print("methodobject PyCMethod_Type C89 positional initializer: PASS")
def function_body(name):
found = re.search(
r"static PyObject \*\s*" + re.escape(name) + r"\s*\([^;]*\)\s*\{(?P<body>.*?)\n\}",
source,
re.S,
)
assert found, name + " definition not found"
return found.group("body")
checks = {
"cfunction_vectorcall_FASTCALL": (
"PyThreadState *tstate;",
"Py_ssize_t nargs;",
"_PyCFunctionFast meth;",
"PyObject *result;",
"tstate = _PyThreadState_GET();",
"nargs = PyVectorcall_NARGS(nargsf);",
"meth = (_PyCFunctionFast)",
"result = meth(",
),
"cfunction_vectorcall_FASTCALL_KEYWORDS": (
"PyThreadState *tstate;",
"Py_ssize_t nargs;",
"_PyCFunctionFastWithKeywords meth;",
"PyObject *result;",
"tstate = _PyThreadState_GET();",
"nargs = PyVectorcall_NARGS(nargsf);",
"meth = (_PyCFunctionFastWithKeywords)",
"result = meth(",
),
"cfunction_vectorcall_NOARGS": (
"Py_ssize_t nargs;",
"PyCFunction meth;",
"PyObject *result;",
"PyThreadState *tstate = _PyThreadState_GET();",
"if (cfunction_check_kwargs(",
"nargs = PyVectorcall_NARGS(nargsf);",
"meth = (PyCFunction)cfunction_enter_call(",
"result = _PyCFunction_TrampolineCall(",
),
"cfunction_vectorcall_O": (
"Py_ssize_t nargs;",
"PyCFunction meth;",
"PyObject *result;",
"PyThreadState *tstate = _PyThreadState_GET();",
"if (cfunction_check_kwargs(",
"nargs = PyVectorcall_NARGS(nargsf);",
"meth = (PyCFunction)cfunction_enter_call(",
"result = _PyCFunction_TrampolineCall(",
),
"cfunction_call": (
"PyThreadState *tstate;",
"int flags;",
"PyCFunction meth;",
"PyObject *self;",
"PyObject *result;",
"assert(kwargs == NULL || PyDict_Check(kwargs));",
"tstate = _PyThreadState_GET();",
"flags = PyCFunction_GET_FLAGS(func);",
"meth = PyCFunction_GET_FUNCTION(func);",
"self = PyCFunction_GET_SELF(func);",
),
}
for name, tokens in checks.items():
body = function_body(name)
positions = [body.find(token) for token in tokens]
assert all(position >= 0 for position in positions) and positions == sorted(positions), (
name + " must keep declarations before statements and preserve evaluation order"
)
for declaration in tokens[:5]:
if " = " not in declaration and declaration.endswith(";"):
assert declaration in body, name + " is missing an opening local declaration"
print("methodobject vectorcall and call paths C89 declarations and evaluation order: PASS")