diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2016-08-19 16:11:43 +0200 |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2016-08-19 16:11:43 +0200 |
commit | 9be7e7b52fa4b48012e956167a344df691195a04 (patch) | |
tree | b556a431bb06664b814b206dee3790eb2ac00464 /Objects/methodobject.c | |
parent | fa46aa78996cbeb30ccbeb9c405a54459d5d55de (diff) | |
download | cpython-git-9be7e7b52fa4b48012e956167a344df691195a04.tar.gz |
Add _PyObject_FastCall()
Issue #27128: Add _PyObject_FastCall(), a new calling convention avoiding a
temporary tuple to pass positional parameters in most cases, but create a
temporary tuple if needed (ex: for the tp_call slot).
The API is prepared to support keyword parameters, but the full implementation
will come later (_PyFunction_FastCall() doesn't support keyword parameters
yet).
Add also:
* _PyStack_AsTuple() helper function: convert a "stack" of parameters to
a tuple.
* _PyCFunction_FastCall(): fast call implementation for C functions
* _PyFunction_FastCall(): fast call implementation for Python functions
Diffstat (limited to 'Objects/methodobject.c')
-rw-r--r-- | Objects/methodobject.c | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 946357f24a..0e26232194 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -145,6 +145,99 @@ PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwds) return _Py_CheckFunctionResult(func, res, NULL); } +PyObject * +_PyCFunction_FastCall(PyObject *func_obj, PyObject **args, int nargs, + PyObject *kwargs) +{ + PyCFunctionObject* func = (PyCFunctionObject*)func_obj; + PyCFunction meth = PyCFunction_GET_FUNCTION(func); + PyObject *self = PyCFunction_GET_SELF(func); + PyObject *result; + int flags; + + /* _PyCFunction_FastCall() must not be called with an exception set, + because it may clear it (directly or indirectly) and so the + caller loses its exception */ + assert(!PyErr_Occurred()); + + flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST); + + switch (flags) + { + case METH_NOARGS: + if (kwargs != NULL && PyDict_Size(kwargs) != 0) { + PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", + func->m_ml->ml_name); + return NULL; + } + + if (nargs != 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no arguments (%zd given)", + func->m_ml->ml_name, nargs); + return NULL; + } + + result = (*meth) (self, NULL); + break; + + case METH_O: + if (kwargs != NULL && PyDict_Size(kwargs) != 0) { + PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", + func->m_ml->ml_name); + return NULL; + } + + if (nargs != 1) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%zd given)", + func->m_ml->ml_name, nargs); + return NULL; + } + + result = (*meth) (self, args[0]); + break; + + case METH_VARARGS: + case METH_VARARGS | METH_KEYWORDS: + { + /* Slow-path: create a temporary tuple */ + PyObject *tuple; + + if (!(flags & METH_KEYWORDS) && kwargs != NULL && PyDict_Size(kwargs) != 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no keyword arguments", + func->m_ml->ml_name); + return NULL; + } + + tuple = _PyStack_AsTuple(args, nargs); + if (tuple == NULL) { + return NULL; + } + + if (flags & METH_KEYWORDS) { + result = (*(PyCFunctionWithKeywords)meth) (self, tuple, kwargs); + } + else { + result = (*meth) (self, tuple); + } + Py_DECREF(tuple); + break; + } + + default: + PyErr_SetString(PyExc_SystemError, + "Bad call flags in PyCFunction_Call. " + "METH_OLDARGS is no longer supported!"); + return NULL; + } + + result = _Py_CheckFunctionResult(func_obj, result, NULL); + + return result; +} + /* Methods (the standard built-in methods, that is) */ static void |