Several changes in one:

(1) dictionaries/mappings now have attributes values() and items() as
well as keys(); at the C level, use the new function mappinggetnext()
to iterate over a dictionary.

(2) "class C(): ..." is now illegal; you must write "class C: ...".

(3) Class objects now know their own name (finally!); and minor
improvements to the way how classes, functions and methods are
represented as strings.

(4) Added an "access" statement and semantics.  (This is still
experimental -- as long as you don't use the keyword 'access' nothing
should be changed.)
This commit is contained in:
Guido van Rossum 1993-05-19 14:50:45 +00:00
parent 687dd13bfe
commit 25831652fd
21 changed files with 1223 additions and 823 deletions

View file

@ -796,7 +796,9 @@ getbuiltin(name)
/* Predefined exceptions */
object *AccessError;
object *AttributeError;
object *ConflictError;
object *EOFError;
object *IOError;
object *ImportError;
@ -827,7 +829,9 @@ newstdexception(name)
static void
initerrors()
{
AccessError = newstdexception("AccessError");
AttributeError = newstdexception("AttributeError");
ConflictError = newstdexception("ConflictError");
EOFError = newstdexception("EOFError");
IOError = newstdexception("IOError");
ImportError = newstdexception("ImportError");

View file

@ -84,9 +84,10 @@ static int cmp_exception PROTO((object *, object *));
static int cmp_member PROTO((object *, object *));
static object *cmp_outcome PROTO((int, object *, object *));
static int import_from PROTO((object *, object *, object *));
static object *build_class PROTO((object *, object *));
static object *build_class PROTO((object *, object *, object *));
static void locals_2_fast PROTO((frameobject *, int));
static void fast_2_locals PROTO((frameobject *));
static int access_statement PROTO((object *, int, frameobject *));
/* Pointer to current frame, used to link new frames to */
@ -743,10 +744,12 @@ eval_code(co, globals, locals, arg)
break;
case BUILD_CLASS:
w = POP();
u = POP();
v = POP();
x = build_class(v, w);
w = POP();
x = build_class(u, v, w);
PUSH(x);
DECREF(u);
DECREF(v);
DECREF(w);
break;
@ -754,12 +757,24 @@ eval_code(co, globals, locals, arg)
case STORE_NAME:
w = GETNAMEV(oparg);
v = POP();
u = dict2lookup(f->f_locals, w);
if (u != NULL && is_accessobject(u)) {
err = setaccessvalue(u, (object *)NULL, v);
DECREF(v);
break;
}
err = dict2insert(f->f_locals, w, v);
DECREF(v);
break;
case DELETE_NAME:
w = GETNAMEV(oparg);
u = dict2lookup(f->f_locals, w);
if (u != NULL && is_accessobject(u)) {
err = setaccessvalue(u, (object *)NULL,
(object *)NULL);
break;
}
if ((err = dict2remove(f->f_locals, w)) != 0)
err_setstr(NameError, getstringvalue(w));
break;
@ -952,12 +967,24 @@ eval_code(co, globals, locals, arg)
case STORE_GLOBAL:
w = GETNAMEV(oparg);
v = POP();
u = dict2lookup(f->f_locals, w);
if (u != NULL && is_accessobject(u)) {
err = setaccessvalue(u, (object *)NULL, v);
DECREF(v);
break;
}
err = dict2insert(f->f_globals, w, v);
DECREF(v);
break;
case DELETE_GLOBAL:
w = GETNAMEV(oparg);
u = dict2lookup(f->f_locals, w);
if (u != NULL && is_accessobject(u)) {
err = setaccessvalue(u, (object *)NULL,
(object *)NULL);
break;
}
if ((err = dict2remove(f->f_globals, w)) != 0)
err_setstr(NameError, getstringvalue(w));
break;
@ -984,6 +1011,11 @@ eval_code(co, globals, locals, arg)
}
}
}
if (is_accessobject(x)) {
x = getaccessvalue(x, (object *)NULL);
if (x == NULL)
break;
}
INCREF(x);
PUSH(x);
break;
@ -1000,6 +1032,11 @@ eval_code(co, globals, locals, arg)
break;
}
}
if (is_accessobject(x)) {
x = getaccessvalue(x, (object *)NULL);
if (x == NULL)
break;
}
INCREF(x);
PUSH(x);
break;
@ -1011,6 +1048,11 @@ eval_code(co, globals, locals, arg)
err_setstr(NameError, getstringvalue(w));
break;
}
if (is_accessobject(x)) {
x = getaccessvalue(x, (object *)NULL);
if (x == NULL)
break;
}
INCREF(x);
PUSH(x);
break;
@ -1041,15 +1083,25 @@ eval_code(co, globals, locals, arg)
"undefined local variable");
break;
}
if (is_accessobject(x)) {
x = getaccessvalue(x, (object *)NULL);
if (x == NULL)
break;
}
INCREF(x);
PUSH(x);
break;
case STORE_FAST:
v = POP();
w = GETLISTITEM(fastlocals, oparg);
if (w != NULL && is_accessobject(w)) {
err = setaccessvalue(w, (object *)NULL, v);
DECREF(v);
break;
}
XDECREF(w);
w = POP();
GETLISTITEM(fastlocals, oparg) = w;
GETLISTITEM(fastlocals, oparg) = v;
break;
case DELETE_FAST:
@ -1059,6 +1111,11 @@ eval_code(co, globals, locals, arg)
"undefined local variable");
break;
}
if (w != NULL && is_accessobject(w)) {
err = setaccessvalue(w, (object *)NULL,
(object *)NULL);
break;
}
DECREF(x);
GETLISTITEM(fastlocals, oparg) = NULL;
break;
@ -1124,6 +1181,13 @@ eval_code(co, globals, locals, arg)
err = import_from(f->f_locals, v, w);
locals_2_fast(f, 0);
break;
case ACCESS_MODE:
v = POP();
w = GETNAMEV(oparg);
err = access_statement(w, (int)getintvalue(v), f);
DECREF(v);
break;
case JUMP_FORWARD:
JUMPBY(oparg);
@ -1483,7 +1547,8 @@ fast_2_locals(f)
/* Merge f->f_fastlocals into f->f_locals */
object *locals, *fast, *map;
object *error_type, *error_value;
int i;
int pos;
object *key, *value;
if (f == NULL)
return;
locals = f->f_locals;
@ -1495,16 +1560,10 @@ fast_2_locals(f)
!is_dictobject(map))
return;
err_get(&error_type, &error_value);
i = getdictsize(map);
while (--i >= 0) {
object *key;
object *value;
pos = 0;
while (mappinggetnext(map, &pos, &key, &value)) {
int j;
key = getdict2key(map, i);
if (key == NULL)
continue;
value = dict2lookup(map, key);
if (value == NULL || !is_intobject(value))
if (!is_intobject(value))
continue;
j = getintvalue(value);
value = getlistitem(fast, j);
@ -1529,7 +1588,8 @@ locals_2_fast(f, clear)
/* Merge f->f_locals into f->f_fastlocals */
object *locals, *fast, *map;
object *error_type, *error_value;
int i;
int pos;
object *key, *value;
if (f == NULL)
return;
locals = f->f_locals;
@ -1541,16 +1601,10 @@ locals_2_fast(f, clear)
!is_dictobject(map))
return;
err_get(&error_type, &error_value);
i = getdictsize(map);
while (--i >= 0) {
object *key;
object *value;
pos = 0;
while (mappinggetnext(map, &pos, &key, &value)) {
int j;
key = getdict2key(map, i);
if (key == NULL)
continue;
value = dict2lookup(map, key);
if (value == NULL || !is_intobject(value))
if (!is_intobject(value))
continue;
j = getintvalue(value);
value = dict2lookup(locals, key);
@ -1907,14 +1961,7 @@ call_builtin(func, arg)
return (*meth)(self, arg);
}
if (is_classobject(func)) {
if (arg != NULL &&
!(is_tupleobject(arg) &&
gettuplesize(arg) == 0)) {
err_setstr(TypeError,
"classobject() allows no arguments");
return NULL;
}
return newinstanceobject(func);
return newinstanceobject(func, arg);
}
err_setstr(TypeError, "call of non-function");
return NULL;
@ -2258,19 +2305,14 @@ import_from(locals, v, name)
object *w, *x;
w = getmoduledict(v);
if (getstringvalue(name)[0] == '*') {
int i;
int n = getdictsize(w);
for (i = 0; i < n; i++) {
name = getdict2key(w, i);
if (name == NULL || getstringvalue(name)[0] == '_')
int pos;
object *name, *value;
pos = 0;
while (mappinggetnext(w, &pos, &name, &value)) {
if (!is_stringobject(name) ||
getstringvalue(name)[0] == '_')
continue;
x = dict2lookup(w, name);
if (x == NULL) {
/* XXX can't happen? */
err_setstr(SystemError, getstringvalue(name));
return -1;
}
if (dict2insert(locals, name, x) != 0)
if (dict2insert(locals, name, value) != 0)
return -1;
}
return 0;
@ -2290,27 +2332,74 @@ import_from(locals, v, name)
}
static object *
build_class(v, w)
object *v; /* None or tuple containing base classes */
object *w; /* dictionary */
build_class(methods, bases, name)
object *methods; /* dictionary */
object *bases; /* tuple containing classes */
object *name; /* string */
{
if (is_tupleobject(v)) {
int i;
for (i = gettuplesize(v); --i >= 0; ) {
object *x = gettupleitem(v, i);
if (!is_classobject(x)) {
err_setstr(TypeError,
"base is not a class object");
return NULL;
}
}
int i;
if (!is_tupleobject(bases)) {
err_setstr(SystemError, "build_class with non-tuple bases");
return NULL;
}
else {
v = NULL;
}
if (!is_dictobject(w)) {
if (!is_dictobject(methods)) {
err_setstr(SystemError, "build_class with non-dictionary");
return NULL;
}
return newclassobject(v, w, (object *) NULL);
if (!is_stringobject(name)) {
err_setstr(SystemError, "build_class witn non-string name");
return NULL;
}
for (i = gettuplesize(bases); --i >= 0; ) {
object *base = gettupleitem(bases, i);
if (!is_classobject(base)) {
err_setstr(TypeError,
"base is not a class object");
return NULL;
}
}
return newclassobject(bases, methods, name);
}
static int
access_statement(name, mode, f)
object *name;
int mode;
frameobject *f;
{
object *value;
int i = -1;
object *ac;
int ret;
if (f->f_localmap == NULL)
value = dict2lookup(f->f_locals, name);
else {
value = dict2lookup(f->f_localmap, name);
if (value == NULL || !is_intobject(value))
value = NULL;
else {
i = getintvalue(value);
if (0 <= i && i < getlistsize(f->f_fastlocals))
value = getlistitem(f->f_fastlocals, i);
else {
value = NULL;
i = -1;
}
}
}
if (value && is_accessobject(value)) {
err_setstr(AccessError, "can't override access");
return -1;
}
err_clear();
ac = newaccessobject(value, (object*)NULL, (typeobject*)NULL, mode);
if (ac == NULL)
return -1;
if (i >= 0)
ret = setlistitem(f->f_fastlocals, i, ac);
else {
ret = dict2insert(f->f_locals, name, ac);
DECREF(ac);
}
return ret;
}

View file

@ -1408,6 +1408,65 @@ com_global_stmt(c, n)
}
}
#define strequ(a, b) (strcmp((a), (b)) == 0)
static void
com_access_stmt(c, n)
struct compiling *c;
node *n;
{
int i, j, k, mode, imode;
object *vmode;
REQ(n, access_stmt);
/* 'access' NAME (',' NAME)* ':' accesstype (',' accesstype)*
accesstype: NAME+ */
/* Find where the colon is */
i = 1;
while (TYPE(CHILD(n,i-1)) != COLON)
i += 1;
/* Calculate the mode mask */
mode = 0;
for (j = i; j < NCH(n); j += 2) {
int r=0,w=0,p=0;
for (k=0; k<NCH(CHILD(n,j)); k++) {
if (strequ(STR(CHILD(CHILD(n,j),k)), "public"))
p = 0;
else if (strequ(STR(CHILD(CHILD(n,j),k)), "protected"))
p = 1;
else if (strequ(STR(CHILD(CHILD(n,j),k)), "private"))
p = 2;
else if (strequ(STR(CHILD(CHILD(n,j),k)), "read"))
r = 1;
else if (strequ(STR(CHILD(CHILD(n,j),k)), "write"))
w = 1;
else /* XXX should make this an exception */
fprintf(stderr, "bad access type %s\n",
STR(CHILD(CHILD(n,j),k)));
}
if (r == 0 && w == 0)
r =w = 1;
if (p == 0) {
if (r == 1) mode |= AC_R_PUBLIC;
if (w == 1) mode |= AC_W_PUBLIC;
} else if (p == 1) {
if (r == 1) mode |= AC_R_PROTECTED;
if (w == 1) mode |= AC_W_PROTECTED;
} else {
if (r == 1) mode |= AC_R_PRIVATE;
if (w == 1) mode |= AC_W_PRIVATE;
}
}
vmode = newintobject((long)mode);
imode = com_addconst(c, vmode);
XDECREF(vmode);
for (i = 1; TYPE(CHILD(n,i-1)) != COLON; i+=2) {
com_addoparg(c, LOAD_CONST, imode);
com_addopname(c, ACCESS_MODE, CHILD(n, i));
}
}
static void
com_if_stmt(c, n)
struct compiling *c;
@ -1726,23 +1785,7 @@ com_funcdef(c, n)
}
static void
com_oldbases(c, n)
struct compiling *c;
node *n;
{
int i;
REQ(n, baselist);
/*
baselist: atom arguments (',' atom arguments)*
arguments: '(' ')'
*/
for (i = 0; i < NCH(n); i += 3)
com_node(c, CHILD(n, i));
com_addoparg(c, BUILD_TUPLE, (NCH(n)+1) / 3);
}
static void
com_newbases(c, n)
com_bases(c, n)
struct compiling *c;
node *n;
{
@ -1759,46 +1802,28 @@ com_classdef(c, n)
struct compiling *c;
node *n;
{
int i;
object *v;
REQ(n, classdef);
/*
classdef: 'class' NAME
['(' testlist ')' |'(' ')' ['=' baselist]] ':' suite
baselist: atom arguments (',' atom arguments)*
arguments: '(' ')'
*/
/* This piece of code must push a tuple on the stack (the bases) */
if (TYPE(CHILD(n, 2)) != LPAR) {
/* New syntax without base classes:
class NAME ':' suite
___________^
*/
/* classdef: class NAME ['(' testlist ')'] ':' suite */
if ((v = newstringobject(STR(CHILD(n, 1)))) == NULL) {
c->c_errors++;
return;
}
/* Push the class name on the stack */
i = com_addconst(c, v);
com_addoparg(c, LOAD_CONST, i);
DECREF(v);
/* Push the tuple of base classes on the stack */
if (TYPE(CHILD(n, 2)) != LPAR)
com_addoparg(c, BUILD_TUPLE, 0);
}
else {
if (TYPE(CHILD(n, 3)) == RPAR) {
/* Old syntax with or without base classes:
class NAME '(' ')' ['=' baselist] ':' suite
_______________^....^...^
*/
if (TYPE(CHILD(n, 4)) == EQUAL)
com_oldbases(c, CHILD(n, 5));
else
com_addoparg(c, BUILD_TUPLE, 0);
}
else {
/* New syntax with base classes:
class NAME '(' testlist ')' ':' suite
_______________^
*/
com_newbases(c, CHILD(n, 3));
}
}
else
com_bases(c, CHILD(n, 3));
v = (object *)compile(n, c->c_filename);
if (v == NULL)
c->c_errors++;
else {
int i = com_addconst(c, v);
i = com_addconst(c, v);
com_addoparg(c, LOAD_CONST, i);
com_addbyte(c, BUILD_FUNCTION);
com_addbyte(c, UNARY_CALL);
@ -1882,6 +1907,9 @@ com_node(c, n)
case global_stmt:
com_global_stmt(c, n);
break;
case access_stmt:
com_access_stmt(c, n);
break;
case if_stmt:
com_if_stmt(c, n);
break;
@ -2084,9 +2112,8 @@ compile_node(c, n)
break;
case classdef: /* A class definition */
/* classdef: 'class' NAME
['(' testlist ')' |'(' ')' ['=' baselist]]
':' suite */
/* classdef: 'class' NAME ['(' testlist ')'] ':' suite */
c->c_name = STR(CHILD(n, 1));
com_node(c, CHILD(n, NCH(n)-1)); /* The suite */
com_addbyte(c, LOAD_LOCALS);
com_addbyte(c, RETURN_VALUE);
@ -2114,8 +2141,8 @@ compile_node(c, n)
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 import is mostly used
at the module level).
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:

File diff suppressed because it is too large Load diff

View file

@ -54,7 +54,7 @@ extern char *argv0;
/* Magic word to reject .pyc files generated by other Python versions */
#define MAGIC 0x99BE3AL
#define MAGIC 0x999901L /* Increment by one for each incompatible change */
static object *modules;
@ -353,46 +353,27 @@ reload_module(m)
return get_module(m, getmodulename(m), (object **)NULL);
}
static void
cleardict(d)
object *d;
{
int i;
for (i = getdictsize(d); --i >= 0; ) {
char *k;
k = getdictkey(d, i);
if (k != NULL)
(void) dictremove(d, k);
}
}
void
doneimport()
{
if (modules != NULL) {
int i;
int pos;
object *modname, *module;
/* Explicitly erase all modules; this is the safest way
to get rid of at least *some* circular dependencies */
for (i = getdictsize(modules); --i >= 0; ) {
object *k;
k = getdict2key(modules, i);
if (k != NULL) {
object *m;
m = dict2lookup(modules, k);
if (m == NULL)
err_clear();
else if (is_moduleobject(m)) {
object *d;
d = getmoduledict(m);
if (d != NULL && is_dictobject(d)) {
cleardict(d);
}
}
pos = 0;
while (mappinggetnext(modules, &pos, &modname, &module)) {
if (is_moduleobject(module)) {
object *dict;
dict = getmoduledict(module);
if (dict != NULL && is_dictobject(dict))
mappingclear(dict);
}
}
cleardict(modules);
mappingclear(modules);
}
DECREF(modules);
modules = NULL;
}

View file

@ -174,17 +174,14 @@ w_object(v, p)
}
}
else if (is_dictobject(v)) {
int pos;
object *key, *value;
w_byte(TYPE_DICT, p);
/* This one is NULL object terminated! */
n = getdictsize(v);
for (i = 0; i < n; i++) {
object *key, *val;
key = getdict2key(v, (int)i);
if (key != NULL) {
val = dict2lookup(v, key); /* Can't be NULL */
w_object(key, p);
w_object(val, p);
}
pos = 0;
while (mappinggetnext(v, &pos, &key, &value)) {
w_object(key, p);
w_object(value, p);
}
w_object((object *)NULL, p);
}