101 lines
4.5 KiB
Python
101 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Focused Visual C++ 6.0 C89 regression checks for memoryobject.c."""
|
|
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
source = Path(sys.argv[1]).read_text()
|
|
|
|
match = re.search(
|
|
r"static int\s+copy_single\(PyMemoryViewObject \*self, const Py_buffer \*dest, const Py_buffer \*src\)\s*\{(?P<body>.*?)\n\}",
|
|
source,
|
|
re.S,
|
|
)
|
|
assert match, "copy_single definition not found"
|
|
body = match.group("body")
|
|
expected = """
|
|
char *mem;
|
|
CHECK_RELEASED_INT_AGAIN(self);
|
|
mem = NULL;
|
|
"""
|
|
assert body.split()[:len(expected.split())] == expected.split(), (
|
|
"copy_single must declare mem before the release-check macro while keeping its initialization after the check"
|
|
)
|
|
assert "char *mem =" not in body, "copy_single must not initialize mem before the release check"
|
|
print("memoryobject copy_single C89 local and release-check order: PASS")
|
|
|
|
assert not re.search(r"\blong\s+long\b", source), "memoryobject must use PY_LONG_LONG spellings supported by VC6"
|
|
assert re.search(r"\bPY_LONG_LONG\b", source), "memoryobject must retain native 64-bit format support through PY_LONG_LONG"
|
|
assert not re.search(r"\b_Bool\b", source), "memoryobject must use the VC6-compatible one-byte bool representation"
|
|
assert "case '?': UNPACK_SINGLE(ld, ptr, unsigned char); goto convert_bool;" in source, "memoryobject bool unpacking must preserve nonzero-to-bool conversion"
|
|
assert "PACK_SINGLE(ptr, ld, unsigned char);" in source, "memoryobject bool packing must use a one-byte type after PyObject_IsTrue normalization"
|
|
print("memoryobject VC6 64-bit and bool format spellings: PASS")
|
|
|
|
match = re.search(r"static PyObject \*\s*memoryiter_next\(memoryiterobject \*it\)\s*\{(?P<body>.*?)\n\}", source, re.S)
|
|
assert match, "memoryiter_next definition not found"
|
|
body = match.group("body")
|
|
assert "Py_buffer *view =" not in body and "char *ptr =" not in body, "memoryiter_next must not declare locals after CHECK_RELEASED"
|
|
assert "if (it->it_index < it->it_length) {\n Py_buffer *view;\n char *ptr;\n\n CHECK_RELEASED(seq);\n view = &(seq->view);\n ptr = (char *)seq->view.buf;" in body, "memoryiter_next must declare conditional locals before release checks and preserve pointer setup order"
|
|
print("memoryobject memoryiter_next C89 locals and release-check order: PASS")
|
|
|
|
match = re.search(r"static PyObject \*\s*memory_iter\(PyObject \*seq\)\s*\{(?P<body>.*?)\n\}", source, re.S)
|
|
assert match, "memory_iter definition not found"
|
|
body = match.group("body")
|
|
expected = """
|
|
PyMemoryViewObject *obj;
|
|
int ndims;
|
|
const char *fmt;
|
|
memoryiterobject *it;
|
|
if (!PyMemoryView_Check(seq))
|
|
"""
|
|
assert body.split()[:len(expected.split())] == expected.split(), "memory_iter must declare locals before validation"
|
|
ordered = ("if (!PyMemoryView_Check(seq))", "obj = (PyMemoryViewObject *)seq;", "ndims = obj->view.ndim;", "fmt = adjust_fmt(&obj->view);", "it = PyObject_GC_New(memoryiterobject, &_PyMemoryIter_Type);")
|
|
positions = [body.find(token) for token in ordered]
|
|
assert all(position >= 0 for position in positions) and positions == sorted(positions), "memory_iter must preserve validation, type/shape setup, format adjustment, and allocation order"
|
|
assert not re.search(r"\b(?:PyMemoryViewObject|int|const char|memoryiterobject)\s+\*?\w+\s*=", body), "memory_iter must not retain late initialized declarations"
|
|
print("memoryobject memory_iter C89 locals and iterator allocation order: PASS")
|
|
|
|
match = re.search(r"PyTypeObject _PyMemoryIter_Type = \{(?P<body>.*?)\n\};", source, re.S)
|
|
assert match, "_PyMemoryIter_Type initializer not found"
|
|
body = match.group("body")
|
|
assert ".tp_" not in body, "_PyMemoryIter_Type must use positional fields instead of C99 designated initializers"
|
|
expected = (
|
|
"PyVarObject_HEAD_INIT(&PyType_Type, 0)",
|
|
'"memory_iterator"',
|
|
"sizeof(memoryiterobject)",
|
|
"0,",
|
|
"(destructor)memoryiter_dealloc",
|
|
"0,",
|
|
"0,",
|
|
"0,",
|
|
"0,",
|
|
"0,",
|
|
"0,",
|
|
"0,",
|
|
"0,",
|
|
"0,",
|
|
"0,",
|
|
"0,",
|
|
"PyObject_GenericGetAttr",
|
|
"0,",
|
|
"0,",
|
|
"Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC",
|
|
"0,",
|
|
"(traverseproc)memoryiter_traverse",
|
|
"0,",
|
|
"0,",
|
|
"0,",
|
|
"PyObject_SelfIter",
|
|
"(iternextfunc)memoryiter_next",
|
|
)
|
|
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), "_PyMemoryIter_Type positional initializer must preserve all configured field slots"
|
|
print("memoryobject _PyMemoryIter_Type C89 positional initializer: PASS")
|