summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2016-08-24 22:29:32 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2016-08-24 22:29:32 (GMT)
commit577e1f8cb41b76ea763910a47abf42a1952ddec3 (patch)
treec813887a0af4c5b7312b2e835677546584c067ea
parent53868aaabb154c11bf666b31662bb4ae8bc6ec79 (diff)
downloadcpython-577e1f8cb41b76ea763910a47abf42a1952ddec3.zip
cpython-577e1f8cb41b76ea763910a47abf42a1952ddec3.tar.gz
cpython-577e1f8cb41b76ea763910a47abf42a1952ddec3.tar.bz2
Add _PyObject_FastCallKeywords()
Issue #27830: Similar to _PyObject_FastCallDict(), but keyword arguments are also passed in the same C array than positional arguments, rather than being passed as a Python dict.
-rw-r--r--Include/abstract.h17
-rw-r--r--Include/funcobject.h6
-rw-r--r--Objects/abstract.c79
-rw-r--r--Python/ceval.c23
4 files changed, 116 insertions, 9 deletions
diff --git a/Include/abstract.h b/Include/abstract.h
index 5820864..474d746 100644
--- a/Include/abstract.h
+++ b/Include/abstract.h
@@ -292,6 +292,23 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
#define _PyObject_CallArg1(func, arg) \
_PyObject_FastCall((func), &(arg), 1)
+ /* Call the callable object func with the "fast call" calling convention:
+ args is a C array for positional arguments followed by (key, value)
+ pairs for keyword arguments.
+
+ nargs is the number of positional parameters at the beginning of stack.
+ nkwargs is the number of (key, value) pairs at the end of stack.
+
+ If nargs and nkwargs are equal to zero, stack can be NULL.
+
+ Return the result on success. Raise an exception and return NULL on
+ error. */
+ PyAPI_FUNC(PyObject *) _PyObject_FastCallKeywords(
+ PyObject *func,
+ PyObject **stack,
+ Py_ssize_t nargs,
+ Py_ssize_t nkwargs);
+
PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(PyObject *func,
PyObject *result,
const char *where);
diff --git a/Include/funcobject.h b/Include/funcobject.h
index 6b89c86..5aff632 100644
--- a/Include/funcobject.h
+++ b/Include/funcobject.h
@@ -64,6 +64,12 @@ PyAPI_FUNC(PyObject *) _PyFunction_FastCallDict(
PyObject **args,
Py_ssize_t nargs,
PyObject *kwargs);
+
+PyAPI_FUNC(PyObject *) _PyFunction_FastCallKeywords(
+ PyObject *func,
+ PyObject **stack,
+ Py_ssize_t nargs,
+ Py_ssize_t nkwargs);
#endif
/* Macros for direct access to these values. Type checks are *not*
diff --git a/Objects/abstract.c b/Objects/abstract.c
index f302281..d271d94 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -2309,6 +2309,85 @@ exit:
return result;
}
+static PyObject *
+_PyStack_AsDict(PyObject **stack, Py_ssize_t nkwargs, PyObject *func)
+{
+ PyObject *kwdict;
+
+ kwdict = PyDict_New();
+ if (kwdict == NULL) {
+ return NULL;
+ }
+
+ while (--nkwargs >= 0) {
+ int err;
+ PyObject *key = *stack++;
+ PyObject *value = *stack++;
+ if (PyDict_GetItem(kwdict, key) != NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s%s got multiple values "
+ "for keyword argument '%U'",
+ PyEval_GetFuncName(func),
+ PyEval_GetFuncDesc(func),
+ key);
+ Py_DECREF(kwdict);
+ return NULL;
+ }
+
+ err = PyDict_SetItem(kwdict, key, value);
+ if (err) {
+ Py_DECREF(kwdict);
+ return NULL;
+ }
+ }
+ return kwdict;
+}
+
+PyObject *
+_PyObject_FastCallKeywords(PyObject *func, PyObject **stack, Py_ssize_t nargs,
+ Py_ssize_t nkwargs)
+{
+ PyObject *args, *kwdict, *result;
+
+ /* _PyObject_FastCallKeywords() 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(nkwargs >= 0);
+ assert((nargs == 0 && nkwargs == 0) || stack != NULL);
+
+ if (PyFunction_Check(func)) {
+ /* Fast-path: avoid temporary tuple or dict */
+ return _PyFunction_FastCallKeywords(func, stack, nargs, nkwargs);
+ }
+
+ if (PyCFunction_Check(func) && nkwargs == 0) {
+ return _PyCFunction_FastCallDict(func, args, nargs, NULL);
+ }
+
+ /* Slow-path: build temporary tuple and/or dict */
+ args = _PyStack_AsTuple(stack, nargs);
+
+ if (nkwargs > 0) {
+ kwdict = _PyStack_AsDict(stack + nargs, nkwargs, func);
+ if (kwdict == NULL) {
+ Py_DECREF(args);
+ return NULL;
+ }
+ }
+ else {
+ kwdict = NULL;
+ }
+
+ result = PyObject_Call(func, args, kwdict);
+ Py_DECREF(args);
+ Py_XDECREF(kwdict);
+ return result;
+}
+
static PyObject*
call_function_tail(PyObject *callable, PyObject *args)
{
diff --git a/Python/ceval.c b/Python/ceval.c
index b082760..266b987 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -113,7 +113,6 @@ static PyObject * call_function(PyObject ***, int, uint64*, uint64*);
#else
static PyObject * call_function(PyObject ***, int);
#endif
-static PyObject * fast_function(PyObject *, PyObject **, Py_ssize_t, Py_ssize_t);
static PyObject * do_call(PyObject *, PyObject ***, Py_ssize_t, Py_ssize_t);
static PyObject * ext_do_call(PyObject *, PyObject ***, int, Py_ssize_t, Py_ssize_t);
static PyObject * update_keyword_args(PyObject *, Py_ssize_t, PyObject ***,
@@ -4767,7 +4766,7 @@ call_function(PyObject ***pp_stack, int oparg
}
READ_TIMESTAMP(*pintr0);
if (PyFunction_Check(func)) {
- x = fast_function(func, (*pp_stack) - n, nargs, nkwargs);
+ x = _PyFunction_FastCallKeywords(func, (*pp_stack) - n, nargs, nkwargs);
}
else {
x = do_call(func, pp_stack, nargs, nkwargs);
@@ -4780,7 +4779,7 @@ call_function(PyObject ***pp_stack, int oparg
/* Clear the stack of the function object. Also removes
the arguments in case they weren't consumed already
- (fast_function() and err_args() leave them on the stack).
+ (_PyFunction_FastCallKeywords() and err_args() leave them on the stack).
*/
while ((*pp_stack) > pfunc) {
w = EXT_POP(*pp_stack);
@@ -4792,7 +4791,7 @@ call_function(PyObject ***pp_stack, int oparg
return x;
}
-/* The fast_function() function optimize calls for which no argument
+/* The _PyFunction_FastCallKeywords() function optimize calls for which no argument
tuple is necessary; the objects are passed directly from the stack.
For the simplest case -- a function that takes only positional
arguments and is called with only positional arguments -- it
@@ -4840,8 +4839,9 @@ _PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t nargs,
/* Similar to _PyFunction_FastCall() but keywords are passed a (key, value)
pairs in stack */
-static PyObject *
-fast_function(PyObject *func, PyObject **stack, Py_ssize_t nargs, Py_ssize_t nkwargs)
+PyObject *
+_PyFunction_FastCallKeywords(PyObject *func, PyObject **stack,
+ Py_ssize_t nargs, Py_ssize_t nkwargs)
{
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
PyObject *globals = PyFunction_GET_GLOBALS(func);
@@ -4850,6 +4850,11 @@ fast_function(PyObject *func, PyObject **stack, Py_ssize_t nargs, Py_ssize_t nkw
PyObject **d;
int nd;
+ assert(func != NULL);
+ assert(nargs >= 0);
+ assert(nkwargs >= 0);
+ assert((nargs == 0 && nkwargs == 0) || stack != NULL);
+
PCALL(PCALL_FUNCTION);
PCALL(PCALL_FAST_FUNCTION);
@@ -4902,14 +4907,14 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs,
Py_ssize_t nd, nk;
PyObject *result;
- PCALL(PCALL_FUNCTION);
- PCALL(PCALL_FAST_FUNCTION);
-
assert(func != NULL);
assert(nargs >= 0);
assert(nargs == 0 || args != NULL);
assert(kwargs == NULL || PyDict_Check(kwargs));
+ PCALL(PCALL_FUNCTION);
+ PCALL(PCALL_FAST_FUNCTION);
+
if (co->co_kwonlyargcount == 0 &&
(kwargs == NULL || PyDict_Size(kwargs) == 0) &&
co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))