From b0024d1d4b9572f21f0d90517eaa491a8ead9b31 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 29 Mar 2019 16:50:35 +0000 Subject: [PATCH 1/2] bpo-36937: Implement PyCall_MakeTpCall --- Objects/call.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/Objects/call.c b/Objects/call.c index 68f9e879fa76b4..bab18b4fc80fb9 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -211,6 +211,61 @@ _PyObject_FastCallKeywords(PyObject *callable, PyObject *const *stack, Py_ssize_ } +PyObject * +PyCall_MakeTpCall(PyObject *callable, PyObject *const *stack, Py_ssize_t nargs, PyObject *kwnames) { + /* Slow-path: build a temporary tuple for positional arguments and a + temporary dictionary for keyword arguments (if any) */ + + ternaryfunc call; + PyObject *argstuple; + PyObject *kwdict, *result; + Py_ssize_t nkwargs; + + nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); + + assert((nargs == 0 && nkwargs == 0) || stack != NULL); + + 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; + } + + argstuple = _PyTuple_FromArray(stack, nargs); + if (argstuple == NULL) { + return NULL; + } + + if (nkwargs > 0) { + kwdict = _PyStack_AsDict(stack + nargs, kwnames); + if (kwdict == NULL) { + Py_DECREF(argstuple); + return NULL; + } + } + else { + kwdict = NULL; + } + + if (Py_EnterRecursiveCall(" while calling a Python object")) { + Py_DECREF(argstuple); + Py_XDECREF(kwdict); + return NULL; + } + + result = (*call)(callable, argstuple, kwdict); + + Py_LeaveRecursiveCall(); + + Py_DECREF(argstuple); + Py_XDECREF(kwdict); + + result = _Py_CheckFunctionResult(callable, result, NULL); + return result; +} + + PyObject * PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs) { From 9c5605265c5aac71aacadea9c20052c487ad0a29 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 16 May 2019 12:18:30 +0200 Subject: [PATCH 2/2] bpo-36937: Implement and use _PyObject_MakeTpCall --- Include/cpython/abstract.h | 8 ++ Objects/call.c | 146 +++++++++---------------------------- 2 files changed, 43 insertions(+), 111 deletions(-) diff --git a/Include/cpython/abstract.h b/Include/cpython/abstract.h index b8b2d449faf306..353c69efe33a02 100644 --- a/Include/cpython/abstract.h +++ b/Include/cpython/abstract.h @@ -55,6 +55,14 @@ PyAPI_FUNC(int) _PyStack_UnpackDict( 40 bytes on the stack. */ #define _PY_FASTCALL_SMALL_STACK 5 +/* Call callable using tp_call. Arguments are the same as + _PyObject_FastCallKeywords() or _PyObject_FastCallDict() + (both forms are supported) */ +PyAPI_FUNC(PyObject *) _PyObject_MakeTpCall( + PyObject *callable, + PyObject *const *args, Py_ssize_t nargs, + PyObject *keywords); + /* Return 1 if callable supports FASTCALL calling convention for positional arguments: see _PyObject_FastCallDict() and _PyObject_FastCallKeywords() */ PyAPI_FUNC(int) _PyObject_HasFastCall(PyObject *callable); diff --git a/Objects/call.c b/Objects/call.c index bab18b4fc80fb9..39a065d65b7ad5 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -103,34 +103,8 @@ _PyObject_FastCallDict(PyObject *callable, PyObject *const *args, Py_ssize_t nar return _PyCFunction_FastCallDict(callable, args, nargs, kwargs); } else { - 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); - return NULL; - } - - argstuple = _PyTuple_FromArray(args, nargs); - if (argstuple == NULL) { - return NULL; - } - - if (Py_EnterRecursiveCall(" while calling a Python object")) { - Py_DECREF(argstuple); - return NULL; - } - - result = (*call)(callable, argstuple, kwargs); - - Py_LeaveRecursiveCall(); - Py_DECREF(argstuple); - - result = _Py_CheckFunctionResult(callable, result, NULL); - return result; + /* Use tp_call instead of FastCall */ + return _PyObject_MakeTpCall(callable, args, nargs, kwargs); } } @@ -158,109 +132,59 @@ _PyObject_FastCallKeywords(PyObject *callable, PyObject *const *stack, Py_ssize_ return _PyCFunction_FastCallKeywords(callable, stack, nargs, kwnames); } else { - /* Slow-path: build a temporary tuple for positional arguments and a - temporary dictionary for keyword arguments (if any) */ - - ternaryfunc call; - PyObject *argstuple; - PyObject *kwdict, *result; - Py_ssize_t nkwargs; - - nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); - assert((nargs == 0 && nkwargs == 0) || stack != NULL); - - 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; - } - - argstuple = _PyTuple_FromArray(stack, nargs); - if (argstuple == NULL) { - return NULL; - } - - if (nkwargs > 0) { - kwdict = _PyStack_AsDict(stack + nargs, kwnames); - if (kwdict == NULL) { - Py_DECREF(argstuple); - return NULL; - } - } - else { - kwdict = NULL; - } - - if (Py_EnterRecursiveCall(" while calling a Python object")) { - Py_DECREF(argstuple); - Py_XDECREF(kwdict); - return NULL; - } - - result = (*call)(callable, argstuple, kwdict); - - Py_LeaveRecursiveCall(); - - Py_DECREF(argstuple); - Py_XDECREF(kwdict); - - result = _Py_CheckFunctionResult(callable, result, NULL); - return result; + /* Use tp_call instead of FastCall */ + return _PyObject_MakeTpCall(callable, stack, nargs, kwnames); } } PyObject * -PyCall_MakeTpCall(PyObject *callable, PyObject *const *stack, Py_ssize_t nargs, PyObject *kwnames) { - /* Slow-path: build a temporary tuple for positional arguments and a - temporary dictionary for keyword arguments (if any) */ - - ternaryfunc call; - PyObject *argstuple; - PyObject *kwdict, *result; - Py_ssize_t nkwargs; - - nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); - - assert((nargs == 0 && nkwargs == 0) || stack != NULL); - - call = callable->ob_type->tp_call; +_PyObject_MakeTpCall(PyObject *callable, PyObject *const *args, Py_ssize_t nargs, PyObject *keywords) +{ + /* Slow path: build a temporary tuple for positional arguments and a + * temporary dictionary for keyword arguments (if any) */ + ternaryfunc call = Py_TYPE(callable)->tp_call; if (call == NULL) { PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", - callable->ob_type->tp_name); + Py_TYPE(callable)->tp_name); return NULL; } - argstuple = _PyTuple_FromArray(stack, nargs); + assert(nargs >= 0); + assert(args != NULL || args == 0); + assert(keywords == NULL || PyTuple_Check(keywords) || PyDict_Check(keywords)); + + PyObject *argstuple = _PyTuple_FromArray(args, nargs); if (argstuple == NULL) { return NULL; } - if (nkwargs > 0) { - kwdict = _PyStack_AsDict(stack + nargs, kwnames); - if (kwdict == NULL) { - Py_DECREF(argstuple); - return NULL; + PyObject *kwdict = keywords; + if (keywords != NULL && PyTuple_Check(keywords)) { + if (PyTuple_GET_SIZE(keywords)) { + assert(args != NULL); + kwdict = _PyStack_AsDict(args + nargs, keywords); + if (kwdict == NULL) { + Py_DECREF(argstuple); + return NULL; + } + } + else { + keywords = kwdict = NULL; } - } - else { - kwdict = NULL; } - if (Py_EnterRecursiveCall(" while calling a Python object")) { - Py_DECREF(argstuple); - Py_XDECREF(kwdict); - return NULL; + PyObject *result = NULL; + if (Py_EnterRecursiveCall(" while calling a Python object") == 0) + { + result = call(callable, argstuple, kwdict); + Py_LeaveRecursiveCall(); } - result = (*call)(callable, argstuple, kwdict); - - Py_LeaveRecursiveCall(); - Py_DECREF(argstuple); - Py_XDECREF(kwdict); - + if (kwdict != keywords) { + Py_DECREF(kwdict); + } result = _Py_CheckFunctionResult(callable, result, NULL); return result; }