port: expand VC6 C89 core frontier
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Guard C89 declaration placement and reference order in iterobject.c."""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import NoReturn
|
||||
|
||||
|
||||
def fail(message: str) -> NoReturn:
|
||||
print(message, file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def extract_function(source: str, signature: str) -> str:
|
||||
start = source.find(signature)
|
||||
if start < 0:
|
||||
fail(f"missing function signature: {signature}")
|
||||
brace = source.find("{", start + len(signature))
|
||||
if brace < 0:
|
||||
fail("missing function body")
|
||||
depth = 0
|
||||
for end in range(brace, len(source)):
|
||||
if source[end] == "{":
|
||||
depth += 1
|
||||
elif source[end] == "}":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
return source[brace + 1:end]
|
||||
fail("unterminated function body")
|
||||
|
||||
|
||||
def ordered(body: str, tokens: tuple[str, ...], message: str) -> None:
|
||||
positions = [body.find(token) for token in tokens]
|
||||
if any(position < 0 for position in positions) or positions != sorted(positions):
|
||||
fail(message)
|
||||
|
||||
|
||||
source = Path(sys.argv[1]).read_text(encoding="utf-8")
|
||||
|
||||
getiter = extract_function(source, "anextawaitable_getiter(anextawaitableobject *obj)")
|
||||
for declaration in ("PyObject *awaitable;", "unaryfunc getter;", "PyObject *new_awaitable;"):
|
||||
if declaration not in getiter:
|
||||
fail("anextawaitable_getiter local must be declared before use: " + declaration)
|
||||
for late in ("PyObject *awaitable =", "unaryfunc getter =", "PyObject *new_awaitable ="):
|
||||
if late in getiter:
|
||||
fail("anextawaitable_getiter must not initialize a local in a late declaration: " + late)
|
||||
branch = getiter.split("if (Py_TYPE(awaitable)->tp_iternext == NULL) {", 1)[1]
|
||||
if branch.find("unaryfunc getter;") > branch.find("getter = Py_TYPE(awaitable)->tp_as_async->am_await;"):
|
||||
fail("anextawaitable_getiter branch locals must be declared before statements")
|
||||
if branch.find("PyObject *new_awaitable;") > branch.find("getter = Py_TYPE(awaitable)->tp_as_async->am_await;"):
|
||||
fail("anextawaitable_getiter branch locals must be declared before statements")
|
||||
ordered(getiter, (
|
||||
"assert(obj->wrapped != NULL);",
|
||||
"awaitable = _PyCoro_GetAwaitableIter(obj->wrapped);",
|
||||
"if (awaitable == NULL)",
|
||||
"if (Py_TYPE(awaitable)->tp_iternext == NULL)",
|
||||
"getter = Py_TYPE(awaitable)->tp_as_async->am_await;",
|
||||
"new_awaitable = getter(awaitable);",
|
||||
"if (new_awaitable == NULL)",
|
||||
"Py_SETREF(awaitable, new_awaitable);",
|
||||
"if (!PyIter_Check(awaitable))",
|
||||
"return awaitable;",
|
||||
), "anextawaitable_getiter must preserve awaitable conversion and cleanup order")
|
||||
|
||||
iternext = extract_function(source, "anextawaitable_iternext(anextawaitableobject *obj)")
|
||||
for declaration in ("PyObject *awaitable;", "PyObject *result;"):
|
||||
if declaration not in iternext:
|
||||
fail("anextawaitable_iternext local must be declared at function entry: " + declaration)
|
||||
if "PyObject *result =" in iternext:
|
||||
fail("anextawaitable_iternext result must not be declared after the NULL guard")
|
||||
ordered(iternext, (
|
||||
"awaitable = anextawaitable_getiter(obj);",
|
||||
"if (awaitable == NULL)",
|
||||
"result = (*Py_TYPE(awaitable)->tp_iternext)(awaitable);",
|
||||
"Py_DECREF(awaitable);",
|
||||
"if (result != NULL)",
|
||||
"if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration))",
|
||||
"_PyGen_SetStopIterationValue(obj->default_value);",
|
||||
"\n return NULL;",
|
||||
), "anextawaitable_iternext must preserve call, decref, and StopAsyncIteration handling order")
|
||||
|
||||
proxy = extract_function(source, "anextawaitable_proxy(anextawaitableobject *obj, char *meth, PyObject *arg)")
|
||||
for declaration in ("PyObject *awaitable;", "PyObject *ret;"):
|
||||
if declaration not in proxy:
|
||||
fail("anextawaitable_proxy local must be declared at function entry: " + declaration)
|
||||
if "PyObject *ret =" in proxy:
|
||||
fail("anextawaitable_proxy result must not be declared after the NULL guard")
|
||||
ordered(proxy, (
|
||||
"awaitable = anextawaitable_getiter(obj);",
|
||||
"if (awaitable == NULL)",
|
||||
"ret = PyObject_CallMethod(awaitable, meth, \"O\", arg);",
|
||||
"Py_DECREF(awaitable);",
|
||||
"if (ret != NULL)",
|
||||
"if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration))",
|
||||
"_PyGen_SetStopIterationValue(obj->default_value);",
|
||||
"\n return NULL;",
|
||||
), "anextawaitable_proxy must preserve call, decref, and exception handling order")
|
||||
|
||||
print("iterobject anextawaitable C89 declarations and reference order: PASS")
|
||||
Reference in New Issue
Block a user