#!/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.*?)\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.*?)\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.*?)\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.*?)\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")