121 lines
3.9 KiB
Python
121 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Compile the exact setiter_reduce as C89 and check ownership and call order."""
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
source = Path(sys.argv[1]).read_text()
|
|
match = re.search(
|
|
r"static PyObject \*\nsetiter_reduce\([^\n]+\)\n\{.*?\n\}", source, re.S
|
|
)
|
|
assert match, "setiter_reduce not found"
|
|
harness = r'''
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
typedef struct { int refs; } PyObject;
|
|
typedef struct {
|
|
PyObject head;
|
|
PyObject *si_set;
|
|
long si_used, si_pos, len;
|
|
} setiterobject;
|
|
#define Py_UNUSED(name) name
|
|
#define _Py_ID(name) name
|
|
static int iter, mode, exhausted;
|
|
static PyObject backing, list_value, builtin, built;
|
|
static setiterobject original;
|
|
static char events[32];
|
|
static void event(char c) {
|
|
size_t n = strlen(events);
|
|
assert(n + 1 < sizeof(events));
|
|
events[n] = c;
|
|
events[n + 1] = '\0';
|
|
}
|
|
static void Py_XINCREF(PyObject *obj) {
|
|
assert(obj == original.si_set);
|
|
assert(strcmp(events, "") == 0);
|
|
event('I');
|
|
if (obj != NULL) ++obj->refs;
|
|
}
|
|
static void Py_XDECREF(PyObject *obj) {
|
|
assert(obj == (mode == 1 ? NULL : original.si_set));
|
|
assert(strcmp(events, "IL") == 0);
|
|
event('D');
|
|
if (obj != NULL) --obj->refs;
|
|
}
|
|
static PyObject *PySequence_List(PyObject *obj) {
|
|
setiterobject *tmp = (setiterobject *)obj;
|
|
assert(tmp != &original);
|
|
assert(tmp->head.refs == original.head.refs);
|
|
assert(tmp->si_set == original.si_set);
|
|
assert(tmp->si_used == original.si_used);
|
|
assert(tmp->si_pos == original.si_pos && tmp->len == original.len);
|
|
assert(backing.refs == (exhausted ? 1 : 2));
|
|
assert(strcmp(events, "I") == 0);
|
|
event('L');
|
|
tmp->si_pos = 99;
|
|
tmp->len = 0;
|
|
if (mode == 1 && tmp->si_set != NULL) {
|
|
--tmp->si_set->refs;
|
|
tmp->si_set = NULL;
|
|
}
|
|
return mode == 2 ? NULL : &list_value;
|
|
}
|
|
static PyObject *_PyEval_GetBuiltin(int *name) {
|
|
assert(name == &iter);
|
|
assert(strcmp(events, "ILD") == 0 && backing.refs == 1);
|
|
event('G');
|
|
return &builtin;
|
|
}
|
|
static PyObject *Py_BuildValue(const char *format, PyObject *func, PyObject *list) {
|
|
assert(strcmp(format, "N(N)") == 0);
|
|
assert(func == &builtin && list == &list_value);
|
|
assert(strcmp(events, "ILDG") == 0 && backing.refs == 1);
|
|
event('B');
|
|
--func->refs;
|
|
--list->refs;
|
|
return mode == 3 ? NULL : &built;
|
|
}
|
|
'''
|
|
harness += match.group(0)
|
|
harness += r'''
|
|
int main(void) {
|
|
setiterobject before;
|
|
PyObject *result;
|
|
for (exhausted = 0; exhausted < 2; ++exhausted) {
|
|
for (mode = 0; mode < 4; ++mode) {
|
|
backing.refs = list_value.refs = builtin.refs = 1;
|
|
memset(&original, 0, sizeof(original));
|
|
original.head.refs = 7;
|
|
original.si_set = exhausted ? NULL : &backing;
|
|
original.si_used = 5;
|
|
original.si_pos = 2;
|
|
original.len = 3;
|
|
memcpy(&before, &original, sizeof(before));
|
|
events[0] = '\0';
|
|
result = setiter_reduce(&original, NULL);
|
|
assert(memcmp(&before, &original, sizeof(before)) == 0);
|
|
assert(backing.refs == 1);
|
|
assert(strcmp(events, mode == 2 ? "ILD" : "ILDGB") == 0);
|
|
assert(result == (mode >= 2 ? NULL : &built));
|
|
assert(list_value.refs == (mode == 2 ? 1 : 0));
|
|
assert(builtin.refs == (mode == 2 ? 1 : 0));
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
'''
|
|
with tempfile.TemporaryDirectory(prefix="vc6-setiter-reduce-") as directory:
|
|
path = Path(directory)
|
|
(path / "test.c").write_text(harness)
|
|
result = subprocess.run([
|
|
"cc", "-std=c89", "-pedantic-errors", "-Wall", "-Wextra", "-Werror",
|
|
"-Wno-unused-parameter", str(path / "test.c"), "-o", str(path / "test"),
|
|
])
|
|
if result.returncode:
|
|
sys.exit(result.returncode)
|
|
subprocess.run([str(path / "test")], check=True)
|
|
print("setiter_reduce C89 declaration/evaluation/cleanup/return: PASS (8 cases)")
|