Files
Python3-Win9x/scripts/vc6-tupleobject-type-vectorcall-c89.test.py

112 lines
4.7 KiB
Python

#!/usr/bin/env python3
"""Compile the exact tuple type as C89 and verify every static slot."""
import re
import subprocess
import sys
import tempfile
from pathlib import Path
source_path = Path(sys.argv[1] if len(sys.argv) > 1 else "cpython/Objects/tupleobject.c")
source = source_path.read_text()
initializers = {}
for name in ("PyTuple_Type",):
match = re.search(r"PyTypeObject " + name + r" = \{.*?\n\};", source, re.S)
assert match, name + " initializer not found"
initializers[name] = match.group(0)
header = (source_path.parent.parent / "Include/cpython/object.h").read_text()
layout = re.search(r"struct _typeobject \{(.*?)\n\};", header, re.S)
assert layout, "PyTypeObject layout not found"
body = re.sub(r"/\*.*?\*/|//[^\n]*", "", layout.group(1), flags=re.S)
fields = re.findall(r"\b(tp_\w+)\b", body)
assert len(fields) == len(set(fields)) == 48, "review changed PyTypeObject layout"
expected = {
"tp_name": '"tuple"',
"tp_basicsize": "sizeof(PyTupleObject) - sizeof(PyObject *)",
"tp_itemsize": "sizeof(PyObject *)",
"tp_dealloc": "(destructor)tupledealloc",
"tp_repr": "(reprfunc)tuplerepr",
"tp_as_sequence": "&tuple_as_sequence",
"tp_as_mapping": "&tuple_as_mapping",
"tp_hash": "(hashfunc)tuplehash",
"tp_getattro": "PyObject_GenericGetAttr",
"tp_flags": "Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TUPLE_SUBCLASS | _Py_TPFLAGS_MATCH_SELF | Py_TPFLAGS_SEQUENCE",
"tp_doc": "tuple_new__doc__",
"tp_traverse": "(traverseproc)tupletraverse",
"tp_richcompare": "tuplerichcompare",
"tp_iter": "tuple_iter",
"tp_methods": "tuple_methods",
"tp_new": "tuple_new",
"tp_free": "PyObject_GC_Del",
"tp_vectorcall": "tuple_vectorcall",
}
assert expected.keys() <= set(fields)
# Use the real field declarations/order, with minimal stand-ins for dependencies.
harness = r'''
#include <assert.h>
#include <stddef.h>
#include <string.h>
typedef long Py_ssize_t;
typedef struct { int unused; } PyObject;
typedef struct { int unused; } PyAsyncMethods;
typedef struct { int unused; } PyNumberMethods;
typedef struct { int unused; } PySequenceMethods;
typedef struct { int unused; } PyMappingMethods;
typedef struct { int unused; } PyBufferProcs;
typedef struct { int unused; } PyMethodDef;
typedef struct { int unused; } PyMemberDef;
typedef struct { int unused; } PyGetSetDef;
typedef struct _typeobject PyTypeObject;
typedef struct { long head; PyObject *ob_item[1]; } PyTupleObject;
#define PyObject_VAR_HEAD long ob_refcnt; PyTypeObject *ob_type; long ob_size;
#define PyVarObject_HEAD_INIT(type, size) 1, type, size,
#define Py_TPFLAGS_DEFAULT 1UL
#define Py_TPFLAGS_HAVE_GC 2UL
#define Py_TPFLAGS_BASETYPE 4UL
#define _Py_TPFLAGS_MATCH_SELF 8UL
#define Py_TPFLAGS_TUPLE_SUBCLASS 16UL
#define Py_TPFLAGS_SEQUENCE 32UL
'''
for name in (
"destructor getattrfunc setattrfunc reprfunc hashfunc ternaryfunc "
"getattrofunc setattrofunc traverseproc inquiry richcmpfunc getiterfunc "
"iternextfunc descrgetfunc descrsetfunc initproc allocfunc newfunc "
"freefunc vectorcallfunc"
).split():
harness += f"typedef void (*{name})(void);\n"
harness += "struct _typeobject {" + body + "};\n"
for name in (
"tuple_new tupledealloc tuplerepr tuplehash PyObject_GenericGetAttr "
"tupletraverse tuplerichcompare tuple_iter PyObject_GC_Del tuple_vectorcall"
).split():
harness += f"static void {name}(void) {{ }}\n"
harness += "static PyTypeObject PyType_Type;\n"
harness += "static PySequenceMethods tuple_as_sequence;\n"
harness += "static PyMappingMethods tuple_as_mapping;\n"
harness += "static PyMethodDef tuple_methods[1];\n"
harness += 'static const char tuple_new__doc__[] = "tuple documentation";\n'
harness += "\n".join(initializers.values()) + "\nint main(void)\n{\n"
for name in initializers:
values = expected
harness += f" assert({name}.ob_refcnt == 1);\n"
harness += f" assert({name}.ob_type == &PyType_Type);\n"
harness += f" assert({name}.ob_size == 0);\n"
for field in fields:
value = values.get(field, "0")
if field in ("tp_name", "tp_doc"):
harness += f" assert(strcmp({name}.{field}, {value}) == 0);\n"
else:
harness += f" assert({name}.{field} == ({value}));\n"
harness += " return 0;\n}\n"
with tempfile.TemporaryDirectory(prefix="vc6-tupleobject-type-vectorcall-") as directory:
path = Path(directory)
(path / "test.c").write_text(harness)
result = subprocess.run([
"cc", "-std=c89", "-pedantic-errors", "-Wall", "-Wextra", "-Werror",
str(path / "test.c"), "-o", str(path / "test"),
])
if result.returncode:
sys.exit(result.returncode)
subprocess.run([str(path / "test")], check=True)
print("PyTuple_Type C89 compile and all 48 semantic field mappings: PASS")