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
This commit is contained in:
Victor Stinner 2017-02-08 12:06:00 +01:00
parent ee0ee9ae8e
commit 7399a05965
2 changed files with 90 additions and 73 deletions

View File

@ -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 = (*call)(callable, argstuple, kwargs);
Py_LeaveRecursiveCall();
Py_DECREF(argstuple);
result = _Py_CheckFunctionResult(callable, result, NULL);
return result;
}
exit:
Py_LeaveRecursiveCall();
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);
if (Py_EnterRecursiveCall(" while calling a Python object")) {
return NULL;
}
result = (*call)(callable, argstuple, kwdict);
Py_LeaveRecursiveCall();
Py_DECREF(argstuple);
Py_XDECREF(kwdict);
result = _Py_CheckFunctionResult(callable, result, NULL);
exit:
Py_LeaveRecursiveCall();
return result;
}
}

View File

@ -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 *