#!/usr/bin/env python3 """Guard VC6-compatible default allocator copies, fields, and call order.""" import re import sys from pathlib import Path source = Path(sys.argv[1]).read_text() # Preserve ctx/malloc/calloc/realloc/free and WITH_PYMALLOC selection. macros = source[source.index("#define MALLOC_ALLOC"):source.index("typedef struct {", source.index("#define MALLOC_ALLOC"))] expected_macros = """ #define MALLOC_ALLOC {NULL, _PyMem_RawMalloc, _PyMem_RawCalloc, _PyMem_RawRealloc, _PyMem_RawFree} #ifdef WITH_PYMALLOC # define PYMALLOC_ALLOC {NULL, _PyObject_Malloc, _PyObject_Calloc, _PyObject_Realloc, _PyObject_Free} #endif #define PYRAW_ALLOC MALLOC_ALLOC #ifdef WITH_PYMALLOC # define PYOBJ_ALLOC PYMALLOC_ALLOC #else # define PYOBJ_ALLOC MALLOC_ALLOC #endif #define PYMEM_ALLOC PYOBJ_ALLOC """ assert macros.split() == expected_macros.split(), ( "default allocator fields or WITH_PYMALLOC selection changed" ) match = re.search( r"\npymem_set_default_allocator\([^)]*\)\s*\{(.*?)\n\}", source, re.S ) assert match, "pymem_set_default_allocator definition not found" body = re.sub(r"/\*.*?\*/", "", match.group(1), flags=re.S) declaration = "PyMemAllocatorEx new_alloc;" if body.count(declaration) != 1 or not body.lstrip().startswith(declaration): sys.exit("new_alloc must be declared once without initializer at function entry") expected = """ if (old_alloc != NULL) { PyMem_GetAllocator(domain, old_alloc); } switch(domain) { case PYMEM_DOMAIN_RAW: { PyMemAllocatorEx alloc = PYRAW_ALLOC; new_alloc = alloc; break; } case PYMEM_DOMAIN_MEM: { PyMemAllocatorEx alloc = PYMEM_ALLOC; new_alloc = alloc; break; } case PYMEM_DOMAIN_OBJ: { PyMemAllocatorEx alloc = PYOBJ_ALLOC; new_alloc = alloc; break; } default: return -1; } PyMem_SetAllocator(domain, &new_alloc); if (debug) { _PyMem_SetupDebugHooksDomain(domain); } return 0; """ if body.replace(declaration, "").split() != expected.split(): sys.exit("default allocator switch, assignments, or call order changed") print("obmalloc default allocator C89 assignments, allocator semantics, and execution order: PASS")