* Python/compile.c (com_argdefs, com_arglist): avoid referencing

CHILD(n,i) for i >= NCH(n)
This commit is contained in:
Guido van Rossum 1994-08-29 12:16:12 +00:00
parent 3a24181837
commit 4ca6c9db81
1 changed files with 306 additions and 159 deletions

View File

@ -1,5 +1,5 @@
/***********************************************************
Copyright 1991, 1992, 1993 by Stichting Mathematisch Centrum,
Copyright 1991, 1992, 1993, 1994 by Stichting Mathematisch Centrum,
Amsterdam, The Netherlands.
All Rights Reserved
@ -127,16 +127,16 @@ typeobject Codetype = {
"code",
sizeof(codeobject),
0,
code_dealloc, /*tp_dealloc*/
(destructor)code_dealloc, /*tp_dealloc*/
0, /*tp_print*/
code_getattr, /*tp_getattr*/
(getattrfunc)code_getattr, /*tp_getattr*/
0, /*tp_setattr*/
code_compare, /*tp_compare*/
code_repr, /*tp_repr*/
(cmpfunc)code_compare, /*tp_compare*/
(reprfunc)code_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
code_hash, /*tp_hash*/
(hashfunc)code_hash, /*tp_hash*/
};
codeobject *
@ -151,16 +151,37 @@ newcodeobject(code, consts, names, filename, name)
int i;
/* Check argument types */
if (code == NULL || !is_stringobject(code) ||
consts == NULL || !is_listobject(consts) ||
names == NULL || !is_listobject(names) ||
consts == NULL ||
names == NULL ||
name == NULL || !(is_stringobject(name) || name == None)) {
err_badcall();
return NULL;
}
/* Allow two lists instead of two tuples */
if (is_listobject(consts) && is_listobject(names)) {
consts = listtuple(consts);
if (consts == NULL)
return NULL;
names = listtuple(names);
if (names == NULL) {
DECREF(consts);
return NULL;
}
}
else if (!is_tupleobject(consts) && !is_tupleobject(names)) {
err_badcall();
return NULL;
}
else {
INCREF(consts);
INCREF(names);
}
/* Make sure the list of names contains only strings */
for (i = getlistsize(names); --i >= 0; ) {
object *v = getlistitem(names, i);
for (i = gettuplesize(names); --i >= 0; ) {
object *v = gettupleitem(names, i);
if (v == NULL || !is_stringobject(v)) {
DECREF(consts);
DECREF(names);
err_badcall();
return NULL;
}
@ -169,15 +190,17 @@ newcodeobject(code, consts, names, filename, name)
if (co != NULL) {
INCREF(code);
co->co_code = (stringobject *)code;
INCREF(consts);
co->co_consts = consts;
INCREF(names);
co->co_names = names;
INCREF(filename);
co->co_filename = filename;
INCREF(name);
co->co_name = name;
}
else {
DECREF(consts);
DECREF(names);
}
return co;
}
@ -194,6 +217,7 @@ struct compiling {
int c_nexti; /* index into c_code */
int c_errors; /* counts errors occurred */
int c_infunction; /* set when compiling a function */
int c_interactive; /* generating code for interactive command */
int c_loops; /* counts nested loops */
int c_begin; /* begin of current loop, for 'continue' */
int c_block[MAXBLOCKS]; /* stack of block types */
@ -249,6 +273,7 @@ static int com_addconst PROTO((struct compiling *, object *));
static int com_addname PROTO((struct compiling *, object *));
static void com_addopname PROTO((struct compiling *, int, node *));
static void com_list PROTO((struct compiling *, node *, int));
static int com_argdefs PROTO((struct compiling *, node *, int *));
static int
com_init(c, filename)
@ -266,6 +291,7 @@ com_init(c, filename)
c->c_nexti = 0;
c->c_errors = 0;
c->c_infunction = 0;
c->c_interactive = 0;
c->c_loops = 0;
c->c_begin = 0;
c->c_nblocks = 0;
@ -462,20 +488,19 @@ static object *
parsenumber(s)
char *s;
{
extern long strtol PROTO((const char *, char **, int));
extern unsigned long strtoul PROTO((const char *, char **, int));
extern double strtod PROTO((const char *, char **));
extern long mystrtol PROTO((const char *, char **, int));
extern unsigned long mystrtoul PROTO((const char *, char **, int));
extern double atof PROTO((const char *));
char *end;
long x;
double xx;
errno = 0;
end = s + strlen(s) - 1;
if (*end == 'l' || *end == 'L')
return long_scan(s, 0);
if (s[0] == '0')
x = (long) strtoul(s, &end, 0);
x = (long) mystrtoul(s, &end, 0);
else
x = strtol(s, &end, 0);
x = mystrtol(s, &end, 0);
if (*end == '\0') {
if (errno != 0) {
err_setstr(OverflowError,
@ -484,22 +509,8 @@ parsenumber(s)
}
return newintobject(x);
}
errno = 0;
xx = strtod(s, &end);
if (*end == '\0') {
#ifndef BROKEN_STRTOD
/* Some strtod() versions (e.g., in older SunOS systems)
set errno incorrectly; better to ignore overflows
than not to be able to use float literals at all! */
if (errno != 0) {
err_setstr(OverflowError, "float literal too large");
return NULL;
}
#endif
return newfloatobject(xx);
}
err_setstr(SystemError, "bad number syntax?!?!");
return NULL;
/* XXX Huge floats may silently fail */
return newfloatobject(atof(s));
}
static object *
@ -510,6 +521,7 @@ parsestr(s)
int len;
char *buf;
char *p;
char *end;
int c;
int quote = *s;
if (quote != '\'' && quote != '\"') {
@ -522,11 +534,20 @@ parsestr(s)
err_badcall();
return NULL;
}
if (len >= 4 && s[0] == quote && s[1] == quote) {
s += 2;
len -= 2;
if (s[--len] != quote || s[--len] != quote) {
err_badcall();
return NULL;
}
}
if (strchr(s, '\\') == NULL)
return newsizedstringobject(s, len);
v = newsizedstringobject((char *)NULL, len);
p = buf = getstringvalue(v);
while (*s != '\0' && *s != quote) {
end = s + len;
while (s < end) {
if (*s != '\\') {
*p++ = *s++;
continue;
@ -534,6 +555,7 @@ parsestr(s)
s++;
switch (*s++) {
/* XXX This assumes ASCII! */
case '\n': break;
case '\\': *p++ = '\\'; break;
case '\'': *p++ = '\''; break;
case '\"': *p++ = '\"'; break;
@ -649,7 +671,14 @@ com_atom(c, n)
com_addoparg(c, LOAD_CONST, i);
break;
case STRING:
if ((v = parsestr(STR(ch))) == NULL) {
if ((v = parsestr(STR(ch))) != NULL) {
/* String literal concatenation */
for (i = 1; i < NCH(n) && v != NULL; i++) {
joinstring_decref(&v,
parsestr(STR(CHILD(n, i))));
}
}
if (v == NULL) {
c->c_errors++;
i = 255;
}
@ -1098,6 +1127,8 @@ com_test(c, n)
if (NCH(n) == 1 && TYPE(CHILD(n, 0)) == lambdef) {
object *v;
int i;
int argcount;
int ndefs = com_argdefs(c, CHILD(n, 0), &argcount);
v = (object *) compile(CHILD(n, 0), c->c_filename);
if (v == NULL) {
c->c_errors++;
@ -1109,6 +1140,8 @@ com_test(c, n)
}
com_addoparg(c, LOAD_CONST, i);
com_addbyte(c, BUILD_FUNCTION);
if (ndefs > 0)
com_addoparg(c, SET_FUNC_ARGS, argcount);
}
else {
int anchor = 0;
@ -1355,7 +1388,10 @@ com_expr_stmt(c, n)
REQ(n, expr_stmt); /* testlist ('=' testlist)* */
com_node(c, CHILD(n, NCH(n)-1));
if (NCH(n) == 1) {
com_addbyte(c, PRINT_EXPR);
if (c->c_interactive)
com_addbyte(c, PRINT_EXPR);
else
com_addbyte(c, POP_TOP);
}
else {
int i;
@ -1625,25 +1661,7 @@ com_for_stmt(c, n)
com_backpatch(c, break_anchor);
}
/* Although 'execpt' and 'finally' clauses can be combined
syntactically, they are compiled separately. In fact,
try: S
except E1: S1
except E2: S2
...
finally: Sf
is equivalent to
try:
try: S
except E1: S1
except E2: S2
...
finally: Sf
meaning that the 'finally' clause is entered even if things
go wrong again in an exception handler. Note that this is
not the case for exception handlers: at most one is entered.
Code generated for "try: S finally: Sf" is as follows:
/* Code generated for "try: S finally: Sf" is as follows:
SETUP_FINALLY L
<code for S>
@ -1710,86 +1728,97 @@ com_for_stmt(c, n)
*/
static void
com_try_stmt(c, n)
com_try_except(c, n)
struct compiling *c;
node *n;
{
int except_anchor = 0;
int end_anchor = 0;
int else_anchor = 0;
int i;
node *ch;
com_addfwref(c, SETUP_EXCEPT, &except_anchor);
block_push(c, SETUP_EXCEPT);
com_node(c, CHILD(n, 2));
com_addbyte(c, POP_BLOCK);
block_pop(c, SETUP_EXCEPT);
com_addfwref(c, JUMP_FORWARD, &else_anchor);
com_backpatch(c, except_anchor);
for (i = 3;
i < NCH(n) && TYPE(ch = CHILD(n, i)) == except_clause;
i += 3) {
/* except_clause: 'except' [expr [',' expr]] */
if (except_anchor == 0) {
err_setstr(SyntaxError,
"default 'except:' must be last");
c->c_errors++;
break;
}
except_anchor = 0;
com_addoparg(c, SET_LINENO, ch->n_lineno);
if (NCH(ch) > 1) {
com_addbyte(c, DUP_TOP);
com_node(c, CHILD(ch, 1));
com_addoparg(c, COMPARE_OP, EXC_MATCH);
com_addfwref(c, JUMP_IF_FALSE, &except_anchor);
com_addbyte(c, POP_TOP);
}
com_addbyte(c, POP_TOP);
if (NCH(ch) > 3)
com_assign(c, CHILD(ch, 3), 1/*assigning*/);
else
com_addbyte(c, POP_TOP);
com_addbyte(c, POP_TOP);
com_node(c, CHILD(n, i+2));
com_addfwref(c, JUMP_FORWARD, &end_anchor);
if (except_anchor) {
com_backpatch(c, except_anchor);
com_addbyte(c, POP_TOP);
}
}
com_addbyte(c, END_FINALLY);
com_backpatch(c, else_anchor);
if (i < NCH(n))
com_node(c, CHILD(n, i+2));
com_backpatch(c, end_anchor);
}
static void
com_try_finally(c, n)
struct compiling *c;
node *n;
{
int finally_anchor = 0;
int except_anchor = 0;
REQ(n, try_stmt);
/* 'try' ':' suite (except_clause ':' suite)*
| 'try' ':' 'finally' ':' suite */
node *ch;
/* XXX This can be simplified because except and finally can
no longer be mixed in a single try statement */
if (NCH(n) > 3 && TYPE(CHILD(n, NCH(n)-3)) != except_clause) {
/* Have a 'finally' clause */
com_addfwref(c, SETUP_FINALLY, &finally_anchor);
block_push(c, SETUP_FINALLY);
}
if (NCH(n) > 3 && TYPE(CHILD(n, 3)) == except_clause) {
/* Have an 'except' clause */
com_addfwref(c, SETUP_EXCEPT, &except_anchor);
block_push(c, SETUP_EXCEPT);
}
com_addfwref(c, SETUP_FINALLY, &finally_anchor);
block_push(c, SETUP_FINALLY);
com_node(c, CHILD(n, 2));
if (except_anchor) {
int end_anchor = 0;
int i;
node *ch;
com_addbyte(c, POP_BLOCK);
block_pop(c, SETUP_EXCEPT);
com_addfwref(c, JUMP_FORWARD, &end_anchor);
com_backpatch(c, except_anchor);
for (i = 3;
i < NCH(n) && TYPE(ch = CHILD(n, i)) == except_clause;
i += 3) {
/* except_clause: 'except' [expr [',' expr]] */
if (except_anchor == 0) {
err_setstr(SyntaxError,
"default 'except:' must be last");
c->c_errors++;
break;
}
except_anchor = 0;
com_addoparg(c, SET_LINENO, ch->n_lineno);
if (NCH(ch) > 1) {
com_addbyte(c, DUP_TOP);
com_node(c, CHILD(ch, 1));
com_addoparg(c, COMPARE_OP, EXC_MATCH);
com_addfwref(c, JUMP_IF_FALSE, &except_anchor);
com_addbyte(c, POP_TOP);
}
com_addbyte(c, POP_TOP);
if (NCH(ch) > 3)
com_assign(c, CHILD(ch, 3), 1/*assigning*/);
else
com_addbyte(c, POP_TOP);
com_addbyte(c, POP_TOP);
com_node(c, CHILD(n, i+2));
com_addfwref(c, JUMP_FORWARD, &end_anchor);
if (except_anchor) {
com_backpatch(c, except_anchor);
com_addbyte(c, POP_TOP);
}
}
com_addbyte(c, END_FINALLY);
com_backpatch(c, end_anchor);
}
if (finally_anchor) {
node *ch;
com_addbyte(c, POP_BLOCK);
block_pop(c, SETUP_FINALLY);
block_push(c, END_FINALLY);
com_addoparg(c, LOAD_CONST, com_addconst(c, None));
com_backpatch(c, finally_anchor);
ch = CHILD(n, NCH(n)-1);
com_addoparg(c, SET_LINENO, ch->n_lineno);
com_node(c, ch);
com_addbyte(c, END_FINALLY);
block_pop(c, END_FINALLY);
}
com_addbyte(c, POP_BLOCK);
block_pop(c, SETUP_FINALLY);
block_push(c, END_FINALLY);
com_addoparg(c, LOAD_CONST, com_addconst(c, None));
com_backpatch(c, finally_anchor);
ch = CHILD(n, NCH(n)-1);
com_addoparg(c, SET_LINENO, ch->n_lineno);
com_node(c, ch);
com_addbyte(c, END_FINALLY);
block_pop(c, END_FINALLY);
}
static void
com_try_stmt(c, n)
struct compiling *c;
node *n;
{
REQ(n, try_stmt);
/* 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite]
| 'try' ':' suite 'finally' ':' suite */
if (TYPE(CHILD(n, 3)) != except_clause)
com_try_finally(c, n);
else
com_try_except(c, n);
}
static void
@ -1830,6 +1859,72 @@ com_continue_stmt(c, n)
XXX if we could pop the exception still on the stack */
}
static int
com_argdefs(c, n, argcount_return)
struct compiling *c;
node *n;
int *argcount_return;
{
int i, nch, nargs, ndefs, star;
if (TYPE(n) == lambdef) {
/* lambdef: 'lambda' [varargslist] ':' test */
n = CHILD(n, 1);
}
else {
REQ(n, funcdef); /* funcdef: 'def' NAME parameters ... */
n = CHILD(n, 2);
REQ(n, parameters); /* parameters: '(' [varargslist] ')' */
n = CHILD(n, 1);
}
if (TYPE(n) != varargslist)
return -1;
/* varargslist:
(fpdef ['=' test] ',')* '*' NAME |
fpdef ['=' test] (',' fpdef ['=' test])* [','] */
nch = NCH(n);
if (nch >= 2 && TYPE(CHILD(n, nch-2)) == STAR) {
star = 1;
nch -= 2;
}
else
star = 0;
nargs = 0;
ndefs = 0;
for (i = 0; i < nch; i++) {
int t;
nargs++;
i++;
if (i >= nch)
break;
t = TYPE(CHILD(n, i));
if (t == EQUAL) {
i++;
ndefs++;
com_node(c, CHILD(n, i));
i++;
if (i >= nch)
break;
t = TYPE(CHILD(n, i));
}
else {
/* Treat "(a=1, b)" as "(a=1, b=None)" */
if (ndefs) {
com_addoparg(c, LOAD_CONST,
com_addconst(c, None));
ndefs++;
}
}
if (t != COMMA)
break;
}
if (star)
nargs ^= 0x4000;
*argcount_return = nargs;
if (ndefs > 0)
com_addoparg(c, BUILD_TUPLE, ndefs);
return ndefs;
}
static void
com_funcdef(c, n)
struct compiling *c;
@ -1842,8 +1937,12 @@ com_funcdef(c, n)
c->c_errors++;
else {
int i = com_addconst(c, v);
int argcount;
int ndefs = com_argdefs(c, n, &argcount);
com_addoparg(c, LOAD_CONST, i);
com_addbyte(c, BUILD_FUNCTION);
if (ndefs > 0)
com_addoparg(c, SET_FUNC_ARGS, argcount);
com_addopname(c, STORE_NAME, CHILD(n, 1));
DECREF(v);
}
@ -2082,25 +2181,52 @@ com_arglist(c, n)
struct compiling *c;
node *n;
{
int i, nargs, op;
int nch, op, nargs, i, t;
REQ(n, varargslist);
/* varargslist:
(fpdef ',')* '*' NAME | fpdef (',' fpdef)* [','] */
op = UNPACK_ARG;
nargs = (NCH(n) + 1) / 2;
for (i = 0; i < NCH(n); i += 2) {
int t = TYPE(CHILD(n, i));
if (t == STAR) {
op = UNPACK_VARARG;
nargs = i/2;
(fpdef ['=' test] ',')* '*' NAME |
fpdef ['=' test] (',' fpdef ['=' test])* [','] */
nch = NCH(n);
if (nch >= 2 && TYPE(CHILD(n, nch-2)) == STAR) {
op = UNPACK_VARARG;
nch -= 2;
}
else
op = UNPACK_ARG;
nargs = 0;
for (i = 0; i < nch; i++) {
nargs++;
i++;
if (i >= nch)
break;
t = TYPE(CHILD(n, i));
if (t == EQUAL) {
i += 2;
if (i >= nch)
break;
t = TYPE(CHILD(n, i));
}
if (t != COMMA)
break;
}
com_addoparg(c, op, nargs);
for (i = 0; i < 2*nargs; i += 2)
for (i = 0; i < nch; i++) {
com_fpdef(c, CHILD(n, i));
i++;
if (i >= nch)
break;
t = TYPE(CHILD(n, i));
if (t == EQUAL) {
i += 2;
if (i >= nch)
break;
t = TYPE(CHILD(n, i));
}
if (t != COMMA)
break;
}
if (op == UNPACK_VARARG)
com_addopname(c, STORE_NAME, CHILD(n, 2*nargs+1));
com_addopname(c, STORE_NAME, CHILD(n, nch+1));
}
static void
@ -2175,11 +2301,13 @@ compile_node(c, n)
case single_input: /* One interactive command */
/* NEWLINE | simple_stmt | compound_stmt NEWLINE */
c->c_interactive++;
n = CHILD(n, 0);
if (TYPE(n) != NEWLINE)
com_node(c, n);
com_addoparg(c, LOAD_CONST, com_addconst(c, None));
com_addbyte(c, RETURN_VALUE);
c->c_interactive--;
break;
case file_input: /* A whole file, or built-in function exec() */
@ -2222,23 +2350,21 @@ compile_node(c, n)
instructions that refer to local variables with LOAD_FAST etc.
The latter instructions are much faster because they don't need to
look up the variable name in a dictionary.
To find all local variables, we check all STORE_NAME and IMPORT_FROM
instructions. This yields all local variables, including arguments,
function definitions, class definitions and import statements.
(We don't check DELETE_NAME instructions, since if there's no
STORE_NAME the DELETE_NAME will surely fail.)
There is one problem: 'from foo import *' introduces local variables
that we can't know while compiling. If this is the case, wo don't
optimize at all (this rarely happens, since this form of import
statement is mostly used at the module level).
Note that, because of this optimization, code like the following
won't work:
eval('x = 1')
print x
To find all local variables, we check all STORE_NAME, IMPORT_FROM and
DELETE_NAME instructions. This yields all local variables, including
arguments, function definitions, class definitions and import
statements.
All remaining LOAD_NAME instructions must refer to non-local (global
or builtin) variables, so are replaced by LOAD_GLOBAL.
There are two problems: 'from foo import *' and 'exec' may introduce
local variables that we can't know while compiling. If this is the
case, we don't optimize at all (this rarely happens, since exec is
rare, & this form of import statement is mostly used at the module
level).
NB: this modifies the string object co->co_code!
*/
@ -2300,7 +2426,7 @@ optimize(c)
}
}
if (nlocals == 0 || dictlookup(locals, "*") != NULL) {
if (dictlookup(locals, "*") != NULL) {
/* Don't optimize anything */
goto end;
}
@ -2315,7 +2441,26 @@ optimize(c)
if (HAS_ARG(opcode))
oparg = NEXTARG();
if (opcode == RESERVE_FAST) {
int i = com_addconst(c, locals);
int i;
object *localmap = newtupleobject(nlocals);
int pos;
object *key, *value;
if (localmap == NULL) { /* XXX mask error */
err_clear();
continue;
}
pos = 0;
while (mappinggetnext(locals, &pos, &key, &value)) {
int j;
if (!is_intobject(value))
continue;
j = getintvalue(value);
if (0 <= j && j < nlocals) {
INCREF(key);
settupleitem(localmap, j, key);
}
}
i = com_addconst(c, localmap);
cur_instr[1] = i & 0xff;
cur_instr[2] = (i>>8) & 0xff;
fast_reserved = 1;
@ -2332,6 +2477,8 @@ optimize(c)
v = dict2lookup(locals, name);
if (v == NULL) {
err_clear();
if (opcode == LOAD_NAME)
cur_instr[0] = LOAD_GLOBAL;
continue;
}
i = getintvalue(v);