From 7399a05965162f46ca00b5453a59f71fdb5851f1 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 8 Feb 2017 12:06:00 +0100 Subject: [PATCH] Issue #29306: Fix usage of Py_EnterRecursiveCall() * *PyCFunction_*Call*() functions now call Py_EnterRecursiveCall(). * PyObject_Call() now calls directly _PyFunction_FastCallDict() and PyCFunction_Call() to avoid calling Py_EnterRecursiveCall() twice per function call --- Objects/abstract.c | 102 ++++++++++++++++++++++------------------- Objects/methodobject.c | 61 ++++++++++++++---------- 2 files changed, 90 insertions(+), 73 deletions(-) diff --git a/Objects/abstract.c b/Objects/abstract.c index 1e394f865a..8d18313ed0 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2239,21 +2239,32 @@ PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs) assert(PyTuple_Check(args)); assert(kwargs == NULL || PyDict_Check(kwargs)); - call = callable->ob_type->tp_call; - if (call == NULL) { - PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", - callable->ob_type->tp_name); - return NULL; + 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; + if (call == NULL) { + PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", + callable->ob_type->tp_name); + return NULL; + } - if (Py_EnterRecursiveCall(" while calling a Python object")) - return NULL; + if (Py_EnterRecursiveCall(" while calling a Python object")) + return NULL; - result = (*call)(callable, args, kwargs); + result = (*call)(callable, args, 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 @@ -2305,9 +2316,6 @@ PyObject * _PyObject_FastCallDict(PyObject *callable, PyObject **args, Py_ssize_t nargs, PyObject *kwargs) { - ternaryfunc call; - PyObject *result = NULL; - /* _PyObject_FastCallDict() must not be called with an exception set, because it can clear it (directly or indirectly) and so the caller loses its exception */ @@ -2318,42 +2326,41 @@ _PyObject_FastCallDict(PyObject *callable, PyObject **args, Py_ssize_t nargs, assert(nargs == 0 || args != NULL); assert(kwargs == NULL || PyDict_Check(kwargs)); - if (Py_EnterRecursiveCall(" while calling a Python object")) { - return NULL; - } - if (PyFunction_Check(callable)) { - result = _PyFunction_FastCallDict(callable, args, nargs, kwargs); + return _PyFunction_FastCallDict(callable, args, nargs, kwargs); } else if (PyCFunction_Check(callable)) { - result = _PyCFunction_FastCallDict(callable, args, nargs, kwargs); + return _PyCFunction_FastCallDict(callable, args, nargs, kwargs); } else { - PyObject *tuple; + PyObject *argstuple, *result; + ternaryfunc call; /* Slow-path: build a temporary tuple */ call = callable->ob_type->tp_call; if (call == NULL) { PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", callable->ob_type->tp_name); - goto exit; + return NULL; } - tuple = _PyStack_AsTuple(args, nargs); - if (tuple == NULL) { - goto exit; + argstuple = _PyStack_AsTuple(args, nargs); + if (argstuple == NULL) { + return NULL; } - result = (*call)(callable, tuple, kwargs); - Py_DECREF(tuple); + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return NULL; + } - result = _Py_CheckFunctionResult(callable, result, NULL); - } + result = (*call)(callable, argstuple, kwargs); -exit: - Py_LeaveRecursiveCall(); + Py_LeaveRecursiveCall(); - return result; + Py_DECREF(argstuple); + result = _Py_CheckFunctionResult(callable, result, NULL); + return result; + } } /* Positional arguments are obj followed by args: @@ -2506,49 +2513,48 @@ _PyObject_FastCallKeywords(PyObject *callable, PyObject **stack, Py_ssize_t narg temporary dictionary for keyword arguments (if any) */ ternaryfunc call; - PyObject *argtuple; + PyObject *argstuple; PyObject *kwdict, *result; Py_ssize_t nkwargs; - result = NULL; nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); assert((nargs == 0 && nkwargs == 0) || stack != NULL); - if (Py_EnterRecursiveCall(" while calling a Python object")) { - return NULL; - } - call = callable->ob_type->tp_call; if (call == NULL) { PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", callable->ob_type->tp_name); - goto exit; + return NULL; } - argtuple = _PyStack_AsTuple(stack, nargs); - if (argtuple == NULL) { - goto exit; + argstuple = _PyStack_AsTuple(stack, nargs); + if (argstuple == NULL) { + return NULL; } if (nkwargs > 0) { kwdict = _PyStack_AsDict(stack + nargs, kwnames); if (kwdict == NULL) { - Py_DECREF(argtuple); - goto exit; + Py_DECREF(argstuple); + return NULL; } } else { kwdict = NULL; } - result = (*call)(callable, argtuple, kwdict); - Py_DECREF(argtuple); - Py_XDECREF(kwdict); + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return NULL; + } - result = _Py_CheckFunctionResult(callable, result, NULL); + result = (*call)(callable, argstuple, kwdict); - exit: Py_LeaveRecursiveCall(); + + Py_DECREF(argstuple); + Py_XDECREF(kwdict); + + result = _Py_CheckFunctionResult(callable, result, NULL); return result; } } diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 6618d78968..07827775ca 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -90,11 +90,6 @@ PyObject * _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwargs) { - PyCFunction meth; - PyObject *result; - int flags; - PyObject *argstuple; - /* _PyMethodDef_RawFastCallDict() must not be called with an exception set, because it can clear it (directly or indirectly) and so the caller loses its exception */ @@ -105,18 +100,23 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **arg assert(nargs == 0 || args != NULL); assert(kwargs == NULL || PyDict_Check(kwargs)); - meth = method->ml_meth; - flags = method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST); + PyCFunction meth = method->ml_meth; + 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) { case METH_NOARGS: - if (nargs != 0) { + if (nargs != 0) { PyErr_Format(PyExc_TypeError, "%.200s() takes no arguments (%zd given)", method->ml_name, nargs); - return NULL; - } + goto exit; + } if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) { goto no_keyword_error; @@ -130,7 +130,7 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **arg PyErr_Format(PyExc_TypeError, "%.200s() takes exactly one argument (%zd given)", method->ml_name, nargs); - return NULL; + goto exit; } if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) { @@ -148,10 +148,11 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **arg /* fall through next case */ case METH_VARARGS | METH_KEYWORDS: + { /* Slow-path: create a temporary tuple for positional arguments */ - argstuple = _PyStack_AsTuple(args, nargs); + PyObject *argstuple = _PyStack_AsTuple(args, nargs); if (argstuple == NULL) { - return NULL; + goto exit; } if (flags & METH_KEYWORDS) { @@ -162,6 +163,7 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **arg } Py_DECREF(argstuple); break; + } case METH_FASTCALL: { @@ -170,7 +172,7 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **arg _PyCFunctionFast fastmeth = (_PyCFunctionFast)meth; if (_PyStack_UnpackDict(args, nargs, kwargs, &stack, &kwnames) < 0) { - return NULL; + goto exit; } result = (*fastmeth) (self, stack, nargs, kwnames); @@ -185,17 +187,19 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **arg PyErr_SetString(PyExc_SystemError, "Bad call flags in _PyMethodDef_RawFastCallDict. " "METH_OLDARGS is no longer supported!"); - return NULL; + goto exit; } - return result; + goto exit; no_keyword_error: PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", method->ml_name, nargs); - return NULL; +exit: + Py_LeaveRecursiveCall(); + return result; } PyObject * @@ -232,7 +236,11 @@ _PyMethodDef_RawFastCallKeywords(PyMethodDef *method, PyObject *self, PyObject * PyCFunction meth = method->ml_meth; int flags = method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST); Py_ssize_t nkwargs = kwnames == NULL ? 0 : PyTuple_Size(kwnames); - PyObject *result; + PyObject *result = NULL; + + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return NULL; + } switch (flags) { @@ -241,7 +249,7 @@ _PyMethodDef_RawFastCallKeywords(PyMethodDef *method, PyObject *self, PyObject * PyErr_Format(PyExc_TypeError, "%.200s() takes no arguments (%zd given)", method->ml_name, nargs); - return NULL; + goto exit; } if (nkwargs) { @@ -256,7 +264,7 @@ _PyMethodDef_RawFastCallKeywords(PyMethodDef *method, PyObject *self, PyObject * PyErr_Format(PyExc_TypeError, "%.200s() takes exactly one argument (%zd given)", method->ml_name, nargs); - return NULL; + goto exit; } if (nkwargs) { @@ -284,7 +292,7 @@ _PyMethodDef_RawFastCallKeywords(PyMethodDef *method, PyObject *self, PyObject * argtuple = _PyStack_AsTuple(args, nargs); if (argtuple == NULL) { - return NULL; + goto exit; } if (flags & METH_KEYWORDS) { @@ -294,7 +302,7 @@ _PyMethodDef_RawFastCallKeywords(PyMethodDef *method, PyObject *self, PyObject * kwdict = _PyStack_AsDict(args + nargs, kwnames); if (kwdict == NULL) { Py_DECREF(argtuple); - return NULL; + goto exit; } } else { @@ -315,16 +323,19 @@ _PyMethodDef_RawFastCallKeywords(PyMethodDef *method, PyObject *self, PyObject * PyErr_SetString(PyExc_SystemError, "Bad call flags in _PyCFunction_FastCallKeywords. " "METH_OLDARGS is no longer supported!"); - return NULL; + goto exit; } - return result; + goto exit; no_keyword_error: PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", method->ml_name); - return NULL; + +exit: + Py_LeaveRecursiveCall(); + return result; } PyObject * -- 2.40.0