diff -r d3d8faaaaade -r 4ea029d1acc3 Include/Python.h --- a/Include/Python.h Thu May 26 06:03:33 2016 +0000 +++ b/Include/Python.h Thu May 26 12:13:31 2016 +0200 @@ -112,6 +112,7 @@ #include "pyarena.h" #include "modsupport.h" +#include "pystack.h" #include "pythonrun.h" #include "pylifecycle.h" #include "ceval.h" diff -r d3d8faaaaade -r 4ea029d1acc3 Include/abstract.h --- a/Include/abstract.h Thu May 26 06:03:33 2016 +0000 +++ b/Include/abstract.h Thu May 26 12:13:31 2016 +0200 @@ -267,10 +267,26 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx PyObject *args, PyObject *kw); #ifndef Py_LIMITED_API + /* Call the callable object func with the "fast call" calling convention: + args is a C array for positional parameters (nargs is the number of + positional paramater), kwargs is a dictionary for keyword parameters. + + If nargs is equal to zero, args can be NULL. kwargs can be NULL. + nargs must be greater or equal to zero. + + Return the result on success. Raise an exception on return NULL on + error. */ + PyAPI_FUNC(PyObject *) _PyObject_FastCall(PyObject *func, + PyObject **args, int nargs, + PyObject *kwargs); + PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(PyObject *func, PyObject *result, const char *where); -#endif +#endif /* Py_LIMITED_API */ + + PyAPI_FUNC(PyObject *) PyObject_CallNoArg(PyObject *func); + PyAPI_FUNC(PyObject *) PyObject_CallArg1(PyObject *func, PyObject *arg); /* Call a callable Python object, callable_object, with diff -r d3d8faaaaade -r 4ea029d1acc3 Include/funcobject.h --- a/Include/funcobject.h Thu May 26 06:03:33 2016 +0000 +++ b/Include/funcobject.h Thu May 26 12:13:31 2016 +0200 @@ -58,6 +58,13 @@ PyAPI_FUNC(int) PyFunction_SetClosure(Py PyAPI_FUNC(PyObject *) PyFunction_GetAnnotations(PyObject *); PyAPI_FUNC(int) PyFunction_SetAnnotations(PyObject *, PyObject *); +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) _PyFunction_FastCall( + PyObject *func, + PyObject **args, int nargs, + PyObject *kwargs); +#endif + /* Macros for direct access to these values. Type checks are *not* done, so use with care. */ #define PyFunction_GET_CODE(func) \ diff -r d3d8faaaaade -r 4ea029d1acc3 Include/methodobject.h --- a/Include/methodobject.h Thu May 26 06:03:33 2016 +0000 +++ b/Include/methodobject.h Thu May 26 12:13:31 2016 +0200 @@ -37,6 +37,12 @@ PyAPI_FUNC(int) PyCFunction_GetFlags(PyO #endif PyAPI_FUNC(PyObject *) PyCFunction_Call(PyObject *, PyObject *, PyObject *); +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) _PyCFunction_FastCall(PyObject *func, + PyObject **args, int nargs, + PyObject *kwargs); +#endif + struct PyMethodDef { const char *ml_name; /* The name of the built-in function/method */ PyCFunction ml_meth; /* The C function that implements it */ diff -r d3d8faaaaade -r 4ea029d1acc3 Include/pystack.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Include/pystack.h Thu May 26 12:13:31 2016 +0200 @@ -0,0 +1,11 @@ +#ifndef Py_STACK_H +#define Py_STACK_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(PyObject*) _PyStack_AsTuple(PyObject **stack, + Py_ssize_t nargs); + +#endif /* !Py_STACK_H */ + diff -r d3d8faaaaade -r 4ea029d1acc3 Makefile.pre.in --- a/Makefile.pre.in Thu May 26 06:03:33 2016 +0000 +++ b/Makefile.pre.in Thu May 26 12:13:31 2016 +0200 @@ -413,6 +413,7 @@ PYTHON_OBJS= \ Python/dtoa.o \ Python/formatter_unicode.o \ Python/fileutils.o \ + Python/pystack.o \ Python/$(DYNLOADFILE) \ $(LIBOBJS) \ $(MACHDEP_OBJS) \ @@ -957,10 +958,11 @@ PYTHON_HEADERS= \ $(srcdir)/Include/pymacro.h \ $(srcdir)/Include/pymem.h \ $(srcdir)/Include/pyport.h \ + $(srcdir)/Include/pystack.h \ $(srcdir)/Include/pystate.h \ $(srcdir)/Include/pystrcmp.h \ + $(srcdir)/Include/pystrhex.h \ $(srcdir)/Include/pystrtod.h \ - $(srcdir)/Include/pystrhex.h \ $(srcdir)/Include/pythonrun.h \ $(srcdir)/Include/pythread.h \ $(srcdir)/Include/pytime.h \ diff -r d3d8faaaaade -r 4ea029d1acc3 Objects/abstract.c --- a/Objects/abstract.c Thu May 26 06:03:33 2016 +0000 +++ b/Objects/abstract.c Thu May 26 12:13:31 2016 +0200 @@ -2091,9 +2091,15 @@ PyMapping_Values(PyObject *o) /* XXX PyCallable_Check() is in object.c */ PyObject * -PyObject_CallObject(PyObject *o, PyObject *a) +PyObject_CallObject(PyObject *func, PyObject *args) { - return PyEval_CallObjectWithKeywords(o, a, NULL); + if (args != NULL) { + assert(PyTuple_Check(args)); + return PyObject_Call(func, args, NULL); + } + else { + return PyObject_CallNoArg(func); + } } PyObject* @@ -2174,30 +2180,89 @@ PyObject_Call(PyObject *func, PyObject * return _Py_CheckFunctionResult(func, result, NULL); } +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); + assert(kwargs == NULL || PyDict_Check(kwargs)); + + 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; +} + +PyObject * +PyObject_CallNoArg(PyObject *func) +{ + return _PyObject_FastCall(func, NULL, 0, 0); +} + +PyObject * +PyObject_CallArg1(PyObject *func, PyObject *arg) +{ + return _PyObject_FastCall(func, &arg, 1, 0); +} + static PyObject* call_function_tail(PyObject *callable, PyObject *args) { - PyObject *retval; + PyObject *result; if (args == NULL) return NULL; - if (!PyTuple_Check(args)) { - PyObject *a; - - a = PyTuple_New(1); - if (a == NULL) { - Py_DECREF(args); - return NULL; - } - PyTuple_SET_ITEM(a, 0, args); - args = a; + if (PyTuple_Check(args)) { + result = PyObject_Call(callable, args, NULL); } - retval = PyObject_Call(callable, args, NULL); + else { + result = PyObject_CallArg1(callable, args); + } Py_DECREF(args); - - return retval; + return result; } PyObject * diff -r d3d8faaaaade -r 4ea029d1acc3 Objects/descrobject.c --- a/Objects/descrobject.c Thu May 26 06:03:33 2016 +0000 +++ b/Objects/descrobject.c Thu May 26 12:13:31 2016 +0200 @@ -1372,9 +1372,6 @@ property_dealloc(PyObject *self) static PyObject * property_descr_get(PyObject *self, PyObject *obj, PyObject *type) { - static PyObject * volatile cached_args = NULL; - PyObject *args; - PyObject *ret; propertyobject *gs = (propertyobject *)self; if (obj == NULL || obj == Py_None) { @@ -1385,29 +1382,7 @@ property_descr_get(PyObject *self, PyObj PyErr_SetString(PyExc_AttributeError, "unreadable attribute"); return NULL; } - args = cached_args; - cached_args = NULL; - if (!args) { - args = PyTuple_New(1); - if (!args) - return NULL; - _PyObject_GC_UNTRACK(args); - } - Py_INCREF(obj); - PyTuple_SET_ITEM(args, 0, obj); - ret = PyObject_Call(gs->prop_get, args, NULL); - if (cached_args == NULL && Py_REFCNT(args) == 1) { - assert(Py_SIZE(args) == 1); - assert(PyTuple_GET_ITEM(args, 0) == obj); - cached_args = args; - Py_DECREF(obj); - } - else { - assert(Py_REFCNT(args) >= 1); - _PyObject_GC_TRACK(args); - Py_DECREF(args); - } - return ret; + return PyObject_CallArg1(gs->prop_get, obj); } static int diff -r d3d8faaaaade -r 4ea029d1acc3 Objects/methodobject.c --- a/Objects/methodobject.c Thu May 26 06:03:33 2016 +0000 +++ b/Objects/methodobject.c Thu May 26 12:13:31 2016 +0200 @@ -145,6 +145,99 @@ PyCFunction_Call(PyObject *func, PyObjec 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 diff -r d3d8faaaaade -r 4ea029d1acc3 PCbuild/pythoncore.vcxproj --- a/PCbuild/pythoncore.vcxproj Thu May 26 06:03:33 2016 +0000 +++ b/PCbuild/pythoncore.vcxproj Thu May 26 12:13:31 2016 +0200 @@ -144,10 +144,11 @@ + + - @@ -377,6 +378,7 @@ + @@ -417,4 +419,4 @@ - \ No newline at end of file + diff -r d3d8faaaaade -r 4ea029d1acc3 Python/ceval.c --- a/Python/ceval.c Thu May 26 06:03:33 2016 +0000 +++ b/Python/ceval.c Thu May 26 12:13:31 2016 +0200 @@ -113,7 +113,7 @@ static PyObject * call_function(PyObject #else static PyObject * call_function(PyObject ***, int); #endif -static PyObject * fast_function(PyObject *, PyObject ***, int, int, int); +static PyObject * fast_function(PyObject *, PyObject **, int, int, int); static PyObject * do_call(PyObject *, PyObject ***, int, int); static PyObject * ext_do_call(PyObject *, PyObject ***, int, int, int); static PyObject * update_keyword_args(PyObject *, int, PyObject ***, @@ -3809,81 +3809,60 @@ too_many_positional(PyCodeObject *co, in Py_DECREF(kwonly_sig); } + /* This is gonna seem *real weird*, but if you put some other code between PyEval_EvalFrame() and PyEval_EvalCodeEx() you will need to adjust the test in the if statements in Misc/gdbinit (pystack and pystackv). */ -static PyObject * -_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, - PyObject **args, int argcount, PyObject **kws, int kwcount, - PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure, - PyObject *name, PyObject *qualname) +static int +fastlocals_dict(PyObject **fastlocals, PyCodeObject *co, + PyObject *kwdict, PyObject *kwargs) { - PyCodeObject* co = (PyCodeObject*)_co; - PyFrameObject *f; - PyObject *retval = NULL; - PyObject **fastlocals, **freevars; - PyThreadState *tstate = PyThreadState_GET(); - PyObject *x, *u; - int total_args = co->co_argcount + co->co_kwonlyargcount; - int i; - int n = argcount; - PyObject *kwdict = NULL; - - if (globals == NULL) { - PyErr_SetString(PyExc_SystemError, - "PyEval_EvalCodeEx: NULL globals"); - return NULL; + const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount; + _Py_IDENTIFIER(items); + PyObject *items, *iter; + PyObject *item = NULL; + + assert(PyDict_Check(kwargs)); + + items = _PyObject_CallMethodId((PyObject *)kwargs, &PyId_items, NULL); + if (items == NULL) { + return -1; } - assert(tstate != NULL); - assert(globals != NULL); - f = PyFrame_New(tstate, co, globals, locals); - if (f == NULL) - return NULL; - - fastlocals = f->f_localsplus; - freevars = f->f_localsplus + co->co_nlocals; - - /* Parse arguments. */ - if (co->co_flags & CO_VARKEYWORDS) { - kwdict = PyDict_New(); - if (kwdict == NULL) - goto fail; - i = total_args; - if (co->co_flags & CO_VARARGS) - i++; - SETLOCAL(i, kwdict); + iter = PyObject_GetIter(items); + Py_DECREF(items); + if (iter == NULL) { + goto fail; } - if (argcount > co->co_argcount) - n = co->co_argcount; - for (i = 0; i < n; i++) { - x = args[i]; - Py_INCREF(x); - SETLOCAL(i, x); - } - if (co->co_flags & CO_VARARGS) { - u = PyTuple_New(argcount - n); - if (u == NULL) - goto fail; - SETLOCAL(total_args, u); - for (i = n; i < argcount; i++) { - x = args[i]; - Py_INCREF(x); - PyTuple_SET_ITEM(u, i-n, x); - } - } - for (i = 0; i < kwcount; i++) { + + do { PyObject **co_varnames; - PyObject *keyword = kws[2*i]; - PyObject *value = kws[2*i + 1]; + PyObject *keyword, *value; int j; + + /* Get first item */ + item = PyIter_Next(iter); + if (item == NULL) { + if (PyErr_Occurred()) { + goto fail; + } + + /* nothing more to add */ + break; + } + assert(PyTuple_CheckExact(item) && PyTuple_GET_SIZE(item) == 2); + + keyword = PyTuple_GET_ITEM(item, 0); + value = PyTuple_GET_ITEM(item, 1); + if (keyword == NULL || !PyUnicode_Check(keyword)) { PyErr_Format(PyExc_TypeError, "%U() keywords must be strings", co->co_name); goto fail; } + /* Speed hack: do raw pointer compares. As names are normally interned this should almost always hit. */ co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item; @@ -3892,6 +3871,7 @@ static PyObject * if (nm == keyword) goto kw_found; } + /* Slow fallback, just in case */ for (j = 0; j < total_args; j++) { PyObject *nm = co_varnames[j]; @@ -3902,6 +3882,7 @@ static PyObject * else if (cmp < 0) goto fail; } + if (j >= total_args && kwdict == NULL) { PyErr_Format(PyExc_TypeError, "%U() got an unexpected " @@ -3910,10 +3891,13 @@ static PyObject * keyword); goto fail; } + if (PyDict_SetItem(kwdict, keyword, value) == -1) { goto fail; } + Py_DECREF(item); continue; + kw_found: if (GETLOCAL(j) != NULL) { PyErr_Format(PyExc_TypeError, @@ -3925,11 +3909,172 @@ static PyObject * } Py_INCREF(value); SETLOCAL(j, value); + + Py_CLEAR(item); + } while (1); + + Py_DECREF(iter); + return 0; + +fail: + Py_DECREF(iter); + Py_XDECREF(item); + return -1; +} + +static PyObject * +_PyEval_EvalCode(PyObject *_co, PyObject *globals, PyObject *locals, + PyObject **args, int argcount, + PyObject **kws, int kwcount, PyObject *kwargs, + PyObject **defs, int defcount, + PyObject *kwdefs, PyObject *closure, + PyObject *name, PyObject *qualname) +{ + PyCodeObject* co = (PyCodeObject*)_co; + PyFrameObject *f; + PyObject *retval = NULL; + PyObject **fastlocals, **freevars; + PyThreadState *tstate; + PyObject *x, *u; + const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount; + Py_ssize_t i, n; + PyObject *kwdict; + + assert((kwcount == 0) || (kws != NULL)); + + if (globals == NULL) { + PyErr_SetString(PyExc_SystemError, + "PyEval_EvalCodeEx: NULL globals"); + return NULL; } + + /* Create the frame */ + tstate = PyThreadState_GET(); + assert(tstate != NULL); + f = PyFrame_New(tstate, co, globals, locals); + if (f == NULL) { + return NULL; + } + fastlocals = f->f_localsplus; + freevars = f->f_localsplus + co->co_nlocals; + + /* Create a dictionary for keyword parameters (**kwags) */ + if (co->co_flags & CO_VARKEYWORDS) { + kwdict = PyDict_New(); + if (kwdict == NULL) + goto fail; + i = total_args; + if (co->co_flags & CO_VARARGS) { + i++; + } + SETLOCAL(i, kwdict); + } + else { + kwdict = NULL; + } + + /* Copy positional arguments into local variables */ + if (argcount > co->co_argcount) { + n = co->co_argcount; + } + else { + n = argcount; + } + for (i = 0; i < n; i++) { + x = args[i]; + Py_INCREF(x); + SETLOCAL(i, x); + } + + /* Pack other positional arguments into the *args argument */ + if (co->co_flags & CO_VARARGS) { + u = PyTuple_New(argcount - n); + if (u == NULL) { + goto fail; + } + SETLOCAL(total_args, u); + for (i = n; i < argcount; i++) { + x = args[i]; + Py_INCREF(x); + PyTuple_SET_ITEM(u, i-n, x); + } + } + + /* Handle keyword arguments (passed as an array of (key, value)) */ + for (i = 0; i < kwcount; i++) { + PyObject **co_varnames; + PyObject *keyword = kws[2*i]; + PyObject *value = kws[2*i + 1]; + int j; + + if (keyword == NULL || !PyUnicode_Check(keyword)) { + PyErr_Format(PyExc_TypeError, + "%U() keywords must be strings", + co->co_name); + goto fail; + } + + /* Speed hack: do raw pointer compares. As names are + normally interned this should almost always hit. */ + co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item; + for (j = 0; j < total_args; j++) { + PyObject *nm = co_varnames[j]; + if (nm == keyword) + goto kw_found; + } + + /* Slow fallback, just in case */ + for (j = 0; j < total_args; j++) { + PyObject *nm = co_varnames[j]; + int cmp = PyObject_RichCompareBool( + keyword, nm, Py_EQ); + if (cmp > 0) + goto kw_found; + else if (cmp < 0) + goto fail; + } + + if (j >= total_args && kwdict == NULL) { + PyErr_Format(PyExc_TypeError, + "%U() got an unexpected " + "keyword argument '%S'", + co->co_name, + keyword); + goto fail; + } + + if (PyDict_SetItem(kwdict, keyword, value) == -1) { + goto fail; + } + continue; + + kw_found: + if (GETLOCAL(j) != NULL) { + PyErr_Format(PyExc_TypeError, + "%U() got multiple " + "values for argument '%S'", + co->co_name, + keyword); + goto fail; + } + Py_INCREF(value); + SETLOCAL(j, value); + } + + /* Handle keyword arguments (passed as a dictionary) */ + if (kwargs != NULL) { + if (fastlocals_dict(fastlocals, co, kwdict, kwargs) < 0) { + goto fail; + } + } + + /* Check the number of positional arguments */ if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) { too_many_positional(co, argcount, defcount, fastlocals); goto fail; } + + /* Add missing positional arguments (copy default values from defs) */ if (argcount < co->co_argcount) { int m = co->co_argcount - defcount; int missing = 0; @@ -3952,6 +4097,8 @@ static PyObject * } } } + + /* Add missing keyword arguments (copy default values from kwdefs) */ if (co->co_kwonlyargcount > 0) { int missing = 0; for (i = co->co_argcount; i < total_args; i++) { @@ -3994,12 +4141,15 @@ static PyObject * goto fail; SETLOCAL(co->co_nlocals + i, c); } + + /* Copy closure variables to free variables */ for (i = 0; i < PyTuple_GET_SIZE(co->co_freevars); ++i) { PyObject *o = PyTuple_GET_ITEM(closure, i); Py_INCREF(o); freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o; } + /* Handle generator/coroutine */ if (co->co_flags & (CO_GENERATOR | CO_COROUTINE)) { PyObject *gen; PyObject *coro_wrapper = tstate->coroutine_wrapper; @@ -4063,10 +4213,10 @@ PyEval_EvalCodeEx(PyObject *_co, PyObjec PyObject **args, int argcount, PyObject **kws, int kwcount, PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) { - return _PyEval_EvalCodeWithName(_co, globals, locals, - args, argcount, kws, kwcount, - defs, defcount, kwdefs, closure, - NULL, NULL); + return _PyEval_EvalCode(_co, globals, locals, + args, argcount, kws, kwcount, NULL, + defs, defcount, kwdefs, closure, + NULL, NULL); } static PyObject * @@ -4585,6 +4735,10 @@ PyEval_CallObjectWithKeywords(PyObject * #endif if (arg == NULL) { + if (kw == NULL) { + return PyObject_CallNoArg(func); + } + arg = PyTuple_New(0); if (arg == NULL) return NULL; @@ -4753,10 +4907,12 @@ call_function(PyObject ***pp_stack, int } else Py_INCREF(func); READ_TIMESTAMP(*pintr0); - if (PyFunction_Check(func)) - x = fast_function(func, pp_stack, n, na, nk); - else + if (PyFunction_Check(func)) { + x = fast_function(func, (*pp_stack) - n, n, na, nk); + } + else { x = do_call(func, pp_stack, na, nk); + } READ_TIMESTAMP(*pintr1); Py_DECREF(func); @@ -4786,62 +4942,124 @@ call_function(PyObject ***pp_stack, int done before evaluating the frame. */ +static PyObject* +_PyFunction_FastCallNoKw(PyObject **args, Py_ssize_t na, + PyCodeObject *co, PyObject *globals) +{ + PyFrameObject *f; + PyThreadState *tstate = PyThreadState_GET(); + PyObject **fastlocals; + Py_ssize_t i; + PyObject *result; + + PCALL(PCALL_FASTER_FUNCTION); + assert(globals != NULL); + /* XXX Perhaps we should create a specialized + PyFrame_New() that doesn't take locals, but does + take builtins without sanity checking them. + */ + assert(tstate != NULL); + f = PyFrame_New(tstate, co, globals, NULL); + if (f == NULL) { + return NULL; + } + + fastlocals = f->f_localsplus; + + for (i = 0; i < na; i++) { + Py_INCREF(*args); + fastlocals[i] = *args++; + } + result = PyEval_EvalFrameEx(f,0); + + ++tstate->recursion_depth; + Py_DECREF(f); + --tstate->recursion_depth; + + return result; +} + static PyObject * -fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk) +fast_function(PyObject *func, PyObject **stack, int n, int na, int nk) { PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); PyObject *globals = PyFunction_GET_GLOBALS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func); - PyObject *kwdefs = PyFunction_GET_KW_DEFAULTS(func); - PyObject *name = ((PyFunctionObject *)func) -> func_name; - PyObject *qualname = ((PyFunctionObject *)func) -> func_qualname; - PyObject **d = NULL; - int nd = 0; + PyObject *kwdefs, *name, *qualname; + PyObject **d; + int nd; PCALL(PCALL_FUNCTION); PCALL(PCALL_FAST_FUNCTION); - if (argdefs == NULL && co->co_argcount == n && - co->co_kwonlyargcount == 0 && nk==0 && - co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { - PyFrameObject *f; - PyObject *retval = NULL; - PyThreadState *tstate = PyThreadState_GET(); - PyObject **fastlocals, **stack; - int i; - - PCALL(PCALL_FASTER_FUNCTION); - assert(globals != NULL); - /* XXX Perhaps we should create a specialized - PyFrame_New() that doesn't take locals, but does - take builtins without sanity checking them. - */ - assert(tstate != NULL); - f = PyFrame_New(tstate, co, globals, NULL); - if (f == NULL) - return NULL; - - fastlocals = f->f_localsplus; - stack = (*pp_stack) - n; - - for (i = 0; i < n; i++) { - Py_INCREF(*stack); - fastlocals[i] = *stack++; - } - retval = PyEval_EvalFrameEx(f,0); - ++tstate->recursion_depth; - Py_DECREF(f); - --tstate->recursion_depth; - return retval; + + if (argdefs == NULL && co->co_argcount == na && + co->co_kwonlyargcount == 0 && nk == 0 && + co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) + { + return _PyFunction_FastCallNoKw(stack, na, co, globals); } + + kwdefs = PyFunction_GET_KW_DEFAULTS(func); + name = ((PyFunctionObject *)func) -> func_name; + qualname = ((PyFunctionObject *)func) -> func_qualname; + if (argdefs != NULL) { d = &PyTuple_GET_ITEM(argdefs, 0); nd = Py_SIZE(argdefs); } - return _PyEval_EvalCodeWithName((PyObject*)co, globals, - (PyObject *)NULL, (*pp_stack)-n, na, - (*pp_stack)-2*nk, nk, d, nd, kwdefs, - PyFunction_GET_CLOSURE(func), - name, qualname); + else { + d = NULL; + nd = 0; + } + return _PyEval_EvalCode((PyObject*)co, globals, (PyObject *)NULL, + stack, na, + stack + na, nk, NULL, + d, nd, kwdefs, + PyFunction_GET_CLOSURE(func), + name, qualname); +} + +PyObject * +_PyFunction_FastCall(PyObject *func, PyObject **args, int nargs, PyObject *kws) +{ + PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); + PyObject *globals = PyFunction_GET_GLOBALS(func); + PyObject *argdefs = PyFunction_GET_DEFAULTS(func); + PyObject *kwdefs, *name, *qualname; + PyObject **d; + int nd; + + PCALL(PCALL_FUNCTION); + PCALL(PCALL_FAST_FUNCTION); + + assert(kws == NULL || PyDict_Check(kws)); + + if (argdefs == NULL && co->co_argcount == nargs && + co->co_kwonlyargcount == 0 && + (kws == NULL || ((PyDictObject *)kws)->ma_used == 0) && + co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) + { + return _PyFunction_FastCallNoKw(args, nargs, co, globals); + } + + kwdefs = PyFunction_GET_KW_DEFAULTS(func); + name = ((PyFunctionObject *)func) -> func_name; + qualname = ((PyFunctionObject *)func) -> func_qualname; + + if (argdefs != NULL) { + d = &PyTuple_GET_ITEM(argdefs, 0); + nd = Py_SIZE(argdefs); + } + else { + d = NULL; + nd = 0; + } + return _PyEval_EvalCode((PyObject*)co, globals, (PyObject *)NULL, + args, nargs, + NULL, 0, kws, + d, nd, kwdefs, + PyFunction_GET_CLOSURE(func), + name, qualname); } static PyObject * diff -r d3d8faaaaade -r 4ea029d1acc3 Python/pystack.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Python/pystack.c Thu May 26 12:13:31 2016 +0200 @@ -0,0 +1,21 @@ +#include "Python.h" + +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; +}