133 lines
5.0 KiB
Python
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")
|