summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Objects/frameobject.c88
-rw-r--r--Python/ceval.c49
-rw-r--r--Python/compile.c120
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);