413 lines
20 KiB
Python
413 lines
20 KiB
Python
#!/usr/bin/env python3
|
|
"""Check the first VC6 C89 frontier in Objects/call.c."""
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
if len(sys.argv) != 2:
|
|
raise SystemExit("usage: vc6-call-first-c89.test.py call.c")
|
|
source = Path(sys.argv[1]).read_text()
|
|
signature = "_PyObject_FastCallDictTstate(PyThreadState *tstate, PyObject *callable,"
|
|
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(callable != NULL);")
|
|
for declaration in ("Py_ssize_t nargs;", "vectorcallfunc func;", "PyObject *res;"):
|
|
if body.count(declaration) != 1 or body.index(declaration) > first_statement:
|
|
raise SystemExit("_PyObject_FastCallDictTstate locals must be declared before the first statement")
|
|
if "Py_ssize_t nargs =" in body or "vectorcallfunc func =" in body:
|
|
raise SystemExit("_PyObject_FastCallDictTstate declarations must be separate from assignments")
|
|
order = (
|
|
"assert(callable != NULL);",
|
|
"assert(!_PyErr_Occurred(tstate));",
|
|
"nargs = PyVectorcall_NARGS(nargsf);",
|
|
"func = _PyVectorcall_Function(callable);",
|
|
"if (func == NULL)",
|
|
"if (kwargs == NULL || PyDict_GET_SIZE(kwargs) == 0)",
|
|
"return _Py_CheckFunctionResult(tstate, callable, res, NULL);",
|
|
)
|
|
positions = [body.index(item) for item in order]
|
|
if positions != sorted(positions):
|
|
raise SystemExit("vectorcall validation/dispatch/result-check order changed")
|
|
|
|
signature = "_PyObject_MakeTpCall(PyThreadState *tstate, PyObject *callable,"
|
|
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(nargs >= 0);")
|
|
for declaration in ("ternaryfunc call;", "PyObject *argstuple;", "PyObject *kwdict;", "PyObject *result;"):
|
|
if body.count(declaration) != 1 or body.index(declaration) > first_statement:
|
|
raise SystemExit("_PyObject_MakeTpCall locals must be declared before the first statement")
|
|
if any(declaration in body for declaration in ("ternaryfunc call =", "PyObject *argstuple =", "PyObject *result =")):
|
|
raise SystemExit("_PyObject_MakeTpCall locals must be declared separately from assignment")
|
|
order = (
|
|
"assert(nargs >= 0);",
|
|
"assert(keywords == NULL || PyTuple_Check(keywords) || PyDict_Check(keywords));",
|
|
"call = Py_TYPE(callable)->tp_call;",
|
|
"if (call == NULL)",
|
|
"argstuple = _PyTuple_FromArray(args, nargs);",
|
|
"if (keywords == NULL || PyDict_Check(keywords))",
|
|
"result = NULL;",
|
|
"if (_Py_EnterRecursiveCallTstate(tstate,",
|
|
"_Py_LeaveRecursiveCallTstate(tstate);",
|
|
"\n Py_DECREF(argstuple);\n if (kwdict != keywords)",
|
|
"if (kwdict != keywords)",
|
|
"return _Py_CheckFunctionResult(tstate, callable, result, NULL);",
|
|
)
|
|
positions = [body.index(item) for item in order]
|
|
if positions != sorted(positions):
|
|
raise SystemExit("tp_call argument construction/reference cleanup order changed")
|
|
|
|
signature = "_PyVectorcall_Call(PyThreadState *tstate, vectorcallfunc func,"
|
|
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(func != NULL);")
|
|
for declaration in ("Py_ssize_t nargs;", "PyObject *const *args;", "PyObject *kwnames;", "PyObject *result;"):
|
|
if body.count(declaration) != 1 or body.index(declaration) > first_statement:
|
|
raise SystemExit("_PyVectorcall_Call locals must be declared before the first statement")
|
|
if "Py_ssize_t nargs =" in body or "PyObject *result =" in body:
|
|
raise SystemExit("_PyVectorcall_Call declarations must be separate from assignments")
|
|
order = (
|
|
"assert(func != NULL);",
|
|
"nargs = PyTuple_GET_SIZE(tuple);",
|
|
"if (kwargs == NULL || PyDict_GET_SIZE(kwargs) == 0)",
|
|
"args = _PyStack_UnpackDict(tstate,",
|
|
"if (args == NULL)",
|
|
"result = func(callable, args,",
|
|
"_PyStack_UnpackDict_Free(args, nargs, kwnames);",
|
|
"return _Py_CheckFunctionResult(tstate, callable, result, NULL);",
|
|
)
|
|
positions = [body.index(item) for item in order]
|
|
if positions != sorted(positions):
|
|
raise SystemExit("vectorcall argument unpack/call/free/check order changed")
|
|
|
|
signature = "PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)"
|
|
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]
|
|
for declaration in ("PyThreadState *tstate;", "Py_ssize_t offset;", "vectorcallfunc func;"):
|
|
if body.count(declaration) != 1 or body.index(declaration) > body.index("tstate = _PyThreadState_GET();"):
|
|
raise SystemExit("PyVectorcall_Call locals must be declared before initialization")
|
|
if "PyThreadState *tstate =" in body or "Py_ssize_t offset =" in body:
|
|
raise SystemExit("PyVectorcall_Call declarations must be separate from assignments")
|
|
order = (
|
|
"tstate = _PyThreadState_GET();",
|
|
"offset = Py_TYPE(callable)->tp_vectorcall_offset;",
|
|
"if (offset <= 0)",
|
|
"assert(PyCallable_Check(callable));",
|
|
"memcpy(&func, (char *) callable + offset, sizeof(func));",
|
|
"if (func == NULL)",
|
|
"return _PyVectorcall_Call(tstate, func, callable, tuple, kwargs);",
|
|
)
|
|
positions = [body.index(item) for item in order]
|
|
if positions != sorted(positions):
|
|
raise SystemExit("PyVectorcall_Call validation/copy/dispatch order changed")
|
|
|
|
signature = "_PyObject_Call(PyThreadState *tstate, PyObject *callable,"
|
|
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(!_PyErr_Occurred(tstate));")
|
|
if body.count("vectorcallfunc vector_func;") != 1 or body.index("vectorcallfunc vector_func;") > first_statement:
|
|
raise SystemExit("_PyObject_Call vectorcall local must be declared before validation")
|
|
if "vectorcallfunc vector_func =" in body:
|
|
raise SystemExit("_PyObject_Call vectorcall declaration must be separate from lookup")
|
|
order = (
|
|
"assert(!_PyErr_Occurred(tstate));",
|
|
"assert(PyTuple_Check(args));",
|
|
"vector_func = _PyVectorcall_Function(callable);",
|
|
"if (vector_func != NULL)",
|
|
"call = Py_TYPE(callable)->tp_call;",
|
|
"if (call == NULL)",
|
|
"if (_Py_EnterRecursiveCallTstate(tstate,",
|
|
"result = (*call)(callable, args, kwargs);",
|
|
"_Py_LeaveRecursiveCallTstate(tstate);",
|
|
"return _Py_CheckFunctionResult(tstate, callable, result, NULL);",
|
|
)
|
|
positions = [body.index(item) for item in order]
|
|
if positions != sorted(positions):
|
|
raise SystemExit("_PyObject_Call validation/dispatch/recursion order changed")
|
|
|
|
signature = "PyObject_CallOneArg(PyObject *func, PyObject *arg)"
|
|
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(arg != NULL);")
|
|
for declaration in ("PyObject *_args[2];", "PyObject **args;", "PyThreadState *tstate;", "size_t nargsf;"):
|
|
if body.count(declaration) != 1 or body.index(declaration) > first_statement:
|
|
raise SystemExit("PyObject_CallOneArg locals must be declared before the assertion")
|
|
if any(text in body for text in ("PyObject **args =", "PyThreadState *tstate =", "size_t nargsf =")):
|
|
raise SystemExit("PyObject_CallOneArg declarations must be separate from assignments")
|
|
order = (
|
|
"assert(arg != NULL);",
|
|
"args = _args + 1;",
|
|
"args[0] = arg;",
|
|
"tstate = _PyThreadState_GET();",
|
|
"nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET;",
|
|
"return _PyObject_VectorcallTstate(tstate, func, args, nargsf, NULL);",
|
|
)
|
|
positions = [body.index(item) for item in order]
|
|
if positions != sorted(positions):
|
|
raise SystemExit("PyObject_CallOneArg vectorcall argument setup order changed")
|
|
|
|
signature = "_PyFunction_Vectorcall(PyObject *func, PyObject* const* stack,"
|
|
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(PyFunction_Check(func));")
|
|
for declaration in ("PyFunctionObject *f;", "Py_ssize_t nargs;", "PyThreadState *tstate;"):
|
|
if body.count(declaration) != 1 or body.index(declaration) > first_statement:
|
|
raise SystemExit("_PyFunction_Vectorcall locals must be declared before the assertion")
|
|
if any(text in body for text in ("PyFunctionObject *f =", "Py_ssize_t nargs =", "PyThreadState *tstate =")):
|
|
raise SystemExit("_PyFunction_Vectorcall declarations must be separate from assignments")
|
|
order = (
|
|
"assert(PyFunction_Check(func));",
|
|
"f = (PyFunctionObject *)func;",
|
|
"nargs = PyVectorcall_NARGS(nargsf);",
|
|
"assert(nargs >= 0);",
|
|
"tstate = _PyThreadState_GET();",
|
|
"assert(nargs == 0 || stack != NULL);",
|
|
"if (((PyCodeObject *)f->func_code)->co_flags & CO_OPTIMIZED)",
|
|
"return _PyEval_Vector(tstate, f, NULL, stack, nargs, kwnames);",
|
|
"return _PyEval_Vector(tstate, f, f->func_globals, stack, nargs, kwnames);",
|
|
)
|
|
positions = [body.index(item) for item in order]
|
|
if positions != sorted(positions):
|
|
raise SystemExit("function vectorcall validation/state/dispatch order changed")
|
|
|
|
signature = "_PyObject_Call_Prepend(PyThreadState *tstate, PyObject *callable,"
|
|
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(PyTuple_Check(args));")
|
|
for declaration in (
|
|
"PyObject *small_stack[_PY_FASTCALL_SMALL_STACK];",
|
|
"PyObject **stack;",
|
|
"Py_ssize_t argcount;",
|
|
"PyObject *result;",
|
|
):
|
|
if body.count(declaration) != 1 or body.index(declaration) > first_statement:
|
|
raise SystemExit("_PyObject_Call_Prepend locals must be declared before the assertion")
|
|
if "Py_ssize_t argcount =" in body or "PyObject *result =" in body:
|
|
raise SystemExit("_PyObject_Call_Prepend declarations must be separate from assignments")
|
|
order = (
|
|
"assert(PyTuple_Check(args));",
|
|
"argcount = PyTuple_GET_SIZE(args);",
|
|
"if (argcount + 1 <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack))",
|
|
"stack = PyMem_Malloc((argcount + 1) * sizeof(PyObject *));",
|
|
"stack[0] = obj;",
|
|
"memcpy(&stack[1],",
|
|
"result = _PyObject_FastCallDictTstate(tstate, callable,",
|
|
"if (stack != small_stack)",
|
|
"return result;",
|
|
)
|
|
positions = [body.index(item) for item in order]
|
|
if positions != sorted(positions):
|
|
raise SystemExit("_PyObject_Call_Prepend stack/call/free order changed")
|
|
|
|
signature = "_PyObject_CallFunction_SizeT(PyObject *callable, const char *format, ...)"
|
|
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_call = body.index("va_start(va, format);")
|
|
for declaration in ("va_list va;", "PyObject *result;"):
|
|
if body.count(declaration) != 1 or body.index(declaration) > first_call:
|
|
raise SystemExit("_PyObject_CallFunction_SizeT locals must be declared before va_start")
|
|
if "PyObject *result =" in body:
|
|
raise SystemExit("_PyObject_CallFunction_SizeT result must be assigned after va_start")
|
|
order = (
|
|
"tstate = _PyThreadState_GET();",
|
|
"va_start(va, format);",
|
|
"result = _PyObject_CallFunctionVa(tstate, callable, format, va, 1);",
|
|
"va_end(va);",
|
|
"return result;",
|
|
)
|
|
positions = [body.index(item) for item in order]
|
|
if positions != sorted(positions):
|
|
raise SystemExit("SizeT wrapper thread/va/result lifecycle order changed")
|
|
|
|
method_wrappers = (
|
|
("PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...)", "PyObject_GetAttrString(obj, name)", 0),
|
|
("PyEval_CallMethod(PyObject *obj, const char *name, const char *format, ...)", "PyObject_GetAttrString(obj, name)", 0),
|
|
("_PyObject_CallMethod(PyObject *obj, PyObject *name,", "PyObject_GetAttr(obj, name)", 1),
|
|
("_PyObject_CallMethodId(PyObject *obj, _Py_Identifier *name,", "_PyObject_GetAttrId(obj, name)", 0),
|
|
("_PyObject_CallMethodFormat(PyThreadState *tstate, PyObject *callable,", None, 0),
|
|
("_PyObject_CallMethod_SizeT(PyObject *obj, const char *name,", "PyObject_GetAttrString(obj, name)", 1),
|
|
("_PyObject_CallMethodId_SizeT(PyObject *obj, _Py_Identifier *name,", "_PyObject_GetAttrId(obj, name)", 1),
|
|
)
|
|
for signature, lookup, size_t in method_wrappers:
|
|
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 (obj == NULL || name == NULL)") if lookup else body.index("va_start(va, format);")
|
|
required = ["va_list va;", "PyObject *retval;"]
|
|
if lookup:
|
|
required.append("PyObject *callable;")
|
|
for declaration in required:
|
|
if body.count(declaration) != 1 or body.index(declaration) > first_statement:
|
|
raise SystemExit(signature.split("(", 1)[0] + " locals must be declared before execution")
|
|
if "PyObject *retval =" in body or (lookup and "PyObject *callable =" in body):
|
|
raise SystemExit(signature.split("(", 1)[0] + " locals must be declared separately from assignments")
|
|
if lookup:
|
|
order = (
|
|
"if (obj == NULL || name == NULL)",
|
|
lookup + ";",
|
|
"if (callable == NULL)",
|
|
"va_start(va, format);",
|
|
"retval = callmethod(tstate, callable, format, va, " + str(size_t) + ");",
|
|
"va_end(va);",
|
|
"Py_DECREF(callable);",
|
|
"return retval;",
|
|
)
|
|
else:
|
|
order = (
|
|
"va_start(va, format);",
|
|
"retval = callmethod(tstate, callable, format, va, " + str(size_t) + ");",
|
|
"va_end(va);",
|
|
"return retval;",
|
|
)
|
|
missing = [item for item in order if item not in body]
|
|
if missing:
|
|
raise SystemExit(signature.split("(", 1)[0] + " missing expected order text: " + repr(missing))
|
|
positions = [body.index(item) for item in order]
|
|
if positions != sorted(positions):
|
|
raise SystemExit(signature.split("(", 1)[0] + " lookup/va/cleanup order changed")
|
|
|
|
signature = "PyObject_VectorcallMethod(PyObject *name, 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(name != NULL);")
|
|
for declaration in ("PyThreadState *tstate;", "PyObject *callable;", "int unbound;", "PyObject *result;"):
|
|
if body.count(declaration) != 1 or body.index(declaration) > first_statement:
|
|
raise SystemExit("PyObject_VectorcallMethod locals must be declared before validation")
|
|
if "PyObject *callable =" in body or "int unbound =" in body or "PyObject *result =" in body:
|
|
raise SystemExit("PyObject_VectorcallMethod declarations must be separate from initialization")
|
|
order = (
|
|
"assert(name != NULL);",
|
|
"assert(args != NULL);",
|
|
"assert(PyVectorcall_NARGS(nargsf) >= 1);",
|
|
"tstate = _PyThreadState_GET();",
|
|
"callable = NULL;",
|
|
"unbound = _PyObject_GetMethod(args[0], name, &callable);",
|
|
"if (callable == NULL)",
|
|
"if (unbound)",
|
|
"else {",
|
|
"result = _PyObject_VectorcallTstate(tstate, callable,",
|
|
"Py_DECREF(callable);",
|
|
"return result;",
|
|
)
|
|
positions = [body.index(item) for item in order]
|
|
if positions != sorted(positions):
|
|
raise SystemExit("vectorcall method lookup/argument adjustment/cleanup order changed")
|
|
|
|
method_objargs = (
|
|
("PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)", False),
|
|
("_PyObject_CallMethodIdObjArgs(PyObject *obj, _Py_Identifier *name, ...)", True),
|
|
)
|
|
for signature, has_oname in method_objargs:
|
|
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 (obj == NULL || name == NULL)")
|
|
required = ["PyObject *callable;", "int is_method;", "va_list vargs;", "PyObject *result;"]
|
|
if has_oname:
|
|
required.append("PyObject *oname;")
|
|
for declaration in required:
|
|
if body.count(declaration) != 1 or body.index(declaration) > first_statement:
|
|
raise SystemExit(signature.split("(", 1)[0] + " locals must be declared before execution")
|
|
if "PyObject *callable =" in body or "int is_method =" in body or "PyObject *result =" in body:
|
|
raise SystemExit(signature.split("(", 1)[0] + " declarations must be separate from initialization")
|
|
order = ["if (obj == NULL || name == NULL)"]
|
|
if has_oname:
|
|
order.extend(("oname = _PyUnicode_FromId(name);", "if (!oname)"))
|
|
order.extend((
|
|
"callable = NULL;",
|
|
"is_method = _PyObject_GetMethod(obj, " + ("oname" if has_oname else "name") + ", &callable);",
|
|
"if (callable == NULL)",
|
|
"obj = is_method ? obj : NULL;",
|
|
"va_start(vargs, name);",
|
|
"result = object_vacall(tstate, obj, callable, vargs);",
|
|
"va_end(vargs);",
|
|
"Py_DECREF(callable);",
|
|
"return result;",
|
|
))
|
|
missing = [item for item in order if item not in body]
|
|
if missing:
|
|
raise SystemExit(signature.split("(", 1)[0] + " missing expected order text: " + repr(missing))
|
|
positions = [body.index(item) for item in order]
|
|
if positions != sorted(positions):
|
|
raise SystemExit(signature.split("(", 1)[0] + " lookup/va/cleanup order changed")
|
|
|
|
signature = "_PyStack_UnpackDict(PyThreadState *tstate,"
|
|
start = source.rindex(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(nargs >= 0);")
|
|
declarations = (
|
|
"Py_ssize_t nkwargs;", "Py_ssize_t maxnargs;", "PyObject **stack;",
|
|
"PyObject *kwnames;", "PyObject **kwstack;", "Py_ssize_t pos;",
|
|
"Py_ssize_t i;", "PyObject *key;", "PyObject *value;",
|
|
"unsigned long keys_are_strings;",
|
|
)
|
|
for declaration in declarations:
|
|
if body.count(declaration) != 1 or body.index(declaration) > first_statement:
|
|
raise SystemExit("_PyStack_UnpackDict locals must be declared before the first assertion")
|
|
if "for (Py_ssize_t i =" in body or "Py_ssize_t nkwargs =" in body or "Py_ssize_t maxnargs =" in body:
|
|
raise SystemExit("_PyStack_UnpackDict declaration/initialization separation failed")
|
|
order = (
|
|
"assert(nargs >= 0);",
|
|
"assert(kwargs != NULL);",
|
|
"nkwargs = PyDict_GET_SIZE(kwargs);",
|
|
"maxnargs = PY_SSIZE_T_MAX / sizeof(args[0]) - 1;",
|
|
"if (nargs > maxnargs - nkwargs)",
|
|
"stack = PyMem_Malloc((1 + nargs + nkwargs) * sizeof(args[0]));",
|
|
"kwnames = PyTuple_New(nkwargs);",
|
|
"stack++; /* For PY_VECTORCALL_ARGUMENTS_OFFSET */",
|
|
"for (i = 0; i < nargs; i++)",
|
|
"kwstack = stack + nargs;",
|
|
"pos = 0;",
|
|
"\n i = 0;\n",
|
|
"keys_are_strings = Py_TPFLAGS_UNICODE_SUBCLASS;",
|
|
"while (PyDict_Next(kwargs, &pos, &key, &value))",
|
|
"if (!keys_are_strings)",
|
|
)
|
|
positions = [body.index(item) for item in order]
|
|
if positions != sorted(positions):
|
|
raise SystemExit("_PyStack_UnpackDict validation/allocation/iteration order changed: " + repr(list(zip(order, positions))))
|
|
|
|
signature = "_PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs,"
|
|
start = source.rindex(signature)
|
|
body_start = source.index("\n{\n", start) + 2
|
|
body_end = source.index("\n}\n", body_start)
|
|
body = source[body_start:body_end]
|
|
loop = body.index("for (i = 0;")
|
|
if body.count("Py_ssize_t i;") != 1 or body.index("Py_ssize_t i;") > loop:
|
|
raise SystemExit("_PyStack_UnpackDict_Free loop index must be declared before execution")
|
|
if "for (Py_ssize_t i =" in body:
|
|
raise SystemExit("_PyStack_UnpackDict_Free loop must use its entry-declared index")
|
|
order = (
|
|
"Py_ssize_t n = PyTuple_GET_SIZE(kwnames) + nargs;",
|
|
"for (i = 0; i < n; i++)",
|
|
"Py_DECREF(stack[i]);",
|
|
"PyMem_Free((PyObject **)stack - 1);",
|
|
"Py_DECREF(kwnames);",
|
|
)
|
|
positions = [body.index(item) for item in order]
|
|
if positions != sorted(positions):
|
|
raise SystemExit("_PyStack_UnpackDict_Free release order changed")
|
|
print("call.c C89 declarations and stack cleanup order: PASS")
|