diff --git a/Objects/frameobject.c b/Objects/frameobject.c index a5300d1de3e..74e70122321 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -251,7 +251,51 @@ PyFrame_BlockPop(PyFrameObject *f) /* Convert between "fast" version of locals and dictionary version */ -/* XXX should also copy free variables and cell variables */ +void +map_to_dict(PyObject *map, int nmap, PyObject *dict, PyObject **values, + int deref) +{ + int j; + for (j = nmap; --j >= 0; ) { + PyObject *key = PyTuple_GetItem(map, j); + PyObject *value = values[j]; + if (deref) + value = PyCell_GET(value); + if (value == NULL) { + PyErr_Clear(); + if (PyDict_DelItem(dict, key) != 0) + PyErr_Clear(); + } + else { + if (PyDict_SetItem(dict, key, value) != 0) + PyErr_Clear(); + } + } +} + +void +dict_to_map(PyObject *map, int nmap, PyObject *dict, PyObject **values, + int deref, int clear) +{ + int j; + for (j = nmap; --j >= 0; ) { + PyObject *key = PyTuple_GetItem(map, j); + PyObject *value = PyDict_GetItem(dict, key); + Py_XINCREF(value); + if (deref) { + if (value) { + if (PyCell_Set(values[j], value) < 0) + PyErr_Clear(); + } else if (clear) { + Py_XDECREF(values[j]); + values[j] = value; + } + } else if (value != NULL || clear) { + Py_XDECREF(values[j]); + values[j] = value; + } + } +} void PyFrame_FastToLocals(PyFrameObject *f) @@ -281,18 +325,19 @@ PyFrame_FastToLocals(PyFrameObject *f) j = PyTuple_Size(map); if (j > f->f_nlocals) j = f->f_nlocals; - for (; --j >= 0; ) { - PyObject *key = PyTuple_GetItem(map, j); - PyObject *value = fast[j]; - if (value == NULL) { - PyErr_Clear(); - if (PyDict_DelItem(locals, key) != 0) - PyErr_Clear(); - } - else { - if (PyDict_SetItem(locals, key, value) != 0) - PyErr_Clear(); + map_to_dict(map, j, locals, fast, 0); + if (f->f_ncells || f->f_nfreevars) { + if (!(PyTuple_Check(f->f_code->co_cellvars) + && PyTuple_Check(f->f_code->co_freevars))) { + Py_DECREF(locals); + return; } + map_to_dict(f->f_code->co_cellvars, + PyTuple_GET_SIZE(f->f_code->co_cellvars), + locals, fast + f->f_nlocals, 1); + map_to_dict(f->f_code->co_freevars, + PyTuple_GET_SIZE(f->f_code->co_freevars), + locals, fast + f->f_nlocals + f->f_ncells, 1); } PyErr_Restore(error_type, error_value, error_traceback); } @@ -318,14 +363,17 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) j = PyTuple_Size(map); if (j > f->f_nlocals) j = f->f_nlocals; - for (; --j >= 0; ) { - PyObject *key = PyTuple_GetItem(map, j); - PyObject *value = PyDict_GetItem(locals, key); - Py_XINCREF(value); - if (value != NULL || clear) { - Py_XDECREF(fast[j]); - fast[j] = value; - } + dict_to_map(f->f_code->co_varnames, j, locals, fast, 0, clear); + if (f->f_ncells || f->f_nfreevars) { + if (!(PyTuple_Check(f->f_code->co_cellvars) + && PyTuple_Check(f->f_code->co_freevars))) + return; + dict_to_map(f->f_code->co_cellvars, + PyTuple_GET_SIZE(f->f_code->co_cellvars), + locals, fast, 1, clear); + dict_to_map(f->f_code->co_freevars, + PyTuple_GET_SIZE(f->f_code->co_freevars), + locals, fast, 1, clear); } PyErr_Restore(error_type, error_value, error_traceback); } diff --git a/Python/ceval.c b/Python/ceval.c index cb5936de616..22b3ea0e0ae 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -570,11 +570,52 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, goto fail; } } - /* Allocate storage for cell vars and copy free vars into frame */ + /* Allocate and initialize storage for cell vars, and copy free + vars into frame. This isn't too efficient right now. */ if (f->f_ncells) { - int i; - for (i = 0; i < f->f_ncells; ++i) - freevars[i] = PyCell_New(NULL); + int i = 0, j = 0, nargs, found; + char *cellname, *argname; + PyObject *c; + + nargs = co->co_argcount; + if (co->co_flags & CO_VARARGS) + nargs++; + if (co->co_flags & CO_VARKEYWORDS) + nargs++; + + /* Check for cells that shadow args */ + for (i = 0; i < f->f_ncells && j < nargs; ++i) { + cellname = PyString_AS_STRING( + PyTuple_GET_ITEM(co->co_cellvars, i)); + found = 0; + while (j < nargs) { + argname = PyString_AS_STRING( + PyTuple_GET_ITEM(co->co_varnames, j)); + if (strcmp(cellname, argname) == 0) { + c = PyCell_New(GETLOCAL(j)); + if (c == NULL) + goto fail; + GETLOCAL(f->f_nlocals + i) = c; + found = 1; + break; + } + j++; + } + if (found == 0) { + c = PyCell_New(NULL); + if (c == NULL) + goto fail; + SETLOCAL(f->f_nlocals + i, c); + } + } + /* Initialize any that are left */ + while (i < f->f_ncells) { + c = PyCell_New(NULL); + if (c == NULL) + goto fail; + SETLOCAL(f->f_nlocals + i, c); + i++; + } } if (f->f_nfreevars) { int i; diff --git a/Python/compile.c b/Python/compile.c index 07729b1b3c2..ed50f7ef41b 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -3552,16 +3552,7 @@ com_arglist(struct compiling *c, node *n) break; REQ(ch, fpdef); /* fpdef: NAME | '(' fplist ')' */ fp = CHILD(ch, 0); - if (TYPE(fp) == NAME) { - PyObject *v; - name = STR(fp); - v = PyDict_GetItemString(c->c_cellvars, name); - if (v) { - com_addoparg(c, LOAD_FAST, narg); - com_addoparg(c, STORE_DEREF, - PyInt_AS_LONG(v)); - } - } else { + if (TYPE(fp) != NAME) { name = nbuf; sprintf(nbuf, ".%d", i); complex = 1; @@ -3576,47 +3567,6 @@ com_arglist(struct compiling *c, node *n) else REQ(ch, COMMA); } - /* Handle *arguments */ - if (i < nch) { - node *ch; - ch = CHILD(n, i); - if (TYPE(ch) != DOUBLESTAR) { - REQ(ch, STAR); - ch = CHILD(n, i+1); - if (TYPE(ch) == NAME) { - PyObject *v; - i += 3; - v = PyDict_GetItemString(c->c_cellvars, - STR(ch)); - if (v) { - com_addoparg(c, LOAD_FAST, narg); - com_addoparg(c, STORE_DEREF, - PyInt_AS_LONG(v)); - } - narg++; - } - } - } - /* Handle **keywords */ - if (i < nch) { - PyObject *v; - node *ch; - ch = CHILD(n, i); - if (TYPE(ch) != DOUBLESTAR) { - REQ(ch, STAR); - ch = CHILD(n, i+1); - REQ(ch, STAR); - ch = CHILD(n, i+2); - } - else - ch = CHILD(n, i+1); - REQ(ch, NAME); - v = PyDict_GetItemString(c->c_cellvars, STR(ch)); - if (v) { - com_addoparg(c, LOAD_FAST, narg); - com_addoparg(c, STORE_DEREF, PyInt_AS_LONG(v)); - } - } if (complex) { /* Generate code for complex arguments only after having counted the simple arguments */ @@ -4137,6 +4087,69 @@ symtable_resolve_free(struct compiling *c, PyObject *name, return 0; } +/* If a variable is a cell and an argument, make sure that appears in + co_cellvars before any variable to its right in varnames. +*/ + + +static int +symtable_cellvar_offsets(PyObject **cellvars, int argcount, + PyObject *varnames, int flags) +{ + PyObject *v, *w, *d, *list = NULL; + int i, pos; + + if (flags & CO_VARARGS) + argcount++; + if (flags & CO_VARKEYWORDS) + argcount++; + for (i = argcount; --i >= 0; ) { + v = PyList_GET_ITEM(varnames, i); + if (PyDict_GetItem(*cellvars, v)) { + if (list == NULL) { + list = PyList_New(1); + if (list == NULL) + return -1; + PyList_SET_ITEM(list, 0, v); + Py_INCREF(v); + } else + PyList_Insert(list, 0, v); + } + } + if (list == NULL || PyList_GET_SIZE(list) == 0) + return 0; + /* There are cellvars that are also arguments. Create a dict + to replace cellvars and put the args at the front. + */ + d = PyDict_New(); + for (i = PyList_GET_SIZE(list); --i >= 0; ) { + v = PyInt_FromLong(i); + if (v == NULL) + goto fail; + if (PyDict_SetItem(d, PyList_GET_ITEM(list, i), v) < 0) + goto fail; + if (PyDict_DelItem(*cellvars, PyList_GET_ITEM(list, i)) < 0) + goto fail; + } + pos = 0; + i = PyList_GET_SIZE(list); + Py_DECREF(list); + while (PyDict_Next(*cellvars, &pos, &v, &w)) { + w = PyInt_FromLong(i++); /* don't care about the old key */ + if (PyDict_SetItem(d, v, w) < 0) { + Py_DECREF(w); + goto fail; + } + Py_DECREF(w); + } + Py_DECREF(*cellvars); + *cellvars = d; + return 1; + fail: + Py_DECREF(d); + return -1; +} + static int symtable_freevar_offsets(PyObject *freevars, int offset) { @@ -4386,6 +4399,11 @@ symtable_load_symbols(struct compiling *c) } } + if (si.si_ncells > 1) { /* one cell is always in order */ + if (symtable_cellvar_offsets(&c->c_cellvars, c->c_argcount, + c->c_varnames, c->c_flags) < 0) + return -1; + } if (symtable_freevar_offsets(c->c_freevars, si.si_ncells) < 0) return -1; return symtable_update_flags(c, ste, &si);