recursive calls in PyObject_Call

refer python/cpython@7399a05965

only partially ported, because RawFastCallKeywords hasn't been ported
This commit is contained in:
ahgamut 2021-11-05 02:46:15 +05:30
parent 4959ac1497
commit 4eb67fa2c5
2 changed files with 75 additions and 65 deletions

View file

@ -2261,6 +2261,16 @@ PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
assert(PyTuple_Check(args)); assert(PyTuple_Check(args));
assert(kwargs == NULL || PyDict_Check(kwargs)); assert(kwargs == NULL || PyDict_Check(kwargs));
if (PyFunction_Check(callable)) {
return _PyFunction_FastCallDict(callable,
&PyTuple_GET_ITEM(args, 0),
PyTuple_GET_SIZE(args),
kwargs);
}
else if (PyCFunction_Check(callable)) {
return PyCFunction_Call(callable, args, kwargs);
}
else {
call = callable->ob_type->tp_call; call = callable->ob_type->tp_call;
if (call == NULL) { if (call == NULL) {
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
@ -2276,6 +2286,7 @@ PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
Py_LeaveRecursiveCall(); Py_LeaveRecursiveCall();
return _Py_CheckFunctionResult(callable, result, NULL); return _Py_CheckFunctionResult(callable, result, NULL);
}
} }
/* Issue #29234: Inlining _PyStack_AsTuple() into callers increases their /* Issue #29234: Inlining _PyStack_AsTuple() into callers increases their
@ -2328,9 +2339,6 @@ PyObject *
_PyObject_FastCallDict(PyObject *callable, PyObject **args, Py_ssize_t nargs, _PyObject_FastCallDict(PyObject *callable, PyObject **args, Py_ssize_t nargs,
PyObject *kwargs) PyObject *kwargs)
{ {
ternaryfunc call;
PyObject *result = NULL;
/* _PyObject_FastCallDict() must not be called with an exception set, /* _PyObject_FastCallDict() must not be called with an exception set,
because it can clear it (directly or indirectly) and so the because it can clear it (directly or indirectly) and so the
caller loses its exception */ caller loses its exception */
@ -2341,42 +2349,41 @@ _PyObject_FastCallDict(PyObject *callable, PyObject **args, Py_ssize_t nargs,
assert(nargs == 0 || args != NULL); assert(nargs == 0 || args != NULL);
assert(kwargs == NULL || PyDict_Check(kwargs)); assert(kwargs == NULL || PyDict_Check(kwargs));
if (Py_EnterRecursiveCall(" while calling a Python object")) {
return NULL;
}
if (PyFunction_Check(callable)) { if (PyFunction_Check(callable)) {
result = _PyFunction_FastCallDict(callable, args, nargs, kwargs); return _PyFunction_FastCallDict(callable, args, nargs, kwargs);
} }
else if (PyCFunction_Check(callable)) { else if (PyCFunction_Check(callable)) {
result = _PyCFunction_FastCallDict(callable, args, nargs, kwargs); return _PyCFunction_FastCallDict(callable, args, nargs, kwargs);
} }
else { else {
PyObject *tuple; PyObject *argstuple, *result;
ternaryfunc call;
/* Slow-path: build a temporary tuple */ /* Slow-path: build a temporary tuple */
call = callable->ob_type->tp_call; call = callable->ob_type->tp_call;
if (call == NULL) { if (call == NULL) {
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
callable->ob_type->tp_name); callable->ob_type->tp_name);
goto exit; return NULL;
} }
tuple = _PyStack_AsTuple(args, nargs); argstuple = _PyStack_AsTuple(args, nargs);
if (tuple == NULL) { if (argstuple == NULL) {
goto exit; return NULL;
} }
result = (*call)(callable, tuple, kwargs); if (Py_EnterRecursiveCall(" while calling a Python object")) {
Py_DECREF(tuple); return NULL;
result = _Py_CheckFunctionResult(callable, result, NULL);
} }
exit: result = (*call)(callable, argstuple, kwargs);
Py_LeaveRecursiveCall(); Py_LeaveRecursiveCall();
Py_DECREF(argstuple);
result = _Py_CheckFunctionResult(callable, result, NULL);
return result; return result;
}
} }
/* Positional arguments are obj followed by args: /* Positional arguments are obj followed by args:
@ -2528,49 +2535,48 @@ _PyObject_FastCallKeywords(PyObject *callable, PyObject **stack, Py_ssize_t narg
temporary dictionary for keyword arguments (if any) */ temporary dictionary for keyword arguments (if any) */
ternaryfunc call; ternaryfunc call;
PyObject *argtuple; PyObject *argstuple;
PyObject *kwdict, *result; PyObject *kwdict, *result;
Py_ssize_t nkwargs; Py_ssize_t nkwargs;
result = NULL;
nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
assert((nargs == 0 && nkwargs == 0) || stack != NULL); assert((nargs == 0 && nkwargs == 0) || stack != NULL);
if (Py_EnterRecursiveCall(" while calling a Python object")) {
return NULL;
}
call = callable->ob_type->tp_call; call = callable->ob_type->tp_call;
if (call == NULL) { if (call == NULL) {
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
callable->ob_type->tp_name); callable->ob_type->tp_name);
goto exit; return NULL;
} }
argtuple = _PyStack_AsTuple(stack, nargs); argstuple = _PyStack_AsTuple(stack, nargs);
if (argtuple == NULL) { if (argstuple == NULL) {
goto exit; return NULL;
} }
if (nkwargs > 0) { if (nkwargs > 0) {
kwdict = _PyStack_AsDict(stack + nargs, kwnames); kwdict = _PyStack_AsDict(stack + nargs, kwnames);
if (kwdict == NULL) { if (kwdict == NULL) {
Py_DECREF(argtuple); Py_DECREF(argstuple);
goto exit; return NULL;
} }
} }
else { else {
kwdict = NULL; kwdict = NULL;
} }
result = (*call)(callable, argtuple, kwdict); if (Py_EnterRecursiveCall(" while calling a Python object")) {
Py_DECREF(argtuple); return NULL;
}
result = (*call)(callable, argstuple, kwdict);
Py_LeaveRecursiveCall();
Py_DECREF(argstuple);
Py_XDECREF(kwdict); Py_XDECREF(kwdict);
result = _Py_CheckFunctionResult(callable, result, NULL); result = _Py_CheckFunctionResult(callable, result, NULL);
exit:
Py_LeaveRecursiveCall();
return result; return result;
} }
} }

View file

@ -103,11 +103,6 @@ PyObject *
_PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **args, _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **args,
Py_ssize_t nargs, PyObject *kwargs) Py_ssize_t nargs, PyObject *kwargs)
{ {
PyCFunction meth;
PyObject *result;
int flags;
PyObject *argstuple;
/* _PyMethodDef_RawFastCallDict() must not be called with an exception set, /* _PyMethodDef_RawFastCallDict() must not be called with an exception set,
because it can clear it (directly or indirectly) and so the because it can clear it (directly or indirectly) and so the
caller loses its exception */ caller loses its exception */
@ -118,8 +113,13 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **arg
assert(nargs == 0 || args != NULL); assert(nargs == 0 || args != NULL);
assert(kwargs == NULL || PyDict_Check(kwargs)); assert(kwargs == NULL || PyDict_Check(kwargs));
meth = method->ml_meth; PyCFunction meth = method->ml_meth;
flags = method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST); int flags = method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST);
PyObject *result = NULL;
if (Py_EnterRecursiveCall(" while calling a Python object")) {
return NULL;
}
switch (flags) switch (flags)
{ {
@ -128,7 +128,7 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **arg
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"%.200s() takes no arguments (%zd given)", "%.200s() takes no arguments (%zd given)",
method->ml_name, nargs); method->ml_name, nargs);
return NULL; goto exit;
} }
if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) { if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) {
@ -143,7 +143,7 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **arg
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"%.200s() takes exactly one argument (%zd given)", "%.200s() takes exactly one argument (%zd given)",
method->ml_name, nargs); method->ml_name, nargs);
return NULL; goto exit;
} }
if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) { if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) {
@ -161,10 +161,11 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **arg
/* fall through next case */ /* fall through next case */
case METH_VARARGS | METH_KEYWORDS: case METH_VARARGS | METH_KEYWORDS:
{
/* Slow-path: create a temporary tuple for positional arguments */ /* Slow-path: create a temporary tuple for positional arguments */
argstuple = _PyStack_AsTuple(args, nargs); PyObject *argstuple = _PyStack_AsTuple(args, nargs);
if (argstuple == NULL) { if (argstuple == NULL) {
return NULL; goto exit;
} }
if (flags & METH_KEYWORDS) { if (flags & METH_KEYWORDS) {
@ -175,6 +176,7 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **arg
} }
Py_DECREF(argstuple); Py_DECREF(argstuple);
break; break;
}
case METH_FASTCALL: case METH_FASTCALL:
{ {
@ -183,7 +185,7 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **arg
_PyCFunctionFast fastmeth = (_PyCFunctionFast)meth; _PyCFunctionFast fastmeth = (_PyCFunctionFast)meth;
if (_PyStack_UnpackDict(args, nargs, kwargs, &stack, &kwnames) < 0) { if (_PyStack_UnpackDict(args, nargs, kwargs, &stack, &kwnames) < 0) {
return NULL; goto exit;
} }
result = (*fastmeth) (self, stack, nargs, kwnames); result = (*fastmeth) (self, stack, nargs, kwnames);
@ -198,17 +200,19 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **arg
PyErr_SetString(PyExc_SystemError, PyErr_SetString(PyExc_SystemError,
"Bad call flags in _PyMethodDef_RawFastCallDict. " "Bad call flags in _PyMethodDef_RawFastCallDict. "
"METH_OLDARGS is no longer supported!"); "METH_OLDARGS is no longer supported!");
return NULL; goto exit;
} }
return result; goto exit;
no_keyword_error: no_keyword_error:
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"%.200s() takes no keyword arguments", "%.200s() takes no keyword arguments",
method->ml_name, nargs); method->ml_name, nargs);
return NULL; exit:
Py_LeaveRecursiveCall();
return result;
} }
PyObject * PyObject *