summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2016-08-19 14:11:43 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2016-08-19 14:11:43 (GMT)
commit9be7e7b52fa4b48012e956167a344df691195a04 (patch)
treeb556a431bb06664b814b206dee3790eb2ac00464 /Objects
parentfa46aa78996cbeb30ccbeb9c405a54459d5d55de (diff)
downloadcpython-9be7e7b52fa4b48012e956167a344df691195a04.zip
cpython-9be7e7b52fa4b48012e956167a344df691195a04.tar.gz
cpython-9be7e7b52fa4b48012e956167a344df691195a04.tar.bz2
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')
-rw-r--r--Objects/abstract.c76
-rw-r--r--Objects/methodobject.c93
2 files changed, 169 insertions, 0 deletions
diff --git a/Objects/abstract.c b/Objects/abstract.c
index 5d8a44b..36401a8 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -2193,6 +2193,82 @@ PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw)
return _Py_CheckFunctionResult(func, result, NULL);
}
+PyObject*
+_PyStack_AsTuple(PyObject **stack, Py_ssize_t nargs)
+{
+ PyObject *args;
+ Py_ssize_t i;
+
+ args = PyTuple_New(nargs);
+ if (args == NULL) {
+ return NULL;
+ }
+
+ for (i=0; i < nargs; i++) {
+ PyObject *item = stack[i];
+ Py_INCREF(item);
+ PyTuple_SET_ITEM(args, i, item);
+ }
+
+ return args;
+}
+
+PyObject *
+_PyObject_FastCall(PyObject *func, PyObject **args, int nargs, PyObject *kwargs)
+{
+ ternaryfunc call;
+ PyObject *result = NULL;
+
+ /* _PyObject_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());
+
+ assert(func != NULL);
+ assert(nargs >= 0);
+ assert(nargs == 0 || args != NULL);
+ /* issue #27128: support for keywords will come later:
+ _PyFunction_FastCall() doesn't support keyword arguments yet */
+ assert(kwargs == NULL);
+
+ if (Py_EnterRecursiveCall(" while calling a Python object")) {
+ return NULL;
+ }
+
+ if (PyFunction_Check(func)) {
+ result = _PyFunction_FastCall(func, args, nargs, kwargs);
+ }
+ else if (PyCFunction_Check(func)) {
+ result = _PyCFunction_FastCall(func, args, nargs, kwargs);
+ }
+ else {
+ PyObject *tuple;
+
+ /* Slow-path: build a temporary tuple */
+ call = func->ob_type->tp_call;
+ if (call == NULL) {
+ PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
+ func->ob_type->tp_name);
+ goto exit;
+ }
+
+ tuple = _PyStack_AsTuple(args, nargs);
+ if (tuple == NULL) {
+ goto exit;
+ }
+
+ result = (*call)(func, tuple, kwargs);
+ Py_DECREF(tuple);
+ }
+
+ result = _Py_CheckFunctionResult(func, result, NULL);
+
+exit:
+ Py_LeaveRecursiveCall();
+
+ return result;
+}
+
static PyObject*
call_function_tail(PyObject *callable, PyObject *args)
{
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
index 946357f..0e26232 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