96 lines
3.5 KiB
Python
96 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Compile the exact range_index body as C89 and check evaluation 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 \*\nrange_index\([^\n]+\)\n\{.*?\n\}", source, re.S
|
|
)
|
|
assert match, "range_index not found"
|
|
harness = r'''
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
typedef long Py_ssize_t;
|
|
typedef struct { int unused; } PyObject;
|
|
typedef struct { PyObject *start, *step; } rangeobject;
|
|
#define PY_ITERSEARCH_INDEX 2
|
|
static int PyExc_ValueError = 1;
|
|
static int scenario;
|
|
static char events[16];
|
|
static PyObject ob, start, step, one, idx, quotient, converted;
|
|
static rangeobject range;
|
|
static void event(char c) {
|
|
size_t n = strlen(events);
|
|
assert(n + 1 < sizeof(events));
|
|
events[n] = c; events[n + 1] = '\0';
|
|
}
|
|
static int PyLong_CheckExact(PyObject *obj) {
|
|
assert(obj == &ob); return scenario != 0 && scenario != 1 && scenario != 8;
|
|
}
|
|
static int PyBool_Check(PyObject *obj) {
|
|
assert(obj == &ob); return scenario == 8;
|
|
}
|
|
static Py_ssize_t _PySequence_IterSearch(PyObject *obj, PyObject *value, int op) {
|
|
assert(obj == (PyObject *)&range && value == &ob && op == PY_ITERSEARCH_INDEX);
|
|
event('I'); return scenario == 0 ? -1 : 3;
|
|
}
|
|
static PyObject *PyLong_FromSsize_t(Py_ssize_t value) {
|
|
assert(value == 3); event('L'); return &converted;
|
|
}
|
|
static int range_contains_long(rangeobject *r, PyObject *value) {
|
|
assert(r == &range && value == &ob); event('C');
|
|
return scenario == 2 ? -1 : scenario != 3;
|
|
}
|
|
static PyObject *PyNumber_Subtract(PyObject *value, PyObject *first) {
|
|
assert(value == &ob && first == &start); event('S');
|
|
return scenario == 4 ? NULL : &idx;
|
|
}
|
|
static PyObject *_PyLong_GetOne(void) { event('O'); return &one; }
|
|
static PyObject *PyNumber_FloorDivide(PyObject *value, PyObject *divisor) {
|
|
assert(value == &idx && divisor == &step); event('F');
|
|
return scenario == 6 ? NULL : "ient;
|
|
}
|
|
static void Py_DECREF(PyObject *value) { assert(value == &idx); event('D'); }
|
|
static void PyErr_Format(int exc, const char *format, PyObject *value) {
|
|
assert(exc == PyExc_ValueError && value == &ob);
|
|
assert(strcmp(format, "%R is not in range") == 0); event('E');
|
|
}
|
|
'''
|
|
harness += match.group(0)
|
|
harness += r'''
|
|
int main(void) {
|
|
static const char *expected[] = {
|
|
"I", "IL", "C", "CE", "CS", "CSO", "CSOFD", "CSOFD", "CSOFD"
|
|
};
|
|
PyObject *result;
|
|
for (scenario = 0; scenario < 9; ++scenario) {
|
|
range.start = &start;
|
|
range.step = scenario == 5 ? &one : &step;
|
|
events[0] = '\0';
|
|
result = range_index(&range, &ob);
|
|
assert(strcmp(events, expected[scenario]) == 0);
|
|
if (scenario == 1) assert(result == &converted);
|
|
else if (scenario == 5) assert(result == &idx);
|
|
else if (scenario == 7 || scenario == 8) assert(result == "ient);
|
|
else assert(result == NULL);
|
|
}
|
|
return 0;
|
|
}
|
|
'''
|
|
with tempfile.TemporaryDirectory(prefix="vc6-rangeobject-index-") 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("range_index C89 compile and evaluation/cleanup order: PASS (9 cases)")
|