port: advance VC6 compatibility frontier
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check classmethod_get C89 local placement and validation order."""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
raise SystemExit("usage: vc6-descrobject-classmethod-c89.test.py descrobject.c object.c")
|
||||
source = Path(sys.argv[1]).read_text()
|
||||
object_source = Path(sys.argv[2]).read_text()
|
||||
|
||||
def body_for(signature, text=None):
|
||||
if text is None:
|
||||
text = source
|
||||
start = text.index(signature)
|
||||
brace = text.index("{", start)
|
||||
depth = 0
|
||||
for pos in range(brace, len(text)):
|
||||
if text[pos] == "{":
|
||||
depth += 1
|
||||
elif text[pos] == "}":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
return text[brace + 1:pos]
|
||||
raise SystemExit("unterminated function: " + signature)
|
||||
|
||||
signature = "classmethod_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type)"
|
||||
body = body_for(signature)
|
||||
if "PyTypeObject *cls;" not in body or body.index("PyTypeObject *cls;") > body.index("if (type == NULL)"):
|
||||
raise SystemExit("classmethod_get cls must be declared before validation")
|
||||
if "PyTypeObject *cls =" in body:
|
||||
raise SystemExit("classmethod_get cls initialization must remain after subtype validation")
|
||||
order = ("if (type == NULL)", "if (!PyType_Check(type))", "if (!PyType_IsSubtype", "cls = NULL;", "if (descr->d_method->ml_flags & METH_METHOD)", "cls = descr->d_common.d_type;", "return PyCMethod_New(descr->d_method, type, NULL, cls);")
|
||||
positions = [body.index(item) for item in order]
|
||||
if positions != sorted(positions):
|
||||
raise SystemExit("classmethod_get validation/class-selection/construction order changed")
|
||||
|
||||
|
||||
def require_entry_declarations(signature, declarations, first_statement):
|
||||
body = body_for(signature)
|
||||
first = body.index(first_statement)
|
||||
for declaration in declarations:
|
||||
if declaration not in body or body.index(declaration) > first:
|
||||
raise SystemExit(signature + " local missing or late: " + declaration)
|
||||
return body
|
||||
|
||||
body = require_entry_declarations("method_check_args(PyObject *func,", ("PyObject *self;",), "assert(!PyErr_Occurred());")
|
||||
if body.index("if (nargs < 1)") > body.index("self = args[0];") or body.index("self = args[0];") > body.index("descr_check((PyDescrObject *)func, self)"):
|
||||
raise SystemExit("method_check_args validation/self-check order changed")
|
||||
|
||||
wrappers = (
|
||||
("method_vectorcall_VARARGS(\n", ("PyObject *argstuple;", "PyCFunction meth;", "PyObject *result;"), "if (method_check_args(func, args, nargs, kwnames))", ("argstuple = _PyTuple_FromArray", "meth = (PyCFunction)method_enter_call", "result = _PyCFunction_TrampolineCall", "_Py_LeaveRecursiveCallTstate(tstate);", "return result;")),
|
||||
("method_vectorcall_VARARGS_KEYWORDS(\n", ("PyObject *argstuple;", "PyObject *result;", "PyObject *kwdict;", "PyCFunctionWithKeywords meth;"), "if (method_check_args(func, args, nargs, NULL))", ("argstuple = _PyTuple_FromArray", "result = NULL;", "kwdict = NULL;", "if (kwnames != NULL", "meth = (PyCFunctionWithKeywords)", "result = _PyCFunctionWithKeywords_TrampolineCall", "_Py_LeaveRecursiveCallTstate(tstate);", "exit:", "Py_DECREF(argstuple);", "Py_XDECREF(kwdict);", "return result;")),
|
||||
("method_vectorcall_FASTCALL_KEYWORDS_METHOD(\n", ("PyCMethod meth;", "PyObject *result;"), "if (method_check_args(func, args, nargs, NULL))", ("meth = (PyCMethod) method_enter_call", "result = meth(args[0],", "_Py_LeaveRecursiveCall();", "return result;")),
|
||||
("method_vectorcall_FASTCALL(\n", ("_PyCFunctionFast meth;", "PyObject *result;"), "if (method_check_args(func, args, nargs, kwnames))", ("meth = (_PyCFunctionFast)", "result = meth(args[0],", "_Py_LeaveRecursiveCallTstate(tstate);", "return result;")),
|
||||
("method_vectorcall_FASTCALL_KEYWORDS(\n", ("_PyCFunctionFastWithKeywords meth;", "PyObject *result;"), "if (method_check_args(func, args, nargs, NULL))", ("meth = (_PyCFunctionFastWithKeywords)", "result = meth(args[0],", "_Py_LeaveRecursiveCallTstate(tstate);", "return result;")),
|
||||
("method_vectorcall_NOARGS(\n", ("PyObject *funcstr;", "PyCFunction meth;", "PyObject *result;"), "if (method_check_args(func, args, nargs, kwnames))", ("if (nargs != 1)", "funcstr = _PyObject_FunctionStr(func);", "meth = (PyCFunction)method_enter_call", "result = _PyCFunction_TrampolineCall", "_Py_LeaveRecursiveCallTstate(tstate);", "return result;")),
|
||||
("method_vectorcall_O(\n", ("PyObject *funcstr;", "PyCFunction meth;", "PyObject *result;"), "if (method_check_args(func, args, nargs, kwnames))", ("if (nargs != 2)", "funcstr = _PyObject_FunctionStr(func);", "meth = (PyCFunction)method_enter_call", "result = _PyCFunction_TrampolineCall", "_Py_LeaveRecursiveCallTstate(tstate);", "return result;")),
|
||||
)
|
||||
for signature, declarations, first_statement, order in wrappers:
|
||||
body = require_entry_declarations(signature, declarations, first_statement)
|
||||
positions = [body.index(item) for item in order]
|
||||
if positions != sorted(positions):
|
||||
raise SystemExit(signature + " dispatch/cleanup order changed")
|
||||
|
||||
body = require_entry_declarations("classmethoddescr_call(PyMethodDescrObject *descr,", ("PyObject *self;", "PyObject *bound;", "PyObject *res;"), "if (argc < 1)")
|
||||
order = ("argc = PyTuple_GET_SIZE(args);", "if (argc < 1)", "self = PyTuple_GET_ITEM(args, 0);", "bound = classmethod_get(descr, NULL, self);", "if (bound == NULL)", "res = PyObject_VectorcallDict", "Py_DECREF(bound);", "return res;")
|
||||
if [body.index(item) for item in order] != sorted(body.index(item) for item in order):
|
||||
raise SystemExit("classmethoddescr_call bind/call/decref order changed")
|
||||
mapping_guard = "#if !defined(MS_WINDOWS) || !defined(_MSC_VER) || _MSC_VER >= 1300"
|
||||
if mapping_guard not in source or "static PyNumberMethods mappingproxy_as_number = {0};" not in source:
|
||||
raise SystemExit("mappingproxy number table must avoid designated initialization on VC6")
|
||||
number_init = body_for("_PyDesc_InitTypes(void)")
|
||||
if "mappingproxy_as_number.nb_or = mappingproxy_or;" not in number_init or "mappingproxy_as_number.nb_inplace_or = mappingproxy_ior;" not in number_init:
|
||||
raise SystemExit("VC6 mappingproxy slots must be assigned before type readiness")
|
||||
init_body = body_for("_PyTypes_InitTypes(PyInterpreterState *interp)", object_source)
|
||||
order = ("_PyClassObject_InitTypes();", "_PyDesc_InitTypes();", "for (size_t i=0;", "PyType_Ready(type)")
|
||||
positions = [init_body.index(item) for item in order]
|
||||
if positions != sorted(positions):
|
||||
raise SystemExit("legacy static type slots must be initialized before PyType_Ready")
|
||||
|
||||
body = body_for("PyDescr_NewMethod(PyTypeObject *type, PyMethodDef *method)")
|
||||
for declaration in ("PyMethodDescrObject *descr;", "vectorcallfunc vectorcall;"):
|
||||
if declaration not in body or body.index(declaration) > body.index("switch (method->ml_flags"):
|
||||
raise SystemExit("PyDescr_NewMethod local missing or late: " + declaration)
|
||||
if body.index("PyMethodDescrObject *descr;") > body.index("switch (method->ml_flags"):
|
||||
raise SystemExit("PyDescr_NewMethod descr must be declared before flag dispatch")
|
||||
order = ("switch (method->ml_flags", "vectorcall = method_vectorcall_VARARGS;", "default:", "descr = (PyMethodDescrObject *)descr_new", "descr->d_method = method;", "descr->vectorcall = vectorcall;", "return (PyObject *)descr;")
|
||||
positions = [body.index(item) for item in order]
|
||||
if positions != sorted(positions):
|
||||
raise SystemExit("PyDescr_NewMethod dispatch/allocation/initialization order changed")
|
||||
body = body_for("property_set_name(PyObject *self, PyObject *args)")
|
||||
for declaration in ("propertyobject *prop;", "PyObject *name;"):
|
||||
if declaration not in body or body.index(declaration) > body.index("if (PyTuple_GET_SIZE(args) != 2)"):
|
||||
raise SystemExit("property_set_name local missing or late: " + declaration)
|
||||
if "propertyobject *prop =" in body or "PyObject *name =" in body:
|
||||
raise SystemExit("property_set_name name/property assignments must remain after arity validation")
|
||||
order = ("if (PyTuple_GET_SIZE(args) != 2)", "prop = (propertyobject *)self;", "name = PyTuple_GET_ITEM(args, 1);", "Py_XINCREF(name);", "Py_XSETREF(prop->prop_name, name);", "Py_RETURN_NONE;")
|
||||
if [body.index(item) for item in order] != sorted(body.index(item) for item in order):
|
||||
raise SystemExit("property_set_name arity/reference-replacement order changed")
|
||||
|
||||
body = body_for("property_descr_get(PyObject *self, PyObject *obj, PyObject *type)")
|
||||
if "propertyobject *gs;" not in body or body.index("propertyobject *gs;") > body.index("if (obj == NULL || obj == Py_None)"):
|
||||
raise SystemExit("property_descr_get gs must be declared before its early return")
|
||||
if "propertyobject *gs =" in body:
|
||||
raise SystemExit("property_descr_get property cast must remain after its early return")
|
||||
order = ("if (obj == NULL || obj == Py_None)", "return self;", "gs = (propertyobject *)self;", "if (gs->prop_get == NULL)", "PyObject *qualname = PyType_GetQualName", "return PyObject_CallOneArg(gs->prop_get, obj);")
|
||||
if [body.index(item) for item in order] != sorted(body.index(item) for item in order):
|
||||
raise SystemExit("property_descr_get early return/getter error/call order changed")
|
||||
print("descrobject C89 declarations, dispatch, and VC6 type init: PASS")
|
||||
Reference in New Issue
Block a user