115 lines
4.0 KiB
Python
115 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Compile exact odictiter_reduce as C89 and check timing and ownership."""
|
|
|
|
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 \*\nodictiter_reduce\(odictiterobject \*di, PyObject \*Py_UNUSED\(ignored\)\)\n\{.*?\n\}",
|
|
source, re.S,
|
|
)
|
|
assert match, "odictiter_reduce definition not found"
|
|
harness = r'''
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
typedef struct { int refs; } PyObject;
|
|
typedef struct { PyObject *di_odict, *di_current; int position; } odictiterobject;
|
|
static PyObject dict, current, list_obj, builtin, result_obj;
|
|
static odictiterobject *original;
|
|
static int stage, failure, consume;
|
|
static int iter;
|
|
#define Py_UNUSED(name) unused
|
|
#define _Py_ID(name) name
|
|
static void Py_XINCREF(PyObject *obj) {
|
|
assert(stage < 2);
|
|
assert(obj == (stage == 0 ? original->di_odict : original->di_current));
|
|
if (obj != NULL) ++obj->refs;
|
|
++stage;
|
|
}
|
|
static PyObject *PySequence_List(PyObject *obj) {
|
|
odictiterobject *tmp = (odictiterobject *)obj;
|
|
assert(stage == 2 && tmp != original);
|
|
assert(tmp->di_odict == original->di_odict);
|
|
assert(tmp->di_current == original->di_current);
|
|
assert(tmp->position == 17);
|
|
assert(dict.refs == (original->di_odict ? 2 : 1));
|
|
assert(current.refs == (original->di_current ? 2 : 1));
|
|
tmp->position = 99;
|
|
if (consume) {
|
|
if (tmp->di_odict) --tmp->di_odict->refs;
|
|
if (tmp->di_current) --tmp->di_current->refs;
|
|
tmp->di_odict = tmp->di_current = NULL;
|
|
}
|
|
stage = 3;
|
|
list_obj.refs = failure == 1 ? 0 : 1;
|
|
return failure == 1 ? NULL : &list_obj;
|
|
}
|
|
static void Py_XDECREF(PyObject *obj) {
|
|
assert(stage == 3 || stage == 4);
|
|
assert(obj == (consume ? NULL : (stage == 3 ? original->di_odict : original->di_current)));
|
|
if (obj != NULL) --obj->refs;
|
|
++stage;
|
|
}
|
|
static PyObject *_PyEval_GetBuiltin(int *id) {
|
|
assert(stage == 5 && id == &iter && failure != 1);
|
|
assert(dict.refs == 1 && current.refs == 1);
|
|
stage = 6;
|
|
builtin.refs = 1;
|
|
return &builtin;
|
|
}
|
|
static PyObject *Py_BuildValue(const char *format, PyObject *func, PyObject *list) {
|
|
assert(stage == 6 && strcmp(format, "N(N)") == 0);
|
|
assert(func == &builtin && list == &list_obj);
|
|
assert(func->refs == 1 && list->refs == 1);
|
|
--func->refs;
|
|
--list->refs;
|
|
stage = 7;
|
|
return failure == 2 ? NULL : &result_obj;
|
|
}
|
|
'''
|
|
harness += match.group(0)
|
|
harness += r'''
|
|
int main(void) {
|
|
int mask;
|
|
odictiterobject di;
|
|
PyObject *result;
|
|
for (mask = 0; mask < 4; ++mask) {
|
|
for (consume = 0; consume < 2; ++consume) {
|
|
for (failure = 0; failure < 3; ++failure) {
|
|
dict.refs = current.refs = 1;
|
|
di.di_odict = mask & 1 ? &dict : NULL;
|
|
di.di_current = mask & 2 ? ¤t : NULL;
|
|
di.position = 17;
|
|
original = &di;
|
|
stage = 0;
|
|
result = odictiter_reduce(&di, NULL);
|
|
assert(result == (failure ? NULL : &result_obj));
|
|
assert(stage == (failure == 1 ? 5 : 7));
|
|
assert(dict.refs == 1 && current.refs == 1);
|
|
assert(list_obj.refs == 0);
|
|
assert(di.di_odict == (mask & 1 ? &dict : NULL));
|
|
assert(di.di_current == (mask & 2 ? ¤t : NULL));
|
|
assert(di.position == 17);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
'''
|
|
with tempfile.TemporaryDirectory(prefix="vc6-odict-list-") as directory:
|
|
path = Path(directory)
|
|
(path / "test.c").write_text(harness)
|
|
compiled = subprocess.run([
|
|
"cc", "-std=c89", "-pedantic-errors", "-Wall", "-Wextra", "-Werror",
|
|
"-Wno-unused-parameter", str(path / "test.c"), "-o", str(path / "test"),
|
|
])
|
|
if compiled.returncode:
|
|
sys.exit(compiled.returncode)
|
|
subprocess.run([str(path / "test")], check=True)
|
|
print("odictiter_reduce C89 compile and copy/incref/list/cleanup/ownership/failure order: PASS (24 cases)")
|