From 220ae7c0bf2054ad01f58e1cf669ac069eb9a574 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 21 Mar 2001 16:43:47 +0000 Subject: Fix PyFrame_FastToLocals() and counterpart to deal with cells and frees. Note there doesn't seem to be any way to test LocalsToFast(), because the instructions that trigger it are illegal in nested scopes with free variables. Fix allocation strategy for cells that are also formal parameters. Instead of emitting LOAD_FAST / STORE_DEREF pairs for each parameter, have the argument handling code in eval_code2() do the right thing. A side-effect of this change is that cell variables that are also arguments are listed at the front of co_cellvars in the order they appear in the argument list. --- Objects/frameobject.c | 88 +++++++++++++++++++++++++++--------- Python/ceval.c | 49 +++++++++++++++++++-- Python/compile.c | 120 +++++++++++++++++++++++++++++--------------------- 3 files changed, 182 insertions(+), 75 deletions(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index a5300d1..74e7012 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 cb5936d..22b3ea0 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 07729b1..ed50f7e 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); -- cgit v0.12