[Python-checkins] python/dist/src/Python symtable.c, 2.10.8.36, 2.10.8.37 newcompile.c, 1.1.2.111, 1.1.2.112
jhylton@users.sourceforge.net
jhylton at users.sourceforge.net
Tue Oct 11 21:18:15 CEST 2005
Update of /cvsroot/python/python/dist/src/Python
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv20508/Python
Modified Files:
Tag: ast-branch
symtable.c newcompile.c
Log Message:
Fix symbol table to catch several scoping syntax errors.
It's illegal to mix import * or bare exec with nested scopes, because
there is no unambiguous way to decide whether to treat variables are
free variables or globals. The symbol table change a bit to detect
these errors.
ste_optimized was renamed ste_unoptimized, to match its use: It
contains a non-zero value with an unoptimized namespace
(e.g. LOAD_NAME) will be used. Since the top-level uses LOAD_NAME,
added OPT_TOPLEVEL along with the other OPT_ defines.
Add an ste_free flag as a cheap way to tell if a block has free
variables, including those inherited from children. Actually compute
ste_free_child and ste_opt_lineno.
Track rename of ste_optimized to ste_unoptimized in symbol table and
compiler.
Fixes test_scope.
Index: symtable.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/symtable.c,v
retrieving revision 2.10.8.36
retrieving revision 2.10.8.37
diff -u -d -r2.10.8.36 -r2.10.8.37
--- symtable.c 31 Aug 2005 01:48:41 -0000 2.10.8.36
+++ symtable.c 11 Oct 2005 19:18:11 -0000 2.10.8.37
@@ -47,14 +47,15 @@
ste->ste_children = v;
ste->ste_type = block;
- ste->ste_optimized = block == FunctionBlock;
+ ste->ste_unoptimized = 0;
+ ste->ste_nested = 0;
+ ste->ste_free = 0;
ste->ste_varargs = 0;
ste->ste_varkeywords = 0;
ste->ste_opt_lineno = 0;
ste->ste_tmpname = 0;
ste->ste_lineno = lineno;
- ste->ste_nested = 0;
if (st->st_cur != NULL &&
(st->st_cur->ste_nested ||
st->st_cur->ste_type == FunctionBlock))
@@ -214,6 +215,7 @@
symtable_enter_block(st, GET_IDENTIFIER(top), ModuleBlock,
(void *)mod, 0);
st->st_top = st->st_cur;
+ st->st_cur->ste_unoptimized = OPT_TOPLEVEL;
/* Any other top-level initialization? */
switch (mod->kind) {
case Module_kind:
@@ -222,7 +224,7 @@
if (!symtable_visit_stmt(st, asdl_seq_GET(seq, i)))
goto error;
break;
- case Expression_kind:
+ case Expression_kind:
if (!symtable_visit_expr(st, mod->v.Expression.body))
goto error;
break;
@@ -335,11 +337,13 @@
/* Decide on scope of name, given flags.
The dicts passed in as arguments are modified as necessary.
+ ste is passed so that flags can be updated.
*/
static int
-analyze_name(PyObject *dict, PyObject *name, int flags, PyObject *bound,
- PyObject *local, PyObject *free, PyObject *global, int nested)
+analyze_name(PySTEntryObject *ste, PyObject *dict, PyObject *name, int flags,
+ PyObject *bound, PyObject *local, PyObject *free,
+ PyObject *global)
{
if (flags & DEF_GLOBAL) {
if (flags & DEF_PARAM) {
@@ -374,6 +378,7 @@
*/
if (bound && PyDict_GetItem(bound, name)) {
SET_SCOPE(dict, name, FREE);
+ ste->ste_free = 1;
if (PyDict_SetItem(free, name, Py_None) < 0)
return 0;
return 1;
@@ -386,6 +391,8 @@
return 1;
}
else {
+ if (ste->ste_nested)
+ ste->ste_free = 1;
SET_SCOPE(dict, name, GLOBAL_IMPLICIT);
return 1;
}
@@ -433,7 +440,53 @@
return success;
}
-/* Enter the final scope information into the st_symbols dict. */
+/* Check for illegal statements in unoptimized namespaces */
+static int
+check_unoptimized(const PySTEntryObject* ste) {
+ char buf[300];
+
+ if (ste->ste_type == ModuleBlock || !ste->ste_unoptimized
+ || !(ste->ste_free || ste->ste_child_free))
+ return 1;
+
+ const char* trailer = (ste->ste_child_free ?
+ "contains a nested function with free variables" :
+ "is a nested function");
+
+ switch (ste->ste_unoptimized) {
+ case OPT_TOPLEVEL: /* exec / import * at top-level is fine */
+ case OPT_EXEC: /* qualified exec is fine */
+ return 1;
+ case OPT_IMPORT_STAR:
+ PyOS_snprintf(buf, sizeof(buf),
+ "import * is not allowed in function '%.100s' "
+ "because it is %s",
+ PyString_AS_STRING(ste->ste_name), trailer);
+ break;
+ case OPT_BARE_EXEC:
+ PyOS_snprintf(buf, sizeof(buf),
+ "unqualified exec is not allowed in function "
+ "'%.100s' it %s",
+ PyString_AS_STRING(ste->ste_name), trailer);
+ break;
+ default:
+ PyOS_snprintf(buf, sizeof(buf),
+ "function '%.100s' uses import * and bare exec, "
+ "which are illegal because it %s",
+ PyString_AS_STRING(ste->ste_name), trailer);
+ break;
+ }
+
+ PyErr_SetString(PyExc_SyntaxError, buf);
+ PyErr_SyntaxLocation(ste->ste_table->st_filename,
+ ste->ste_opt_lineno);
+ return 0;
+}
+
+/* Enter the final scope information into the st_symbols dict.
+ *
+ * All arguments are dicts. Modifies symbols, others are read-only.
+*/
static int
update_symbols(PyObject *symbols, PyObject *scope,
PyObject *bound, PyObject *free, int class)
@@ -501,11 +554,11 @@
/* Make final symbol table decisions for block of ste.
Arguments:
+ ste -- current symtable entry (input/output)
bound -- set of variables bound in enclosing scopes (input)
free -- set of free variables in enclosed scopes (output)
globals -- set of declared global variables in enclosing scopes (input)
*/
-
static int
analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
@@ -547,8 +600,8 @@
assert(PyDict_Check(ste->ste_symbols));
while (PyDict_Next(ste->ste_symbols, &pos, &name, &v)) {
flags = PyInt_AS_LONG(v);
- if (!analyze_name(scope, name, flags, bound, local, free,
- global, ste->ste_nested))
+ if (!analyze_name(ste, scope, name, flags, bound, local, free,
+ global))
goto error;
}
@@ -565,12 +618,15 @@
goto error;
}
+ /* Recursively call analyze_block() on each child block */
for (i = 0; i < PyList_GET_SIZE(ste->ste_children); ++i) {
PyObject *c = PyList_GET_ITEM(ste->ste_children, i);
assert(c && PySTEntry_Check(c));
- if (!analyze_block((PySTEntryObject *)c, newbound, newfree,
- newglobal))
+ PySTEntryObject* entry = (PySTEntryObject*)c;
+ if (!analyze_block(entry, newbound, newfree, newglobal))
goto error;
+ if (entry->ste_free || entry->ste_child_free)
+ ste->ste_child_free = 1;
}
if (ste->ste_type == FunctionBlock && !analyze_cells(scope, newfree))
@@ -578,6 +634,8 @@
if (!update_symbols(ste->ste_symbols, scope, bound, newfree,
ste->ste_type == ClassBlock))
goto error;
+ if (!check_unoptimized(ste))
+ goto error;
if (PyDict_Update(free, newfree) < 0)
goto error;
@@ -871,17 +929,29 @@
break;
case Import_kind:
VISIT_SEQ(st, alias, s->v.Import.names);
+ /* XXX Don't have the lineno available inside
+ visit_alias */
+ if (st->st_cur->ste_unoptimized && !st->st_cur->ste_opt_lineno)
+ st->st_cur->ste_opt_lineno = s->lineno;
break;
case ImportFrom_kind:
VISIT_SEQ(st, alias, s->v.ImportFrom.names);
+ /* XXX Don't have the lineno available inside
+ visit_alias */
+ if (st->st_cur->ste_unoptimized && !st->st_cur->ste_opt_lineno)
+ st->st_cur->ste_opt_lineno = s->lineno;
break;
case Exec_kind:
VISIT(st, expr, s->v.Exec.body);
- st->st_cur->ste_optimized = 0;
+ if (!st->st_cur->ste_opt_lineno)
+ st->st_cur->ste_opt_lineno = s->lineno;
if (s->v.Exec.globals) {
+ st->st_cur->ste_unoptimized |= OPT_EXEC;
VISIT(st, expr, s->v.Exec.globals);
if (s->v.Exec.locals)
VISIT(st, expr, s->v.Exec.locals);
+ } else {
+ st->st_cur->ste_unoptimized |= OPT_BARE_EXEC;
}
break;
case Global_kind: {
@@ -1134,7 +1204,7 @@
"import * only allowed at module level"))
return 0;
}
- st->st_cur->ste_optimized = 0;
+ st->st_cur->ste_unoptimized |= OPT_IMPORT_STAR;
return 1;
}
}
Index: newcompile.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/Attic/newcompile.c,v
retrieving revision 1.1.2.111
retrieving revision 1.1.2.112
diff -u -d -r1.1.2.111 -r1.1.2.112
--- newcompile.c 7 Oct 2005 18:42:50 -0000 1.1.2.111
+++ newcompile.c 11 Oct 2005 19:18:11 -0000 1.1.2.112
@@ -2289,7 +2289,7 @@
optype = OP_FAST;
break;
case GLOBAL_IMPLICIT:
- if (c->u->u_ste->ste_optimized)
+ if (!c->u->u_ste->ste_unoptimized)
optype = OP_GLOBAL;
break;
case GLOBAL_EXPLICIT:
@@ -3490,7 +3490,7 @@
if (ste->ste_type != ModuleBlock)
flags |= CO_NEWLOCALS;
if (ste->ste_type == FunctionBlock) {
- if (ste->ste_optimized)
+ if (!ste->ste_unoptimized)
flags |= CO_OPTIMIZED;
if (ste->ste_nested)
flags |= CO_NESTED;
More information about the Python-checkins
mailing list