From 3f16b927b6791867124ec969049fde4d61df0157 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 9 Apr 2014 23:55:56 -0400 Subject: PEP 465: a dedicated infix operator for matrix multiplication (closes #21176) --- Python/ceval.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 5db88be620..ab7afa90d5 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1495,6 +1495,18 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(BINARY_MATRIX_MULTIPLY) { + PyObject *right = POP(); + PyObject *left = TOP(); + PyObject *res = PyNumber_MatrixMultiply(left, right); + Py_DECREF(left); + Py_DECREF(right); + SET_TOP(res); + if (res == NULL) + goto error; + DISPATCH(); + } + TARGET(BINARY_TRUE_DIVIDE) { PyObject *divisor = POP(); PyObject *dividend = TOP(); @@ -1685,6 +1697,18 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(INPLACE_MATRIX_MULTIPLY) { + PyObject *right = POP(); + PyObject *left = TOP(); + PyObject *res = PyNumber_InPlaceMatrixMultiply(left, right); + Py_DECREF(left); + Py_DECREF(right); + SET_TOP(res); + if (res == NULL) + goto error; + DISPATCH(); + } + TARGET(INPLACE_TRUE_DIVIDE) { PyObject *divisor = POP(); PyObject *dividend = TOP(); -- cgit v1.2.1 From cb7bff4fcfe8f736d1095a0be0c363dfec14a564 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 16 Jun 2014 15:59:28 +0200 Subject: Issue #21205: Add a new ``__qualname__`` attribute to generator, the qualified name, and use it in the representation of a generator (``repr(gen)``). The default name of the generator (``__name__`` attribute) is now get from the function instead of the code. Use ``gen.gi_code.co_name`` to get the name of the code. --- Python/ceval.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index e14e77270c..3ede055070 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3401,10 +3401,11 @@ too_many_positional(PyCodeObject *co, int given, int defcount, PyObject **fastlo PyEval_EvalFrame() and PyEval_EvalCodeEx() you will need to adjust the test in the if statements in Misc/gdbinit (pystack and pystackv). */ -PyObject * -PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, +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 **defs, int defcount, PyObject *kwdefs, PyObject *closure, + PyObject *name, PyObject *qualname) { PyCodeObject* co = (PyCodeObject*)_co; PyFrameObject *f; @@ -3596,7 +3597,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, /* Create a new generator that owns the ready to run frame * and return that as the value. */ - return PyGen_New(f); + return PyGen_NewWithQualName(f, name, qualname); } retval = PyEval_EvalFrameEx(f,0); @@ -3615,6 +3616,16 @@ fail: /* Jump here from prelude on failure */ return retval; } +PyObject * +PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, + 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); +} static PyObject * special_lookup(PyObject *o, _Py_Identifier *id) @@ -4313,6 +4324,8 @@ fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk) 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; @@ -4355,10 +4368,11 @@ fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk) d = &PyTuple_GET_ITEM(argdefs, 0); nd = Py_SIZE(argdefs); } - return PyEval_EvalCodeEx((PyObject*)co, globals, - (PyObject *)NULL, (*pp_stack)-n, na, - (*pp_stack)-2*nk, nk, d, nd, kwdefs, - PyFunction_GET_CLOSURE(func)); + 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); } static PyObject * -- cgit v1.2.1 From c54b6df1ddcec5fa1d13efe50cf7b2a12388d8bc Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Mon, 13 Oct 2014 20:19:45 +0200 Subject: Issue #17636: Circular imports involving relative imports are now supported. --- Python/ceval.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 2dbf591d45..4b1d6ca969 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4693,11 +4693,29 @@ static PyObject * import_from(PyObject *v, PyObject *name) { PyObject *x; + _Py_IDENTIFIER(__name__); + PyObject *fullmodname, *pkgname; x = PyObject_GetAttr(v, name); - if (x == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { + if (x != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError)) + return x; + /* Issue #17636: in case this failed because of a circular relative + import, try to fallback on reading the module directly from + sys.modules. */ + PyErr_Clear(); + pkgname = _PyObject_GetAttrId(v, &PyId___name__); + if (pkgname == NULL) + return NULL; + fullmodname = PyUnicode_FromFormat("%U.%U", pkgname, name); + Py_DECREF(pkgname); + if (fullmodname == NULL) + return NULL; + x = PyDict_GetItem(PyImport_GetModuleDict(), fullmodname); + if (x == NULL) PyErr_Format(PyExc_ImportError, "cannot import name %R", name); - } + else + Py_INCREF(x); + Py_DECREF(fullmodname); return x; } -- cgit v1.2.1 From b0a85d474df68ed8e72a01506fa74ceca3587aef Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 17 Feb 2015 10:14:30 +0200 Subject: Issue #22883: Got rid of outdated references to PyInt and PyString in comments. --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index e09ff3423c..5cefdcf088 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4606,7 +4606,7 @@ ext_call_fail: return result; } -/* Extract a slice index from a PyInt or PyLong or an object with the +/* Extract a slice index from a PyLong or an object with the nb_index slot defined, and store in *pi. Silently reduce values larger than PY_SSIZE_T_MAX to PY_SSIZE_T_MAX, and silently boost values less than -PY_SSIZE_T_MAX-1 to -PY_SSIZE_T_MAX-1. -- cgit v1.2.1 From ef35d11c6986437b4eac74a6657377607c34841a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 6 Mar 2015 23:35:27 +0100 Subject: Issue #23571: PyObject_Call(), PyCFunction_Call() and call_function() now raise a SystemError if a function returns a result and raises an exception. The SystemError is chained to the previous exception. Refactor also PyObject_Call() and PyCFunction_Call() to make them more readable. Remove some checks which became useless (duplicate checks). Change reviewed by Serhiy Storchaka. --- Python/ceval.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 5cefdcf088..1c6089d5c0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3192,8 +3192,7 @@ fast_block_end: if (why != WHY_RETURN) retval = NULL; - assert((retval != NULL && !PyErr_Occurred()) - || (retval == NULL && PyErr_Occurred())); + assert((retval != NULL) ^ (PyErr_Occurred() != NULL)); fast_yield: if (co->co_flags & CO_GENERATOR) { @@ -3254,7 +3253,7 @@ exit_eval_frame: f->f_executing = 0; tstate->frame = f->f_back; - return retval; + return _Py_CheckFunctionResult(retval, "PyEval_EvalFrameEx"); } static void @@ -4119,13 +4118,6 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw) { PyObject *result; -#ifdef Py_DEBUG - /* PyEval_CallObjectWithKeywords() must not be called with an exception - set, because it may clear it (directly or indirectly) - and so the caller looses its exception */ - assert(!PyErr_Occurred()); -#endif - if (arg == NULL) { arg = PyTuple_New(0); if (arg == NULL) @@ -4149,8 +4141,6 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw) result = PyObject_Call(func, arg, kw); Py_DECREF(arg); - assert((result != NULL && !PyErr_Occurred()) - || (result == NULL && PyErr_Occurred())); return result; } @@ -4253,11 +4243,15 @@ call_function(PyObject ***pp_stack, int oparg PyObject *self = PyCFunction_GET_SELF(func); if (flags & METH_NOARGS && na == 0) { C_TRACE(x, (*meth)(self,NULL)); + + x = _Py_CheckFunctionResult(x, "call_function"); } else if (flags & METH_O && na == 1) { PyObject *arg = EXT_POP(*pp_stack); C_TRACE(x, (*meth)(self,arg)); Py_DECREF(arg); + + x = _Py_CheckFunctionResult(x, "call_function"); } else { err_args(func, flags, na); @@ -4277,7 +4271,8 @@ call_function(PyObject ***pp_stack, int oparg x = NULL; } } - } else { + } + else { if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { /* optimize access to bound methods */ PyObject *self = PyMethod_GET_SELF(func); @@ -4299,9 +4294,9 @@ call_function(PyObject ***pp_stack, int oparg x = do_call(func, pp_stack, na, nk); READ_TIMESTAMP(*pintr1); Py_DECREF(func); + + assert((x != NULL) ^ (PyErr_Occurred() != NULL)); } - assert((x != NULL && !PyErr_Occurred()) - || (x == NULL && PyErr_Occurred())); /* Clear the stack of the function object. Also removes the arguments in case they weren't consumed already @@ -4313,8 +4308,7 @@ call_function(PyObject ***pp_stack, int oparg PCALL(PCALL_POP); } - assert((x != NULL && !PyErr_Occurred()) - || (x == NULL && PyErr_Occurred())); + assert((x != NULL) ^ (PyErr_Occurred() != NULL)); return x; } @@ -4601,8 +4595,6 @@ ext_call_fail: Py_XDECREF(callargs); Py_XDECREF(kwdict); Py_XDECREF(stararg); - assert((result != NULL && !PyErr_Occurred()) - || (result == NULL && PyErr_Occurred())); return result; } -- cgit v1.2.1 From e2e154b313a94ae72f8e0c05f7698d07c2ba1999 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 16 Mar 2015 11:52:32 +0100 Subject: Issue #23571: Restore removed assert(!PyErr_Occurred()); in PyEval_CallObjectWithKeywords() Sorry Serhiy, I missed your explanation because the review email was moved to my Spam folder :-( --- Python/ceval.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 1c6089d5c0..25fbc0fc79 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4118,6 +4118,13 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw) { PyObject *result; +#ifdef Py_DEBUG + /* PyEval_CallObjectWithKeywords() must not be called with an exception + set. It raises a new exception if parameters are invalid or if + PyTuple_New() fails, and so the original exception is lost. */ + assert(!PyErr_Occurred()); +#endif + if (arg == NULL) { arg = PyTuple_New(0); if (arg == NULL) -- cgit v1.2.1 From 814337d3955510ed32321e0c38239415d224b57b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 21 Mar 2015 15:04:43 +0100 Subject: Issue #23571: _Py_CheckFunctionResult() now gives the name of the function which returned an invalid result (result+error or no result without error) in the exception message. Add also unit test to check that the exception contains the name of the function. Special case: the final _PyEval_EvalFrameEx() check doesn't mention the function since it didn't execute a single function but a whole frame. --- Python/ceval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 25fbc0fc79..d68cdc6292 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3253,7 +3253,7 @@ exit_eval_frame: f->f_executing = 0; tstate->frame = f->f_back; - return _Py_CheckFunctionResult(retval, "PyEval_EvalFrameEx"); + return _Py_CheckFunctionResult(NULL, retval, "PyEval_EvalFrameEx"); } static void @@ -4251,14 +4251,14 @@ call_function(PyObject ***pp_stack, int oparg if (flags & METH_NOARGS && na == 0) { C_TRACE(x, (*meth)(self,NULL)); - x = _Py_CheckFunctionResult(x, "call_function"); + x = _Py_CheckFunctionResult(func, x, NULL); } else if (flags & METH_O && na == 1) { PyObject *arg = EXT_POP(*pp_stack); C_TRACE(x, (*meth)(self,arg)); Py_DECREF(arg); - x = _Py_CheckFunctionResult(x, "call_function"); + x = _Py_CheckFunctionResult(func, x, NULL); } else { err_args(func, flags, na); -- cgit v1.2.1 From 9d6c9533eba49f69a10f9fba83b98bb46d07f2d3 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Wed, 15 Apr 2015 17:08:45 -0400 Subject: #23949: Improve tuple unpacking error messages. Patch by Arnon Yaari. --- Python/ceval.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index d68cdc6292..2f3d3ad8b8 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3825,9 +3825,17 @@ unpack_iterable(PyObject *v, int argcnt, int argcntafter, PyObject **sp) if (w == NULL) { /* Iterator done, via error or exhaustion. */ if (!PyErr_Occurred()) { - PyErr_Format(PyExc_ValueError, - "need more than %d value%s to unpack", - i, i == 1 ? "" : "s"); + if (argcntafter == -1) { + PyErr_Format(PyExc_ValueError, + "not enough values to unpack (expected %d, got %d)", + argcnt, i); + } + else { + PyErr_Format(PyExc_ValueError, + "not enough values to unpack " + "(expected at least %d, got %d)", + argcnt + argcntafter, i); + } } goto Error; } @@ -3844,8 +3852,9 @@ unpack_iterable(PyObject *v, int argcnt, int argcntafter, PyObject **sp) return 1; } Py_DECREF(w); - PyErr_Format(PyExc_ValueError, "too many values to unpack " - "(expected %d)", argcnt); + PyErr_Format(PyExc_ValueError, + "too many values to unpack (expected %d)", + argcnt); goto Error; } @@ -3857,8 +3866,9 @@ unpack_iterable(PyObject *v, int argcnt, int argcntafter, PyObject **sp) ll = PyList_GET_SIZE(l); if (ll < argcntafter) { - PyErr_Format(PyExc_ValueError, "need more than %zd values to unpack", - argcnt + ll); + PyErr_Format(PyExc_ValueError, + "not enough values to unpack (expected at least %d, got %zd)", + argcnt + argcntafter, argcnt + ll); goto Error; } -- cgit v1.2.1 From 2571248c875b79a134b3a6fd4010c2a2e6e2c7aa Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 5 May 2015 20:16:41 -0400 Subject: PEP 448: additional unpacking generalizations (closes #2292) Patch by Neil Girdhar. --- Python/ceval.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 159 insertions(+), 5 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 2f3d3ad8b8..5ee9077332 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -12,8 +12,10 @@ #include "Python.h" #include "code.h" +#include "dictobject.h" #include "frameobject.h" #include "opcode.h" +#include "setobject.h" #include "structmember.h" #include @@ -2379,6 +2381,43 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET_WITH_IMPL(BUILD_TUPLE_UNPACK, _build_list_unpack) + TARGET(BUILD_LIST_UNPACK) + _build_list_unpack: { + int convert_to_tuple = opcode == BUILD_TUPLE_UNPACK; + int i; + PyObject *sum = PyList_New(0); + PyObject *return_value; + if (sum == NULL) + goto error; + + for (i = oparg; i > 0; i--) { + PyObject *none_val; + + none_val = _PyList_Extend((PyListObject *)sum, PEEK(i)); + if (none_val == NULL) { + Py_DECREF(sum); + goto error; + } + Py_DECREF(none_val); + } + + if (convert_to_tuple) { + return_value = PyList_AsTuple(sum); + Py_DECREF(sum); + if (return_value == NULL) + goto error; + } + else { + return_value = sum; + } + + while (oparg--) + Py_DECREF(POP()); + PUSH(return_value); + DISPATCH(); + } + TARGET(BUILD_SET) { PyObject *set = PySet_New(NULL); int err = 0; @@ -2398,14 +2437,127 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(BUILD_SET_UNPACK) { + int i; + PyObject *sum = PySet_New(NULL); + if (sum == NULL) + goto error; + + for (i = oparg; i > 0; i--) { + if (_PySet_Update(sum, PEEK(i)) < 0) { + Py_DECREF(sum); + goto error; + } + } + + while (oparg--) + Py_DECREF(POP()); + PUSH(sum); + DISPATCH(); + } + TARGET(BUILD_MAP) { PyObject *map = _PyDict_NewPresized((Py_ssize_t)oparg); if (map == NULL) goto error; + while (--oparg >= 0) { + int err; + PyObject *key = TOP(); + PyObject *value = SECOND(); + STACKADJ(-2); + err = PyDict_SetItem(map, key, value); + Py_DECREF(value); + Py_DECREF(key); + if (err != 0) { + Py_DECREF(map); + goto error; + } + } PUSH(map); DISPATCH(); } + TARGET_WITH_IMPL(BUILD_MAP_UNPACK_WITH_CALL, _build_map_unpack) + TARGET(BUILD_MAP_UNPACK) + _build_map_unpack: { + int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL; + int num_maps; + int function_location; + int i; + PyObject *sum = PyDict_New(); + if (sum == NULL) + goto error; + if (with_call) { + num_maps = oparg & 0xff; + function_location = (oparg>>8) & 0xff; + } + else { + num_maps = oparg; + } + + for (i = num_maps; i > 0; i--) { + PyObject *arg = PEEK(i); + if (with_call) { + PyObject *intersection = _PyDictView_Intersect(sum, arg); + + if (intersection == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyObject *func = ( + PEEK(function_location + num_maps)); + PyErr_Format(PyExc_TypeError, + "%.200s%.200s argument after ** " + "must be a mapping, not %.200s", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + arg->ob_type->tp_name); + } + Py_DECREF(sum); + goto error; + } + + if (PySet_GET_SIZE(intersection)) { + Py_ssize_t idx = 0; + PyObject *key; + PyObject *func = PEEK(function_location + num_maps); + Py_hash_t hash; + _PySet_NextEntry(intersection, &idx, &key, &hash); + if (!PyUnicode_Check(key)) { + PyErr_Format(PyExc_TypeError, + "%.200s%.200s keywords must be strings", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func)); + } else { + PyErr_Format(PyExc_TypeError, + "%.200s%.200s got multiple " + "values for keyword argument '%U'", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + key); + } + Py_DECREF(intersection); + Py_DECREF(sum); + goto error; + } + Py_DECREF(intersection); + } + + if (PyDict_Update(sum, arg) < 0) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Format(PyExc_TypeError, + "'%.200s' object is not a mapping", + arg->ob_type->tp_name); + } + Py_DECREF(sum); + goto error; + } + } + + while (num_maps--) + Py_DECREF(POP()); + PUSH(sum); + DISPATCH(); + } + TARGET(STORE_MAP) { PyObject *key = TOP(); PyObject *value = SECOND(); @@ -3050,6 +3202,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto dispatch_opcode; } + #if USE_COMPUTED_GOTOS _unknown_opcode: #endif @@ -4557,6 +4710,12 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) kwdict = d; } } + if (nk > 0) { + kwdict = update_keyword_args(kwdict, nk, pp_stack, func); + if (kwdict == NULL) + goto ext_call_fail; + } + if (flags & CALL_FLAG_VAR) { stararg = EXT_POP(*pp_stack); if (!PyTuple_Check(stararg)) { @@ -4578,11 +4737,6 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) } nstar = PyTuple_GET_SIZE(stararg); } - if (nk > 0) { - kwdict = update_keyword_args(kwdict, nk, pp_stack, func); - if (kwdict == NULL) - goto ext_call_fail; - } callargs = update_star_args(na, nstar, stararg, pp_stack); if (callargs == NULL) goto ext_call_fail; -- cgit v1.2.1 From ea639832c36905ecb07caba111f614c4bbf9bdd6 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Mon, 11 May 2015 22:57:16 -0400 Subject: PEP 0492 -- Coroutines with async and await syntax. Issue #24017. --- Python/ceval.c | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 223 insertions(+), 8 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 5ee9077332..8acd08201e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1926,11 +1926,133 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto fast_block_end; } + TARGET(GET_AITER) { + getaiterfunc getter = NULL; + PyObject *iter = NULL; + PyObject *awaitable = NULL; + PyObject *obj = TOP(); + PyTypeObject *type = Py_TYPE(obj); + + if (type->tp_as_async != NULL) + getter = type->tp_as_async->am_aiter; + + if (getter != NULL) { + iter = (*getter)(obj); + Py_DECREF(obj); + if (iter == NULL) { + SET_TOP(NULL); + goto error; + } + } + else { + SET_TOP(NULL); + PyErr_Format( + PyExc_TypeError, + "'async for' requires an object with " + "__aiter__ method, got %.100s", + type->tp_name); + Py_DECREF(obj); + goto error; + } + + awaitable = _PyGen_GetAwaitableIter(iter); + if (awaitable == NULL) { + SET_TOP(NULL); + PyErr_Format( + PyExc_TypeError, + "'async for' received an invalid object " + "from __aiter__: %.100s", + Py_TYPE(iter)->tp_name); + + Py_DECREF(iter); + goto error; + } else + Py_DECREF(iter); + + SET_TOP(awaitable); + DISPATCH(); + } + + TARGET(GET_ANEXT) { + aiternextfunc getter = NULL; + PyObject *next_iter = NULL; + PyObject *awaitable = NULL; + PyObject *aiter = TOP(); + PyTypeObject *type = Py_TYPE(aiter); + + if (type->tp_as_async != NULL) + getter = type->tp_as_async->am_anext; + + if (getter != NULL) { + next_iter = (*getter)(aiter); + if (next_iter == NULL) { + goto error; + } + } + else { + PyErr_Format( + PyExc_TypeError, + "'async for' requires an iterator with " + "__anext__ method, got %.100s", + type->tp_name); + goto error; + } + + awaitable = _PyGen_GetAwaitableIter(next_iter); + if (awaitable == NULL) { + PyErr_Format( + PyExc_TypeError, + "'async for' received an invalid object " + "from __anext__: %.100s", + Py_TYPE(next_iter)->tp_name); + + Py_DECREF(next_iter); + goto error; + } else + Py_DECREF(next_iter); + + PUSH(awaitable); + DISPATCH(); + } + + TARGET(GET_AWAITABLE) { + PyObject *iterable = TOP(); + PyObject *iter = _PyGen_GetAwaitableIter(iterable); + + Py_DECREF(iterable); + + SET_TOP(iter); /* Even if it's NULL */ + + if (iter == NULL) { + goto error; + } + + DISPATCH(); + } + TARGET(YIELD_FROM) { PyObject *v = POP(); PyObject *reciever = TOP(); int err; if (PyGen_CheckExact(reciever)) { + if ( + (((PyCodeObject*) \ + ((PyGenObject*)reciever)->gi_code)->co_flags & + CO_COROUTINE) + && !(co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) + { + /* If we're yielding-from a coroutine object from a regular + generator object - raise an error. */ + + Py_CLEAR(v); + Py_CLEAR(reciever); + SET_TOP(NULL); + + PyErr_SetString(PyExc_TypeError, + "cannot 'yield from' a coroutine object " + "from a generator"); + goto error; + } retval = _PyGen_Send((PyGenObject *)reciever, v); } else { _Py_IDENTIFIER(send); @@ -2822,11 +2944,26 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(GET_ITER) { /* before: [obj]; after [getiter(obj)] */ PyObject *iterable = TOP(); - PyObject *iter = PyObject_GetIter(iterable); - Py_DECREF(iterable); - SET_TOP(iter); - if (iter == NULL) - goto error; + PyObject *iter; + /* If we have a generator object on top -- keep it there, + it's already an iterator. + + This is needed to allow use of 'async def' coroutines + in 'yield from' expression from generator-based coroutines + (decorated with types.coroutine()). + + 'yield from' is compiled to GET_ITER..YIELD_FROM combination, + but since coroutines raise TypeError in their 'tp_iter' we + need a way for them to "pass through" the GET_ITER. + */ + if (!PyGen_CheckExact(iterable)) { + /* `iterable` is not a generator. */ + iter = PyObject_GetIter(iterable); + Py_DECREF(iterable); + SET_TOP(iter); + if (iter == NULL) + goto error; + } PREDICT(FOR_ITER); DISPATCH(); } @@ -2883,6 +3020,39 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(BEFORE_ASYNC_WITH) { + _Py_IDENTIFIER(__aexit__); + _Py_IDENTIFIER(__aenter__); + + PyObject *mgr = TOP(); + PyObject *exit = special_lookup(mgr, &PyId___aexit__), + *enter; + PyObject *res; + if (exit == NULL) + goto error; + SET_TOP(exit); + enter = special_lookup(mgr, &PyId___aenter__); + Py_DECREF(mgr); + if (enter == NULL) + goto error; + res = PyObject_CallFunctionObjArgs(enter, NULL); + Py_DECREF(enter); + if (res == NULL) + goto error; + PUSH(res); + DISPATCH(); + } + + TARGET(SETUP_ASYNC_WITH) { + PyObject *res = POP(); + /* Setup the finally block before pushing the result + of __aenter__ on the stack. */ + PyFrame_BlockSetup(f, SETUP_FINALLY, INSTR_OFFSET() + oparg, + STACK_LEVEL()); + PUSH(res); + DISPATCH(); + } + TARGET(SETUP_WITH) { _Py_IDENTIFIER(__exit__); _Py_IDENTIFIER(__enter__); @@ -2909,7 +3079,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } - TARGET(WITH_CLEANUP) { + TARGET(WITH_CLEANUP_START) { /* At the top of the stack are 1-6 values indicating how/why we entered the finally clause: - TOP = None @@ -2937,7 +3107,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) PyObject *exit_func; PyObject *exc = TOP(), *val = Py_None, *tb = Py_None, *res; - int err; if (exc == Py_None) { (void)POP(); exit_func = TOP(); @@ -2987,10 +3156,23 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (res == NULL) goto error; + PUSH(exc); + PUSH(res); + PREDICT(WITH_CLEANUP_FINISH); + DISPATCH(); + } + + PREDICTED(WITH_CLEANUP_FINISH); + TARGET(WITH_CLEANUP_FINISH) { + PyObject *res = POP(); + PyObject *exc = POP(); + int err; + if (exc != Py_None) err = PyObject_IsTrue(res); else err = 0; + Py_DECREF(res); if (err < 0) @@ -3751,6 +3933,9 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, } if (co->co_flags & CO_GENERATOR) { + PyObject *gen; + PyObject *coroutine_wrapper; + /* Don't need to keep the reference to f_back, it will be set * when the generator is resumed. */ Py_CLEAR(f->f_back); @@ -3759,7 +3944,19 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, /* Create a new generator that owns the ready to run frame * and return that as the value. */ - return PyGen_NewWithQualName(f, name, qualname); + gen = PyGen_NewWithQualName(f, name, qualname); + if (gen == NULL) + return NULL; + + if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) { + coroutine_wrapper = PyEval_GetCoroutineWrapper(); + if (coroutine_wrapper != NULL) { + PyObject *wrapped = + PyObject_CallFunction(coroutine_wrapper, "N", gen); + gen = wrapped; + } + } + return gen; } retval = PyEval_EvalFrameEx(f,0); @@ -4205,6 +4402,24 @@ PyEval_SetTrace(Py_tracefunc func, PyObject *arg) || (tstate->c_profilefunc != NULL)); } +void +PyEval_SetCoroutineWrapper(PyObject *wrapper) +{ + PyThreadState *tstate = PyThreadState_GET(); + + Py_CLEAR(tstate->coroutine_wrapper); + + Py_XINCREF(wrapper); + tstate->coroutine_wrapper = wrapper; +} + +PyObject * +PyEval_GetCoroutineWrapper() +{ + PyThreadState *tstate = PyThreadState_GET(); + return tstate->coroutine_wrapper; +} + PyObject * PyEval_GetBuiltins(void) { -- cgit v1.2.1 From eae221194a86e192c96afab97b373220997a4db8 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Mon, 11 May 2015 23:19:34 -0400 Subject: Fix warnings for PyEval_GetCoroutineWrapper --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 8acd08201e..77085c25a0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4414,7 +4414,7 @@ PyEval_SetCoroutineWrapper(PyObject *wrapper) } PyObject * -PyEval_GetCoroutineWrapper() +PyEval_GetCoroutineWrapper(void) { PyThreadState *tstate = PyThreadState_GET(); return tstate->coroutine_wrapper; -- cgit v1.2.1 From ba830e670e1b0ab5ff88eb735312220e399d400c Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Wed, 13 May 2015 15:54:02 +1000 Subject: Issue 24017: fix for "async with" refcounting * adds missing INCREF in WITH_CLEANUP_START * adds missing DECREF in WITH_CLEANUP_FINISH * adds several new tests Yury created while investigating this --- Python/ceval.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 77085c25a0..afb0f89aa8 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3156,6 +3156,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (res == NULL) goto error; + Py_INCREF(exc); /* Duplicating the exception on the stack */ PUSH(exc); PUSH(res); PREDICT(WITH_CLEANUP_FINISH); @@ -3174,6 +3175,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) err = 0; Py_DECREF(res); + Py_DECREF(exc); if (err < 0) goto error; -- cgit v1.2.1 From eef24c3a5da25a828bc79c777e85ae604167a977 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 28 May 2015 11:21:31 -0400 Subject: Issue 24017: Drop getawaitablefunc and friends in favor of unaryfunc. --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index afb0f89aa8..7d39d9bfea 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1927,7 +1927,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } TARGET(GET_AITER) { - getaiterfunc getter = NULL; + unaryfunc getter = NULL; PyObject *iter = NULL; PyObject *awaitable = NULL; PyObject *obj = TOP(); @@ -1974,7 +1974,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } TARGET(GET_ANEXT) { - aiternextfunc getter = NULL; + unaryfunc getter = NULL; PyObject *next_iter = NULL; PyObject *awaitable = NULL; PyObject *aiter = TOP(); -- cgit v1.2.1 From ffe5c8f2c45e238c70ac87fecf23aeef676a65f6 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 28 May 2015 14:30:26 -0500 Subject: in dict displays, evaluate the key before the value (closes #11205) Patch partially by Steve Dougherty. --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 7d39d9bfea..eb36975012 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2584,8 +2584,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto error; while (--oparg >= 0) { int err; - PyObject *key = TOP(); - PyObject *value = SECOND(); + PyObject *value = TOP(); + PyObject *key = SECOND(); STACKADJ(-2); err = PyDict_SetItem(map, key, value); Py_DECREF(value); -- cgit v1.2.1 From 2a8855656d526cce898303bb03c66274f06e7b51 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 28 May 2015 14:40:08 -0500 Subject: remove STORE_MAP, since it's unused --- Python/ceval.c | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index eb36975012..06772b1bec 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2680,21 +2680,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } - TARGET(STORE_MAP) { - PyObject *key = TOP(); - PyObject *value = SECOND(); - PyObject *map = THIRD(); - int err; - STACKADJ(-2); - assert(PyDict_CheckExact(map)); - err = PyDict_SetItem(map, key, value); - Py_DECREF(value); - Py_DECREF(key); - if (err != 0) - goto error; - DISPATCH(); - } - TARGET(MAP_ADD) { PyObject *key = TOP(); PyObject *value = SECOND(); -- cgit v1.2.1 From dab9b839d0e4eb315c219433c19c999729a39a95 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Mon, 1 Jun 2015 12:15:23 -0400 Subject: Issue 24017: Make PyEval_(Set|Get)CoroutineWrapper private --- Python/ceval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 06772b1bec..bb2c0b96a5 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3936,7 +3936,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, return NULL; if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) { - coroutine_wrapper = PyEval_GetCoroutineWrapper(); + coroutine_wrapper = _PyEval_GetCoroutineWrapper(); if (coroutine_wrapper != NULL) { PyObject *wrapped = PyObject_CallFunction(coroutine_wrapper, "N", gen); @@ -4390,7 +4390,7 @@ PyEval_SetTrace(Py_tracefunc func, PyObject *arg) } void -PyEval_SetCoroutineWrapper(PyObject *wrapper) +_PyEval_SetCoroutineWrapper(PyObject *wrapper) { PyThreadState *tstate = PyThreadState_GET(); @@ -4401,7 +4401,7 @@ PyEval_SetCoroutineWrapper(PyObject *wrapper) } PyObject * -PyEval_GetCoroutineWrapper(void) +_PyEval_GetCoroutineWrapper(void) { PyThreadState *tstate = PyThreadState_GET(); return tstate->coroutine_wrapper; -- cgit v1.2.1 From f28b7448cd4c7d325fee3c4bf4584f655eafccdb Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Tue, 2 Jun 2015 18:43:51 -0400 Subject: Issue 24342: Let wrapper set by sys.set_coroutine_wrapper fail gracefully --- Python/ceval.c | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index bb2c0b96a5..2a1db17b45 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3921,7 +3921,6 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, if (co->co_flags & CO_GENERATOR) { PyObject *gen; - PyObject *coroutine_wrapper; /* Don't need to keep the reference to f_back, it will be set * when the generator is resumed. */ @@ -3935,14 +3934,9 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, if (gen == NULL) return NULL; - if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) { - coroutine_wrapper = _PyEval_GetCoroutineWrapper(); - if (coroutine_wrapper != NULL) { - PyObject *wrapped = - PyObject_CallFunction(coroutine_wrapper, "N", gen); - gen = wrapped; - } - } + if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) + return _PyEval_ApplyCoroutineWrapper(gen); + return gen; } @@ -4407,6 +4401,33 @@ _PyEval_GetCoroutineWrapper(void) return tstate->coroutine_wrapper; } +PyObject * +_PyEval_ApplyCoroutineWrapper(PyObject *gen) +{ + PyObject *wrapped; + PyThreadState *tstate = PyThreadState_GET(); + PyObject *wrapper = tstate->coroutine_wrapper; + + if (tstate->in_coroutine_wrapper) { + assert(wrapper != NULL); + PyErr_Format(PyExc_RuntimeError, + "coroutine wrapper %.150R attempted " + "to recursively wrap %.150R", + wrapper, + gen); + return NULL; + } + + if (wrapper == NULL) { + return gen; + } + + tstate->in_coroutine_wrapper = 1; + wrapped = PyObject_CallFunction(wrapper, "N", gen); + tstate->in_coroutine_wrapper = 0; + return wrapped; +} + PyObject * PyEval_GetBuiltins(void) { -- cgit v1.2.1 From 95f436fe00004830fd5551208337c8945d8bd1ba Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Tue, 2 Jun 2015 22:30:31 -0400 Subject: Issue 24342: No need to use PyAPI_FUNC for _PyEval_ApplyCoroutineWrapper --- Python/ceval.c | 58 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 2a1db17b45..96ed6ed4ee 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -146,6 +146,8 @@ static void format_exc_unbound(PyCodeObject *co, int oparg); static PyObject * unicode_concatenate(PyObject *, PyObject *, PyFrameObject *, unsigned char *); static PyObject * special_lookup(PyObject *, _Py_Identifier *); +static PyObject * apply_coroutine_wrapper(PyObject *); + #define NAME_ERROR_MSG \ "name '%.200s' is not defined" @@ -3935,7 +3937,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, return NULL; if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) - return _PyEval_ApplyCoroutineWrapper(gen); + return apply_coroutine_wrapper(gen); return gen; } @@ -4401,33 +4403,6 @@ _PyEval_GetCoroutineWrapper(void) return tstate->coroutine_wrapper; } -PyObject * -_PyEval_ApplyCoroutineWrapper(PyObject *gen) -{ - PyObject *wrapped; - PyThreadState *tstate = PyThreadState_GET(); - PyObject *wrapper = tstate->coroutine_wrapper; - - if (tstate->in_coroutine_wrapper) { - assert(wrapper != NULL); - PyErr_Format(PyExc_RuntimeError, - "coroutine wrapper %.150R attempted " - "to recursively wrap %.150R", - wrapper, - gen); - return NULL; - } - - if (wrapper == NULL) { - return gen; - } - - tstate->in_coroutine_wrapper = 1; - wrapped = PyObject_CallFunction(wrapper, "N", gen); - tstate->in_coroutine_wrapper = 0; - return wrapped; -} - PyObject * PyEval_GetBuiltins(void) { @@ -5257,6 +5232,33 @@ unicode_concatenate(PyObject *v, PyObject *w, return res; } +static PyObject * +apply_coroutine_wrapper(PyObject *gen) +{ + PyObject *wrapped; + PyThreadState *tstate = PyThreadState_GET(); + PyObject *wrapper = tstate->coroutine_wrapper; + + if (tstate->in_coroutine_wrapper) { + assert(wrapper != NULL); + PyErr_Format(PyExc_RuntimeError, + "coroutine wrapper %.200R attempted " + "to recursively wrap %.200R", + wrapper, + gen); + return NULL; + } + + if (wrapper == NULL) { + return gen; + } + + tstate->in_coroutine_wrapper = 1; + wrapped = PyObject_CallFunction(wrapper, "N", gen); + tstate->in_coroutine_wrapper = 0; + return wrapped; +} + #ifdef DYNAMIC_EXECUTION_PROFILE static PyObject * -- cgit v1.2.1 From 2848485266c7a82fd88138ca05b11e61015b2827 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 4 Jun 2015 10:16:51 -0400 Subject: Issue 24374: Plug refleak in set_coroutine_wrapper --- Python/ceval.c | 50 +++++++++++++++++++------------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 96ed6ed4ee..641f9db317 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -146,8 +146,6 @@ static void format_exc_unbound(PyCodeObject *co, int oparg); static PyObject * unicode_concatenate(PyObject *, PyObject *, PyFrameObject *, unsigned char *); static PyObject * special_lookup(PyObject *, _Py_Identifier *); -static PyObject * apply_coroutine_wrapper(PyObject *); - #define NAME_ERROR_MSG \ "name '%.200s' is not defined" @@ -3923,6 +3921,18 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, if (co->co_flags & CO_GENERATOR) { PyObject *gen; + PyObject *coro_wrapper = tstate->coroutine_wrapper; + int is_coro = co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE); + + if (is_coro && tstate->in_coroutine_wrapper) { + assert(coro_wrapper != NULL); + PyErr_Format(PyExc_RuntimeError, + "coroutine wrapper %.200R attempted " + "to recursively wrap %.200R", + coro_wrapper, + co); + goto fail; + } /* Don't need to keep the reference to f_back, it will be set * when the generator is resumed. */ @@ -3936,8 +3946,13 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, if (gen == NULL) return NULL; - if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) - return apply_coroutine_wrapper(gen); + if (is_coro && coro_wrapper != NULL) { + PyObject *wrapped; + tstate->in_coroutine_wrapper = 1; + wrapped = PyObject_CallFunction(coro_wrapper, "N", gen); + tstate->in_coroutine_wrapper = 0; + return wrapped; + } return gen; } @@ -5232,33 +5247,6 @@ unicode_concatenate(PyObject *v, PyObject *w, return res; } -static PyObject * -apply_coroutine_wrapper(PyObject *gen) -{ - PyObject *wrapped; - PyThreadState *tstate = PyThreadState_GET(); - PyObject *wrapper = tstate->coroutine_wrapper; - - if (tstate->in_coroutine_wrapper) { - assert(wrapper != NULL); - PyErr_Format(PyExc_RuntimeError, - "coroutine wrapper %.200R attempted " - "to recursively wrap %.200R", - wrapper, - gen); - return NULL; - } - - if (wrapper == NULL) { - return gen; - } - - tstate->in_coroutine_wrapper = 1; - wrapped = PyObject_CallFunction(wrapper, "N", gen); - tstate->in_coroutine_wrapper = 0; - return wrapped; -} - #ifdef DYNAMIC_EXECUTION_PROFILE static PyObject * -- cgit v1.2.1 From 15be527fca539925b6ba49e0d0089f13f1e210ee Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Mon, 22 Jun 2015 12:19:30 -0400 Subject: Issue #24400: Introduce a distinct type for 'async def' coroutines. Summary of changes: 1. Coroutines now have a distinct, separate from generators type at the C level: PyGen_Type, and a new typedef PyCoroObject. PyCoroObject shares the initial segment of struct layout with PyGenObject, making it possible to reuse existing generators machinery. The new type is exposed as 'types.CoroutineType'. As a consequence of having a new type, CO_GENERATOR flag is no longer applied to coroutines. 2. Having a separate type for coroutines made it possible to add an __await__ method to the type. Although it is not used by the interpreter (see details on that below), it makes coroutines naturally (without using __instancecheck__) conform to collections.abc.Coroutine and collections.abc.Awaitable ABCs. [The __instancecheck__ is still used for generator-based coroutines, as we don't want to add __await__ for generators.] 3. Add new opcode: GET_YIELD_FROM_ITER. The opcode is needed to allow passing native coroutines to the YIELD_FROM opcode. Before this change, 'yield from o' expression was compiled to: (o) GET_ITER LOAD_CONST YIELD_FROM Now, we use GET_YIELD_FROM_ITER instead of GET_ITER. The reason for adding a new opcode is that GET_ITER is used in some contexts (such as 'for .. in' loops) where passing a coroutine object is invalid. 4. Add two new introspection functions to the inspec module: getcoroutinestate(c) and getcoroutinelocals(c). 5. inspect.iscoroutine(o) is updated to test if 'o' is a native coroutine object. Before this commit it used abc.Coroutine, and it was requested to update inspect.isgenerator(o) to use abc.Generator; it was decided, however, that inspect functions should really be tailored for checking for native types. 6. sys.set_coroutine_wrapper(w) API is updated to work with only native coroutines. Since types.coroutine decorator supports any type of callables now, it would be confusing that it does not work for all types of coroutines. 7. Exceptions logic in generators C implementation was updated to raise clearer messages for coroutines: Before: TypeError("generator raised StopIteration") After: TypeError("coroutine raised StopIteration") --- Python/ceval.c | 79 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 40 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index e127a731fd..12df0fe3fc 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1191,7 +1191,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ f->f_executing = 1; - if (co->co_flags & CO_GENERATOR) { + if (co->co_flags & (CO_GENERATOR | CO_COROUTINE)) { if (!throwflag && f->f_exc_type != NULL && f->f_exc_type != Py_None) { /* We were in an except handler when we left, restore the exception state which was put aside @@ -1955,7 +1955,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto error; } - awaitable = _PyGen_GetAwaitableIter(iter); + awaitable = _PyCoro_GetAwaitableIter(iter); if (awaitable == NULL) { SET_TOP(NULL); PyErr_Format( @@ -1998,7 +1998,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto error; } - awaitable = _PyGen_GetAwaitableIter(next_iter); + awaitable = _PyCoro_GetAwaitableIter(next_iter); if (awaitable == NULL) { PyErr_Format( PyExc_TypeError, @@ -2017,7 +2017,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(GET_AWAITABLE) { PyObject *iterable = TOP(); - PyObject *iter = _PyGen_GetAwaitableIter(iterable); + PyObject *iter = _PyCoro_GetAwaitableIter(iterable); Py_DECREF(iterable); @@ -2034,25 +2034,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) PyObject *v = POP(); PyObject *reciever = TOP(); int err; - if (PyGen_CheckExact(reciever)) { - if ( - (((PyCodeObject*) \ - ((PyGenObject*)reciever)->gi_code)->co_flags & - CO_COROUTINE) - && !(co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) - { - /* If we're yielding-from a coroutine object from a regular - generator object - raise an error. */ - - Py_CLEAR(v); - Py_CLEAR(reciever); - SET_TOP(NULL); - - PyErr_SetString(PyExc_TypeError, - "cannot 'yield from' a coroutine object " - "from a generator"); - goto error; - } + if (PyGen_CheckExact(reciever) || PyCoro_CheckExact(reciever)) { retval = _PyGen_Send((PyGenObject *)reciever, v); } else { _Py_IDENTIFIER(send); @@ -2929,19 +2911,33 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(GET_ITER) { /* before: [obj]; after [getiter(obj)] */ PyObject *iterable = TOP(); - PyObject *iter; - /* If we have a generator object on top -- keep it there, - it's already an iterator. - - This is needed to allow use of 'async def' coroutines - in 'yield from' expression from generator-based coroutines - (decorated with types.coroutine()). + PyObject *iter = PyObject_GetIter(iterable); + Py_DECREF(iterable); + SET_TOP(iter); + if (iter == NULL) + goto error; + PREDICT(FOR_ITER); + DISPATCH(); + } - 'yield from' is compiled to GET_ITER..YIELD_FROM combination, - but since coroutines raise TypeError in their 'tp_iter' we - need a way for them to "pass through" the GET_ITER. - */ - if (!PyGen_CheckExact(iterable)) { + TARGET(GET_YIELD_FROM_ITER) { + /* before: [obj]; after [getiter(obj)] */ + PyObject *iterable = TOP(); + PyObject *iter; + if (PyCoro_CheckExact(iterable)) { + /* `iterable` is a coroutine */ + if (!(co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) { + /* and it is used in a 'yield from' expression of a + regular generator. */ + Py_DECREF(iterable); + SET_TOP(NULL); + PyErr_SetString(PyExc_TypeError, + "cannot 'yield from' a coroutine object " + "in a non-coroutine generator"); + goto error; + } + } + else if (!PyGen_CheckExact(iterable)) { /* `iterable` is not a generator. */ iter = PyObject_GetIter(iterable); Py_DECREF(iterable); @@ -2949,7 +2945,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (iter == NULL) goto error; } - PREDICT(FOR_ITER); DISPATCH(); } @@ -3517,7 +3512,7 @@ fast_block_end: assert((retval != NULL) ^ (PyErr_Occurred() != NULL)); fast_yield: - if (co->co_flags & CO_GENERATOR) { + if (co->co_flags & (CO_GENERATOR | CO_COROUTINE)) { /* The purpose of this block is to put aside the generator's exception state and restore that of the calling frame. If the current @@ -3919,10 +3914,10 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o; } - if (co->co_flags & CO_GENERATOR) { + if (co->co_flags & (CO_GENERATOR | CO_COROUTINE)) { PyObject *gen; PyObject *coro_wrapper = tstate->coroutine_wrapper; - int is_coro = co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE); + int is_coro = co->co_flags & CO_COROUTINE; if (is_coro && tstate->in_coroutine_wrapper) { assert(coro_wrapper != NULL); @@ -3942,7 +3937,11 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, /* Create a new generator that owns the ready to run frame * and return that as the value. */ - gen = PyGen_NewWithQualName(f, name, qualname); + if (is_coro) { + gen = PyCoro_New(f, name, qualname); + } else { + gen = PyGen_NewWithQualName(f, name, qualname); + } if (gen == NULL) return NULL; -- cgit v1.2.1 From 1f0eea05f78b032be72ee5b5787421acef61588d Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Fri, 3 Jul 2015 01:04:23 -0400 Subject: Issue #19235: Add new RecursionError exception. Patch by Georg Brandl. --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 12df0fe3fc..e68ae33bfd 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -737,7 +737,7 @@ _Py_CheckRecursiveCall(const char *where) if (tstate->recursion_depth > recursion_limit) { --tstate->recursion_depth; tstate->overflowed = 1; - PyErr_Format(PyExc_RuntimeError, + PyErr_Format(PyExc_RecursionError, "maximum recursion depth exceeded%s", where); return -1; -- cgit v1.2.1 From f0604e5a21390a16c9cc1ee32cbbad313599345c Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 5 Jul 2015 10:37:25 -0500 Subject: set items in dict displays from left to right (closes #24569) --- Python/ceval.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index e68ae33bfd..ac52ad91e2 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2561,22 +2561,25 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } TARGET(BUILD_MAP) { + int i; PyObject *map = _PyDict_NewPresized((Py_ssize_t)oparg); if (map == NULL) goto error; - while (--oparg >= 0) { + for (i = oparg; i > 0; i--) { int err; - PyObject *value = TOP(); - PyObject *key = SECOND(); - STACKADJ(-2); + PyObject *key = PEEK(2*i); + PyObject *value = PEEK(2*i - 1); err = PyDict_SetItem(map, key, value); - Py_DECREF(value); - Py_DECREF(key); if (err != 0) { Py_DECREF(map); goto error; } } + + while (oparg--) { + Py_DECREF(POP()); + Py_DECREF(POP()); + } PUSH(map); DISPATCH(); } -- cgit v1.2.1 From f6c8d9988e5a4deaa45f6b7e6436674f0d6013e4 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Tue, 11 Aug 2015 18:01:31 -0700 Subject: Issue #24492: make sure that ``from ... import ...` raises an ImportError if __name__ is not defined on a package. Thanks to Armin Rigo for the bug report and diagnosing the cause. --- Python/ceval.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index ac52ad91e2..8d2cdc2eda 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5085,19 +5085,24 @@ import_from(PyObject *v, PyObject *name) sys.modules. */ PyErr_Clear(); pkgname = _PyObject_GetAttrId(v, &PyId___name__); - if (pkgname == NULL) - return NULL; + if (pkgname == NULL) { + goto error; + } fullmodname = PyUnicode_FromFormat("%U.%U", pkgname, name); Py_DECREF(pkgname); - if (fullmodname == NULL) + if (fullmodname == NULL) { return NULL; + } x = PyDict_GetItem(PyImport_GetModuleDict(), fullmodname); - if (x == NULL) - PyErr_Format(PyExc_ImportError, "cannot import name %R", name); - else - Py_INCREF(x); Py_DECREF(fullmodname); + if (x == NULL) { + goto error; + } + Py_INCREF(x); return x; + error: + PyErr_Format(PyExc_ImportError, "cannot import name %R", name); + return NULL; } static int -- cgit v1.2.1 From 008499d3f1761db65815faebb31bf95ab7ef9cd4 Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Tue, 3 Nov 2015 12:45:05 -0500 Subject: Issue 25483: Add an opcode to make f-string formatting more robust. --- Python/ceval.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index f9e8ef8859..60db703212 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3363,6 +3363,63 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(FORMAT_VALUE) { + /* Handles f-string value formatting. */ + PyObject *result; + PyObject *fmt_spec; + PyObject *value; + PyObject *(*conv_fn)(PyObject *); + int which_conversion = oparg & FVC_MASK; + int have_fmt_spec = (oparg & FVS_MASK) == FVS_HAVE_SPEC; + + fmt_spec = have_fmt_spec ? POP() : NULL; + value = TOP(); + + /* See if any conversion is specified. */ + switch (which_conversion) { + case FVC_STR: conv_fn = PyObject_Str; break; + case FVC_REPR: conv_fn = PyObject_Repr; break; + case FVC_ASCII: conv_fn = PyObject_ASCII; break; + + /* Must be 0 (meaning no conversion), since only four + values are allowed by (oparg & FVC_MASK). */ + default: conv_fn = NULL; break; + } + + /* If there's a conversion function, call it and replace + value with that result. Otherwise, just use value, + without conversion. */ + if (conv_fn) { + result = conv_fn(value); + Py_DECREF(value); + if (!result) { + Py_XDECREF(fmt_spec); + goto error; + } + value = result; + } + + /* If value is a unicode object, and there's no fmt_spec, + then we know the result of format(value) is value + itself. In that case, skip calling format(). I plan to + move this optimization in to PyObject_Format() + itself. */ + if (PyUnicode_CheckExact(value) && fmt_spec == NULL) { + /* Do nothing, just transfer ownership to result. */ + result = value; + } else { + /* Actually call format(). */ + result = PyObject_Format(value, fmt_spec); + Py_DECREF(value); + Py_XDECREF(fmt_spec); + if (!result) + goto error; + } + + SET_TOP(result); + DISPATCH(); + } + TARGET(EXTENDED_ARG) { opcode = NEXTOP(); oparg = oparg<<16 | NEXTARG(); -- cgit v1.2.1 From 8bd2d0b8bd744a976d163daee2bd7fdb5f8dd184 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 5 Nov 2015 13:55:20 +0100 Subject: Issue #25556: Fix LOAD_GLOBAL bytecode when globals type is not dict and the requested name doesn't exist in globals: clear the KeyError exception before calling PyObject_GetItem(). Fail also if the raised exception is not a KeyError. --- Python/ceval.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index f9e8ef8859..beabfebc1c 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2363,6 +2363,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) /* Slow-path if globals or builtins is not a dict */ v = PyObject_GetItem(f->f_globals, name); if (v == NULL) { + if (!PyErr_ExceptionMatches(PyExc_KeyError)) + goto error; + PyErr_Clear(); + v = PyObject_GetItem(f->f_builtins, name); if (v == NULL) { if (PyErr_ExceptionMatches(PyExc_KeyError)) -- cgit v1.2.1 From f9e61eb42ff3724992971455a230f6cd4fc2f987 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 5 Nov 2015 13:56:58 +0100 Subject: Issue #25556: Add assertions to PyObject_GetItem() to ensure that an exception is raised when it returns NULL. Simplify also ceval.c: rely on the fact that PyObject_GetItem() raised an exception when it returns NULL. --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 67ea388d85..7f9ef6fc5b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2307,7 +2307,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } else { v = PyObject_GetItem(locals, name); - if (v == NULL && _PyErr_OCCURRED()) { + if (v == NULL) { if (!PyErr_ExceptionMatches(PyExc_KeyError)) goto error; PyErr_Clear(); @@ -2426,7 +2426,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } else { value = PyObject_GetItem(locals, name); - if (value == NULL && PyErr_Occurred()) { + if (value == NULL) { if (!PyErr_ExceptionMatches(PyExc_KeyError)) goto error; PyErr_Clear(); -- cgit v1.2.1 From e3999b6277403eccdefe1d11ef1391eec7f9701e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 20 Nov 2015 09:24:02 +0100 Subject: Issue #25557: Refactor _PyDict_LoadGlobal() Don't fallback to PyDict_GetItemWithError() if the hash is unknown: compute the hash instead. Add also comments to explain the optimization a little bit. --- Python/ceval.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 7f9ef6fc5b..1d656495f5 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2347,26 +2347,33 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) PyObject *name = GETITEM(names, oparg); PyObject *v; if (PyDict_CheckExact(f->f_globals) - && PyDict_CheckExact(f->f_builtins)) { + && PyDict_CheckExact(f->f_builtins)) + { v = _PyDict_LoadGlobal((PyDictObject *)f->f_globals, (PyDictObject *)f->f_builtins, name); if (v == NULL) { - if (!_PyErr_OCCURRED()) + if (!_PyErr_OCCURRED()) { + /* _PyDict_LoadGlobal() returns NULL without raising + * an exception if the key doesn't exist */ format_exc_check_arg(PyExc_NameError, NAME_ERROR_MSG, name); + } goto error; } Py_INCREF(v); } else { /* Slow-path if globals or builtins is not a dict */ + + /* namespace 1: globals */ v = PyObject_GetItem(f->f_globals, name); if (v == NULL) { if (!PyErr_ExceptionMatches(PyExc_KeyError)) goto error; PyErr_Clear(); + /* namespace 2: builtins */ v = PyObject_GetItem(f->f_builtins, name); if (v == NULL) { if (PyErr_ExceptionMatches(PyExc_KeyError)) -- cgit v1.2.1 From 585a1eed45faea53f836b1db2ac211d7f14b44c8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 24 Dec 2015 10:35:59 +0200 Subject: Issue #20440: Massive replacing unsafe attribute setting code with special macro Py_SETREF. --- Python/ceval.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index beabfebc1c..5fa555ed43 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3214,8 +3214,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) Py_INCREF(self); func = PyMethod_GET_FUNCTION(func); Py_INCREF(func); - Py_DECREF(*pfunc); - *pfunc = self; + Py_SETREF(*pfunc, self); na++; /* n++; */ } else @@ -4670,8 +4669,7 @@ call_function(PyObject ***pp_stack, int oparg Py_INCREF(self); func = PyMethod_GET_FUNCTION(func); Py_INCREF(func); - Py_DECREF(*pfunc); - *pfunc = self; + Py_SETREF(*pfunc, self); na++; n++; } else -- cgit v1.2.1 From 5c633fc25fc6f9beee3ccb9ebc06633d1c9a1bc3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 25 Dec 2015 20:01:53 +0200 Subject: Issue #25923: Added more const qualifiers to signatures of static and private functions. --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index dd9360c34f..6f74e86c3b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -125,7 +125,7 @@ static PyObject * load_args(PyObject ***, int); #ifdef LLTRACE static int lltrace; -static int prtrace(PyObject *, char *); +static int prtrace(PyObject *, const char *); #endif static int call_trace(Py_tracefunc, PyObject *, PyThreadState *, PyFrameObject *, @@ -4308,7 +4308,7 @@ Error: #ifdef LLTRACE static int -prtrace(PyObject *v, char *str) +prtrace(PyObject *v, const char *str) { printf("%s ", str); if (PyObject_Print(v, stdout, 0) != 0) -- cgit v1.2.1 From 49b4fe42c04e1eec009aac77da7f44d985b1abbb Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 27 Dec 2015 12:36:18 +0200 Subject: Issue #20440: Applied yet one patch for using Py_SETREF. The patch is automatically generated, it replaces the code that uses Py_CLEAR. --- Python/ceval.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 5fa555ed43..786adbf7e1 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4410,10 +4410,8 @@ _PyEval_SetCoroutineWrapper(PyObject *wrapper) { PyThreadState *tstate = PyThreadState_GET(); - Py_CLEAR(tstate->coroutine_wrapper); - Py_XINCREF(wrapper); - tstate->coroutine_wrapper = wrapper; + Py_SETREF(tstate->coroutine_wrapper, wrapper); } PyObject * -- cgit v1.2.1 From 70b5333c6e61084ae63938c49748d084d4ac8f76 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Sun, 31 Jan 2016 06:30:56 +0000 Subject: Issue #4806: Avoid masking original TypeError in call with * unpacking Based on patch by Hagen F?rstenau and Daniel Urban. --- Python/ceval.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 786adbf7e1..aee0e6bc85 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4935,16 +4935,18 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) stararg = EXT_POP(*pp_stack); if (!PyTuple_Check(stararg)) { PyObject *t = NULL; + if (Py_TYPE(stararg)->tp_iter == NULL && + !PySequence_Check(stararg)) { + PyErr_Format(PyExc_TypeError, + "%.200s%.200s argument after * " + "must be an iterable, not %.200s", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + stararg->ob_type->tp_name); + goto ext_call_fail; + } t = PySequence_Tuple(stararg); if (t == NULL) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Format(PyExc_TypeError, - "%.200s%.200s argument after * " - "must be a sequence, not %.200s", - PyEval_GetFuncName(func), - PyEval_GetFuncDesc(func), - stararg->ob_type->tp_name); - } goto ext_call_fail; } Py_DECREF(stararg); -- cgit v1.2.1 From 265edce9784c4e47eb1c5533dd958185e124dc49 Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Fri, 5 Feb 2016 18:23:08 -0500 Subject: Fix issue 26287: While handling FORMAT_VALUE opcode, the top of stack was being corrupted if an error occurred in PyObject_Format(). --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 743a969f6b..b815ccd9d0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3383,7 +3383,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) int have_fmt_spec = (oparg & FVS_MASK) == FVS_HAVE_SPEC; fmt_spec = have_fmt_spec ? POP() : NULL; - value = TOP(); + value = POP(); /* See if any conversion is specified. */ switch (which_conversion) { @@ -3426,7 +3426,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto error; } - SET_TOP(result); + PUSH(result); DISPATCH(); } -- cgit v1.2.1 From 54dbf866920611f5a7cd4053f0f5cff8f133b0c6 Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Fri, 5 Feb 2016 18:26:20 -0500 Subject: Switch to more idiomatic C code. --- Python/ceval.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index b815ccd9d0..8904d7acaf 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3399,10 +3399,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) /* If there's a conversion function, call it and replace value with that result. Otherwise, just use value, without conversion. */ - if (conv_fn) { + if (conv_fn != NULL) { result = conv_fn(value); Py_DECREF(value); - if (!result) { + if (result == NULL) { Py_XDECREF(fmt_spec); goto error; } @@ -3422,8 +3422,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) result = PyObject_Format(value, fmt_spec); Py_DECREF(value); Py_XDECREF(fmt_spec); - if (!result) + if (result == NULL) { goto error; + } } PUSH(result); -- cgit v1.2.1 From 09bfcfc935da1ed8c753055ae1f317dc59764bc3 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Wed, 2 Mar 2016 11:30:46 -0500 Subject: coroutines: Error when awaiting on coroutine that's being awaited Issue #25888 --- Python/ceval.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index aee0e6bc85..f9ecad46cf 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2021,6 +2021,21 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) Py_DECREF(iterable); + if (iter != NULL && PyCoro_CheckExact(iter)) { + PyObject *yf = _PyGen_yf((PyGenObject*)iter); + if (yf != NULL) { + /* `iter` is a coroutine object that is being + awaited, `yf` is a pointer to the current awaitable + being awaited on. */ + Py_DECREF(yf); + Py_CLEAR(iter); + PyErr_SetString( + PyExc_RuntimeError, + "coroutine is being awaited already"); + /* The code below jumps to `error` if `iter` is NULL. */ + } + } + SET_TOP(iter); /* Even if it's NULL */ if (iter == NULL) { -- cgit v1.2.1 From 224d00c29359fb24b36b4328b1b4d5e87866f09e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 19 Apr 2016 23:37:17 +0300 Subject: Issue #26802: Optimized calling a function with *args only positional arguments. Patch by Joe Jevnik. --- Python/ceval.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index d69b8d6cc6..88dc1139eb 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4890,6 +4890,21 @@ update_star_args(int nstack, int nstar, PyObject *stararg, { PyObject *callargs, *w; + if (!nstack) { + if (!stararg) { + /* There are no positional arguments on the stack and there is no + sequence to be unpacked. */ + return PyTuple_New(0); + } + if (PyTuple_CheckExact(stararg)) { + /* No arguments are passed on the stack and the sequence is not a + tuple subclass so we can just pass the stararg tuple directly + to the function. */ + Py_INCREF(stararg); + return stararg; + } + } + callargs = PyTuple_New(nstack + nstar); if (callargs == NULL) { return NULL; -- cgit v1.2.1 From 43db0a5e63391ae17f825f5f01f62811bd299b8e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 8 May 2016 23:36:44 +0300 Subject: Issue #18531: Single var-keyword argument of dict subtype was passed unscathed to the C-defined function. Now it is converted to exact dict. --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 88dc1139eb..b461030060 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4993,7 +4993,7 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) if (flags & CALL_FLAG_KW) { kwdict = EXT_POP(*pp_stack); - if (!PyDict_Check(kwdict)) { + if (!PyDict_CheckExact(kwdict)) { PyObject *d; d = PyDict_New(); if (d == NULL) -- cgit v1.2.1 From 2e584bb8793b8c50689fa680f4cd7e64ae6bae6a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 24 May 2016 09:15:14 +0300 Subject: Issue #26647: Python interpreter now uses 16-bit wordcode instead of bytecode. Patch by Demur Rumed. --- Python/ceval.c | 131 +++++++++++++++++++++++++-------------------------------- 1 file changed, 57 insertions(+), 74 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 9870a552fa..9110e80809 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -886,24 +886,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) /* Import the static jump table */ #include "opcode_targets.h" -/* This macro is used when several opcodes defer to the same implementation - (e.g. SETUP_LOOP, SETUP_FINALLY) */ -#define TARGET_WITH_IMPL(op, impl) \ - TARGET_##op: \ - opcode = op; \ - if (HAS_ARG(op)) \ - oparg = NEXTARG(); \ - case op: \ - goto impl; \ - #define TARGET(op) \ TARGET_##op: \ - opcode = op; \ - if (HAS_ARG(op)) \ - oparg = NEXTARG(); \ case op: - #define DISPATCH() \ { \ if (!_Py_atomic_load_relaxed(&eval_breaker)) { \ @@ -917,7 +903,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) { \ if (!lltrace && !_Py_TracingPossible) { \ f->f_lasti = INSTR_OFFSET(); \ - goto *opcode_targets[*next_instr++]; \ + opcode = NEXTOP(); \ + oparg = NEXTARG(); \ + goto *opcode_targets[opcode]; \ } \ goto fast_next_opcode; \ } @@ -926,7 +914,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) { \ if (!_Py_TracingPossible) { \ f->f_lasti = INSTR_OFFSET(); \ - goto *opcode_targets[*next_instr++]; \ + opcode = NEXTOP(); \ + oparg = NEXTARG(); \ + goto *opcode_targets[opcode]; \ } \ goto fast_next_opcode; \ } @@ -935,10 +925,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) #else #define TARGET(op) \ case op: -#define TARGET_WITH_IMPL(op, impl) \ - /* silence compiler warnings about `impl` unused */ \ - if (0) goto impl; \ - case op: + #define DISPATCH() continue #define FAST_DISPATCH() goto fast_next_opcode #endif @@ -995,9 +982,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) /* Code access macros */ #define INSTR_OFFSET() ((int)(next_instr - first_instr)) -#define NEXTOP() (*next_instr++) -#define NEXTARG() (next_instr += 2, (next_instr[-1]<<8) + next_instr[-2]) -#define PEEKARG() ((next_instr[2]<<8) + next_instr[1]) +#define NEXTOP() (next_instr+=2, next_instr[-2]) +#define NEXTARG() (next_instr[-1]) +#define PEEKARG() (next_instr[1]) #define JUMPTO(x) (next_instr = first_instr + (x)) #define JUMPBY(x) (next_instr += (x)) @@ -1012,10 +999,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) processor's own internal branch predication has a high likelihood of success, resulting in a nearly zero-overhead transition to the next opcode. A successful prediction saves a trip through the eval-loop - including its two unpredictable branches, the HAS_ARG test and the - switch-case. Combined with the processor's internal branch prediction, - a successful PREDICT has the effect of making the two opcodes run as if - they were a single new opcode with the bodies combined. + including its unpredictable switch-case branch. Combined with the + processor's internal branch prediction, a successful PREDICT has the + effect of making the two opcodes run as if they were a single new opcode + with the bodies combined. If collecting opcode statistics, your choices are to either keep the predictions turned-on and interpret the results as if some opcodes @@ -1030,13 +1017,18 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) #if defined(DYNAMIC_EXECUTION_PROFILE) || USE_COMPUTED_GOTOS #define PREDICT(op) if (0) goto PRED_##op -#define PREDICTED(op) PRED_##op: -#define PREDICTED_WITH_ARG(op) PRED_##op: #else -#define PREDICT(op) if (*next_instr == op) goto PRED_##op -#define PREDICTED(op) PRED_##op: next_instr++ -#define PREDICTED_WITH_ARG(op) PRED_##op: oparg = PEEKARG(); next_instr += 3 +#define PREDICT(op) \ + do{ \ + if (*next_instr == op){ \ + opcode = op; \ + oparg = PEEKARG(); \ + next_instr += 2; \ + goto PRED_##op; \ + } \ + } while(0) #endif +#define PREDICTED(op) PRED_##op: /* Stack manipulation macros */ @@ -1100,7 +1092,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } #define UNWIND_EXCEPT_HANDLER(b) \ - { \ + do { \ PyObject *type, *value, *traceback; \ assert(STACK_LEVEL() >= (b)->b_level + 3); \ while (STACK_LEVEL() > (b)->b_level + 3) { \ @@ -1116,7 +1108,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) Py_XDECREF(type); \ Py_XDECREF(value); \ Py_XDECREF(traceback); \ - } + } while(0) /* Start of code */ @@ -1166,15 +1158,11 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) fastlocals = f->f_localsplus; freevars = f->f_localsplus + co->co_nlocals; first_instr = (unsigned char*) PyBytes_AS_STRING(co->co_code); - /* An explanation is in order for the next line. - - f->f_lasti now refers to the index of the last instruction - executed. You might think this was obvious from the name, but - this wasn't always true before 2.3! PyFrame_New now sets - f->f_lasti to -1 (i.e. the index *before* the first instruction) - and YIELD_VALUE doesn't fiddle with f_lasti any more. So this - does work. Promise. - YIELD_FROM sets f_lasti to itself, in order to repeated yield + /* + f->f_lasti refers to the index of the last instruction, + unless it's -1 in which case next_instr should be first_instr. + + YIELD_FROM sets f_lasti to itself, in order to repeatedly yield multiple values. When the PREDICT() macros are enabled, some opcode pairs follow in @@ -1183,9 +1171,12 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) were a single new opcode; accordingly,f->f_lasti will point to the first code in the pair (for instance, GET_ITER followed by FOR_ITER is effectively a single opcode and f->f_lasti will point - at to the beginning of the combined pair.) + to the beginning of the combined pair.) */ - next_instr = first_instr + f->f_lasti + 1; + next_instr = first_instr; + if (f->f_lasti >= 0) { + next_instr += f->f_lasti + 2; + } stack_pointer = f->f_stacktop; assert(stack_pointer != NULL); f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ @@ -1323,10 +1314,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) /* Extract opcode and argument */ opcode = NEXTOP(); - oparg = 0; /* allows oparg to be stored in a register because - it doesn't have to be remembered across a full loop */ - if (HAS_ARG(opcode)) - oparg = NEXTARG(); + oparg = NEXTARG(); dispatch_opcode: #ifdef DYNAMIC_EXECUTION_PROFILE #ifdef DXPAIRS @@ -1384,7 +1372,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) FAST_DISPATCH(); } - PREDICTED_WITH_ARG(STORE_FAST); + PREDICTED(STORE_FAST); TARGET(STORE_FAST) { PyObject *value = POP(); SETLOCAL(oparg, value); @@ -2075,7 +2063,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) f->f_stacktop = stack_pointer; why = WHY_YIELD; /* and repeat... */ - f->f_lasti--; + f->f_lasti -= 2; goto fast_yield; } @@ -2213,7 +2201,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } - PREDICTED_WITH_ARG(UNPACK_SEQUENCE); + PREDICTED(UNPACK_SEQUENCE); TARGET(UNPACK_SEQUENCE) { PyObject *seq = POP(), *item, **items; if (PyTuple_CheckExact(seq) && @@ -2511,9 +2499,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } - TARGET_WITH_IMPL(BUILD_TUPLE_UNPACK, _build_list_unpack) - TARGET(BUILD_LIST_UNPACK) - _build_list_unpack: { + TARGET(BUILD_TUPLE_UNPACK) + TARGET(BUILD_LIST_UNPACK) { int convert_to_tuple = opcode == BUILD_TUPLE_UNPACK; int i; PyObject *sum = PyList_New(0); @@ -2610,9 +2597,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } - TARGET_WITH_IMPL(BUILD_MAP_UNPACK_WITH_CALL, _build_map_unpack) - TARGET(BUILD_MAP_UNPACK) - _build_map_unpack: { + TARGET(BUILD_MAP_UNPACK_WITH_CALL) + TARGET(BUILD_MAP_UNPACK) { int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL; int num_maps; int function_location; @@ -2819,7 +2805,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) FAST_DISPATCH(); } - PREDICTED_WITH_ARG(POP_JUMP_IF_FALSE); + PREDICTED(POP_JUMP_IF_FALSE); TARGET(POP_JUMP_IF_FALSE) { PyObject *cond = POP(); int err; @@ -2843,7 +2829,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } - PREDICTED_WITH_ARG(POP_JUMP_IF_TRUE); + PREDICTED(POP_JUMP_IF_TRUE); TARGET(POP_JUMP_IF_TRUE) { PyObject *cond = POP(); int err; @@ -2920,7 +2906,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } - PREDICTED_WITH_ARG(JUMP_ABSOLUTE); + PREDICTED(JUMP_ABSOLUTE); TARGET(JUMP_ABSOLUTE) { JUMPTO(oparg); #if FAST_LOOPS @@ -2977,7 +2963,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } - PREDICTED_WITH_ARG(FOR_ITER); + PREDICTED(FOR_ITER); TARGET(FOR_ITER) { /* before: [iter]; after: [iter, iter()] *or* [] */ PyObject *iter = TOP(); @@ -3015,10 +3001,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto fast_block_end; } - TARGET_WITH_IMPL(SETUP_LOOP, _setup_finally) - TARGET_WITH_IMPL(SETUP_EXCEPT, _setup_finally) - TARGET(SETUP_FINALLY) - _setup_finally: { + TARGET(SETUP_LOOP) + TARGET(SETUP_EXCEPT) + TARGET(SETUP_FINALLY) { /* NOTE: If you add any new block-setup opcodes that are not try/except/finally handlers, you may need to update the PyGen_NeedsFinalizing() function. @@ -3213,10 +3198,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } - TARGET_WITH_IMPL(CALL_FUNCTION_VAR, _call_function_var_kw) - TARGET_WITH_IMPL(CALL_FUNCTION_KW, _call_function_var_kw) - TARGET(CALL_FUNCTION_VAR_KW) - _call_function_var_kw: { + TARGET(CALL_FUNCTION_VAR) + TARGET(CALL_FUNCTION_KW) + TARGET(CALL_FUNCTION_VAR_KW) { int na = oparg & 0xff; int nk = (oparg>>8) & 0xff; int flags = (opcode - CALL_FUNCTION) & 3; @@ -3258,9 +3242,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } - TARGET_WITH_IMPL(MAKE_CLOSURE, _make_function) - TARGET(MAKE_FUNCTION) - _make_function: { + TARGET(MAKE_CLOSURE) + TARGET(MAKE_FUNCTION) { int posdefaults = oparg & 0xff; int kwdefaults = (oparg>>8) & 0xff; int num_annotations = (oparg >> 16) & 0x7fff; @@ -3450,7 +3433,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(EXTENDED_ARG) { opcode = NEXTOP(); - oparg = oparg<<16 | NEXTARG(); + oparg = oparg<<8 | NEXTARG(); goto dispatch_opcode; } -- cgit v1.2.1 From 4b544d647c1d3b3f5543066ca6d3759105cb8f34 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 25 May 2016 20:02:01 +0300 Subject: Issue #27097: Python interpreter is now about 7% faster due to optimized instruction decoding. Based on patch by Demur Rumed. --- Python/ceval.c | 74 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 29 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 9110e80809..96e0842631 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -144,7 +144,7 @@ static int import_all_from(PyObject *, PyObject *); static void format_exc_check_arg(PyObject *, const char *, PyObject *); static void format_exc_unbound(PyCodeObject *co, int oparg); static PyObject * unicode_concatenate(PyObject *, PyObject *, - PyFrameObject *, unsigned char *); + PyFrameObject *, const unsigned short *); static PyObject * special_lookup(PyObject *, _Py_Identifier *); #define NAME_ERROR_MSG \ @@ -800,7 +800,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) int lastopcode = 0; #endif PyObject **stack_pointer; /* Next free slot in value stack */ - unsigned char *next_instr; + const unsigned short *next_instr; int opcode; /* Current opcode */ int oparg; /* Current opcode argument, if any */ enum why_code why; /* Reason for block stack unwind */ @@ -818,7 +818,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) time it is tested. */ int instr_ub = -1, instr_lb = 0, instr_prev = -1; - unsigned char *first_instr; + const unsigned short *first_instr; PyObject *names; PyObject *consts; @@ -903,8 +903,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) { \ if (!lltrace && !_Py_TracingPossible) { \ f->f_lasti = INSTR_OFFSET(); \ - opcode = NEXTOP(); \ - oparg = NEXTARG(); \ + NEXTOPARG(); \ goto *opcode_targets[opcode]; \ } \ goto fast_next_opcode; \ @@ -914,8 +913,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) { \ if (!_Py_TracingPossible) { \ f->f_lasti = INSTR_OFFSET(); \ - opcode = NEXTOP(); \ - oparg = NEXTARG(); \ + NEXTOPARG(); \ goto *opcode_targets[opcode]; \ } \ goto fast_next_opcode; \ @@ -981,12 +979,23 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) /* Code access macros */ -#define INSTR_OFFSET() ((int)(next_instr - first_instr)) -#define NEXTOP() (next_instr+=2, next_instr[-2]) -#define NEXTARG() (next_instr[-1]) -#define PEEKARG() (next_instr[1]) -#define JUMPTO(x) (next_instr = first_instr + (x)) -#define JUMPBY(x) (next_instr += (x)) +#ifdef WORDS_BIGENDIAN + #define OPCODE(word) ((word) >> 8) + #define OPARG(word) ((word) & 255) +#else + #define OPCODE(word) ((word) & 255) + #define OPARG(word) ((word) >> 8) +#endif +/* The integer overflow is checked by an assertion below. */ +#define INSTR_OFFSET() (2*(int)(next_instr - first_instr)) +#define NEXTOPARG() do { \ + unsigned short word = *next_instr; \ + opcode = OPCODE(word); \ + oparg = OPARG(word); \ + next_instr++; \ + } while (0) +#define JUMPTO(x) (next_instr = first_instr + (x)/2) +#define JUMPBY(x) (next_instr += (x)/2) /* OpCode prediction macros Some opcodes tend to come in pairs thus making it possible to @@ -1020,10 +1029,11 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) #else #define PREDICT(op) \ do{ \ - if (*next_instr == op){ \ - opcode = op; \ - oparg = PEEKARG(); \ - next_instr += 2; \ + unsigned short word = *next_instr; \ + opcode = OPCODE(word); \ + if (opcode == op){ \ + oparg = OPARG(word); \ + next_instr++; \ goto PRED_##op; \ } \ } while(0) @@ -1157,7 +1167,11 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) consts = co->co_consts; fastlocals = f->f_localsplus; freevars = f->f_localsplus + co->co_nlocals; - first_instr = (unsigned char*) PyBytes_AS_STRING(co->co_code); + assert(PyBytes_Check(co->co_code)); + assert(PyBytes_GET_SIZE(co->co_code) <= INT_MAX); + assert(PyBytes_GET_SIZE(co->co_code) % 2 == 0); + assert(_Py_IS_ALIGNED(PyBytes_AS_STRING(co->co_code), unsigned short)); + first_instr = (unsigned short*) PyBytes_AS_STRING(co->co_code); /* f->f_lasti refers to the index of the last instruction, unless it's -1 in which case next_instr should be first_instr. @@ -1175,7 +1189,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) */ next_instr = first_instr; if (f->f_lasti >= 0) { - next_instr += f->f_lasti + 2; + assert(f->f_lasti % 2 == 0); + next_instr += f->f_lasti/2 + 1; } stack_pointer = f->f_stacktop; assert(stack_pointer != NULL); @@ -1240,7 +1255,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) Py_MakePendingCalls() above. */ if (_Py_atomic_load_relaxed(&eval_breaker)) { - if (*next_instr == SETUP_FINALLY) { + if (OPCODE(*next_instr) == SETUP_FINALLY) { /* Make the last opcode before a try: finally: block uninterruptible. */ goto fast_next_opcode; @@ -1313,8 +1328,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) /* Extract opcode and argument */ - opcode = NEXTOP(); - oparg = NEXTARG(); + NEXTOPARG(); dispatch_opcode: #ifdef DYNAMIC_EXECUTION_PROFILE #ifdef DXPAIRS @@ -3432,8 +3446,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } TARGET(EXTENDED_ARG) { - opcode = NEXTOP(); - oparg = oparg<<8 | NEXTARG(); + int oldoparg = oparg; + NEXTOPARG(); + oparg |= oldoparg << 8; goto dispatch_opcode; } @@ -5288,7 +5303,7 @@ format_exc_unbound(PyCodeObject *co, int oparg) static PyObject * unicode_concatenate(PyObject *v, PyObject *w, - PyFrameObject *f, unsigned char *next_instr) + PyFrameObject *f, const unsigned short *next_instr) { PyObject *res; if (Py_REFCNT(v) == 2) { @@ -5298,10 +5313,11 @@ unicode_concatenate(PyObject *v, PyObject *w, * 'variable'. We try to delete the variable now to reduce * the refcnt to 1. */ - switch (*next_instr) { + int opcode, oparg; + NEXTOPARG(); + switch (opcode) { case STORE_FAST: { - int oparg = PEEKARG(); PyObject **fastlocals = f->f_localsplus; if (GETLOCAL(oparg) == v) SETLOCAL(oparg, NULL); @@ -5311,7 +5327,7 @@ unicode_concatenate(PyObject *v, PyObject *w, { PyObject **freevars = (f->f_localsplus + f->f_code->co_nlocals); - PyObject *c = freevars[PEEKARG()]; + PyObject *c = freevars[oparg]; if (PyCell_GET(c) == v) PyCell_Set(c, NULL); break; @@ -5319,7 +5335,7 @@ unicode_concatenate(PyObject *v, PyObject *w, case STORE_NAME: { PyObject *names = f->f_code->co_names; - PyObject *name = GETITEM(names, PEEKARG()); + PyObject *name = GETITEM(names, oparg); PyObject *locals = f->f_locals; if (PyDict_CheckExact(locals) && PyDict_GetItem(locals, name) == v) { -- cgit v1.2.1 From 29c057d142a5086e2d941ee2add76cc661e8153b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 25 May 2016 20:35:44 +0300 Subject: Fixed the use of _Py_IS_ALIGNED (issue #27097). --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 96e0842631..5f8dbcd152 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1170,7 +1170,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) assert(PyBytes_Check(co->co_code)); assert(PyBytes_GET_SIZE(co->co_code) <= INT_MAX); assert(PyBytes_GET_SIZE(co->co_code) % 2 == 0); - assert(_Py_IS_ALIGNED(PyBytes_AS_STRING(co->co_code), unsigned short)); + assert(_Py_IS_ALIGNED(PyBytes_AS_STRING(co->co_code), 2)); first_instr = (unsigned short*) PyBytes_AS_STRING(co->co_code); /* f->f_lasti refers to the index of the last instruction, -- cgit v1.2.1 From 5486e94c1ad7342d7a892631cc8e7ff33a022ae8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 12 Jun 2016 00:39:41 +0300 Subject: Issue #27140: Added BUILD_CONST_KEY_MAP opcode. --- Python/ceval.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index b6ce67cb48..1d3bc90093 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2647,6 +2647,39 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(BUILD_CONST_KEY_MAP) { + int i; + PyObject *map; + PyObject *keys = TOP(); + if (!PyTuple_CheckExact(keys) || + PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) { + PyErr_SetString(PyExc_SystemError, + "bad BUILD_CONST_KEY_MAP keys argument"); + goto error; + } + map = _PyDict_NewPresized((Py_ssize_t)oparg); + if (map == NULL) { + goto error; + } + for (i = oparg; i > 0; i--) { + int err; + PyObject *key = PyTuple_GET_ITEM(keys, oparg - i); + PyObject *value = PEEK(i + 1); + err = PyDict_SetItem(map, key, value); + if (err != 0) { + Py_DECREF(map); + goto error; + } + } + + Py_DECREF(POP()); + while (oparg--) { + Py_DECREF(POP()); + } + PUSH(map); + DISPATCH(); + } + TARGET(BUILD_MAP_UNPACK_WITH_CALL) TARGET(BUILD_MAP_UNPACK) { int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL; -- cgit v1.2.1 From fa60de800763bc0f7b70d67ca749fce15d2d53de Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 12 Jun 2016 17:36:24 +0300 Subject: Issue #27095: Simplified MAKE_FUNCTION and removed MAKE_CLOSURE opcodes. Patch by Demur Rumed. --- Python/ceval.c | 124 ++++++++++----------------------------------------------- 1 file changed, 22 insertions(+), 102 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 1d3bc90093..38ac509117 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3325,116 +3325,36 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } - TARGET(MAKE_CLOSURE) TARGET(MAKE_FUNCTION) { - int posdefaults = oparg & 0xff; - int kwdefaults = (oparg>>8) & 0xff; - int num_annotations = (oparg >> 16) & 0x7fff; - - PyObject *qualname = POP(); /* qualname */ - PyObject *code = POP(); /* code object */ - PyObject *func = PyFunction_NewWithQualName(code, f->f_globals, qualname); - Py_DECREF(code); - Py_DECREF(qualname); + PyObject *qualname = POP(); + PyObject *codeobj = POP(); + PyFunctionObject *func = (PyFunctionObject *) + PyFunction_NewWithQualName(codeobj, f->f_globals, qualname); - if (func == NULL) + Py_DECREF(codeobj); + Py_DECREF(qualname); + if (func == NULL) { goto error; - - if (opcode == MAKE_CLOSURE) { - PyObject *closure = POP(); - if (PyFunction_SetClosure(func, closure) != 0) { - /* Can't happen unless bytecode is corrupt. */ - Py_DECREF(func); - Py_DECREF(closure); - goto error; - } - Py_DECREF(closure); } - if (num_annotations > 0) { - Py_ssize_t name_ix; - PyObject *names = POP(); /* names of args with annotations */ - PyObject *anns = PyDict_New(); - if (anns == NULL) { - Py_DECREF(func); - Py_DECREF(names); - goto error; - } - name_ix = PyTuple_Size(names); - assert(num_annotations == name_ix+1); - while (name_ix > 0) { - PyObject *name, *value; - int err; - --name_ix; - name = PyTuple_GET_ITEM(names, name_ix); - value = POP(); - err = PyDict_SetItem(anns, name, value); - Py_DECREF(value); - if (err != 0) { - Py_DECREF(anns); - Py_DECREF(func); - Py_DECREF(names); - goto error; - } - } - Py_DECREF(names); - - if (PyFunction_SetAnnotations(func, anns) != 0) { - /* Can't happen unless - PyFunction_SetAnnotations changes. */ - Py_DECREF(anns); - Py_DECREF(func); - goto error; - } - Py_DECREF(anns); + if (oparg & 0x08) { + assert(PyTuple_CheckExact(TOP())); + func ->func_closure = POP(); } - - /* XXX Maybe this should be a separate opcode? */ - if (kwdefaults > 0) { - PyObject *defs = PyDict_New(); - if (defs == NULL) { - Py_DECREF(func); - goto error; - } - while (--kwdefaults >= 0) { - PyObject *v = POP(); /* default value */ - PyObject *key = POP(); /* kw only arg name */ - int err = PyDict_SetItem(defs, key, v); - Py_DECREF(v); - Py_DECREF(key); - if (err != 0) { - Py_DECREF(defs); - Py_DECREF(func); - goto error; - } - } - if (PyFunction_SetKwDefaults(func, defs) != 0) { - /* Can't happen unless - PyFunction_SetKwDefaults changes. */ - Py_DECREF(func); - Py_DECREF(defs); - goto error; - } - Py_DECREF(defs); + if (oparg & 0x04) { + assert(PyDict_CheckExact(TOP())); + func->func_annotations = POP(); } - if (posdefaults > 0) { - PyObject *defs = PyTuple_New(posdefaults); - if (defs == NULL) { - Py_DECREF(func); - goto error; - } - while (--posdefaults >= 0) - PyTuple_SET_ITEM(defs, posdefaults, POP()); - if (PyFunction_SetDefaults(func, defs) != 0) { - /* Can't happen unless - PyFunction_SetDefaults changes. */ - Py_DECREF(defs); - Py_DECREF(func); - goto error; - } - Py_DECREF(defs); + if (oparg & 0x02) { + assert(PyDict_CheckExact(TOP())); + func->func_kwdefaults = POP(); + } + if (oparg & 0x01) { + assert(PyTuple_CheckExact(TOP())); + func->func_defaults = POP(); } - PUSH(func); + + PUSH((PyObject *)func); DISPATCH(); } -- cgit v1.2.1 From c0b41a4a9d4af3fb92d612dc4ca041864d64c5e1 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Jun 2016 18:58:57 +0300 Subject: Issue #27255: Added more predictions in ceval.c. --- Python/ceval.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 38ac509117..341d36df48 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1000,8 +1000,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) /* OpCode prediction macros Some opcodes tend to come in pairs thus making it possible to predict the second code when the first is run. For example, - COMPARE_OP is often followed by JUMP_IF_FALSE or JUMP_IF_TRUE. And, - those opcodes are often followed by a POP_TOP. + COMPARE_OP is often followed by POP_JUMP_IF_FALSE or POP_JUMP_IF_TRUE. Verifying the prediction costs a single high-speed test of a register variable against a constant. If the pairing was good, then the @@ -1379,6 +1378,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) FAST_DISPATCH(); } + PREDICTED(LOAD_CONST); TARGET(LOAD_CONST) { PyObject *value = GETITEM(consts, oparg); Py_INCREF(value); @@ -2008,6 +2008,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } SET_TOP(awaitable); + PREDICT(LOAD_CONST); DISPATCH(); } @@ -2050,9 +2051,11 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) Py_DECREF(next_iter); PUSH(awaitable); + PREDICT(LOAD_CONST); DISPATCH(); } + PREDICTED(GET_AWAITABLE); TARGET(GET_AWAITABLE) { PyObject *iterable = TOP(); PyObject *iter = _PyCoro_GetAwaitableIter(iterable); @@ -2080,6 +2083,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) goto error; } + PREDICT(LOAD_CONST); DISPATCH(); } @@ -2135,6 +2139,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + PREDICTED(POP_BLOCK); TARGET(POP_BLOCK) { PyTryBlock *b = PyFrame_BlockPop(f); UNWIND_BLOCK(b); @@ -3015,6 +3020,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (iter == NULL) goto error; PREDICT(FOR_ITER); + PREDICT(CALL_FUNCTION); DISPATCH(); } @@ -3043,6 +3049,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (iter == NULL) goto error; } + PREDICT(LOAD_CONST); DISPATCH(); } @@ -3068,6 +3075,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) STACKADJ(-1); Py_DECREF(iter); JUMPBY(oparg); + PREDICT(POP_BLOCK); DISPATCH(); } @@ -3117,6 +3125,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (res == NULL) goto error; PUSH(res); + PREDICT(GET_AWAITABLE); DISPATCH(); } @@ -3265,6 +3274,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) DISPATCH(); } + PREDICTED(CALL_FUNCTION); TARGET(CALL_FUNCTION) { PyObject **sp, *res; PCALL(PCALL_ALL); -- cgit v1.2.1 From e6bfce7e71d0ab3c4226fe23aa638b74b84cb677 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Jun 2016 21:39:12 +0300 Subject: Issue #27352: Correct the validation of the ImportFrom AST node and simplify the implementation of the IMPORT_NAME opcode. --- Python/ceval.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 341d36df48..2b4f7ccfaa 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2820,21 +2820,13 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) Py_INCREF(func); from = POP(); level = TOP(); - if (PyLong_AsLong(level) != -1 || PyErr_Occurred()) - args = PyTuple_Pack(5, + args = PyTuple_Pack(5, name, f->f_globals, f->f_locals == NULL ? Py_None : f->f_locals, from, level); - else - args = PyTuple_Pack(4, - name, - f->f_globals, - f->f_locals == NULL ? - Py_None : f->f_locals, - from); Py_DECREF(level); Py_DECREF(from); if (args == NULL) { -- cgit v1.2.1 From 47f7f2f22d067813b1406f7aa9328b53f264aa6a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 2 Aug 2016 22:51:21 +0300 Subject: Issue #22557: Now importing already imported modules is up to 2.5 times faster. --- Python/ceval.c | 79 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 28 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 7c664ad6c6..7ca3ad253f 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -139,6 +139,7 @@ static int maybe_call_line_trace(Py_tracefunc, PyObject *, PyThreadState *, PyFrameObject *, int *, int *, int *); static PyObject * cmp_outcome(int, PyObject *, PyObject *); +static PyObject * import_name(PyFrameObject *, PyObject *, PyObject *, PyObject *); static PyObject * import_from(PyObject *, PyObject *); static int import_all_from(PyObject *, PyObject *); static void format_exc_check_arg(PyObject *, const char *, PyObject *); @@ -2808,37 +2809,15 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } TARGET(IMPORT_NAME) { - _Py_IDENTIFIER(__import__); PyObject *name = GETITEM(names, oparg); - PyObject *func = _PyDict_GetItemId(f->f_builtins, &PyId___import__); - PyObject *from, *level, *args, *res; - if (func == NULL) { - PyErr_SetString(PyExc_ImportError, - "__import__ not found"); - goto error; - } - Py_INCREF(func); - from = POP(); - level = TOP(); - args = PyTuple_Pack(5, - name, - f->f_globals, - f->f_locals == NULL ? - Py_None : f->f_locals, - from, - level); - Py_DECREF(level); - Py_DECREF(from); - if (args == NULL) { - Py_DECREF(func); - STACKADJ(-1); - goto error; - } + PyObject *fromlist = POP(); + PyObject *level = TOP(); + PyObject *res; READ_TIMESTAMP(intr0); - res = PyEval_CallObject(func, args); + res = import_name(f, name, fromlist, level); + Py_DECREF(level); + Py_DECREF(fromlist); READ_TIMESTAMP(intr1); - Py_DECREF(args); - Py_DECREF(func); SET_TOP(res); if (res == NULL) goto error; @@ -5158,6 +5137,50 @@ cmp_outcome(int op, PyObject *v, PyObject *w) return v; } +static PyObject * +import_name(PyFrameObject *f, PyObject *name, PyObject *fromlist, PyObject *level) +{ + _Py_IDENTIFIER(__import__); + PyObject *import_func, *args, *res; + + import_func = _PyDict_GetItemId(f->f_builtins, &PyId___import__); + if (import_func == NULL) { + PyErr_SetString(PyExc_ImportError, "__import__ not found"); + return NULL; + } + + /* Fast path for not overloaded __import__. */ + if (import_func == PyThreadState_GET()->interp->import_func) { + int ilevel = _PyLong_AsInt(level); + if (ilevel == -1 && PyErr_Occurred()) { + return NULL; + } + res = PyImport_ImportModuleLevelObject( + name, + f->f_globals, + f->f_locals == NULL ? Py_None : f->f_locals, + fromlist, + ilevel); + return res; + } + + Py_INCREF(import_func); + args = PyTuple_Pack(5, + name, + f->f_globals, + f->f_locals == NULL ? Py_None : f->f_locals, + fromlist, + level); + if (args == NULL) { + Py_DECREF(import_func); + return NULL; + } + res = PyEval_CallObject(import_func, args); + Py_DECREF(args); + Py_DECREF(import_func); + return res; +} + static PyObject * import_from(PyObject *v, PyObject *name) { -- cgit v1.2.1 From e5d72bbbf9876f82f8d6358392e408c9d3d76bcd Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 16 Aug 2016 23:40:29 +0200 Subject: Issue #27128: Cleanup _PyEval_EvalCodeWithName() * Add comments * Add empty lines for readability * PEP 7 style for if block * Remove useless assert(globals != NULL); (globals is tested a few lines before) --- Python/ceval.c | 54 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 11 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 7ca3ad253f..6e4c6aa627 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3793,12 +3793,13 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyFrameObject *f; PyObject *retval = NULL; PyObject **fastlocals, **freevars; - PyThreadState *tstate = PyThreadState_GET(); + PyThreadState *tstate; PyObject *x, *u; int total_args = co->co_argcount + co->co_kwonlyargcount; - int i; - int n = argcount; - PyObject *kwdict = NULL; + int i, n; + PyObject *kwdict; + + assert((kwcount == 0) || (kws != NULL)); if (globals == NULL) { PyErr_SetString(PyExc_SystemError, @@ -3806,36 +3807,50 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, return NULL; } + /* Create the frame */ + tstate = PyThreadState_GET(); assert(tstate != NULL); - assert(globals != NULL); f = PyFrame_New(tstate, co, globals, locals); - if (f == NULL) + if (f == NULL) { return NULL; - + } fastlocals = f->f_localsplus; freevars = f->f_localsplus + co->co_nlocals; - /* Parse arguments. */ + /* 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) + if (co->co_flags & CO_VARARGS) { i++; + } SETLOCAL(i, kwdict); } - if (argcount > co->co_argcount) + 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) + if (u == NULL) { goto fail; + } SETLOCAL(total_args, u); for (i = n; i < argcount; i++) { x = args[i]; @@ -3843,17 +3858,21 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, 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; @@ -3862,6 +3881,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, if (nm == keyword) goto kw_found; } + /* Slow fallback, just in case */ for (j = 0; j < total_args; j++) { PyObject *nm = co_varnames[j]; @@ -3872,6 +3892,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, else if (cmp < 0) goto fail; } + if (j >= total_args && kwdict == NULL) { PyErr_Format(PyExc_TypeError, "%U() got an unexpected " @@ -3880,10 +3901,12 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, keyword); goto fail; } + if (PyDict_SetItem(kwdict, keyword, value) == -1) { goto fail; } continue; + kw_found: if (GETLOCAL(j) != NULL) { PyErr_Format(PyExc_TypeError, @@ -3896,10 +3919,14 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, Py_INCREF(value); SETLOCAL(j, value); } + + /* 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; @@ -3922,6 +3949,8 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, } } } + + /* 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++) { @@ -3964,12 +3993,15 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, 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; -- cgit v1.2.1 From 967cc3e92fb9079e9893d6f4fd09df814751fb87 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 16 Aug 2016 23:39:42 +0200 Subject: Use Py_ssize_t in _PyEval_EvalCodeWithName() Issue #27128, #18295: replace int type with Py_ssize_t for index variables used for positional arguments. It should help to avoid integer overflow and help to emit better machine code for "i++" (no trap needed for overflow). Make also the total_args variable constant. --- Python/ceval.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 6e4c6aa627..07ac167359 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3795,8 +3795,8 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyObject **fastlocals, **freevars; PyThreadState *tstate; PyObject *x, *u; - int total_args = co->co_argcount + co->co_kwonlyargcount; - int i, n; + const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount; + Py_ssize_t i, n; PyObject *kwdict; assert((kwcount == 0) || (kws != NULL)); @@ -3864,7 +3864,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyObject **co_varnames; PyObject *keyword = kws[2*i]; PyObject *value = kws[2*i + 1]; - int j; + Py_ssize_t j; if (keyword == NULL || !PyUnicode_Check(keyword)) { PyErr_Format(PyExc_TypeError, @@ -3928,11 +3928,13 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, /* Add missing positional arguments (copy default values from defs) */ if (argcount < co->co_argcount) { - int m = co->co_argcount - defcount; - int missing = 0; - for (i = argcount; i < m; i++) - if (GETLOCAL(i) == NULL) + Py_ssize_t m = co->co_argcount - defcount; + Py_ssize_t missing = 0; + for (i = argcount; i < m; i++) { + if (GETLOCAL(i) == NULL) { missing++; + } + } if (missing) { missing_arguments(co, missing, defcount, fastlocals); goto fail; @@ -3952,7 +3954,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, /* Add missing keyword arguments (copy default values from kwdefs) */ if (co->co_kwonlyargcount > 0) { - int missing = 0; + Py_ssize_t missing = 0; for (i = co->co_argcount; i < total_args; i++) { PyObject *name; if (GETLOCAL(i) != NULL) -- cgit v1.2.1 From d7a4362203dbd80afd88e89b46e649d7c3095821 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 19 Aug 2016 16:11:43 +0200 Subject: 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 --- Python/ceval.c | 159 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 113 insertions(+), 46 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 8e7d5c2f77..b9b21d14be 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -113,7 +113,7 @@ static PyObject * call_function(PyObject ***, int, uint64*, uint64*); #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 ***, @@ -3779,6 +3779,7 @@ too_many_positional(PyCodeObject *co, int given, int defcount, PyObject **fastlo 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). */ @@ -4068,8 +4069,10 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) { return _PyEval_EvalCodeWithName(_co, globals, locals, - args, argcount, kws, kwcount, - defs, defcount, kwdefs, closure, + args, argcount, + kws, kwcount, + defs, defcount, + kwdefs, closure, NULL, NULL); } @@ -4757,10 +4760,12 @@ call_function(PyObject ***pp_stack, int oparg } 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); @@ -4790,62 +4795,124 @@ call_function(PyObject ***pp_stack, int oparg 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, *closure, *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; + 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); + } - fastlocals = f->f_localsplus; - stack = (*pp_stack) - n; + kwdefs = PyFunction_GET_KW_DEFAULTS(func); + closure = PyFunction_GET_CLOSURE(func); + name = ((PyFunctionObject *)func) -> func_name; + qualname = ((PyFunctionObject *)func) -> func_qualname; - 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) { + d = &PyTuple_GET_ITEM(argdefs, 0); + nd = Py_SIZE(argdefs); + } + else { + d = NULL; + nd = 0; + } + return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, + stack, na, + stack + na, nk, + d, nd, kwdefs, + closure, name, qualname); +} + +PyObject * +_PyFunction_FastCall(PyObject *func, PyObject **args, int nargs, PyObject *kwargs) +{ + PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); + PyObject *globals = PyFunction_GET_GLOBALS(func); + PyObject *argdefs = PyFunction_GET_DEFAULTS(func); + PyObject *kwdefs, *closure, *name, *qualname; + PyObject **d; + int nd; + + PCALL(PCALL_FUNCTION); + PCALL(PCALL_FAST_FUNCTION); + + /* issue #27128: support for keywords will come later */ + assert(kwargs == NULL); + + if (argdefs == NULL && co->co_argcount == nargs && + co->co_kwonlyargcount == 0 && + co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) + { + return _PyFunction_FastCallNoKw(args, nargs, co, globals); } + + kwdefs = PyFunction_GET_KW_DEFAULTS(func); + closure = PyFunction_GET_CLOSURE(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_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, + args, nargs, + NULL, 0, + d, nd, kwdefs, + closure, name, qualname); } static PyObject * -- cgit v1.2.1 From 5ec8e204a810f77c1fee138c4625c4d1e78aae46 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 19 Aug 2016 16:42:42 +0200 Subject: PyEval_CallObjectWithKeywords() uses fast call Issue #27128: Modify PyEval_CallObjectWithKeywords() to use _PyObject_FastCall() when args==NULL and kw==NULL. It avoids the creation of a temporary empty tuple for positional arguments. --- Python/ceval.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index b9b21d14be..75eaa8110a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4592,6 +4592,10 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw) #endif if (arg == NULL) { + if (kw == NULL) { + return _PyObject_FastCall(func, NULL, 0, 0); + } + arg = PyTuple_New(0); if (arg == NULL) return NULL; -- cgit v1.2.1 From 06f5b1d3dd2314af2e1f97e8da45bb28b0969f5e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 19 Aug 2016 17:12:23 +0200 Subject: Fix PyObject_Call() parameter names Issue #27128: arg=>args, kw=>kwargs. Same change for PyEval_CallObjectWithKeywords(). --- Python/ceval.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 75eaa8110a..905859ef3b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4580,7 +4580,7 @@ PyEval_MergeCompilerFlags(PyCompilerFlags *cf) The arg must be a tuple or NULL. The kw must be a dict or NULL. */ PyObject * -PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw) +PyEval_CallObjectWithKeywords(PyObject *func, PyObject *args, PyObject *kwargs) { PyObject *result; @@ -4591,32 +4591,33 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw) assert(!PyErr_Occurred()); #endif - if (arg == NULL) { - if (kw == NULL) { + if (args == NULL) { + if (kwargs == NULL) { return _PyObject_FastCall(func, NULL, 0, 0); } - arg = PyTuple_New(0); - if (arg == NULL) + args = PyTuple_New(0); + if (args == NULL) return NULL; } - else if (!PyTuple_Check(arg)) { + else if (!PyTuple_Check(args)) { PyErr_SetString(PyExc_TypeError, "argument list must be a tuple"); return NULL; } - else - Py_INCREF(arg); + else { + Py_INCREF(args); + } - if (kw != NULL && !PyDict_Check(kw)) { + if (kwargs != NULL && !PyDict_Check(kwargs)) { PyErr_SetString(PyExc_TypeError, "keyword list must be a dictionary"); - Py_DECREF(arg); + Py_DECREF(args); return NULL; } - result = PyObject_Call(func, arg, kw); - Py_DECREF(arg); + result = PyObject_Call(func, args, kwargs); + Py_DECREF(args); return result; } -- cgit v1.2.1 From a3c8f9cb0c66423277a78c96665190349499a60c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 20 Aug 2016 00:44:42 +0200 Subject: import_name() now uses fast call Issue #27128: import_name() now calls _PyObject_FastCall() to avoid the creation of a temporary tuple. --- Python/ceval.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 905859ef3b..d16e93207a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5247,7 +5247,8 @@ static PyObject * import_name(PyFrameObject *f, PyObject *name, PyObject *fromlist, PyObject *level) { _Py_IDENTIFIER(__import__); - PyObject *import_func, *args, *res; + PyObject *import_func, *res; + PyObject* stack[5]; import_func = _PyDict_GetItemId(f->f_builtins, &PyId___import__); if (import_func == NULL) { @@ -5271,18 +5272,13 @@ import_name(PyFrameObject *f, PyObject *name, PyObject *fromlist, PyObject *leve } Py_INCREF(import_func); - args = PyTuple_Pack(5, - name, - f->f_globals, - f->f_locals == NULL ? Py_None : f->f_locals, - fromlist, - level); - if (args == NULL) { - Py_DECREF(import_func); - return NULL; - } - res = PyEval_CallObject(import_func, args); - Py_DECREF(args); + + stack[0] = name; + stack[1] = f->f_globals; + stack[2] = f->f_locals == NULL ? Py_None : f->f_locals; + stack[3] = fromlist; + stack[4] = level; + res = _PyObject_FastCall(import_func, stack, 5, NULL); Py_DECREF(import_func); return res; } -- cgit v1.2.1 From da778ce9a4619576587ed97e96041d101c489129 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 22 Aug 2016 12:29:42 +0200 Subject: Optimize call to Python function without argument Issue #27128. When a Python function is called with no arguments, but all parameters have a default value: use default values as arguments for the fast path. --- Python/ceval.c | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index d16e93207a..bd0cbe758f 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4801,8 +4801,8 @@ call_function(PyObject ***pp_stack, int oparg */ static PyObject* -_PyFunction_FastCallNoKw(PyObject **args, Py_ssize_t na, - PyCodeObject *co, PyObject *globals) +_PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t na, + PyObject *globals) { PyFrameObject *f; PyThreadState *tstate = PyThreadState_GET(); @@ -4837,8 +4837,10 @@ _PyFunction_FastCallNoKw(PyObject **args, Py_ssize_t na, return result; } +/* Similar to _PyFunction_FastCall() but keywords are passed a (key, value) + pairs in stack */ static PyObject * -fast_function(PyObject *func, PyObject **stack, int n, int na, int nk) +fast_function(PyObject *func, PyObject **stack, int n, int nargs, int nk) { PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); PyObject *globals = PyFunction_GET_GLOBALS(func); @@ -4850,11 +4852,20 @@ fast_function(PyObject *func, PyObject **stack, int n, int na, int nk) PCALL(PCALL_FUNCTION); PCALL(PCALL_FAST_FUNCTION); - if (argdefs == NULL && co->co_argcount == na && - co->co_kwonlyargcount == 0 && nk == 0 && + if (co->co_kwonlyargcount == 0 && nk == 0 && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { - return _PyFunction_FastCallNoKw(stack, na, co, globals); + if (argdefs == NULL && co->co_argcount == nargs) { + return _PyFunction_FastCallNoKw(co, stack, nargs, globals); + } + else if (nargs == 0 && argdefs != NULL + && co->co_argcount == Py_SIZE(argdefs)) { + /* function called with no arguments, but all parameters have + a default value: use default values as arguments .*/ + stack = &PyTuple_GET_ITEM(argdefs, 0); + return _PyFunction_FastCallNoKw(co, stack, Py_SIZE(argdefs), + globals); + } } kwdefs = PyFunction_GET_KW_DEFAULTS(func); @@ -4871,8 +4882,8 @@ fast_function(PyObject *func, PyObject **stack, int n, int na, int nk) nd = 0; } return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, - stack, na, - stack + na, nk, + stack, nargs, + stack + nargs, nk, d, nd, kwdefs, closure, name, qualname); } @@ -4893,11 +4904,20 @@ _PyFunction_FastCall(PyObject *func, PyObject **args, int nargs, PyObject *kwarg /* issue #27128: support for keywords will come later */ assert(kwargs == NULL); - if (argdefs == NULL && co->co_argcount == nargs && - co->co_kwonlyargcount == 0 && + if (co->co_kwonlyargcount == 0 && kwargs == NULL && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { - return _PyFunction_FastCallNoKw(args, nargs, co, globals); + if (argdefs == NULL && co->co_argcount == nargs) { + return _PyFunction_FastCallNoKw(co, args, nargs, globals); + } + else if (nargs == 0 && argdefs != NULL + && co->co_argcount == Py_SIZE(argdefs)) { + /* function called with no arguments, but all parameters have + a default value: use default values as arguments .*/ + args = &PyTuple_GET_ITEM(argdefs, 0); + return _PyFunction_FastCallNoKw(co, args, Py_SIZE(argdefs), + globals); + } } kwdefs = PyFunction_GET_KW_DEFAULTS(func); -- cgit v1.2.1 From de03c6d8066496680a1ea99ff9e67e28852b0007 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 22 Aug 2016 22:48:54 +0200 Subject: Rename _PyObject_FastCall() to _PyObject_FastCallDict() Issue #27809: * Rename _PyObject_FastCall() function to _PyObject_FastCallDict() * Add _PyObject_FastCall(), _PyObject_CallNoArg() and _PyObject_CallArg1() macros calling _PyObject_FastCallDict() --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index bd0cbe758f..96380bca96 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4593,7 +4593,7 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *args, PyObject *kwargs) if (args == NULL) { if (kwargs == NULL) { - return _PyObject_FastCall(func, NULL, 0, 0); + return _PyObject_CallNoArg(func); } args = PyTuple_New(0); @@ -5298,7 +5298,7 @@ import_name(PyFrameObject *f, PyObject *name, PyObject *fromlist, PyObject *leve stack[2] = f->f_locals == NULL ? Py_None : f->f_locals; stack[3] = fromlist; stack[4] = level; - res = _PyObject_FastCall(import_func, stack, 5, NULL); + res = _PyObject_FastCall(import_func, stack, 5); Py_DECREF(import_func); return res; } -- cgit v1.2.1 From 9dae6156b5e3127cb8bfaa1bd36cecadaad5d60b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 22 Aug 2016 23:15:44 +0200 Subject: _PyFunction_FastCallDict() supports keyword args Issue #27809: * Rename _PyFunction_FastCall() to _PyFunction_FastCallDict() * Rename _PyCFunction_FastCall() to _PyCFunction_FastCallDict() * _PyFunction_FastCallDict() now supports keyword arguments --- Python/ceval.c | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 96380bca96..d656fab3ee 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4889,24 +4889,29 @@ fast_function(PyObject *func, PyObject **stack, int n, int nargs, int nk) } PyObject * -_PyFunction_FastCall(PyObject *func, PyObject **args, int nargs, PyObject *kwargs) +_PyFunction_FastCallDict(PyObject *func, PyObject **args, int nargs, + PyObject *kwargs) { PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); PyObject *globals = PyFunction_GET_GLOBALS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func); PyObject *kwdefs, *closure, *name, *qualname; + PyObject *kwtuple, **k; PyObject **d; int nd; + Py_ssize_t nk; + PyObject *result; PCALL(PCALL_FUNCTION); PCALL(PCALL_FAST_FUNCTION); - /* issue #27128: support for keywords will come later */ - assert(kwargs == NULL); + assert(kwargs == NULL || PyDict_Check(kwargs)); - if (co->co_kwonlyargcount == 0 && kwargs == NULL && + if (co->co_kwonlyargcount == 0 && + (kwargs == NULL || PyDict_Size(kwargs) == 0) && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { + /* Fast paths */ if (argdefs == NULL && co->co_argcount == nargs) { return _PyFunction_FastCallNoKw(co, args, nargs, globals); } @@ -4920,6 +4925,30 @@ _PyFunction_FastCall(PyObject *func, PyObject **args, int nargs, PyObject *kwarg } } + if (kwargs != NULL) { + Py_ssize_t pos, i; + nk = PyDict_Size(kwargs); + + kwtuple = PyTuple_New(2 * nk); + if (kwtuple == NULL) { + return NULL; + } + + k = &PyTuple_GET_ITEM(kwtuple, 0); + pos = i = 0; + while (PyDict_Next(kwargs, &pos, &k[i], &k[i+1])) { + Py_INCREF(k[i]); + Py_INCREF(k[i+1]); + i += 2; + } + nk = i / 2; + } + else { + kwtuple = NULL; + k = NULL; + nk = 0; + } + kwdefs = PyFunction_GET_KW_DEFAULTS(func); closure = PyFunction_GET_CLOSURE(func); name = ((PyFunctionObject *)func) -> func_name; @@ -4933,11 +4962,14 @@ _PyFunction_FastCall(PyObject *func, PyObject **args, int nargs, PyObject *kwarg d = NULL; nd = 0; } - return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, - args, nargs, - NULL, 0, - d, nd, kwdefs, - closure, name, qualname); + + result = _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, + args, nargs, + k, (int)nk, + d, nd, kwdefs, + closure, name, qualname); + Py_XDECREF(kwtuple); + return result; } static PyObject * -- cgit v1.2.1 From 02a024beaef0a0ae5b8cc2151ba0314fd58f6fe4 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 22 Aug 2016 23:17:30 +0200 Subject: Issue #27809: Cleanup _PyEval_EvalCodeWithName() * Rename nm to name * PEP 7: add { ... } to if/else blocks --- Python/ceval.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index d656fab3ee..9c6593791f 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3878,28 +3878,28 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, 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) + PyObject *name = co_varnames[j]; + if (name == 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) + PyObject *name = co_varnames[j]; + int cmp = PyObject_RichCompareBool( keyword, name, Py_EQ); + if (cmp > 0) { goto kw_found; - else if (cmp < 0) + } + 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); + "%U() got an unexpected keyword argument '%S'", + co->co_name, keyword); goto fail; } @@ -3911,10 +3911,8 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, kw_found: if (GETLOCAL(j) != NULL) { PyErr_Format(PyExc_TypeError, - "%U() got multiple " - "values for argument '%S'", - co->co_name, - keyword); + "%U() got multiple values for argument '%S'", + co->co_name, keyword); goto fail; } Py_INCREF(value); -- cgit v1.2.1 From 947bc084fc2339a44e3f55169653edd8bd17c248 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 22 Aug 2016 23:26:00 +0200 Subject: PyEval_CallObjectWithKeywords() uses fast call with kwargs Issue #27809. _PyObject_FastCallDict() now supports keyword arguments, and so the args==NULL fast-path can also be used when kwargs is not NULL. --- Python/ceval.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 9c6593791f..fd456ceed1 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4590,30 +4590,22 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *args, PyObject *kwargs) #endif if (args == NULL) { - if (kwargs == NULL) { - return _PyObject_CallNoArg(func); - } - - args = PyTuple_New(0); - if (args == NULL) - return NULL; + return _PyObject_FastCallDict(func, NULL, 0, kwargs); } - else if (!PyTuple_Check(args)) { + + if (!PyTuple_Check(args)) { PyErr_SetString(PyExc_TypeError, "argument list must be a tuple"); return NULL; } - else { - Py_INCREF(args); - } if (kwargs != NULL && !PyDict_Check(kwargs)) { PyErr_SetString(PyExc_TypeError, "keyword list must be a dictionary"); - Py_DECREF(args); return NULL; } + Py_INCREF(args); result = PyObject_Call(func, args, kwargs); Py_DECREF(args); -- cgit v1.2.1 From 2106b8b6a210490c36471334570461db73cfddee Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 23 Aug 2016 00:25:01 +0200 Subject: PyEval_CallObjectWithKeywords() doesn't inc/decref Issue #27809: PyEval_CallObjectWithKeywords() doesn't increment temporary the reference counter of the args tuple (positional arguments). The caller already holds a strong reference to it. --- Python/ceval.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index fd456ceed1..f9759f0f70 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4580,8 +4580,6 @@ PyEval_MergeCompilerFlags(PyCompilerFlags *cf) PyObject * PyEval_CallObjectWithKeywords(PyObject *func, PyObject *args, PyObject *kwargs) { - PyObject *result; - #ifdef Py_DEBUG /* PyEval_CallObjectWithKeywords() must not be called with an exception set. It raises a new exception if parameters are invalid or if @@ -4605,11 +4603,7 @@ PyEval_CallObjectWithKeywords(PyObject *func, PyObject *args, PyObject *kwargs) return NULL; } - Py_INCREF(args); - result = PyObject_Call(func, args, kwargs); - Py_DECREF(args); - - return result; + return PyObject_Call(func, args, kwargs); } const char * -- cgit v1.2.1 From c79e5123bcf2d1e514a0420e89c62fa334d57ba9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 25 Aug 2016 00:04:09 +0200 Subject: Use Py_ssize_t type for number of arguments Issue #27848: use Py_ssize_t rather than C int for the number of function positional and keyword arguments. --- Python/ceval.c | 197 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 114 insertions(+), 83 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index f9759f0f70..b082760d3f 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -113,13 +113,13 @@ static PyObject * call_function(PyObject ***, int, uint64*, uint64*); #else static PyObject * call_function(PyObject ***, int); #endif -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 ***, +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 ***, PyObject *); -static PyObject * update_star_args(int, int, PyObject *, PyObject ***); -static PyObject * load_args(PyObject ***, int); +static PyObject * update_star_args(Py_ssize_t, Py_ssize_t, PyObject *, PyObject ***); +static PyObject * load_args(PyObject ***, Py_ssize_t); #define CALL_FLAG_VAR 1 #define CALL_FLAG_KW 2 @@ -2558,7 +2558,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(BUILD_TUPLE_UNPACK) TARGET(BUILD_LIST_UNPACK) { int convert_to_tuple = opcode == BUILD_TUPLE_UNPACK; - int i; + Py_ssize_t i; PyObject *sum = PyList_New(0); PyObject *return_value; if (sum == NULL) @@ -2611,7 +2611,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } TARGET(BUILD_SET_UNPACK) { - int i; + Py_ssize_t i; PyObject *sum = PySet_New(NULL); if (sum == NULL) goto error; @@ -2630,7 +2630,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } TARGET(BUILD_MAP) { - int i; + Py_ssize_t i; PyObject *map = _PyDict_NewPresized((Py_ssize_t)oparg); if (map == NULL) goto error; @@ -2654,7 +2654,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } TARGET(BUILD_CONST_KEY_MAP) { - int i; + Py_ssize_t i; PyObject *map; PyObject *keys = TOP(); if (!PyTuple_CheckExact(keys) || @@ -2691,7 +2691,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL; int num_maps; int function_location; - int i; + Py_ssize_t i; PyObject *sum = PyDict_New(); if (sum == NULL) goto error; @@ -3265,16 +3265,20 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(CALL_FUNCTION_VAR) TARGET(CALL_FUNCTION_KW) TARGET(CALL_FUNCTION_VAR_KW) { - int na = oparg & 0xff; - int nk = (oparg>>8) & 0xff; + Py_ssize_t nargs = oparg & 0xff; + Py_ssize_t nkwargs = (oparg>>8) & 0xff; int flags = (opcode - CALL_FUNCTION) & 3; - int n = na + 2 * nk; + Py_ssize_t n; PyObject **pfunc, *func, **sp, *res; PCALL(PCALL_ALL); - if (flags & CALL_FLAG_VAR) + + n = nargs + 2 * nkwargs; + if (flags & CALL_FLAG_VAR) { n++; - if (flags & CALL_FLAG_KW) + } + if (flags & CALL_FLAG_KW) { n++; + } pfunc = stack_pointer - n - 1; func = *pfunc; @@ -3285,13 +3289,14 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) func = PyMethod_GET_FUNCTION(func); Py_INCREF(func); Py_SETREF(*pfunc, self); - na++; - /* n++; */ - } else + nargs++; + } + else { Py_INCREF(func); + } sp = stack_pointer; READ_TIMESTAMP(intr0); - res = ext_do_call(func, &sp, flags, na, nk); + res = ext_do_call(func, &sp, flags, nargs, nkwargs); READ_TIMESTAMP(intr1); stack_pointer = sp; Py_DECREF(func); @@ -3579,9 +3584,11 @@ fast_yield: state came into existence in this frame. (An uncaught exception would have why == WHY_EXCEPTION, and we wouldn't be here). */ int i; - for (i = 0; i < f->f_iblock; i++) - if (f->f_blockstack[i].b_type == EXCEPT_HANDLER) + for (i = 0; i < f->f_iblock; i++) { + if (f->f_blockstack[i].b_type == EXCEPT_HANDLER) { break; + } + } if (i == f->f_iblock) /* We did not create this exception. */ restore_and_clear_exc_state(tstate, f); @@ -3692,12 +3699,12 @@ format_missing(const char *kind, PyCodeObject *co, PyObject *names) } static void -missing_arguments(PyCodeObject *co, int missing, int defcount, +missing_arguments(PyCodeObject *co, Py_ssize_t missing, Py_ssize_t defcount, PyObject **fastlocals) { - int i, j = 0; - int start, end; - int positional = defcount != -1; + Py_ssize_t i, j = 0; + Py_ssize_t start, end; + int positional = (defcount != -1); const char *kind = positional ? "positional" : "keyword-only"; PyObject *missing_names; @@ -3730,33 +3737,39 @@ missing_arguments(PyCodeObject *co, int missing, int defcount, } static void -too_many_positional(PyCodeObject *co, int given, int defcount, PyObject **fastlocals) +too_many_positional(PyCodeObject *co, Py_ssize_t given, Py_ssize_t defcount, + PyObject **fastlocals) { int plural; - int kwonly_given = 0; - int i; + Py_ssize_t kwonly_given = 0; + Py_ssize_t i; PyObject *sig, *kwonly_sig; + Py_ssize_t co_argcount = co->co_argcount; assert((co->co_flags & CO_VARARGS) == 0); /* Count missing keyword-only args. */ - for (i = co->co_argcount; i < co->co_argcount + co->co_kwonlyargcount; i++) - if (GETLOCAL(i) != NULL) + for (i = co_argcount; i < co_argcount + co->co_kwonlyargcount; i++) { + if (GETLOCAL(i) != NULL) { kwonly_given++; + } + } if (defcount) { - int atleast = co->co_argcount - defcount; + Py_ssize_t atleast = co_argcount - defcount; plural = 1; - sig = PyUnicode_FromFormat("from %d to %d", atleast, co->co_argcount); + sig = PyUnicode_FromFormat("from %zd to %zd", atleast, co_argcount); } else { - plural = co->co_argcount != 1; - sig = PyUnicode_FromFormat("%d", co->co_argcount); + plural = (co_argcount != 1); + sig = PyUnicode_FromFormat("%zd", co_argcount); } if (sig == NULL) return; if (kwonly_given) { - const char *format = " positional argument%s (and %d keyword-only argument%s)"; - kwonly_sig = PyUnicode_FromFormat(format, given != 1 ? "s" : "", kwonly_given, - kwonly_given != 1 ? "s" : ""); + const char *format = " positional argument%s (and %zd keyword-only argument%s)"; + kwonly_sig = PyUnicode_FromFormat(format, + given != 1 ? "s" : "", + kwonly_given, + kwonly_given != 1 ? "s" : ""); if (kwonly_sig == NULL) { Py_DECREF(sig); return; @@ -3768,7 +3781,7 @@ too_many_positional(PyCodeObject *co, int given, int defcount, PyObject **fastlo assert(kwonly_sig != NULL); } PyErr_Format(PyExc_TypeError, - "%U() takes %U positional argument%s but %d%U %s given", + "%U() takes %U positional argument%s but %zd%U %s given", co->co_name, sig, plural ? "s" : "", @@ -3786,8 +3799,10 @@ too_many_positional(PyCodeObject *co, int given, int defcount, PyObject **fastlo 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 **args, Py_ssize_t argcount, + PyObject **kws, Py_ssize_t kwcount, + PyObject **defs, Py_ssize_t defcount, + PyObject *kwdefs, PyObject *closure, PyObject *name, PyObject *qualname) { PyCodeObject* co = (PyCodeObject*)_co; @@ -4633,16 +4648,16 @@ PyEval_GetFuncDesc(PyObject *func) } static void -err_args(PyObject *func, int flags, int nargs) +err_args(PyObject *func, int flags, Py_ssize_t nargs) { if (flags & METH_NOARGS) PyErr_Format(PyExc_TypeError, - "%.200s() takes no arguments (%d given)", + "%.200s() takes no arguments (%zd given)", ((PyCFunctionObject *)func)->m_ml->ml_name, nargs); else PyErr_Format(PyExc_TypeError, - "%.200s() takes exactly one argument (%d given)", + "%.200s() takes exactly one argument (%zd given)", ((PyCFunctionObject *)func)->m_ml->ml_name, nargs); } @@ -4685,9 +4700,9 @@ call_function(PyObject ***pp_stack, int oparg #endif ) { - int na = oparg & 0xff; - int nk = (oparg>>8) & 0xff; - int n = na + 2 * nk; + Py_ssize_t nargs = oparg & 0xff; + Py_ssize_t nkwargs = (oparg>>8) & 0xff; + int n = nargs + 2 * nkwargs; PyObject **pfunc = (*pp_stack) - n - 1; PyObject *func = *pfunc; PyObject *x, *w; @@ -4695,7 +4710,7 @@ call_function(PyObject ***pp_stack, int oparg /* Always dispatch PyCFunction first, because these are presumed to be the most frequent callable object. */ - if (PyCFunction_Check(func) && nk == 0) { + if (PyCFunction_Check(func) && nkwargs == 0) { int flags = PyCFunction_GET_FLAGS(func); PyThreadState *tstate = PyThreadState_GET(); @@ -4703,12 +4718,12 @@ call_function(PyObject ***pp_stack, int oparg if (flags & (METH_NOARGS | METH_O)) { PyCFunction meth = PyCFunction_GET_FUNCTION(func); PyObject *self = PyCFunction_GET_SELF(func); - if (flags & METH_NOARGS && na == 0) { + if (flags & METH_NOARGS && nargs == 0) { C_TRACE(x, (*meth)(self,NULL)); x = _Py_CheckFunctionResult(func, x, NULL); } - else if (flags & METH_O && na == 1) { + else if (flags & METH_O && nargs == 1) { PyObject *arg = EXT_POP(*pp_stack); C_TRACE(x, (*meth)(self,arg)); Py_DECREF(arg); @@ -4716,13 +4731,13 @@ call_function(PyObject ***pp_stack, int oparg x = _Py_CheckFunctionResult(func, x, NULL); } else { - err_args(func, flags, na); + err_args(func, flags, nargs); x = NULL; } } else { PyObject *callargs; - callargs = load_args(pp_stack, na); + callargs = load_args(pp_stack, nargs); if (callargs != NULL) { READ_TIMESTAMP(*pintr0); C_TRACE(x, PyCFunction_Call(func,callargs,NULL)); @@ -4744,16 +4759,18 @@ call_function(PyObject ***pp_stack, int oparg func = PyMethod_GET_FUNCTION(func); Py_INCREF(func); Py_SETREF(*pfunc, self); - na++; + nargs++; n++; - } else + } + else { Py_INCREF(func); + } READ_TIMESTAMP(*pintr0); if (PyFunction_Check(func)) { - x = fast_function(func, (*pp_stack) - n, n, na, nk); + x = fast_function(func, (*pp_stack) - n, nargs, nkwargs); } else { - x = do_call(func, pp_stack, na, nk); + x = do_call(func, pp_stack, nargs, nkwargs); } READ_TIMESTAMP(*pintr1); Py_DECREF(func); @@ -4785,7 +4802,7 @@ call_function(PyObject ***pp_stack, int oparg */ static PyObject* -_PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t na, +_PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t nargs, PyObject *globals) { PyFrameObject *f; @@ -4808,7 +4825,7 @@ _PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t na, fastlocals = f->f_localsplus; - for (i = 0; i < na; i++) { + for (i = 0; i < nargs; i++) { Py_INCREF(*args); fastlocals[i] = *args++; } @@ -4824,7 +4841,7 @@ _PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t na, /* Similar to _PyFunction_FastCall() but keywords are passed a (key, value) pairs in stack */ static PyObject * -fast_function(PyObject *func, PyObject **stack, int n, int nargs, int nk) +fast_function(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); @@ -4836,7 +4853,7 @@ fast_function(PyObject *func, PyObject **stack, int n, int nargs, int nk) PCALL(PCALL_FUNCTION); PCALL(PCALL_FAST_FUNCTION); - if (co->co_kwonlyargcount == 0 && nk == 0 && + if (co->co_kwonlyargcount == 0 && nkwargs == 0 && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { if (argdefs == NULL && co->co_argcount == nargs) { @@ -4867,13 +4884,13 @@ fast_function(PyObject *func, PyObject **stack, int n, int nargs, int nk) } return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, stack, nargs, - stack + nargs, nk, + stack + nargs, nkwargs, d, nd, kwdefs, closure, name, qualname); } PyObject * -_PyFunction_FastCallDict(PyObject *func, PyObject **args, int nargs, +_PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, PyObject *kwargs) { PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); @@ -4882,13 +4899,15 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, int nargs, PyObject *kwdefs, *closure, *name, *qualname; PyObject *kwtuple, **k; PyObject **d; - int nd; - Py_ssize_t nk; + 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)); if (co->co_kwonlyargcount == 0 && @@ -4949,7 +4968,7 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, int nargs, result = _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, args, nargs, - k, (int)nk, + k, nk, d, nd, kwdefs, closure, name, qualname); Py_XDECREF(kwtuple); @@ -4957,7 +4976,7 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, int nargs, } static PyObject * -update_keyword_args(PyObject *orig_kwdict, int nk, PyObject ***pp_stack, +update_keyword_args(PyObject *orig_kwdict, Py_ssize_t nk, PyObject ***pp_stack, PyObject *func) { PyObject *kwdict = NULL; @@ -4997,7 +5016,7 @@ update_keyword_args(PyObject *orig_kwdict, int nk, PyObject ***pp_stack, } static PyObject * -update_star_args(int nstack, int nstar, PyObject *stararg, +update_star_args(Py_ssize_t nstack, Py_ssize_t nstar, PyObject *stararg, PyObject ***pp_stack) { PyObject *callargs, *w; @@ -5021,14 +5040,16 @@ update_star_args(int nstack, int nstar, PyObject *stararg, if (callargs == NULL) { return NULL; } + if (nstar) { - int i; + Py_ssize_t i; for (i = 0; i < nstar; i++) { - PyObject *a = PyTuple_GET_ITEM(stararg, i); - Py_INCREF(a); - PyTuple_SET_ITEM(callargs, nstack + i, a); + PyObject *arg = PyTuple_GET_ITEM(stararg, i); + Py_INCREF(arg); + PyTuple_SET_ITEM(callargs, nstack + i, arg); } } + while (--nstack >= 0) { w = EXT_POP(*pp_stack); PyTuple_SET_ITEM(callargs, nstack, w); @@ -5037,7 +5058,7 @@ update_star_args(int nstack, int nstar, PyObject *stararg, } static PyObject * -load_args(PyObject ***pp_stack, int na) +load_args(PyObject ***pp_stack, Py_ssize_t na) { PyObject *args = PyTuple_New(na); PyObject *w; @@ -5052,18 +5073,18 @@ load_args(PyObject ***pp_stack, int na) } static PyObject * -do_call(PyObject *func, PyObject ***pp_stack, int na, int nk) +do_call(PyObject *func, PyObject ***pp_stack, Py_ssize_t nargs, Py_ssize_t nkwargs) { PyObject *callargs = NULL; PyObject *kwdict = NULL; PyObject *result = NULL; - if (nk > 0) { - kwdict = update_keyword_args(NULL, nk, pp_stack, func); + if (nkwargs > 0) { + kwdict = update_keyword_args(NULL, nkwargs, pp_stack, func); if (kwdict == NULL) goto call_fail; } - callargs = load_args(pp_stack, na); + callargs = load_args(pp_stack, nargs); if (callargs == NULL) goto call_fail; #ifdef CALL_PROFILE @@ -5095,9 +5116,10 @@ call_fail: } static PyObject * -ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) +ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, + Py_ssize_t nargs, Py_ssize_t nkwargs) { - int nstar = 0; + Py_ssize_t nstar; PyObject *callargs = NULL; PyObject *stararg = NULL; PyObject *kwdict = NULL; @@ -5132,8 +5154,9 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) kwdict = d; } } - if (nk > 0) { - kwdict = update_keyword_args(kwdict, nk, pp_stack, func); + + if (nkwargs > 0) { + kwdict = update_keyword_args(kwdict, nkwargs, pp_stack, func); if (kwdict == NULL) goto ext_call_fail; } @@ -5161,9 +5184,15 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) } nstar = PyTuple_GET_SIZE(stararg); } - callargs = update_star_args(na, nstar, stararg, pp_stack); - if (callargs == NULL) + else { + nstar = 0; + } + + callargs = update_star_args(nargs, nstar, stararg, pp_stack); + if (callargs == NULL) { goto ext_call_fail; + } + #ifdef CALL_PROFILE /* At this point, we have to look at the type of func to update the call stats properly. Do it here so as to avoid @@ -5184,8 +5213,10 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) PyThreadState *tstate = PyThreadState_GET(); C_TRACE(result, PyCFunction_Call(func, callargs, kwdict)); } - else + else { result = PyObject_Call(func, callargs, kwdict); + } + ext_call_fail: Py_XDECREF(callargs); Py_XDECREF(kwdict); -- cgit v1.2.1 From 5f2acb2c1a143794ef61db34590ceee4b2d89680 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 25 Aug 2016 00:29:32 +0200 Subject: 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. --- Python/ceval.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index b082760d3f..266b987b4c 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)) -- cgit v1.2.1 From 2c15df27be9ecb04e0c14313de88a6d9299cb261 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 25 Aug 2016 23:26:50 +0200 Subject: Issue #27830: Revert, remove _PyFunction_FastCallKeywords() --- Python/ceval.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 266b987b4c..00d52b4b8d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -113,6 +113,7 @@ 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 ***, @@ -4766,7 +4767,7 @@ call_function(PyObject ***pp_stack, int oparg } READ_TIMESTAMP(*pintr0); if (PyFunction_Check(func)) { - x = _PyFunction_FastCallKeywords(func, (*pp_stack) - n, nargs, nkwargs); + x = fast_function(func, (*pp_stack) - n, nargs, nkwargs); } else { x = do_call(func, pp_stack, nargs, nkwargs); @@ -4779,7 +4780,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 - (_PyFunction_FastCallKeywords() and err_args() leave them on the stack). + (fast_function() and err_args() leave them on the stack). */ while ((*pp_stack) > pfunc) { w = EXT_POP(*pp_stack); @@ -4791,7 +4792,7 @@ call_function(PyObject ***pp_stack, int oparg return x; } -/* The _PyFunction_FastCallKeywords() function optimize calls for which no argument +/* The fast_function() 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 @@ -4839,9 +4840,8 @@ _PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t nargs, /* Similar to _PyFunction_FastCall() but keywords are passed a (key, value) pairs in stack */ -PyObject * -_PyFunction_FastCallKeywords(PyObject *func, PyObject **stack, - Py_ssize_t nargs, Py_ssize_t nkwargs) +static PyObject * +fast_function(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); -- cgit v1.2.1 From 4846ba81c69ebc815b35a89ed54fef7b5ffb1417 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 30 Aug 2016 10:47:49 -0700 Subject: Issue #27895: Spelling fixes (Contributed by Ville Skytt?). --- Python/ceval.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 00d52b4b8d..5a542f0063 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2090,16 +2090,16 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(YIELD_FROM) { PyObject *v = POP(); - PyObject *reciever = TOP(); + PyObject *receiver = TOP(); int err; - if (PyGen_CheckExact(reciever) || PyCoro_CheckExact(reciever)) { - retval = _PyGen_Send((PyGenObject *)reciever, v); + if (PyGen_CheckExact(receiver) || PyCoro_CheckExact(receiver)) { + retval = _PyGen_Send((PyGenObject *)receiver, v); } else { _Py_IDENTIFIER(send); if (v == Py_None) - retval = Py_TYPE(reciever)->tp_iternext(reciever); + retval = Py_TYPE(receiver)->tp_iternext(receiver); else - retval = _PyObject_CallMethodIdObjArgs(reciever, &PyId_send, v, NULL); + retval = _PyObject_CallMethodIdObjArgs(receiver, &PyId_send, v, NULL); } Py_DECREF(v); if (retval == NULL) { @@ -2110,7 +2110,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) err = _PyGen_FetchStopIterationValue(&val); if (err < 0) goto error; - Py_DECREF(reciever); + Py_DECREF(receiver); SET_TOP(val); DISPATCH(); } -- cgit v1.2.1 From 0b77d53b22873d0be290d30321a03f4769489ddc Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Mon, 5 Sep 2016 15:33:46 -0700 Subject: Implement the frame evaluation API aspect of PEP 523. --- Python/ceval.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 5a542f0063..05563a001c 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -796,6 +796,13 @@ PyEval_EvalFrame(PyFrameObject *f) { PyObject * PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) +{ + PyThreadState *tstate = PyThreadState_GET(); + return tstate->interp->eval_frame(f, throwflag); +} + +PyObject * +_PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) { #ifdef DXPAIRS int lastopcode = 0; -- cgit v1.2.1 From 0c3c0375111bde5f7586e6c951f08a0035039a39 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 6 Sep 2016 22:07:53 +0300 Subject: Issue #27078: Added BUILD_STRING opcode. Optimized f-strings evaluation. --- Python/ceval.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 05563a001c..bf19a5b2b4 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2538,6 +2538,24 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(BUILD_STRING) { + PyObject *str; + PyObject *empty = PyUnicode_New(0, 0); + if (empty == NULL) { + goto error; + } + str = _PyUnicode_JoinArray(empty, stack_pointer - oparg, oparg); + Py_DECREF(empty); + if (str == NULL) + goto error; + while (--oparg >= 0) { + PyObject *item = POP(); + Py_DECREF(item); + } + PUSH(str); + DISPATCH(); + } + TARGET(BUILD_TUPLE) { PyObject *tup = PyTuple_New(oparg); if (tup == NULL) -- cgit v1.2.1 From 7cbba2deb3fc49bd66e11b34f8e210371bac5913 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Wed, 7 Sep 2016 11:16:41 -0700 Subject: Add the co_extra field and accompanying APIs to code objects. This completes PEP 523. --- Python/ceval.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index bf19a5b2b4..9109ea59f2 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5608,3 +5608,17 @@ _Py_GetDXProfile(PyObject *self, PyObject *args) } #endif + +Py_ssize_t +_PyEval_RequestCodeExtraIndex(freefunc free) +{ + PyThreadState *tstate = PyThreadState_Get(); + Py_ssize_t new_index; + + if (tstate->co_extra_user_count == MAX_CO_EXTRA_USERS - 1) { + return -1; + } + new_index = tstate->co_extra_user_count++; + tstate->co_extra_freefuncs[new_index] = free; + return new_index; +} -- cgit v1.2.1 From 703d5e702019a06489e01aa120c50783e6496ef6 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 8 Sep 2016 14:45:40 -0700 Subject: Merge --- Python/ceval.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 9109ea59f2..0c3ef7b71b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2619,14 +2619,16 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) TARGET(BUILD_SET) { PyObject *set = PySet_New(NULL); int err = 0; + int i; if (set == NULL) goto error; - while (--oparg >= 0) { - PyObject *item = POP(); + for (i = oparg; i > 0; i--) { + PyObject *item = PEEK(i); if (err == 0) err = PySet_Add(set, item); Py_DECREF(item); } + STACKADJ(-oparg); if (err != 0) { Py_DECREF(set); goto error; -- cgit v1.2.1 From b164476aaf77ceffac36ddbbdc7f4df90fbfebd3 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 8 Sep 2016 20:50:03 -0700 Subject: Issue #27985: Implement PEP 526 -- Syntax for Variable Annotations. Patch by Ivan Levkivskyi. --- Python/ceval.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 0c3ef7b71b..a52ee8a582 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1873,6 +1873,62 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(STORE_ANNOTATION) { + _Py_IDENTIFIER(__annotations__); + PyObject *ann_dict; + PyObject *ann = POP(); + PyObject *name = GETITEM(names, oparg); + int err; + if (f->f_locals == NULL) { + PyErr_Format(PyExc_SystemError, + "no locals found when storing annotation"); + Py_DECREF(ann); + goto error; + } + /* first try to get __annotations__ from locals... */ + if (PyDict_CheckExact(f->f_locals)) { + ann_dict = _PyDict_GetItemId(f->f_locals, + &PyId___annotations__); + if (ann_dict == NULL) { + PyErr_SetString(PyExc_NameError, + "__annotations__ not found"); + Py_DECREF(ann); + goto error; + } + Py_INCREF(ann_dict); + } + else { + PyObject *ann_str = _PyUnicode_FromId(&PyId___annotations__); + if (ann_str == NULL) { + Py_DECREF(ann); + goto error; + } + ann_dict = PyObject_GetItem(f->f_locals, ann_str); + if (ann_dict == NULL) { + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_SetString(PyExc_NameError, + "__annotations__ not found"); + } + Py_DECREF(ann); + goto error; + } + } + /* ...if succeeded, __annotations__[name] = ann */ + if (PyDict_CheckExact(ann_dict)) { + err = PyDict_SetItem(ann_dict, name, ann); + } + else { + err = PyObject_SetItem(ann_dict, name, ann); + } + Py_DECREF(ann_dict); + if (err != 0) { + Py_DECREF(ann); + goto error; + } + Py_DECREF(ann); + DISPATCH(); + } + TARGET(DELETE_SUBSCR) { PyObject *sub = TOP(); PyObject *container = SECOND(); @@ -2680,6 +2736,62 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(SETUP_ANNOTATIONS) { + _Py_IDENTIFIER(__annotations__); + int err; + PyObject *ann_dict; + if (f->f_locals == NULL) { + PyErr_Format(PyExc_SystemError, + "no locals found when setting up annotations"); + goto error; + } + /* check if __annotations__ in locals()... */ + if (PyDict_CheckExact(f->f_locals)) { + ann_dict = _PyDict_GetItemId(f->f_locals, + &PyId___annotations__); + if (ann_dict == NULL) { + /* ...if not, create a new one */ + ann_dict = PyDict_New(); + if (ann_dict == NULL) { + goto error; + } + err = _PyDict_SetItemId(f->f_locals, + &PyId___annotations__, ann_dict); + Py_DECREF(ann_dict); + if (err != 0) { + goto error; + } + } + } + else { + /* do the same if locals() is not a dict */ + PyObject *ann_str = _PyUnicode_FromId(&PyId___annotations__); + if (ann_str == NULL) { + break; + } + ann_dict = PyObject_GetItem(f->f_locals, ann_str); + if (ann_dict == NULL) { + if (!PyErr_ExceptionMatches(PyExc_KeyError)) { + goto error; + } + PyErr_Clear(); + ann_dict = PyDict_New(); + if (ann_dict == NULL) { + goto error; + } + err = PyObject_SetItem(f->f_locals, ann_str, ann_dict); + Py_DECREF(ann_dict); + if (err != 0) { + goto error; + } + } + else { + Py_DECREF(ann_dict); + } + } + DISPATCH(); + } + TARGET(BUILD_CONST_KEY_MAP) { Py_ssize_t i; PyObject *map; -- cgit v1.2.1 From 17668cfa5e0e6f50376cc233d9b063b908a19845 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 8 Sep 2016 22:01:51 -0700 Subject: Issue #28003: Implement PEP 525 -- Asynchronous Generators. --- Python/ceval.c | 112 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 83 insertions(+), 29 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index a52ee8a582..f737a2f3a0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1204,7 +1204,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ f->f_executing = 1; - if (co->co_flags & (CO_GENERATOR | CO_COROUTINE)) { + if (co->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) { if (!throwflag && f->f_exc_type != NULL && f->f_exc_type != Py_None) { /* We were in an except handler when we left, restore the exception state which was put aside @@ -2083,36 +2083,45 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) PyObject *aiter = TOP(); PyTypeObject *type = Py_TYPE(aiter); - if (type->tp_as_async != NULL) - getter = type->tp_as_async->am_anext; + if (PyAsyncGen_CheckExact(aiter)) { + awaitable = type->tp_as_async->am_anext(aiter); + if (awaitable == NULL) { + goto error; + } + } else { + if (type->tp_as_async != NULL){ + getter = type->tp_as_async->am_anext; + } - if (getter != NULL) { - next_iter = (*getter)(aiter); - if (next_iter == NULL) { + if (getter != NULL) { + next_iter = (*getter)(aiter); + if (next_iter == NULL) { + goto error; + } + } + else { + PyErr_Format( + PyExc_TypeError, + "'async for' requires an iterator with " + "__anext__ method, got %.100s", + type->tp_name); goto error; } - } - else { - PyErr_Format( - PyExc_TypeError, - "'async for' requires an iterator with " - "__anext__ method, got %.100s", - type->tp_name); - goto error; - } - awaitable = _PyCoro_GetAwaitableIter(next_iter); - if (awaitable == NULL) { - PyErr_Format( - PyExc_TypeError, - "'async for' received an invalid object " - "from __anext__: %.100s", - Py_TYPE(next_iter)->tp_name); + awaitable = _PyCoro_GetAwaitableIter(next_iter); + if (awaitable == NULL) { + PyErr_Format( + PyExc_TypeError, + "'async for' received an invalid object " + "from __anext__: %.100s", + Py_TYPE(next_iter)->tp_name); - Py_DECREF(next_iter); - goto error; - } else - Py_DECREF(next_iter); + Py_DECREF(next_iter); + goto error; + } else { + Py_DECREF(next_iter); + } + } PUSH(awaitable); PREDICT(LOAD_CONST); @@ -2187,6 +2196,17 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) TARGET(YIELD_VALUE) { retval = POP(); + + if (co->co_flags & CO_ASYNC_GENERATOR) { + PyObject *w = _PyAsyncGenValueWrapperNew(retval); + Py_DECREF(retval); + if (w == NULL) { + retval = NULL; + goto error; + } + retval = w; + } + f->f_stacktop = stack_pointer; why = WHY_YIELD; goto fast_yield; @@ -3712,7 +3732,7 @@ fast_block_end: assert((retval != NULL) ^ (PyErr_Occurred() != NULL)); fast_yield: - if (co->co_flags & (CO_GENERATOR | CO_COROUTINE)) { + if (co->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) { /* The purpose of this block is to put aside the generator's exception state and restore that of the calling frame. If the current @@ -4156,8 +4176,8 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o; } - /* Handle generator/coroutine */ - if (co->co_flags & (CO_GENERATOR | CO_COROUTINE)) { + /* Handle generator/coroutine/asynchronous generator */ + if (co->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) { PyObject *gen; PyObject *coro_wrapper = tstate->coroutine_wrapper; int is_coro = co->co_flags & CO_COROUTINE; @@ -4182,6 +4202,8 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, * and return that as the value. */ if (is_coro) { gen = PyCoro_New(f, name, qualname); + } else if (co->co_flags & CO_ASYNC_GENERATOR) { + gen = PyAsyncGen_New(f, name, qualname); } else { gen = PyGen_NewWithQualName(f, name, qualname); } @@ -4660,6 +4682,38 @@ _PyEval_GetCoroutineWrapper(void) return tstate->coroutine_wrapper; } +void +_PyEval_SetAsyncGenFirstiter(PyObject *firstiter) +{ + PyThreadState *tstate = PyThreadState_GET(); + + Py_XINCREF(firstiter); + Py_XSETREF(tstate->async_gen_firstiter, firstiter); +} + +PyObject * +_PyEval_GetAsyncGenFirstiter(void) +{ + PyThreadState *tstate = PyThreadState_GET(); + return tstate->async_gen_firstiter; +} + +void +_PyEval_SetAsyncGenFinalizer(PyObject *finalizer) +{ + PyThreadState *tstate = PyThreadState_GET(); + + Py_XINCREF(finalizer); + Py_XSETREF(tstate->async_gen_finalizer, finalizer); +} + +PyObject * +_PyEval_GetAsyncGenFinalizer(void) +{ + PyThreadState *tstate = PyThreadState_GET(); + return tstate->async_gen_finalizer; +} + PyObject * PyEval_GetBuiltins(void) { -- cgit v1.2.1 From 5f9440b6881420f506ab7cb897c30d14933dcfea Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 8 Sep 2016 23:38:21 -0700 Subject: ceval: tighten the code of STORE_ANNOTATION --- Python/ceval.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index f737a2f3a0..75ec7b2abe 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1921,11 +1921,10 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) err = PyObject_SetItem(ann_dict, name, ann); } Py_DECREF(ann_dict); + Py_DECREF(ann); if (err != 0) { - Py_DECREF(ann); goto error; } - Py_DECREF(ann); DISPATCH(); } -- cgit v1.2.1 From 2b5865ab2c78d3bdaf8ad7047762b5d0d41d8654 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 9 Sep 2016 10:17:08 -0700 Subject: Rework CALL_FUNCTION* opcodes Issue #27213: Rework CALL_FUNCTION* opcodes to produce shorter and more efficient bytecode: * CALL_FUNCTION now only accepts position arguments * CALL_FUNCTION_KW accepts position arguments and keyword arguments, but keys of keyword arguments are packed into a constant tuple. * CALL_FUNCTION_EX is the most generic, it expects a tuple and a dict for positional and keyword arguments. CALL_FUNCTION_VAR and CALL_FUNCTION_VAR_KW opcodes have been removed. 2 tests of test_traceback are currently broken: skip test, the issue #28050 was created to track the issue. Patch by Demur Rumed, design by Serhiy Storchaka, reviewed by Serhiy Storchaka and Victor Stinner. --- Python/ceval.c | 492 ++++++++++++++++++++++++--------------------------------- 1 file changed, 205 insertions(+), 287 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 75ec7b2abe..9b9245ed42 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -109,19 +109,15 @@ typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *); /* Forward declarations */ #ifdef WITH_TSC -static PyObject * call_function(PyObject ***, int, uint64*, uint64*); +static PyObject * call_function(PyObject ***, Py_ssize_t, PyObject *, uint64*, uint64*); #else -static PyObject * call_function(PyObject ***, int); +static PyObject * call_function(PyObject ***, Py_ssize_t, PyObject *); #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 ***, - PyObject *); -static PyObject * update_star_args(Py_ssize_t, Py_ssize_t, PyObject *, PyObject ***); +static PyObject * fast_function(PyObject *, PyObject ***, Py_ssize_t, PyObject *); +static PyObject * do_call(PyObject *, PyObject ***, Py_ssize_t, PyObject *); +static PyObject * do_call_core(PyObject *, PyObject *, PyObject *); +static PyObject * create_keyword_args(PyObject *, PyObject ***, PyObject *); static PyObject * load_args(PyObject ***, Py_ssize_t); -#define CALL_FLAG_VAR 1 -#define CALL_FLAG_KW 2 #ifdef LLTRACE static int lltrace; @@ -2659,8 +2655,14 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) TARGET(BUILD_LIST_UNPACK) { int convert_to_tuple = opcode == BUILD_TUPLE_UNPACK; Py_ssize_t i; - PyObject *sum = PyList_New(0); + PyObject *sum; PyObject *return_value; + + if (convert_to_tuple && oparg == 1 && PyTuple_CheckExact(TOP())) { + DISPATCH(); + } + + sum = PyList_New(0); if (sum == NULL) goto error; @@ -2847,29 +2849,25 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) TARGET(BUILD_MAP_UNPACK_WITH_CALL) TARGET(BUILD_MAP_UNPACK) { int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL; - int num_maps; - int function_location; Py_ssize_t i; - PyObject *sum = PyDict_New(); + PyObject *sum; + + if (with_call && oparg == 1 && PyDict_CheckExact(TOP())) { + DISPATCH(); + } + + sum = PyDict_New(); if (sum == NULL) goto error; - if (with_call) { - num_maps = oparg & 0xff; - function_location = (oparg>>8) & 0xff; - } - else { - num_maps = oparg; - } - for (i = num_maps; i > 0; i--) { + for (i = oparg; i > 0; i--) { PyObject *arg = PEEK(i); - if (with_call) { + if (with_call && PyDict_Size(sum)) { PyObject *intersection = _PyDictView_Intersect(sum, arg); if (intersection == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyObject *func = ( - PEEK(function_location + num_maps)); + PyObject *func = PEEK(2 + oparg); PyErr_Format(PyExc_TypeError, "%.200s%.200s argument after ** " "must be a mapping, not %.200s", @@ -2884,7 +2882,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) if (PySet_GET_SIZE(intersection)) { Py_ssize_t idx = 0; PyObject *key; - PyObject *func = PEEK(function_location + num_maps); + PyObject *func = PEEK(2 + oparg); Py_hash_t hash; _PySet_NextEntry(intersection, &idx, &key, &hash); if (!PyUnicode_Check(key)) { @@ -2918,7 +2916,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) } } - while (num_maps--) + while (oparg--) Py_DECREF(POP()); PUSH(sum); DISPATCH(); @@ -3409,63 +3407,62 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) PCALL(PCALL_ALL); sp = stack_pointer; #ifdef WITH_TSC - res = call_function(&sp, oparg, &intr0, &intr1); + res = call_function(&sp, oparg, NULL, &intr0, &intr1); #else - res = call_function(&sp, oparg); + res = call_function(&sp, oparg, NULL); #endif stack_pointer = sp; PUSH(res); - if (res == NULL) + if (res == NULL) { goto error; + } DISPATCH(); } - TARGET(CALL_FUNCTION_VAR) - TARGET(CALL_FUNCTION_KW) - TARGET(CALL_FUNCTION_VAR_KW) { - Py_ssize_t nargs = oparg & 0xff; - Py_ssize_t nkwargs = (oparg>>8) & 0xff; - int flags = (opcode - CALL_FUNCTION) & 3; - Py_ssize_t n; - PyObject **pfunc, *func, **sp, *res; + TARGET(CALL_FUNCTION_KW) { + PyObject **sp, *res, *names; + + names = POP(); + assert(PyTuple_CheckExact(names) && PyTuple_GET_SIZE(names) <= oparg); PCALL(PCALL_ALL); + sp = stack_pointer; +#ifdef WITH_TSC + res = call_function(&sp, oparg, names, &intr0, &intr1); +#else + res = call_function(&sp, oparg, names); +#endif + stack_pointer = sp; + PUSH(res); + Py_DECREF(names); - n = nargs + 2 * nkwargs; - if (flags & CALL_FLAG_VAR) { - n++; - } - if (flags & CALL_FLAG_KW) { - n++; + if (res == NULL) { + goto error; } - pfunc = stack_pointer - n - 1; - func = *pfunc; + DISPATCH(); + } - if (PyMethod_Check(func) - && PyMethod_GET_SELF(func) != NULL) { - PyObject *self = PyMethod_GET_SELF(func); - Py_INCREF(self); - func = PyMethod_GET_FUNCTION(func); - Py_INCREF(func); - Py_SETREF(*pfunc, self); - nargs++; - } - else { - Py_INCREF(func); + TARGET(CALL_FUNCTION_EX) { + PyObject *func, *callargs, *kwargs = NULL, *result; + PCALL(PCALL_ALL); + if (oparg & 0x01) { + kwargs = POP(); + assert(PyDict_CheckExact(kwargs)); } - sp = stack_pointer; + callargs = POP(); + assert(PyTuple_CheckExact(callargs)); + func = TOP(); + READ_TIMESTAMP(intr0); - res = ext_do_call(func, &sp, flags, nargs, nkwargs); + result = do_call_core(func, callargs, kwargs); READ_TIMESTAMP(intr1); - stack_pointer = sp; Py_DECREF(func); + Py_DECREF(callargs); + Py_XDECREF(kwargs); - while (stack_pointer > pfunc) { - PyObject *o = POP(); - Py_DECREF(o); - } - PUSH(res); - if (res == NULL) + SET_TOP(result); + if (result == NULL) { goto error; + } DISPATCH(); } @@ -3950,7 +3947,6 @@ too_many_positional(PyCodeObject *co, Py_ssize_t given, Py_ssize_t defcount, 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). */ @@ -3959,6 +3955,7 @@ static PyObject * _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyObject **args, Py_ssize_t argcount, PyObject **kws, Py_ssize_t kwcount, + PyObject *kwnames, PyObject **kwstack, PyObject **defs, Py_ssize_t defcount, PyObject *kwdefs, PyObject *closure, PyObject *name, PyObject *qualname) @@ -3972,6 +3969,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount; Py_ssize_t i, n; PyObject *kwdict; + Py_ssize_t kwcount2 = kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames); assert((kwcount == 0) || (kws != NULL)); @@ -4033,7 +4031,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, } } - /* Handle keyword arguments (passed as an array of (key, value)) */ + /* Handle keyword arguments passed as an array of (key, value) pairs */ for (i = 0; i < kwcount; i++) { PyObject **co_varnames; PyObject *keyword = kws[2*i]; @@ -4092,6 +4090,61 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, SETLOCAL(j, value); } + /* Handle keyword arguments passed as keys tuple + values array */ + for (i = 0; i < kwcount2; i++) { + PyObject **co_varnames; + PyObject *keyword = PyTuple_GET_ITEM(kwnames, i); + PyObject *value = kwstack[i]; + 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_found2; + } + /* 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_found2; + 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_found2: + 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); + } + /* Check the number of positional arguments */ if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) { too_many_positional(co, argcount, defcount, fastlocals); @@ -4244,6 +4297,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, return _PyEval_EvalCodeWithName(_co, globals, locals, args, argcount, kws, kwcount, + NULL, NULL, defs, defcount, kwdefs, closure, NULL, NULL); @@ -4886,28 +4940,27 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \ } static PyObject * -call_function(PyObject ***pp_stack, int oparg +call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *names #ifdef WITH_TSC , uint64* pintr0, uint64* pintr1 #endif ) { - Py_ssize_t nargs = oparg & 0xff; - Py_ssize_t nkwargs = (oparg>>8) & 0xff; - int n = nargs + 2 * nkwargs; - PyObject **pfunc = (*pp_stack) - n - 1; + PyObject **pfunc = (*pp_stack) - oparg - 1; PyObject *func = *pfunc; PyObject *x, *w; + Py_ssize_t nk = names == NULL ? 0 : PyTuple_GET_SIZE(names); + Py_ssize_t nargs = oparg - nk; /* Always dispatch PyCFunction first, because these are presumed to be the most frequent callable object. */ - if (PyCFunction_Check(func) && nkwargs == 0) { + if (PyCFunction_Check(func)) { int flags = PyCFunction_GET_FLAGS(func); PyThreadState *tstate = PyThreadState_GET(); PCALL(PCALL_CFUNCTION); - if (flags & (METH_NOARGS | METH_O)) { + if (names == NULL && flags & (METH_NOARGS | METH_O)) { PyCFunction meth = PyCFunction_GET_FUNCTION(func); PyObject *self = PyCFunction_GET_SELF(func); if (flags & METH_NOARGS && nargs == 0) { @@ -4928,48 +4981,57 @@ call_function(PyObject ***pp_stack, int oparg } } else { - PyObject *callargs; + PyObject *callargs, *kwdict = NULL; + if (names != NULL) { + kwdict = create_keyword_args(names, pp_stack, func); + if (kwdict == NULL) { + x = NULL; + goto cfuncerror; + } + } callargs = load_args(pp_stack, nargs); if (callargs != NULL) { READ_TIMESTAMP(*pintr0); - C_TRACE(x, PyCFunction_Call(func,callargs,NULL)); + C_TRACE(x, PyCFunction_Call(func, callargs, kwdict)); READ_TIMESTAMP(*pintr1); - Py_XDECREF(callargs); + Py_DECREF(callargs); } else { x = NULL; } + Py_XDECREF(kwdict); } } else { - if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { - /* optimize access to bound methods */ - PyObject *self = PyMethod_GET_SELF(func); - PCALL(PCALL_METHOD); - PCALL(PCALL_BOUND_METHOD); - Py_INCREF(self); - func = PyMethod_GET_FUNCTION(func); - Py_INCREF(func); - Py_SETREF(*pfunc, self); - nargs++; - n++; - } - else { - Py_INCREF(func); - } - READ_TIMESTAMP(*pintr0); - if (PyFunction_Check(func)) { - x = fast_function(func, (*pp_stack) - n, nargs, nkwargs); - } - else { - x = do_call(func, pp_stack, nargs, nkwargs); - } - READ_TIMESTAMP(*pintr1); - Py_DECREF(func); - - assert((x != NULL) ^ (PyErr_Occurred() != NULL)); + if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { + /* optimize access to bound methods */ + PyObject *self = PyMethod_GET_SELF(func); + PCALL(PCALL_METHOD); + PCALL(PCALL_BOUND_METHOD); + Py_INCREF(self); + func = PyMethod_GET_FUNCTION(func); + Py_INCREF(func); + Py_SETREF(*pfunc, self); + nargs++; + } + else { + Py_INCREF(func); + } + + READ_TIMESTAMP(*pintr0); + if (PyFunction_Check(func)) { + x = fast_function(func, pp_stack, nargs, names); + } else { + x = do_call(func, pp_stack, nargs, names); + } + READ_TIMESTAMP(*pintr1); + + Py_DECREF(func); } +cfuncerror: + assert((x != NULL) ^ (PyErr_Occurred() != NULL)); + /* 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). @@ -4980,7 +5042,6 @@ call_function(PyObject ***pp_stack, int oparg PCALL(PCALL_POP); } - assert((x != NULL) ^ (PyErr_Occurred() != NULL)); return x; } @@ -5033,19 +5094,16 @@ _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) +fast_function(PyObject *func, PyObject ***pp_stack, Py_ssize_t nargs, PyObject *names) { PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); PyObject *globals = PyFunction_GET_GLOBALS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func); PyObject *kwdefs, *closure, *name, *qualname; PyObject **d; - int nd; - - assert(func != NULL); - assert(nargs >= 0); - assert(nkwargs >= 0); - assert((nargs == 0 && nkwargs == 0) || stack != NULL); + Py_ssize_t nkwargs = names == NULL ? 0 : PyTuple_GET_SIZE(names); + Py_ssize_t nd; + PyObject **stack = (*pp_stack)-nargs-nkwargs; PCALL(PCALL_FUNCTION); PCALL(PCALL_FAST_FUNCTION); @@ -5081,8 +5139,9 @@ fast_function(PyObject *func, PyObject **stack, Py_ssize_t nargs, Py_ssize_t nkw } return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, stack, nargs, - stack + nargs, nkwargs, - d, nd, kwdefs, + NULL, 0, + names, stack + nargs, + d, (int)nd, kwdefs, closure, name, qualname); } @@ -5166,6 +5225,7 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, result = _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, args, nargs, k, nk, + NULL, NULL, d, nd, kwdefs, closure, name, qualname); Py_XDECREF(kwtuple); @@ -5173,22 +5233,17 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, } static PyObject * -update_keyword_args(PyObject *orig_kwdict, Py_ssize_t nk, PyObject ***pp_stack, +create_keyword_args(PyObject *names, PyObject ***pp_stack, PyObject *func) { - PyObject *kwdict = NULL; - if (orig_kwdict == NULL) - kwdict = PyDict_New(); - else { - kwdict = PyDict_Copy(orig_kwdict); - Py_DECREF(orig_kwdict); - } + Py_ssize_t nk = PyTuple_GET_SIZE(names); + PyObject *kwdict = _PyDict_NewPresized(nk); if (kwdict == NULL) return NULL; while (--nk >= 0) { int err; PyObject *value = EXT_POP(*pp_stack); - PyObject *key = EXT_POP(*pp_stack); + PyObject *key = PyTuple_GET_ITEM(names, nk); if (PyDict_GetItem(kwdict, key) != NULL) { PyErr_Format(PyExc_TypeError, "%.200s%s got multiple values " @@ -5196,13 +5251,11 @@ update_keyword_args(PyObject *orig_kwdict, Py_ssize_t nk, PyObject ***pp_stack, PyEval_GetFuncName(func), PyEval_GetFuncDesc(func), key); - Py_DECREF(key); Py_DECREF(value); Py_DECREF(kwdict); return NULL; } err = PyDict_SetItem(kwdict, key, value); - Py_DECREF(key); Py_DECREF(value); if (err) { Py_DECREF(kwdict); @@ -5213,183 +5266,51 @@ update_keyword_args(PyObject *orig_kwdict, Py_ssize_t nk, PyObject ***pp_stack, } static PyObject * -update_star_args(Py_ssize_t nstack, Py_ssize_t nstar, PyObject *stararg, - PyObject ***pp_stack) +load_args(PyObject ***pp_stack, Py_ssize_t nargs) { - PyObject *callargs, *w; + PyObject *args = PyTuple_New(nargs); - if (!nstack) { - if (!stararg) { - /* There are no positional arguments on the stack and there is no - sequence to be unpacked. */ - return PyTuple_New(0); - } - if (PyTuple_CheckExact(stararg)) { - /* No arguments are passed on the stack and the sequence is not a - tuple subclass so we can just pass the stararg tuple directly - to the function. */ - Py_INCREF(stararg); - return stararg; - } - } - - callargs = PyTuple_New(nstack + nstar); - if (callargs == NULL) { + if (args == NULL) { return NULL; } - if (nstar) { - Py_ssize_t i; - for (i = 0; i < nstar; i++) { - PyObject *arg = PyTuple_GET_ITEM(stararg, i); - Py_INCREF(arg); - PyTuple_SET_ITEM(callargs, nstack + i, arg); - } - } - - while (--nstack >= 0) { - w = EXT_POP(*pp_stack); - PyTuple_SET_ITEM(callargs, nstack, w); - } - return callargs; -} - -static PyObject * -load_args(PyObject ***pp_stack, Py_ssize_t na) -{ - PyObject *args = PyTuple_New(na); - PyObject *w; - - if (args == NULL) - return NULL; - while (--na >= 0) { - w = EXT_POP(*pp_stack); - PyTuple_SET_ITEM(args, na, w); + while (--nargs >= 0) { + PyObject *arg= EXT_POP(*pp_stack); + PyTuple_SET_ITEM(args, nargs, arg); } return args; } static PyObject * -do_call(PyObject *func, PyObject ***pp_stack, Py_ssize_t nargs, Py_ssize_t nkwargs) +do_call(PyObject *func, PyObject ***pp_stack, Py_ssize_t nargs, PyObject *kwnames) { - PyObject *callargs = NULL; - PyObject *kwdict = NULL; - PyObject *result = NULL; + PyObject *callargs, *kwdict, *result; - if (nkwargs > 0) { - kwdict = update_keyword_args(NULL, nkwargs, pp_stack, func); - if (kwdict == NULL) - goto call_fail; + if (kwnames != NULL) { + kwdict = create_keyword_args(kwnames, pp_stack, func); + if (kwdict == NULL) { + return NULL; + } + } + else { + kwdict = NULL; } + callargs = load_args(pp_stack, nargs); - if (callargs == NULL) - goto call_fail; -#ifdef CALL_PROFILE - /* At this point, we have to look at the type of func to - update the call stats properly. Do it here so as to avoid - exposing the call stats machinery outside ceval.c - */ - if (PyFunction_Check(func)) - PCALL(PCALL_FUNCTION); - else if (PyMethod_Check(func)) - PCALL(PCALL_METHOD); - else if (PyType_Check(func)) - PCALL(PCALL_TYPE); - else if (PyCFunction_Check(func)) - PCALL(PCALL_CFUNCTION); - else - PCALL(PCALL_OTHER); -#endif - if (PyCFunction_Check(func)) { - PyThreadState *tstate = PyThreadState_GET(); - C_TRACE(result, PyCFunction_Call(func, callargs, kwdict)); + if (callargs == NULL) { + Py_XDECREF(kwdict); + return NULL; } - else - result = PyObject_Call(func, callargs, kwdict); -call_fail: + + result = do_call_core(func, callargs, kwdict); Py_XDECREF(callargs); Py_XDECREF(kwdict); return result; } static PyObject * -ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, - Py_ssize_t nargs, Py_ssize_t nkwargs) +do_call_core(PyObject *func, PyObject *callargs, PyObject *kwdict) { - Py_ssize_t nstar; - PyObject *callargs = NULL; - PyObject *stararg = NULL; - PyObject *kwdict = NULL; - PyObject *result = NULL; - - if (flags & CALL_FLAG_KW) { - kwdict = EXT_POP(*pp_stack); - if (!PyDict_CheckExact(kwdict)) { - PyObject *d; - d = PyDict_New(); - if (d == NULL) - goto ext_call_fail; - if (PyDict_Update(d, kwdict) != 0) { - Py_DECREF(d); - /* PyDict_Update raises attribute - * error (percolated from an attempt - * to get 'keys' attribute) instead of - * a type error if its second argument - * is not a mapping. - */ - if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Format(PyExc_TypeError, - "%.200s%.200s argument after ** " - "must be a mapping, not %.200s", - PyEval_GetFuncName(func), - PyEval_GetFuncDesc(func), - kwdict->ob_type->tp_name); - } - goto ext_call_fail; - } - Py_DECREF(kwdict); - kwdict = d; - } - } - - if (nkwargs > 0) { - kwdict = update_keyword_args(kwdict, nkwargs, pp_stack, func); - if (kwdict == NULL) - goto ext_call_fail; - } - - if (flags & CALL_FLAG_VAR) { - stararg = EXT_POP(*pp_stack); - if (!PyTuple_Check(stararg)) { - PyObject *t = NULL; - if (Py_TYPE(stararg)->tp_iter == NULL && - !PySequence_Check(stararg)) { - PyErr_Format(PyExc_TypeError, - "%.200s%.200s argument after * " - "must be an iterable, not %.200s", - PyEval_GetFuncName(func), - PyEval_GetFuncDesc(func), - stararg->ob_type->tp_name); - goto ext_call_fail; - } - t = PySequence_Tuple(stararg); - if (t == NULL) { - goto ext_call_fail; - } - Py_DECREF(stararg); - stararg = t; - } - nstar = PyTuple_GET_SIZE(stararg); - } - else { - nstar = 0; - } - - callargs = update_star_args(nargs, nstar, stararg, pp_stack); - if (callargs == NULL) { - goto ext_call_fail; - } - #ifdef CALL_PROFILE /* At this point, we have to look at the type of func to update the call stats properly. Do it here so as to avoid @@ -5406,19 +5327,16 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, else PCALL(PCALL_OTHER); #endif + if (PyCFunction_Check(func)) { + PyObject *result; PyThreadState *tstate = PyThreadState_GET(); C_TRACE(result, PyCFunction_Call(func, callargs, kwdict)); + return result; } else { - result = PyObject_Call(func, callargs, kwdict); + return PyObject_Call(func, callargs, kwdict); } - -ext_call_fail: - Py_XDECREF(callargs); - Py_XDECREF(kwdict); - Py_XDECREF(stararg); - return result; } /* Extract a slice index from a PyLong or an object with the -- cgit v1.2.1 From 90854a874a441fd07a196e9e8a4a8b6c1ff0e670 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 9 Sep 2016 12:36:44 -0700 Subject: Add _PyObject_FastCallKeywords() Issue #27830: Add _PyObject_FastCallKeywords(): avoid the creation of a temporary dictionary for keyword arguments. Other changes: * Cleanup call_function() and fast_function() (ex: rename nk to nkwargs) * Remove now useless do_call(), replaced with _PyObject_FastCallKeywords() --- Python/ceval.c | 89 ++++++++++++++++++++++++---------------------------------- 1 file changed, 36 insertions(+), 53 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 9b9245ed42..0e874b7f21 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -113,8 +113,7 @@ static PyObject * call_function(PyObject ***, Py_ssize_t, PyObject *, uint64*, u #else static PyObject * call_function(PyObject ***, Py_ssize_t, PyObject *); #endif -static PyObject * fast_function(PyObject *, PyObject ***, Py_ssize_t, PyObject *); -static PyObject * do_call(PyObject *, PyObject ***, Py_ssize_t, PyObject *); +static PyObject * fast_function(PyObject *, PyObject **, Py_ssize_t, PyObject *); static PyObject * do_call_core(PyObject *, PyObject *, PyObject *); static PyObject * create_keyword_args(PyObject *, PyObject ***, PyObject *); static PyObject * load_args(PyObject ***, Py_ssize_t); @@ -4940,7 +4939,7 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \ } static PyObject * -call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *names +call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames #ifdef WITH_TSC , uint64* pintr0, uint64* pintr1 #endif @@ -4949,8 +4948,8 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *names PyObject **pfunc = (*pp_stack) - oparg - 1; PyObject *func = *pfunc; PyObject *x, *w; - Py_ssize_t nk = names == NULL ? 0 : PyTuple_GET_SIZE(names); - Py_ssize_t nargs = oparg - nk; + Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); + Py_ssize_t nargs = oparg - nkwargs; /* Always dispatch PyCFunction first, because these are presumed to be the most frequent callable object. @@ -4960,7 +4959,7 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *names PyThreadState *tstate = PyThreadState_GET(); PCALL(PCALL_CFUNCTION); - if (names == NULL && flags & (METH_NOARGS | METH_O)) { + if (kwnames == NULL && flags & (METH_NOARGS | METH_O)) { PyCFunction meth = PyCFunction_GET_FUNCTION(func); PyObject *self = PyCFunction_GET_SELF(func); if (flags & METH_NOARGS && nargs == 0) { @@ -4982,8 +4981,8 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *names } else { PyObject *callargs, *kwdict = NULL; - if (names != NULL) { - kwdict = create_keyword_args(names, pp_stack, func); + if (kwnames != NULL) { + kwdict = create_keyword_args(kwnames, pp_stack, func); if (kwdict == NULL) { x = NULL; goto cfuncerror; @@ -5003,6 +5002,9 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *names } } else { + Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); + PyObject **stack; + if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { /* optimize access to bound methods */ PyObject *self = PyMethod_GET_SELF(func); @@ -5018,11 +5020,14 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *names Py_INCREF(func); } + stack = (*pp_stack) - nargs - nkwargs; + READ_TIMESTAMP(*pintr0); if (PyFunction_Check(func)) { - x = fast_function(func, pp_stack, nargs, names); - } else { - x = do_call(func, pp_stack, nargs, names); + x = fast_function(func, stack, nargs, kwnames); + } + else { + x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames); } READ_TIMESTAMP(*pintr1); @@ -5055,8 +5060,8 @@ cfuncerror: */ static PyObject* -_PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t nargs, - PyObject *globals) +_PyFunction_FastCall(PyCodeObject *co, PyObject **args, Py_ssize_t nargs, + PyObject *globals) { PyFrameObject *f; PyThreadState *tstate = PyThreadState_GET(); @@ -5091,19 +5096,19 @@ _PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t nargs, return result; } -/* Similar to _PyFunction_FastCall() but keywords are passed a (key, value) - pairs in stack */ static PyObject * -fast_function(PyObject *func, PyObject ***pp_stack, Py_ssize_t nargs, PyObject *names) +fast_function(PyObject *func, PyObject **stack, + Py_ssize_t nargs, PyObject *kwnames) { PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); PyObject *globals = PyFunction_GET_GLOBALS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func); PyObject *kwdefs, *closure, *name, *qualname; PyObject **d; - Py_ssize_t nkwargs = names == NULL ? 0 : PyTuple_GET_SIZE(names); + Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); Py_ssize_t nd; - PyObject **stack = (*pp_stack)-nargs-nkwargs; + + assert((nargs == 0 && nkwargs == 0) || stack != NULL); PCALL(PCALL_FUNCTION); PCALL(PCALL_FAST_FUNCTION); @@ -5112,15 +5117,14 @@ fast_function(PyObject *func, PyObject ***pp_stack, Py_ssize_t nargs, PyObject * co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { if (argdefs == NULL && co->co_argcount == nargs) { - return _PyFunction_FastCallNoKw(co, stack, nargs, globals); + return _PyFunction_FastCall(co, stack, nargs, globals); } else if (nargs == 0 && argdefs != NULL && co->co_argcount == Py_SIZE(argdefs)) { /* function called with no arguments, but all parameters have a default value: use default values as arguments .*/ stack = &PyTuple_GET_ITEM(argdefs, 0); - return _PyFunction_FastCallNoKw(co, stack, Py_SIZE(argdefs), - globals); + return _PyFunction_FastCall(co, stack, Py_SIZE(argdefs), globals); } } @@ -5140,11 +5144,18 @@ fast_function(PyObject *func, PyObject ***pp_stack, Py_ssize_t nargs, PyObject * return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, stack, nargs, NULL, 0, - names, stack + nargs, + kwnames, stack + nargs, d, (int)nd, kwdefs, closure, name, qualname); } +PyObject * +_PyFunction_FastCallKeywords(PyObject *func, PyObject **stack, + Py_ssize_t nargs, PyObject *kwnames) +{ + return fast_function(func, stack, nargs, kwnames); +} + PyObject * _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, PyObject *kwargs) @@ -5172,15 +5183,14 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, { /* Fast paths */ if (argdefs == NULL && co->co_argcount == nargs) { - return _PyFunction_FastCallNoKw(co, args, nargs, globals); + return _PyFunction_FastCall(co, args, nargs, globals); } else if (nargs == 0 && argdefs != NULL && co->co_argcount == Py_SIZE(argdefs)) { /* function called with no arguments, but all parameters have a default value: use default values as arguments .*/ args = &PyTuple_GET_ITEM(argdefs, 0); - return _PyFunction_FastCallNoKw(co, args, Py_SIZE(argdefs), - globals); + return _PyFunction_FastCall(co, args, Py_SIZE(argdefs), globals); } } @@ -5242,8 +5252,8 @@ create_keyword_args(PyObject *names, PyObject ***pp_stack, return NULL; while (--nk >= 0) { int err; - PyObject *value = EXT_POP(*pp_stack); PyObject *key = PyTuple_GET_ITEM(names, nk); + PyObject *value = EXT_POP(*pp_stack); if (PyDict_GetItem(kwdict, key) != NULL) { PyErr_Format(PyExc_TypeError, "%.200s%s got multiple values " @@ -5281,33 +5291,6 @@ load_args(PyObject ***pp_stack, Py_ssize_t nargs) return args; } -static PyObject * -do_call(PyObject *func, PyObject ***pp_stack, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *callargs, *kwdict, *result; - - if (kwnames != NULL) { - kwdict = create_keyword_args(kwnames, pp_stack, func); - if (kwdict == NULL) { - return NULL; - } - } - else { - kwdict = NULL; - } - - callargs = load_args(pp_stack, nargs); - if (callargs == NULL) { - Py_XDECREF(kwdict); - return NULL; - } - - result = do_call_core(func, callargs, kwdict); - Py_XDECREF(callargs); - Py_XDECREF(kwdict); - return result; -} - static PyObject * do_call_core(PyObject *func, PyObject *callargs, PyObject *kwdict) { -- cgit v1.2.1 From 2615f24fb54cece9f22c5e2b1d206e8dd559631a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 9 Sep 2016 14:07:44 -0700 Subject: Issue #27810: Add _PyCFunction_FastCallKeywords() Use _PyCFunction_FastCallKeywords() in ceval.c: it allows to remove a lot of code from ceval.c which was only used to call C functions. --- Python/ceval.c | 164 ++++++++++----------------------------------------------- 1 file changed, 28 insertions(+), 136 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 0e874b7f21..b2ddaa3170 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -115,8 +115,6 @@ static PyObject * call_function(PyObject ***, Py_ssize_t, PyObject *); #endif static PyObject * fast_function(PyObject *, PyObject **, Py_ssize_t, PyObject *); static PyObject * do_call_core(PyObject *, PyObject *, PyObject *); -static PyObject * create_keyword_args(PyObject *, PyObject ***, PyObject *); -static PyObject * load_args(PyObject ***, Py_ssize_t); #ifdef LLTRACE static int lltrace; @@ -4892,21 +4890,6 @@ PyEval_GetFuncDesc(PyObject *func) return " object"; } -static void -err_args(PyObject *func, int flags, Py_ssize_t nargs) -{ - if (flags & METH_NOARGS) - PyErr_Format(PyExc_TypeError, - "%.200s() takes no arguments (%zd given)", - ((PyCFunctionObject *)func)->m_ml->ml_name, - nargs); - else - PyErr_Format(PyExc_TypeError, - "%.200s() takes exactly one argument (%zd given)", - ((PyCFunctionObject *)func)->m_ml->ml_name, - nargs); -} - #define C_TRACE(x, call) \ if (tstate->use_tracing && tstate->c_profilefunc) { \ if (call_trace(tstate->c_profilefunc, tstate->c_profileobj, \ @@ -4950,91 +4933,49 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames PyObject *x, *w; Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); Py_ssize_t nargs = oparg - nkwargs; + PyObject **stack; /* Always dispatch PyCFunction first, because these are presumed to be the most frequent callable object. */ if (PyCFunction_Check(func)) { - int flags = PyCFunction_GET_FLAGS(func); PyThreadState *tstate = PyThreadState_GET(); PCALL(PCALL_CFUNCTION); - if (kwnames == NULL && flags & (METH_NOARGS | METH_O)) { - PyCFunction meth = PyCFunction_GET_FUNCTION(func); - PyObject *self = PyCFunction_GET_SELF(func); - if (flags & METH_NOARGS && nargs == 0) { - C_TRACE(x, (*meth)(self,NULL)); - x = _Py_CheckFunctionResult(func, x, NULL); - } - else if (flags & METH_O && nargs == 1) { - PyObject *arg = EXT_POP(*pp_stack); - C_TRACE(x, (*meth)(self,arg)); - Py_DECREF(arg); + stack = (*pp_stack) - nargs - nkwargs; + C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames)); + } + else { + if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { + /* optimize access to bound methods */ + PyObject *self = PyMethod_GET_SELF(func); + PCALL(PCALL_METHOD); + PCALL(PCALL_BOUND_METHOD); + Py_INCREF(self); + func = PyMethod_GET_FUNCTION(func); + Py_INCREF(func); + Py_SETREF(*pfunc, self); + nargs++; + } + else { + Py_INCREF(func); + } - x = _Py_CheckFunctionResult(func, x, NULL); - } - else { - err_args(func, flags, nargs); - x = NULL; - } + stack = (*pp_stack) - nargs - nkwargs; + + READ_TIMESTAMP(*pintr0); + if (PyFunction_Check(func)) { + x = fast_function(func, stack, nargs, kwnames); } else { - PyObject *callargs, *kwdict = NULL; - if (kwnames != NULL) { - kwdict = create_keyword_args(kwnames, pp_stack, func); - if (kwdict == NULL) { - x = NULL; - goto cfuncerror; - } - } - callargs = load_args(pp_stack, nargs); - if (callargs != NULL) { - READ_TIMESTAMP(*pintr0); - C_TRACE(x, PyCFunction_Call(func, callargs, kwdict)); - READ_TIMESTAMP(*pintr1); - Py_DECREF(callargs); - } - else { - x = NULL; - } - Py_XDECREF(kwdict); + x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames); } - } - else { - Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); - PyObject **stack; - - if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { - /* optimize access to bound methods */ - PyObject *self = PyMethod_GET_SELF(func); - PCALL(PCALL_METHOD); - PCALL(PCALL_BOUND_METHOD); - Py_INCREF(self); - func = PyMethod_GET_FUNCTION(func); - Py_INCREF(func); - Py_SETREF(*pfunc, self); - nargs++; - } - else { - Py_INCREF(func); - } - - stack = (*pp_stack) - nargs - nkwargs; - - READ_TIMESTAMP(*pintr0); - if (PyFunction_Check(func)) { - x = fast_function(func, stack, nargs, kwnames); - } - else { - x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames); - } - READ_TIMESTAMP(*pintr1); - - Py_DECREF(func); + READ_TIMESTAMP(*pintr1); + + Py_DECREF(func); } -cfuncerror: assert((x != NULL) ^ (PyErr_Occurred() != NULL)); /* Clear the stack of the function object. Also removes @@ -5242,55 +5183,6 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, return result; } -static PyObject * -create_keyword_args(PyObject *names, PyObject ***pp_stack, - PyObject *func) -{ - Py_ssize_t nk = PyTuple_GET_SIZE(names); - PyObject *kwdict = _PyDict_NewPresized(nk); - if (kwdict == NULL) - return NULL; - while (--nk >= 0) { - int err; - PyObject *key = PyTuple_GET_ITEM(names, nk); - PyObject *value = EXT_POP(*pp_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(value); - Py_DECREF(kwdict); - return NULL; - } - err = PyDict_SetItem(kwdict, key, value); - Py_DECREF(value); - if (err) { - Py_DECREF(kwdict); - return NULL; - } - } - return kwdict; -} - -static PyObject * -load_args(PyObject ***pp_stack, Py_ssize_t nargs) -{ - PyObject *args = PyTuple_New(nargs); - - if (args == NULL) { - return NULL; - } - - while (--nargs >= 0) { - PyObject *arg= EXT_POP(*pp_stack); - PyTuple_SET_ITEM(args, nargs, arg); - } - return args; -} - static PyObject * do_call_core(PyObject *func, PyObject *callargs, PyObject *kwdict) { -- cgit v1.2.1 From 831deb2ea3f31f273438acc797ae12d183a21422 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 9 Sep 2016 14:57:58 -0700 Subject: remove ceval timestamp support --- Python/ceval.c | 154 +-------------------------------------------------------- 1 file changed, 1 insertion(+), 153 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index b2ddaa3170..2d42fe28ee 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -20,82 +20,6 @@ #include -#ifndef WITH_TSC - -#define READ_TIMESTAMP(var) - -#else - -typedef unsigned long long uint64; - -/* PowerPC support. - "__ppc__" appears to be the preprocessor definition to detect on OS X, whereas - "__powerpc__" appears to be the correct one for Linux with GCC -*/ -#if defined(__ppc__) || defined (__powerpc__) - -#define READ_TIMESTAMP(var) ppc_getcounter(&var) - -static void -ppc_getcounter(uint64 *v) -{ - unsigned long tbu, tb, tbu2; - - loop: - asm volatile ("mftbu %0" : "=r" (tbu) ); - asm volatile ("mftb %0" : "=r" (tb) ); - asm volatile ("mftbu %0" : "=r" (tbu2)); - if (__builtin_expect(tbu != tbu2, 0)) goto loop; - - /* The slightly peculiar way of writing the next lines is - compiled better by GCC than any other way I tried. */ - ((long*)(v))[0] = tbu; - ((long*)(v))[1] = tb; -} - -#elif defined(__i386__) - -/* this is for linux/x86 (and probably any other GCC/x86 combo) */ - -#define READ_TIMESTAMP(val) \ - __asm__ __volatile__("rdtsc" : "=A" (val)) - -#elif defined(__x86_64__) - -/* for gcc/x86_64, the "A" constraint in DI mode means *either* rax *or* rdx; - not edx:eax as it does for i386. Since rdtsc puts its result in edx:eax - even in 64-bit mode, we need to use "a" and "d" for the lower and upper - 32-bit pieces of the result. */ - -#define READ_TIMESTAMP(val) do { \ - unsigned int h, l; \ - __asm__ __volatile__("rdtsc" : "=a" (l), "=d" (h)); \ - (val) = ((uint64)l) | (((uint64)h) << 32); \ - } while(0) - - -#else - -#error "Don't know how to implement timestamp counter for this architecture" - -#endif - -void dump_tsc(int opcode, int ticked, uint64 inst0, uint64 inst1, - uint64 loop0, uint64 loop1, uint64 intr0, uint64 intr1) -{ - uint64 intr, inst, loop; - PyThreadState *tstate = PyThreadState_Get(); - if (!tstate->interp->tscdump) - return; - intr = intr1 - intr0; - inst = inst1 - inst0 - intr; - loop = loop1 - loop0 - intr; - fprintf(stderr, "opcode=%03d t=%d inst=%06lld loop=%06lld\n", - opcode, ticked, inst, loop); -} - -#endif - /* Turn this on if your compiler chokes on the big switch: */ /* #define CASE_TOO_BIG 1 */ @@ -108,11 +32,7 @@ void dump_tsc(int opcode, int ticked, uint64 inst0, uint64 inst1, typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *); /* Forward declarations */ -#ifdef WITH_TSC -static PyObject * call_function(PyObject ***, Py_ssize_t, PyObject *, uint64*, uint64*); -#else static PyObject * call_function(PyObject ***, Py_ssize_t, PyObject *); -#endif static PyObject * fast_function(PyObject *, PyObject **, Py_ssize_t, PyObject *); static PyObject * do_call_core(PyObject *, PyObject *, PyObject *); @@ -938,46 +858,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) #define GETITEM(v, i) PyTuple_GetItem((v), (i)) #endif -#ifdef WITH_TSC -/* Use Pentium timestamp counter to mark certain events: - inst0 -- beginning of switch statement for opcode dispatch - inst1 -- end of switch statement (may be skipped) - loop0 -- the top of the mainloop - loop1 -- place where control returns again to top of mainloop - (may be skipped) - intr1 -- beginning of long interruption - intr2 -- end of long interruption - - Many opcodes call out to helper C functions. In some cases, the - time in those functions should be counted towards the time for the - opcode, but not in all cases. For example, a CALL_FUNCTION opcode - calls another Python function; there's no point in charge all the - bytecode executed by the called function to the caller. - - It's hard to make a useful judgement statically. In the presence - of operator overloading, it's impossible to tell if a call will - execute new Python code or not. - - It's a case-by-case judgement. I'll use intr1 for the following - cases: - - IMPORT_STAR - IMPORT_FROM - CALL_FUNCTION (and friends) - - */ - uint64 inst0, inst1, loop0, loop1, intr0 = 0, intr1 = 0; - int ticked = 0; - - READ_TIMESTAMP(inst0); - READ_TIMESTAMP(inst1); - READ_TIMESTAMP(loop0); - READ_TIMESTAMP(loop1); - - /* shut up the compiler */ - opcode = 0; -#endif - /* Code access macros */ #ifdef WORDS_BIGENDIAN @@ -1225,23 +1105,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) #endif for (;;) { -#ifdef WITH_TSC - if (inst1 == 0) { - /* Almost surely, the opcode executed a break - or a continue, preventing inst1 from being set - on the way out of the loop. - */ - READ_TIMESTAMP(inst1); - loop1 = inst1; - } - dump_tsc(opcode, ticked, inst0, inst1, loop0, loop1, - intr0, intr1); - ticked = 0; - inst1 = 0; - intr0 = 0; - intr1 = 0; - READ_TIMESTAMP(loop0); -#endif assert(stack_pointer >= f->f_valuestack); /* else underflow */ assert(STACK_LEVEL() <= co->co_stacksize); /* else overflow */ assert(!PyErr_Occurred()); @@ -1260,9 +1123,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) a try: finally: block uninterruptible. */ goto fast_next_opcode; } -#ifdef WITH_TSC - ticked = 1; -#endif if (_Py_atomic_load_relaxed(&pendingcalls_to_do)) { if (Py_MakePendingCalls() < 0) goto error; @@ -3403,11 +3263,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) PyObject **sp, *res; PCALL(PCALL_ALL); sp = stack_pointer; -#ifdef WITH_TSC - res = call_function(&sp, oparg, NULL, &intr0, &intr1); -#else res = call_function(&sp, oparg, NULL); -#endif stack_pointer = sp; PUSH(res); if (res == NULL) { @@ -3423,11 +3279,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) assert(PyTuple_CheckExact(names) && PyTuple_GET_SIZE(names) <= oparg); PCALL(PCALL_ALL); sp = stack_pointer; -#ifdef WITH_TSC - res = call_function(&sp, oparg, names, &intr0, &intr1); -#else res = call_function(&sp, oparg, names); -#endif stack_pointer = sp; PUSH(res); Py_DECREF(names); @@ -4922,11 +4774,7 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \ } static PyObject * -call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames -#ifdef WITH_TSC - , uint64* pintr0, uint64* pintr1 -#endif - ) +call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) { PyObject **pfunc = (*pp_stack) - oparg - 1; PyObject *func = *pfunc; -- cgit v1.2.1 From f261e8a43d5683fb84faec07d5e3ad4d6fb5b95f Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 9 Sep 2016 15:02:11 -0700 Subject: remove READ_TIMESTAMP macro --- Python/ceval.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 2d42fe28ee..ac482e137b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1213,9 +1213,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) } #endif - /* Main switch on opcode */ - READ_TIMESTAMP(inst0); - switch (opcode) { /* BEWARE! -- cgit v1.2.1 From 6f1dfed100c862df022ddcdc11e82f9cba9fbaec Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 9 Sep 2016 15:03:18 -0700 Subject: remove more READ_TIMESTAMP --- Python/ceval.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index ac482e137b..d3bd8b569b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2823,11 +2823,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) PyObject *fromlist = POP(); PyObject *level = TOP(); PyObject *res; - READ_TIMESTAMP(intr0); res = import_name(f, name, fromlist, level); Py_DECREF(level); Py_DECREF(fromlist); - READ_TIMESTAMP(intr1); SET_TOP(res); if (res == NULL) goto error; @@ -2846,9 +2844,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) "no locals found during 'import *'"); goto error; } - READ_TIMESTAMP(intr0); err = import_all_from(locals, from); - READ_TIMESTAMP(intr1); PyFrame_LocalsToFast(f, 0); Py_DECREF(from); if (err != 0) @@ -2860,9 +2856,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) PyObject *name = GETITEM(names, oparg); PyObject *from = TOP(); PyObject *res; - READ_TIMESTAMP(intr0); res = import_from(from, name); - READ_TIMESTAMP(intr1); PUSH(res); if (res == NULL) goto error; @@ -3298,9 +3292,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) assert(PyTuple_CheckExact(callargs)); func = TOP(); - READ_TIMESTAMP(intr0); result = do_call_core(func, callargs, kwargs); - READ_TIMESTAMP(intr1); Py_DECREF(func); Py_DECREF(callargs); Py_XDECREF(kwargs); @@ -3451,7 +3443,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) assert(0); error: - READ_TIMESTAMP(inst1); assert(why == WHY_NOT); why = WHY_EXCEPTION; @@ -3555,7 +3546,6 @@ fast_block_end: if (why != WHY_NOT) break; - READ_TIMESTAMP(loop1); assert(!PyErr_Occurred()); @@ -4809,14 +4799,12 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) stack = (*pp_stack) - nargs - nkwargs; - READ_TIMESTAMP(*pintr0); if (PyFunction_Check(func)) { x = fast_function(func, stack, nargs, kwnames); } else { x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames); } - READ_TIMESTAMP(*pintr1); Py_DECREF(func); } -- cgit v1.2.1 From 7d9e9f40d8903147693be8d2bbb4a129005ca49e Mon Sep 17 00:00:00 2001 From: ?ukasz Langa Date: Fri, 9 Sep 2016 17:37:37 -0700 Subject: DTrace support: function calls, GC activity, line execution Tested on macOS 10.11 dtrace, Ubuntu 16.04 SystemTap, and libbcc. Largely based by an initial patch by Jes?s Cea Avi?n, with some influence from Dave Malcolm's SystemTap patch and Nikhil Benesch's unification patch. Things deliberately left out for simplicity: - ustack helpers, I have no way of testing them at this point since they are Solaris-specific - PyFrameObject * in function__entry/function__return, this is SystemTap-specific - SPARC support - dynamic tracing - sys module dtrace facility introspection All of those might be added later. --- Python/ceval.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index d3bd8b569b..a396e81ef7 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -15,6 +15,7 @@ #include "dictobject.h" #include "frameobject.h" #include "opcode.h" +#include "pydtrace.h" #include "setobject.h" #include "structmember.h" @@ -50,6 +51,9 @@ static void call_exc_trace(Py_tracefunc, PyObject *, PyThreadState *, PyFrameObject *); static int maybe_call_line_trace(Py_tracefunc, PyObject *, PyThreadState *, PyFrameObject *, int *, int *, int *); +static void maybe_dtrace_line(PyFrameObject *, int *, int *, int *); +static void dtrace_function_entry(PyFrameObject *); +static void dtrace_function_return(PyFrameObject *); static PyObject * cmp_outcome(int, PyObject *, PyObject *); static PyObject * import_name(PyFrameObject *, PyObject *, PyObject *, PyObject *); @@ -822,7 +826,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) #ifdef LLTRACE #define FAST_DISPATCH() \ { \ - if (!lltrace && !_Py_TracingPossible) { \ + if (!lltrace && !_Py_TracingPossible && !PyDTrace_LINE_ENABLED()) { \ f->f_lasti = INSTR_OFFSET(); \ NEXTOPARG(); \ goto *opcode_targets[opcode]; \ @@ -832,7 +836,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) #else #define FAST_DISPATCH() \ { \ - if (!_Py_TracingPossible) { \ + if (!_Py_TracingPossible && !PyDTrace_LINE_ENABLED()) { \ f->f_lasti = INSTR_OFFSET(); \ NEXTOPARG(); \ goto *opcode_targets[opcode]; \ @@ -1042,6 +1046,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) } } + if (PyDTrace_FUNCTION_ENTRY_ENABLED()) + dtrace_function_entry(f); + co = f->f_code; names = co->co_names; consts = co->co_consts; @@ -1162,6 +1169,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) fast_next_opcode: f->f_lasti = INSTR_OFFSET(); + if (PyDTrace_LINE_ENABLED()) + maybe_dtrace_line(f, &instr_lb, &instr_ub, &instr_prev); + /* line-by-line tracing support */ if (_Py_TracingPossible && @@ -3620,6 +3630,8 @@ fast_yield: /* pop frame */ exit_eval_frame: + if (PyDTrace_FUNCTION_RETURN_ENABLED()) + dtrace_function_return(f); Py_LeaveRecursiveCall(); f->f_executing = 0; tstate->frame = f->f_back; @@ -5415,3 +5427,65 @@ _PyEval_RequestCodeExtraIndex(freefunc free) tstate->co_extra_freefuncs[new_index] = free; return new_index; } + +static void +dtrace_function_entry(PyFrameObject *f) +{ + char* filename; + char* funcname; + int lineno; + + filename = PyUnicode_AsUTF8(f->f_code->co_filename); + funcname = PyUnicode_AsUTF8(f->f_code->co_name); + lineno = PyCode_Addr2Line(f->f_code, f->f_lasti); + + PyDTrace_FUNCTION_ENTRY(filename, funcname, lineno); +} + +static void +dtrace_function_return(PyFrameObject *f) +{ + char* filename; + char* funcname; + int lineno; + + filename = PyUnicode_AsUTF8(f->f_code->co_filename); + funcname = PyUnicode_AsUTF8(f->f_code->co_name); + lineno = PyCode_Addr2Line(f->f_code, f->f_lasti); + + PyDTrace_FUNCTION_RETURN(filename, funcname, lineno); +} + +/* DTrace equivalent of maybe_call_line_trace. */ +static void +maybe_dtrace_line(PyFrameObject *frame, + int *instr_lb, int *instr_ub, int *instr_prev) +{ + int line = frame->f_lineno; + char *co_filename, *co_name; + + /* If the last instruction executed isn't in the current + instruction window, reset the window. + */ + if (frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub) { + PyAddrPair bounds; + line = _PyCode_CheckLineNumber(frame->f_code, frame->f_lasti, + &bounds); + *instr_lb = bounds.ap_lower; + *instr_ub = bounds.ap_upper; + } + /* If the last instruction falls at the start of a line or if + it represents a jump backwards, update the frame's line + number and call the trace function. */ + if (frame->f_lasti == *instr_lb || frame->f_lasti < *instr_prev) { + frame->f_lineno = line; + co_filename = PyUnicode_AsUTF8(frame->f_code->co_filename); + if (!co_filename) + co_filename = "?"; + co_name = PyUnicode_AsUTF8(frame->f_code->co_name); + if (!co_name) + co_name = "?"; + PyDTrace_LINE(co_filename, co_name, line); + } + *instr_prev = frame->f_lasti; +} -- cgit v1.2.1 From 2fdfdf47618ed421642a743b67d2616c7dae138e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 11 Sep 2016 13:48:15 +0300 Subject: Issue #27129: Replaced wordcode related magic constants with macros. --- Python/ceval.c | 48 +++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index a396e81ef7..11ad1fed26 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -62,7 +62,7 @@ static int import_all_from(PyObject *, PyObject *); static void format_exc_check_arg(PyObject *, const char *, PyObject *); static void format_exc_unbound(PyCodeObject *co, int oparg); static PyObject * unicode_concatenate(PyObject *, PyObject *, - PyFrameObject *, const unsigned short *); + PyFrameObject *, const _Py_CODEUNIT *); static PyObject * special_lookup(PyObject *, _Py_Identifier *); #define NAME_ERROR_MSG \ @@ -725,7 +725,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) int lastopcode = 0; #endif PyObject **stack_pointer; /* Next free slot in value stack */ - const unsigned short *next_instr; + const _Py_CODEUNIT *next_instr; int opcode; /* Current opcode */ int oparg; /* Current opcode argument, if any */ enum why_code why; /* Reason for block stack unwind */ @@ -743,7 +743,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) time it is tested. */ int instr_ub = -1, instr_lb = 0, instr_prev = -1; - const unsigned short *first_instr; + const _Py_CODEUNIT *first_instr; PyObject *names; PyObject *consts; @@ -864,23 +864,16 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) /* Code access macros */ -#ifdef WORDS_BIGENDIAN - #define OPCODE(word) ((word) >> 8) - #define OPARG(word) ((word) & 255) -#else - #define OPCODE(word) ((word) & 255) - #define OPARG(word) ((word) >> 8) -#endif /* The integer overflow is checked by an assertion below. */ -#define INSTR_OFFSET() (2*(int)(next_instr - first_instr)) +#define INSTR_OFFSET() (sizeof(_Py_CODEUNIT) * (int)(next_instr - first_instr)) #define NEXTOPARG() do { \ - unsigned short word = *next_instr; \ - opcode = OPCODE(word); \ - oparg = OPARG(word); \ + _Py_CODEUNIT word = *next_instr; \ + opcode = _Py_OPCODE(word); \ + oparg = _Py_OPARG(word); \ next_instr++; \ } while (0) -#define JUMPTO(x) (next_instr = first_instr + (x)/2) -#define JUMPBY(x) (next_instr += (x)/2) +#define JUMPTO(x) (next_instr = first_instr + (x) / sizeof(_Py_CODEUNIT)) +#define JUMPBY(x) (next_instr += (x) / sizeof(_Py_CODEUNIT)) /* OpCode prediction macros Some opcodes tend to come in pairs thus making it possible to @@ -913,10 +906,10 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) #else #define PREDICT(op) \ do{ \ - unsigned short word = *next_instr; \ - opcode = OPCODE(word); \ + _Py_CODEUNIT word = *next_instr; \ + opcode = _Py_OPCODE(word); \ if (opcode == op){ \ - oparg = OPARG(word); \ + oparg = _Py_OPARG(word); \ next_instr++; \ goto PRED_##op; \ } \ @@ -1056,9 +1049,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) freevars = f->f_localsplus + co->co_nlocals; assert(PyBytes_Check(co->co_code)); assert(PyBytes_GET_SIZE(co->co_code) <= INT_MAX); - assert(PyBytes_GET_SIZE(co->co_code) % 2 == 0); - assert(_Py_IS_ALIGNED(PyBytes_AS_STRING(co->co_code), 2)); - first_instr = (unsigned short*) PyBytes_AS_STRING(co->co_code); + assert(PyBytes_GET_SIZE(co->co_code) % sizeof(_Py_CODEUNIT) == 0); + assert(_Py_IS_ALIGNED(PyBytes_AS_STRING(co->co_code), sizeof(_Py_CODEUNIT))); + first_instr = (_Py_CODEUNIT *) PyBytes_AS_STRING(co->co_code); /* f->f_lasti refers to the index of the last instruction, unless it's -1 in which case next_instr should be first_instr. @@ -1074,10 +1067,11 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) FOR_ITER is effectively a single opcode and f->f_lasti will point to the beginning of the combined pair.) */ + assert(f->f_lasti >= -1); next_instr = first_instr; if (f->f_lasti >= 0) { - assert(f->f_lasti % 2 == 0); - next_instr += f->f_lasti/2 + 1; + assert(f->f_lasti % sizeof(_Py_CODEUNIT) == 0); + next_instr += f->f_lasti / sizeof(_Py_CODEUNIT) + 1; } stack_pointer = f->f_stacktop; assert(stack_pointer != NULL); @@ -1125,7 +1119,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) Py_MakePendingCalls() above. */ if (_Py_atomic_load_relaxed(&eval_breaker)) { - if (OPCODE(*next_instr) == SETUP_FINALLY) { + if (_Py_OPCODE(*next_instr) == SETUP_FINALLY) { /* Make the last opcode before a try: finally: block uninterruptible. */ goto fast_next_opcode; @@ -2049,7 +2043,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) f->f_stacktop = stack_pointer; why = WHY_YIELD; /* and repeat... */ - f->f_lasti -= 2; + f->f_lasti -= sizeof(_Py_CODEUNIT); goto fast_yield; } @@ -5321,7 +5315,7 @@ format_exc_unbound(PyCodeObject *co, int oparg) static PyObject * unicode_concatenate(PyObject *v, PyObject *w, - PyFrameObject *f, const unsigned short *next_instr) + PyFrameObject *f, const _Py_CODEUNIT *next_instr) { PyObject *res; if (Py_REFCNT(v) == 2) { -- cgit v1.2.1 From e74d571e49d3fdb4389b79fa1392dd75e2acb916 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 12 Sep 2016 00:52:40 +0300 Subject: Issue #27213: Fixed different issues with reworked CALL_FUNCTION* opcodes. * BUILD_TUPLE_UNPACK and BUILD_MAP_UNPACK_WITH_CALL no longer generated with single tuple or dict. * Restored more informative error messages for incorrect var-positional and var-keyword arguments. * Removed code duplications in _PyEval_EvalCodeWithName(). * Removed redundant runtime checks and parameters in _PyStack_AsDict(). * Added a workaround and enabled previously disabled test in test_traceback. * Removed dead code from the dis module. --- Python/ceval.c | 141 +++++++++++++++++++++++---------------------------------- 1 file changed, 57 insertions(+), 84 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 11ad1fed26..06d3a659bd 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2513,14 +2513,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) TARGET(BUILD_LIST_UNPACK) { int convert_to_tuple = opcode == BUILD_TUPLE_UNPACK; Py_ssize_t i; - PyObject *sum; + PyObject *sum = PyList_New(0); PyObject *return_value; - if (convert_to_tuple && oparg == 1 && PyTuple_CheckExact(TOP())) { - DISPATCH(); - } - - sum = PyList_New(0); if (sum == NULL) goto error; @@ -2708,13 +2703,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) TARGET(BUILD_MAP_UNPACK) { int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL; Py_ssize_t i; - PyObject *sum; - - if (with_call && oparg == 1 && PyDict_CheckExact(TOP())) { - DISPATCH(); - } - - sum = PyDict_New(); + PyObject *sum = PyDict_New(); if (sum == NULL) goto error; @@ -3290,11 +3279,53 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) PCALL(PCALL_ALL); if (oparg & 0x01) { kwargs = POP(); + if (!PyDict_CheckExact(kwargs)) { + PyObject *d = PyDict_New(); + if (d == NULL) + goto error; + if (PyDict_Update(d, kwargs) != 0) { + Py_DECREF(d); + /* PyDict_Update raises attribute + * error (percolated from an attempt + * to get 'keys' attribute) instead of + * a type error if its second argument + * is not a mapping. + */ + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + func = SECOND(); + PyErr_Format(PyExc_TypeError, + "%.200s%.200s argument after ** " + "must be a mapping, not %.200s", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + kwargs->ob_type->tp_name); + } + goto error; + } + Py_DECREF(kwargs); + kwargs = d; + } assert(PyDict_CheckExact(kwargs)); } callargs = POP(); - assert(PyTuple_CheckExact(callargs)); func = TOP(); + if (!PyTuple_Check(callargs)) { + if (Py_TYPE(callargs)->tp_iter == NULL && + !PySequence_Check(callargs)) { + PyErr_Format(PyExc_TypeError, + "%.200s%.200s argument after * " + "must be an iterable, not %.200s", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + callargs->ob_type->tp_name); + goto error; + } + Py_SETREF(callargs, PySequence_Tuple(callargs)); + if (callargs == NULL) { + goto error; + } + } + assert(PyTuple_Check(callargs)); result = do_call_core(func, callargs, kwargs); Py_DECREF(func); @@ -3796,8 +3827,8 @@ too_many_positional(PyCodeObject *co, Py_ssize_t given, Py_ssize_t defcount, static PyObject * _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyObject **args, Py_ssize_t argcount, - PyObject **kws, Py_ssize_t kwcount, - PyObject *kwnames, PyObject **kwstack, + PyObject **kwnames, PyObject **kwargs, + Py_ssize_t kwcount, int kwstep, PyObject **defs, Py_ssize_t defcount, PyObject *kwdefs, PyObject *closure, PyObject *name, PyObject *qualname) @@ -3811,9 +3842,6 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount; Py_ssize_t i, n; PyObject *kwdict; - Py_ssize_t kwcount2 = kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames); - - assert((kwcount == 0) || (kws != NULL)); if (globals == NULL) { PyErr_SetString(PyExc_SystemError, @@ -3873,11 +3901,12 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, } } - /* Handle keyword arguments passed as an array of (key, value) pairs */ - for (i = 0; i < kwcount; i++) { + /* Handle keyword arguments passed as two strided arrays */ + kwcount *= kwstep; + for (i = 0; i < kwcount; i += kwstep) { PyObject **co_varnames; - PyObject *keyword = kws[2*i]; - PyObject *value = kws[2*i + 1]; + PyObject *keyword = kwnames[i]; + PyObject *value = kwargs[i]; Py_ssize_t j; if (keyword == NULL || !PyUnicode_Check(keyword)) { @@ -3932,61 +3961,6 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, SETLOCAL(j, value); } - /* Handle keyword arguments passed as keys tuple + values array */ - for (i = 0; i < kwcount2; i++) { - PyObject **co_varnames; - PyObject *keyword = PyTuple_GET_ITEM(kwnames, i); - PyObject *value = kwstack[i]; - 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_found2; - } - /* 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_found2; - 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_found2: - 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); - } - /* Check the number of positional arguments */ if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) { too_many_positional(co, argcount, defcount, fastlocals); @@ -4138,8 +4112,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, { return _PyEval_EvalCodeWithName(_co, globals, locals, args, argcount, - kws, kwcount, - NULL, NULL, + kws, kws + 1, kwcount, 2, defs, defcount, kwdefs, closure, NULL, NULL); @@ -4923,8 +4896,9 @@ fast_function(PyObject *func, PyObject **stack, } return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, stack, nargs, - NULL, 0, - kwnames, stack + nargs, + nkwargs ? &PyTuple_GET_ITEM(kwnames, 0) : NULL, + stack + nargs, + nkwargs, 1, d, (int)nd, kwdefs, closure, name, qualname); } @@ -5014,8 +4988,7 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, result = _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, args, nargs, - k, nk, - NULL, NULL, + k, k + 1, nk, 2, d, nd, kwdefs, closure, name, qualname); Py_XDECREF(kwtuple); -- cgit v1.2.1 From 3308039cf159aa66866e32c9dd0e9a39ac64a73f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 12 Sep 2016 11:16:37 +0200 Subject: Issue #27213: Fix reference leaks --- Python/ceval.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 06d3a659bd..c9ac03f90f 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3300,6 +3300,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) PyEval_GetFuncDesc(func), kwargs->ob_type->tp_name); } + Py_DECREF(kwargs); goto error; } Py_DECREF(kwargs); @@ -3318,6 +3319,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) PyEval_GetFuncName(func), PyEval_GetFuncDesc(func), callargs->ob_type->tp_name); + Py_DECREF(callargs); goto error; } Py_SETREF(callargs, PySequence_Tuple(callargs)); -- cgit v1.2.1 From af28671b2c1ff0e7b5c27f588b5620de8d17e676 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 12 Sep 2016 13:37:07 +0200 Subject: Document kwnames in _PyObject_FastCallKeywords() and _PyStack_AsDict() Issue #27213. --- Python/ceval.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index c9ac03f90f..ff36d365b3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4863,7 +4863,12 @@ fast_function(PyObject *func, PyObject **stack, Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); Py_ssize_t nd; + assert(PyFunction_Check(func)); + assert(nargs >= 0); + assert(kwnames == NULL || PyTuple_CheckExact(kwnames)); assert((nargs == 0 && nkwargs == 0) || stack != NULL); + /* kwnames must only contains str strings, no subclass, and all keys must + be unique */ PCALL(PCALL_FUNCTION); PCALL(PCALL_FAST_FUNCTION); -- cgit v1.2.1 From f66fc0cf7578db63bce994ce14465f7b61aaa614 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 22 Sep 2016 19:41:20 +0300 Subject: Issue #28086: Single var-positional argument of tuple subtype was passed unscathed to the C-defined function. Now it is converted to exact tuple. --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index ff36d365b3..39cf33019d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3310,7 +3310,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) } callargs = POP(); func = TOP(); - if (!PyTuple_Check(callargs)) { + if (!PyTuple_CheckExact(callargs)) { if (Py_TYPE(callargs)->tp_iter == NULL && !PySequence_Check(callargs)) { PyErr_Format(PyExc_TypeError, @@ -3327,7 +3327,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) goto error; } } - assert(PyTuple_Check(callargs)); + assert(PyTuple_CheckExact(callargs)); result = do_call_core(func, callargs, kwargs); Py_DECREF(func); -- cgit v1.2.1 From a9d40323166c43221f8a4f57170cb057f95ad2dc Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 2 Oct 2016 10:33:46 +0300 Subject: Issue #28257: Improved error message when pass a non-iterable as a var-positional argument. Added opcode BUILD_TUPLE_UNPACK_WITH_CALL. --- Python/ceval.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 39cf33019d..717ac33891 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2509,9 +2509,10 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) DISPATCH(); } + TARGET(BUILD_TUPLE_UNPACK_WITH_CALL) TARGET(BUILD_TUPLE_UNPACK) TARGET(BUILD_LIST_UNPACK) { - int convert_to_tuple = opcode == BUILD_TUPLE_UNPACK; + int convert_to_tuple = opcode != BUILD_LIST_UNPACK; Py_ssize_t i; PyObject *sum = PyList_New(0); PyObject *return_value; @@ -2524,6 +2525,16 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) none_val = _PyList_Extend((PyListObject *)sum, PEEK(i)); if (none_val == NULL) { + if (opcode == BUILD_TUPLE_UNPACK_WITH_CALL && + PyErr_ExceptionMatches(PyExc_TypeError)) { + PyObject *func = PEEK(1 + oparg); + PyErr_Format(PyExc_TypeError, + "%.200s%.200s argument after * " + "must be an iterable, not %.200s", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + PEEK(i)->ob_type->tp_name); + } Py_DECREF(sum); goto error; } -- cgit v1.2.1 From 5ca9d515bdb90ee5588987dd8f8b2b723b21f4c3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 2 Oct 2016 11:06:43 +0300 Subject: Issue #27358: Optimized merging var-keyword arguments and improved error message when pass a non-mapping as a var-keyword argument. --- Python/ceval.c | 98 ++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 54 insertions(+), 44 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 717ac33891..c443305211 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2710,9 +2710,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) DISPATCH(); } - TARGET(BUILD_MAP_UNPACK_WITH_CALL) TARGET(BUILD_MAP_UNPACK) { - int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL; Py_ssize_t i; PyObject *sum = PyDict_New(); if (sum == NULL) @@ -2720,55 +2718,67 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) for (i = oparg; i > 0; i--) { PyObject *arg = PEEK(i); - if (with_call && PyDict_Size(sum)) { - PyObject *intersection = _PyDictView_Intersect(sum, arg); - - if (intersection == NULL) { - if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyObject *func = PEEK(2 + oparg); - PyErr_Format(PyExc_TypeError, - "%.200s%.200s argument after ** " - "must be a mapping, not %.200s", - PyEval_GetFuncName(func), - PyEval_GetFuncDesc(func), - arg->ob_type->tp_name); - } - Py_DECREF(sum); - goto error; - } - - if (PySet_GET_SIZE(intersection)) { - Py_ssize_t idx = 0; - PyObject *key; - PyObject *func = PEEK(2 + oparg); - Py_hash_t hash; - _PySet_NextEntry(intersection, &idx, &key, &hash); - if (!PyUnicode_Check(key)) { - PyErr_Format(PyExc_TypeError, - "%.200s%.200s keywords must be strings", - PyEval_GetFuncName(func), - PyEval_GetFuncDesc(func)); - } else { - PyErr_Format(PyExc_TypeError, - "%.200s%.200s got multiple " - "values for keyword argument '%U'", - PyEval_GetFuncName(func), - PyEval_GetFuncDesc(func), - key); - } - Py_DECREF(intersection); - Py_DECREF(sum); - goto error; + if (PyDict_Update(sum, arg) < 0) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Format(PyExc_TypeError, + "'%.200s' object is not a mapping1", + arg->ob_type->tp_name); } - Py_DECREF(intersection); + Py_DECREF(sum); + goto error; } + } - if (PyDict_Update(sum, arg) < 0) { + while (oparg--) + Py_DECREF(POP()); + PUSH(sum); + DISPATCH(); + } + + TARGET(BUILD_MAP_UNPACK_WITH_CALL) { + Py_ssize_t i; + PyObject *sum = PyDict_New(); + if (sum == NULL) + goto error; + + for (i = oparg; i > 0; i--) { + PyObject *arg = PEEK(i); + if (_PyDict_MergeEx(sum, arg, 2) < 0) { + PyObject *func = PEEK(2 + oparg); if (PyErr_ExceptionMatches(PyExc_AttributeError)) { PyErr_Format(PyExc_TypeError, - "'%.200s' object is not a mapping", + "%.200s%.200s argument after ** " + "must be a mapping, not %.200s", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), arg->ob_type->tp_name); } + else if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + if (val && PyTuple_Check(val) && PyTuple_GET_SIZE(val) == 1) { + PyObject *key = PyTuple_GET_ITEM(val, 0); + if (!PyUnicode_Check(key)) { + PyErr_Format(PyExc_TypeError, + "%.200s%.200s keywords must be strings", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func)); + } else { + PyErr_Format(PyExc_TypeError, + "%.200s%.200s got multiple " + "values for keyword argument '%U'", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + key); + } + Py_XDECREF(exc); + Py_XDECREF(val); + Py_XDECREF(tb); + } + else { + PyErr_Restore(exc, val, tb); + } + } Py_DECREF(sum); goto error; } -- cgit v1.2.1 From 1344635504637fc1aedbde3896bef330448a490f Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sun, 2 Oct 2016 13:08:25 +0300 Subject: Issue #27358: Fix typo in error message --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index c443305211..82cc9a2835 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2721,7 +2721,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) if (PyDict_Update(sum, arg) < 0) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) { PyErr_Format(PyExc_TypeError, - "'%.200s' object is not a mapping1", + "'%.200s' object is not a mapping", arg->ob_type->tp_name); } Py_DECREF(sum); -- cgit v1.2.1 From 1407e3dac2e80b37f8c292cb06d92f39b912d615 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Tue, 8 Nov 2016 15:13:07 -0500 Subject: Issue #27243: Change PendingDeprecationWarning -> DeprecationWarning. As it was agreed in the issue, __aiter__ returning an awaitable should result in PendingDeprecationWarning in 3.5 and in DeprecationWarning in 3.6. --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 82cc9a2835..0add7ecc09 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1911,7 +1911,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) Py_DECREF(iter); if (PyErr_WarnFormat( - PyExc_PendingDeprecationWarning, 1, + PyExc_DeprecationWarning, 1, "'%.100s' implements legacy __aiter__ protocol; " "__aiter__ should return an asynchronous " "iterator, not awaitable", -- cgit v1.2.1 From cd04b2ede7e37916bf65f25d1e549e5de80dff94 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 11 Nov 2016 04:31:18 -0800 Subject: Issue #28665: Harmonize STORE_DEREF with STORE_FAST and LOAD_DEREF giving a 40% speedup. --- Python/ceval.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index b2c90cc3b4..6bdc9983e3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2462,8 +2462,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) TARGET(STORE_DEREF) { PyObject *v = POP(); PyObject *cell = freevars[oparg]; - PyCell_Set(cell, v); - Py_DECREF(v); + PyObject *oldobj = PyCell_GET(cell); + PyCell_SET(cell, v); + Py_XDECREF(oldobj); DISPATCH(); } -- cgit v1.2.1 From 57c0f2e61c8a5893c37e576a3a03ba5e57c1132c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 20 Nov 2016 09:13:07 +0200 Subject: Replaced outdated macros _PyUnicode_AsString and _PyUnicode_AsStringAndSize with PyUnicode_AsUTF8 and PyUnicode_AsUTF8AndSize. --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 6bdc9983e3..a9d7c2f6d3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4712,7 +4712,7 @@ PyEval_GetFuncName(PyObject *func) if (PyMethod_Check(func)) return PyEval_GetFuncName(PyMethod_GET_FUNCTION(func)); else if (PyFunction_Check(func)) - return _PyUnicode_AsString(((PyFunctionObject*)func)->func_name); + return PyUnicode_AsUTF8(((PyFunctionObject*)func)->func_name); else if (PyCFunction_Check(func)) return ((PyCFunctionObject*)func)->m_ml->ml_name; else @@ -5286,7 +5286,7 @@ format_exc_check_arg(PyObject *exc, const char *format_str, PyObject *obj) if (!obj) return; - obj_str = _PyUnicode_AsString(obj); + obj_str = PyUnicode_AsUTF8(obj); if (!obj_str) return; -- cgit v1.2.1 From a016e87125f3ab6157fea573e77a44a24c3565c8 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 21 Nov 2016 17:24:23 -0800 Subject: Issue #27100: With statement reports missing __enter__ before __exit__. (Contributed by Jonathan Ellington.) --- Python/ceval.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index a9d7c2f6d3..ebf073a87f 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3133,15 +3133,15 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) _Py_IDENTIFIER(__exit__); _Py_IDENTIFIER(__enter__); PyObject *mgr = TOP(); - PyObject *exit = special_lookup(mgr, &PyId___exit__), *enter; + PyObject *enter = special_lookup(mgr, &PyId___enter__), *exit; PyObject *res; + if (enter == NULL) + goto error; + exit = special_lookup(mgr, &PyId___exit__); if (exit == NULL) goto error; SET_TOP(exit); - enter = special_lookup(mgr, &PyId___enter__); Py_DECREF(mgr); - if (enter == NULL) - goto error; res = PyObject_CallFunctionObjArgs(enter, NULL); Py_DECREF(enter); if (res == NULL) -- cgit v1.2.1 From ea51eb9cef802e33478f61e5f7eb682f57fafce2 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 22 Nov 2016 11:50:40 -0800 Subject: Issue #27100: Fix ref leak --- Python/ceval.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index ebf073a87f..83296f637f 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3138,8 +3138,10 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) if (enter == NULL) goto error; exit = special_lookup(mgr, &PyId___exit__); - if (exit == NULL) + if (exit == NULL) { + Py_DECREF(enter); goto error; + } SET_TOP(exit); Py_DECREF(mgr); res = PyObject_CallFunctionObjArgs(enter, NULL); -- cgit v1.2.1 From d27f18c0fd746ef7078a84dfe0fcbfa18d43d0c8 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 24 Nov 2016 22:33:01 +0100 Subject: Fix _PyGen_yf() Issue #28782: Fix a bug in the implementation ``yield from`` when checking if the next instruction is YIELD_FROM. Regression introduced by WORDCODE (issue #26647). Reviewed by Serhiy Storchaka and Yury Selivanov. --- Python/ceval.c | 1 + 1 file changed, 1 insertion(+) (limited to 'Python/ceval.c') diff --git a/Python/ceval.c b/Python/ceval.c index 83296f637f..d5172b9631 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2043,6 +2043,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) f->f_stacktop = stack_pointer; why = WHY_YIELD; /* and repeat... */ + assert(f->f_lasti >= (int)sizeof(_Py_CODEUNIT)); f->f_lasti -= sizeof(_Py_CODEUNIT); goto fast_yield; } -- cgit v1.2.1