#!/usr/bin/env python3 """Focused Visual C++ 6.0 C89 regression checks for object.c.""" import re import sys from pathlib import Path source = Path(sys.argv[1]).read_text() def body(name): found = re.search(re.escape(name) + r"\s*\([^;]*\)\s*\{(?P.*?)\n\}", source, re.S) assert found, name + " definition not found" return found.group("body") def check(name, tokens, message): text = body(name) positions = [text.find(token) for token in tokens] assert all(position >= 0 for position in positions) and positions == sorted(positions), message return text text = check("PyObject_CallFinalizerFromDealloc", ( "Py_ssize_t refcnt;", "if (Py_REFCNT(self) != 0)", "refcnt = Py_REFCNT(self);", "_Py_NewReference(self);", "Py_SET_REFCNT(self, refcnt);", ), "PyObject_CallFinalizerFromDealloc must hoist refcnt while preserving resurrection order") assert "Py_ssize_t refcnt =" not in text print("object PyObject_CallFinalizerFromDealloc C89 local and refcount order: PASS") text = check("_PyObject_Dump", ( "PyTypeObject *type;", "PyGILState_STATE gil;", "PyObject *error_type, *error_value, *error_traceback;", "if (_PyObject_IsFreed(op))", "type = Py_TYPE(op);", "gil = PyGILState_Ensure();", "PyErr_Fetch(&error_type, &error_value, &error_traceback);", ), "_PyObject_Dump must hoist locals without changing diagnostics or exception preservation") assert "PyTypeObject *type =" not in text and "PyGILState_STATE gil =" not in text print("object _PyObject_Dump C89 locals and exception preservation: PASS") text = check("PyObject_Repr", ( "PyObject *res;", "PyThreadState *tstate;", "if (PyErr_CheckSignals())", "tstate = _PyThreadState_GET();", ), "PyObject_Repr must hoist tstate while preserving signal-check order") assert "PyThreadState *tstate =" not in text print("object PyObject_Repr C89 local and signal-check order: PASS") text = body("_PyObject_DictPointer") expected = ''' Py_ssize_t dictoffset; PyTypeObject *tp = Py_TYPE(obj); if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) { return _PyObject_ManagedDictPointer(obj); } dictoffset = tp->tp_dictoffset; if (dictoffset == 0) return NULL; if (dictoffset < 0) { Py_ssize_t tsize = Py_SIZE(obj); size_t size; if (tsize < 0) { tsize = -tsize; } size = _PyObject_VAR_SIZE(tp, tsize); assert(size <= (size_t)PY_SSIZE_T_MAX); dictoffset += (Py_ssize_t)size; _PyObject_ASSERT(obj, dictoffset > 0); _PyObject_ASSERT(obj, dictoffset % SIZEOF_VOID_P == 0); } return (PyObject **) ((char *)obj + dictoffset); ''' assert text.split() == expected.split(), ( "_PyObject_DictPointer must hoist only size to the block's opening declarations " "and preserve the size expression and statement order" ) print("object _PyObject_DictPointer C89 size declaration and offset calculation order: PASS")