69 lines
3.3 KiB
Python
69 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Check only the two static type initializer mappings at the VC6 frontier."""
|
|
import re
|
|
from pathlib import Path
|
|
|
|
source = Path('cpython/Objects/typeobject.c').read_text()
|
|
header = Path('cpython/Include/cpython/object.h').read_text()
|
|
|
|
|
|
def uncomment(text):
|
|
return re.sub(r'/\*.*?\*/|//[^\n]*', '', text, flags=re.S)
|
|
|
|
|
|
def check(name, declaration, layout, prefix, expected, head=''):
|
|
match = re.search(declaration + ' ' + name + r' = \{(.*?)\n\};', source, re.S)
|
|
assert match, name + ': initializer missing'
|
|
body = uncomment(match.group(1)).strip()
|
|
assert not re.search(r'\.\w+\s*=', body), name + ': designated initializer is not VC6/C89 compatible'
|
|
if head:
|
|
assert body.startswith(head), name + ': object header changed'
|
|
body = body[len(head):]
|
|
fields = re.findall(r'\b(' + prefix + r'\w+)\b', uncomment(layout))
|
|
assert len(fields) == len(set(fields)) == (36 if prefix == 'nb_' else 48)
|
|
# Split only at top-level commas (offsetof arguments contain commas).
|
|
values = [v.strip() for v in re.split(r',\s*(?![^()]*\))', body) if v.strip()]
|
|
assert len(values) <= len(fields), name + ': excess initializer entries'
|
|
values += ['0'] * (len(fields) - len(values))
|
|
normalize = lambda value: re.sub(r'\s+', '', value)
|
|
actual = dict(zip(fields, map(normalize, values)))
|
|
wanted = {field: normalize(expected.get(field, '0')) for field in fields}
|
|
assert expected.keys() <= actual.keys(), name + ': expected fields absent from header'
|
|
assert actual == wanted, name + ': field mappings changed: ' + repr({k: (actual[k], wanted[k]) for k in fields if actual[k] != wanted[k]})
|
|
print(f'{name}: C89 positional structure and all {len(fields)} field mappings PASS')
|
|
|
|
|
|
number_layout = re.search(r'typedef struct \{([^{}]*)\} PyNumberMethods;', header, re.S)
|
|
assert number_layout, 'PyNumberMethods layout missing'
|
|
check('type_as_number', 'static PyNumberMethods', number_layout.group(1), 'nb_', {
|
|
'nb_or': '_Py_union_type_or',
|
|
})
|
|
type_layout = re.search(r'struct _typeobject \{(.*?)\n\};', header, re.S)
|
|
assert type_layout, 'PyTypeObject layout missing'
|
|
check('PyType_Type', 'PyTypeObject', type_layout.group(1), 'tp_', {
|
|
'tp_name': '"type"',
|
|
'tp_basicsize': 'sizeof(PyHeapTypeObject)',
|
|
'tp_itemsize': 'sizeof(PyMemberDef)',
|
|
'tp_dealloc': '(destructor)type_dealloc',
|
|
'tp_vectorcall_offset': 'offsetof(PyTypeObject, tp_vectorcall)',
|
|
'tp_repr': '(reprfunc)type_repr',
|
|
'tp_as_number': '&type_as_number',
|
|
'tp_call': '(ternaryfunc)type_call',
|
|
'tp_getattro': '(getattrofunc)type_getattro',
|
|
'tp_setattro': '(setattrofunc)type_setattro',
|
|
'tp_flags': 'Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TYPE_SUBCLASS | Py_TPFLAGS_HAVE_VECTORCALL',
|
|
'tp_doc': 'type_doc',
|
|
'tp_traverse': '(traverseproc)type_traverse',
|
|
'tp_clear': '(inquiry)type_clear',
|
|
'tp_weaklistoffset': 'offsetof(PyTypeObject, tp_weaklist)',
|
|
'tp_methods': 'type_methods',
|
|
'tp_members': 'type_members',
|
|
'tp_getset': 'type_getsets',
|
|
'tp_dictoffset': 'offsetof(PyTypeObject, tp_dict)',
|
|
'tp_init': 'type_init',
|
|
'tp_new': 'type_new',
|
|
'tp_free': 'PyObject_GC_Del',
|
|
'tp_is_gc': '(inquiry)type_is_gc',
|
|
'tp_vectorcall': 'type_vectorcall',
|
|
}, 'PyVarObject_HEAD_INIT(&PyType_Type, 0)')
|