]> granicus.if.org Git - python/commitdiff
Issue #29306: Fix usage of Py_EnterRecursiveCall()
authorVictor Stinner <victor.stinner@gmail.com>
Wed, 8 Feb 2017 11:06:00 +0000 (12:06 +0100)
committerVictor Stinner <victor.stinner@gmail.com>
Wed, 8 Feb 2017 11:06:00 +0000 (12:06 +0100)
* *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
Objects/methodobject.c

index 1e394f865a902ee62803e79f86be8c8be256df80..8d18313ed08f1ef9a63a5c1a0a68a6889ee8c141 100644 (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 = _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;
     }
 }
index 6618d789681ccb392a6fdec7e683b36a6fde6be5..07827775ca0e9b1238804bd72e8a26f3bbfde6ee 100644 (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 *