port: expand VC6 C89 core frontier
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
#!/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")
|
||||
Reference in New Issue
Block a user