diff options
author | Jeroen Demeyer <J.Demeyer@UGent.be> | 2019-05-29 18:31:52 (GMT) |
---|---|---|
committer | Petr Viktorin <encukou@gmail.com> | 2019-05-29 18:31:52 (GMT) |
commit | aacc77fbd77640a8f03638216fa09372cc21673d (patch) | |
tree | fd64be1c4c1167a8bf708d1fd22c733cf3a9a30f /Objects/call.c | |
parent | d30da5dd9a8a965cf24a22bbaff8a5b1341c2944 (diff) | |
download | cpython-aacc77fbd77640a8f03638216fa09372cc21673d.zip cpython-aacc77fbd77640a8f03638216fa09372cc21673d.tar.gz cpython-aacc77fbd77640a8f03638216fa09372cc21673d.tar.bz2 |
bpo-36974: implement PEP 590 (GH-13185)
Co-authored-by: Jeroen Demeyer <J.Demeyer@UGent.be>
Co-authored-by: Mark Shannon <mark@hotpy.org>
Diffstat (limited to 'Objects/call.c')
-rw-r--r-- | Objects/call.c | 208 |
1 files changed, 105 insertions, 103 deletions
diff --git a/Objects/call.c b/Objects/call.c index b608492..183a5c2 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -5,6 +5,10 @@ #include "frameobject.h" +static PyObject * +cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs); + + int _PyObject_HasFastCall(PyObject *callable) { @@ -83,131 +87,132 @@ _Py_CheckFunctionResult(PyObject *callable, PyObject *result, const char *where) /* --- Core PyObject call functions ------------------------------- */ PyObject * -_PyObject_FastCallDict(PyObject *callable, PyObject *const *args, Py_ssize_t nargs, - PyObject *kwargs) +_PyObject_FastCallDict(PyObject *callable, PyObject *const *args, + size_t nargsf, PyObject *kwargs) { /* _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 */ assert(!PyErr_Occurred()); - assert(callable != NULL); + + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); assert(nargs >= 0); assert(nargs == 0 || args != NULL); assert(kwargs == NULL || PyDict_Check(kwargs)); - if (PyFunction_Check(callable)) { - return _PyFunction_FastCallDict(callable, args, nargs, kwargs); + vectorcallfunc func = _PyVectorcall_Function(callable); + if (func == NULL) { + /* Use tp_call instead */ + return _PyObject_MakeTpCall(callable, args, nargs, kwargs); } - else if (PyCFunction_Check(callable)) { - return _PyCFunction_FastCallDict(callable, args, nargs, kwargs); + + PyObject *res; + if (kwargs == NULL) { + res = func(callable, args, nargsf, NULL); } 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) { + PyObject *kwnames; + PyObject *const *newargs; + if (_PyStack_UnpackDict(args, nargs, kwargs, &newargs, &kwnames) < 0) { return NULL; } - - if (Py_EnterRecursiveCall(" while calling a Python object")) { - Py_DECREF(argstuple); - return NULL; + res = func(callable, newargs, nargs, kwnames); + if (kwnames != NULL) { + Py_ssize_t i, n = PyTuple_GET_SIZE(kwnames) + nargs; + for (i = 0; i < n; i++) { + Py_DECREF(newargs[i]); + } + PyMem_Free((PyObject **)newargs); + Py_DECREF(kwnames); } - - result = (*call)(callable, argstuple, kwargs); - - Py_LeaveRecursiveCall(); - Py_DECREF(argstuple); - - result = _Py_CheckFunctionResult(callable, result, NULL); - return result; } + return _Py_CheckFunctionResult(callable, res, NULL); } PyObject * -_PyObject_FastCallKeywords(PyObject *callable, PyObject *const *stack, Py_ssize_t nargs, - PyObject *kwnames) +_PyObject_MakeTpCall(PyObject *callable, PyObject *const *args, Py_ssize_t nargs, PyObject *keywords) { - /* _PyObject_FastCallKeywords() must not be called with an exception set, - because it can clear it (directly or indirectly) and so the - caller loses its exception */ - assert(!PyErr_Occurred()); + /* 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", + Py_TYPE(callable)->tp_name); + return NULL; + } assert(nargs >= 0); - assert(kwnames == NULL || PyTuple_CheckExact(kwnames)); - - /* kwnames must only contains str strings, no subclass, and all keys must - be unique: these checks are implemented in Python/ceval.c and - _PyArg_ParseStackAndKeywords(). */ - - if (PyFunction_Check(callable)) { - return _PyFunction_FastCallKeywords(callable, stack, nargs, kwnames); + assert(nargs == 0 || args != NULL); + assert(keywords == NULL || PyTuple_Check(keywords) || PyDict_Check(keywords)); + PyObject *argstuple = _PyTuple_FromArray(args, nargs); + if (argstuple == NULL) { + return NULL; } - if (PyCFunction_Check(callable)) { - return _PyCFunction_FastCallKeywords(callable, stack, nargs, kwnames); + + PyObject *kwdict; + if (keywords == NULL || PyDict_Check(keywords)) { + kwdict = keywords; } 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 (PyTuple_GET_SIZE(keywords)) { + assert(args != NULL); + kwdict = _PyStack_AsDict(args + nargs, keywords); if (kwdict == NULL) { Py_DECREF(argstuple); return NULL; } } else { - kwdict = NULL; + keywords = 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_DECREF(argstuple); + if (kwdict != keywords) { + Py_DECREF(kwdict); + } - Py_LeaveRecursiveCall(); + result = _Py_CheckFunctionResult(callable, result, NULL); + return result; +} - Py_DECREF(argstuple); - Py_XDECREF(kwdict); - result = _Py_CheckFunctionResult(callable, result, NULL); - return result; +PyObject * +PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs) +{ + vectorcallfunc func = _PyVectorcall_Function(callable); + if (func == NULL) { + PyErr_Format(PyExc_TypeError, "'%.200s' object does not support vectorcall", + Py_TYPE(callable)->tp_name); + return NULL; } + PyObject *const *args; + Py_ssize_t nargs = PyTuple_GET_SIZE(tuple); + PyObject *kwnames; + if (_PyStack_UnpackDict(_PyTuple_ITEMS(tuple), nargs, + kwargs, &args, &kwnames) < 0) { + return NULL; + } + PyObject *result = func(callable, args, nargs, kwnames); + if (kwnames != NULL) { + Py_ssize_t i, n = PyTuple_GET_SIZE(kwnames) + nargs; + for (i = 0; i < n; i++) { + Py_DECREF(args[i]); + } + PyMem_Free((PyObject **)args); + Py_DECREF(kwnames); + } + + return result; } @@ -224,14 +229,13 @@ PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs) assert(PyTuple_Check(args)); assert(kwargs == NULL || PyDict_Check(kwargs)); - if (PyFunction_Check(callable)) { - return _PyFunction_FastCallDict(callable, - _PyTuple_ITEMS(args), - PyTuple_GET_SIZE(args), - kwargs); + if (_PyVectorcall_Function(callable) != NULL) { + return PyVectorcall_Call(callable, args, kwargs); } else if (PyCFunction_Check(callable)) { - return PyCFunction_Call(callable, args, kwargs); + /* This must be a METH_VARARGS function, otherwise we would be + * in the previous case */ + return cfunction_call_varargs(callable, args, kwargs); } else { call = callable->ob_type->tp_call; @@ -384,9 +388,10 @@ _PyFunction_FastCallDict(PyObject *func, PyObject *const *args, Py_ssize_t nargs return result; } + PyObject * -_PyFunction_FastCallKeywords(PyObject *func, PyObject *const *stack, - Py_ssize_t nargs, PyObject *kwnames) +_PyFunction_FastCallKeywords(PyObject *func, PyObject* const* stack, + size_t nargsf, PyObject *kwnames) { PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); PyObject *globals = PyFunction_GET_GLOBALS(func); @@ -397,6 +402,7 @@ _PyFunction_FastCallKeywords(PyObject *func, PyObject *const *stack, Py_ssize_t nd; assert(PyFunction_Check(func)); + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); assert(nargs >= 0); assert(kwnames == NULL || PyTuple_CheckExact(kwnames)); assert((nargs == 0 && nkwargs == 0) || stack != NULL); @@ -725,13 +731,14 @@ exit: PyObject * _PyCFunction_FastCallKeywords(PyObject *func, - PyObject *const *args, Py_ssize_t nargs, + PyObject *const *args, size_t nargsf, PyObject *kwnames) { PyObject *result; assert(func != NULL); assert(PyCFunction_Check(func)); + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); result = _PyMethodDef_RawFastCallKeywords(((PyCFunctionObject*)func)->m_ml, PyCFunction_GET_SELF(func), @@ -751,6 +758,7 @@ cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs) PyObject *self = PyCFunction_GET_SELF(func); PyObject *result; + assert(PyCFunction_GET_FLAGS(func) & METH_VARARGS); if (PyCFunction_GET_FLAGS(func) & METH_KEYWORDS) { if (Py_EnterRecursiveCall(" while calling a Python object")) { return NULL; @@ -783,18 +791,12 @@ cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs) PyObject * PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwargs) { - /* first try METH_VARARGS to pass directly args tuple unchanged. - _PyMethodDef_RawFastCallDict() creates a new temporary tuple - for METH_VARARGS. */ + /* For METH_VARARGS, we cannot use vectorcall as the vectorcall pointer + * is NULL. This is intentional, since vectorcall would be slower. */ if (PyCFunction_GET_FLAGS(func) & METH_VARARGS) { return cfunction_call_varargs(func, args, kwargs); } - else { - return _PyCFunction_FastCallDict(func, - _PyTuple_ITEMS(args), - PyTuple_GET_SIZE(args), - kwargs); - } + return PyVectorcall_Call(func, args, kwargs); } |