mirror of
https://github.com/python/cpython.git
synced 2025-08-04 17:08:35 +00:00
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
This commit is contained in:
parent
fa46aa7899
commit
9be7e7b52f
6 changed files with 314 additions and 49 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue