port: advance VC6 compatibility frontier
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check C89 locals and mutation/cleanup order in method_vectorcall."""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
raise SystemExit("usage: vc6-classobject-c89.test.py classobject.c")
|
||||
source = Path(sys.argv[1]).read_text()
|
||||
signature = "method_vectorcall(PyObject *method, PyObject *const *args,"
|
||||
start = source.index(signature)
|
||||
body_start = source.index("\n{\n", start) + 2
|
||||
body_end = source.index("\n}\n", body_start)
|
||||
body = source[body_start:body_end]
|
||||
first_statement = body.index("assert(Py_IS_TYPE(method, &PyMethod_Type));")
|
||||
for declaration in ("PyThreadState *tstate;", "PyObject *self;", "PyObject *func;", "Py_ssize_t nargs;", "PyObject *result;"):
|
||||
if body.count(declaration) != 1 or body.index(declaration) > first_statement:
|
||||
raise SystemExit("method_vectorcall locals must be declared before the assertion")
|
||||
if any(text in body for text in ("PyThreadState *tstate =", "PyObject *self =", "PyObject *func =", "Py_ssize_t nargs =")):
|
||||
raise SystemExit("method_vectorcall declarations must be separate from assignments")
|
||||
branch_start = body.index("if (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET) {")
|
||||
branch_end = body.index("\n else {", branch_start)
|
||||
branch = body[branch_start:branch_end]
|
||||
if "PyObject **newargs;\n PyObject *tmp;" not in branch:
|
||||
raise SystemExit("offset branch locals must be declared at block entry")
|
||||
if not (branch.index("newargs = (PyObject**)args - 1;") < branch.index("nargs += 1;") < branch.index("tmp = newargs[0];") < branch.index("newargs[0] = self;") < branch.index("result = _PyObject_VectorcallTstate") < branch.index("newargs[0] = tmp;")):
|
||||
raise SystemExit("offset argument mutation and restoration order changed")
|
||||
else_start = body.index("\n else {", branch_start)
|
||||
else_body = body[else_start:]
|
||||
first_exec = else_body.index("nkwargs =")
|
||||
for declaration in ("Py_ssize_t nkwargs;", "Py_ssize_t totalargs;", "PyObject *newargs_stack[_PY_FASTCALL_SMALL_STACK];", "PyObject **newargs;"):
|
||||
if else_body.count(declaration) != 1 or else_body.index(declaration) > first_exec:
|
||||
raise SystemExit("non-offset branch locals must be declared before initialization")
|
||||
if not (else_body.index("nkwargs =") < else_body.index("totalargs =") < else_body.index("if (totalargs == 0)") < else_body.index("if (totalargs <=") < else_body.index("newargs = newargs_stack;") < else_body.index("newargs[0] = self;") < else_body.index("result = _PyObject_VectorcallTstate") < else_body.index("if (newargs != newargs_stack)")):
|
||||
raise SystemExit("non-offset allocation/call/free order changed")
|
||||
|
||||
signature = "PyMethod_New(PyObject *func, PyObject *self)"
|
||||
start = source.index(signature)
|
||||
body_start = source.index("\n{\n", start) + 2
|
||||
body_end = source.index("\n}\n", body_start)
|
||||
body = source[body_start:body_end]
|
||||
first_statement = body.index("if (self == NULL)")
|
||||
if body.count("PyMethodObject *im;") != 1 or body.index("PyMethodObject *im;") > first_statement:
|
||||
raise SystemExit("PyMethod_New object local must be declared before validation")
|
||||
if "PyMethodObject *im =" in body:
|
||||
raise SystemExit("PyMethod_New allocation must remain at its original execution point")
|
||||
order = (
|
||||
"if (self == NULL)",
|
||||
"im = PyObject_GC_New(PyMethodObject, &PyMethod_Type);",
|
||||
"if (im == NULL)",
|
||||
"im->im_weakreflist = NULL;",
|
||||
"Py_INCREF(func);",
|
||||
"im->im_func = func;",
|
||||
"Py_INCREF(self);",
|
||||
"im->im_self = self;",
|
||||
"im->vectorcall = method_vectorcall;",
|
||||
"_PyObject_GC_TRACK(im);",
|
||||
"return (PyObject *)im;",
|
||||
)
|
||||
positions = [body.index(item) for item in order]
|
||||
if positions != sorted(positions):
|
||||
raise SystemExit("PyMethod_New allocation/reference/GC order changed")
|
||||
print("classobject C89 declarations and method initialization order: PASS")
|
||||
Reference in New Issue
Block a user