Merge back to main trunk
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index 0b5e75c..a22193f 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -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
@@ -34,15 +34,16 @@
 #define OFF(x) offsetof(frameobject, x)
 
 static struct memberlist frame_memberlist[] = {
-	{"f_back",	T_OBJECT,	OFF(f_back)},
-	{"f_code",	T_OBJECT,	OFF(f_code)},
-	{"f_globals",	T_OBJECT,	OFF(f_globals)},
-	{"f_locals",	T_OBJECT,	OFF(f_locals)},
-	{"f_owner",	T_OBJECT,	OFF(f_owner)},
-/*	{"f_fastlocals",T_OBJECT,	OFF(f_fastlocals)}, /* XXX Unsafe */
-	{"f_localmap",	T_OBJECT,	OFF(f_localmap)},
-	{"f_lasti",	T_INT,		OFF(f_lasti)},
-	{"f_lineno",	T_INT,		OFF(f_lineno)},
+	{"f_back",	T_OBJECT,	OFF(f_back),	RO},
+	{"f_code",	T_OBJECT,	OFF(f_code),	RO},
+	{"f_globals",	T_OBJECT,	OFF(f_globals),	RO},
+	{"f_locals",	T_OBJECT,	OFF(f_locals),	RO},
+	{"f_owner",	T_OBJECT,	OFF(f_owner),	RO},
+/*	{"f_fastlocals",T_OBJECT,	OFF(f_fastlocals),RO}, /* XXX Unsafe */
+	{"f_localmap",	T_OBJECT,	OFF(f_localmap),RO},
+	{"f_lasti",	T_INT,		OFF(f_lasti),	RO},
+	{"f_lineno",	T_INT,		OFF(f_lineno),	RO},
+	{"f_trace",	T_OBJECT,	OFF(f_trace)},
 	{NULL}	/* Sentinel */
 };
 
@@ -51,9 +52,20 @@
 	frameobject *f;
 	char *name;
 {
+	if (strcmp(name, "f_locals") == 0)
+		fast_2_locals(f);
 	return getmember((char *)f, frame_memberlist, name);
 }
 
+static int
+frame_setattr(f, name, value)
+	frameobject *f;
+	char *name;
+	object *value;
+{
+	return setmember((char *)f, frame_memberlist, name, value);
+}
+
 /* Stack frames are allocated and deallocated at a considerable rate.
    In an attempt to improve the speed of function calls, we maintain a
    separate free list of stack frames (just like integers are
@@ -88,6 +100,7 @@
 	XDECREF(f->f_owner);
 	XDECREF(f->f_fastlocals);
 	XDECREF(f->f_localmap);
+	XDECREF(f->f_trace);
 	f->f_back = free_list;
 	free_list = f;
 }
@@ -98,10 +111,10 @@
 	"frame",
 	sizeof(frameobject),
 	0,
-	frame_dealloc,	/*tp_dealloc*/
+	(destructor)frame_dealloc, /*tp_dealloc*/
 	0,		/*tp_print*/
-	frame_getattr,	/*tp_getattr*/
-	0,		/*tp_setattr*/
+	(getattrfunc)frame_getattr, /*tp_getattr*/
+	(setattrfunc)frame_setattr, /*tp_setattr*/
 	0,		/*tp_compare*/
 	0,		/*tp_repr*/
 	0,		/*tp_as_number*/
@@ -167,6 +180,7 @@
 		f->f_iblock = 0;
 		f->f_lasti = 0;
 		f->f_lineno = -1;
+		f->f_trace = NULL;
 		if (f->f_valuestack == NULL || f->f_blockstack == NULL) {
 			err_nomem();
 			DECREF(f);
@@ -225,3 +239,74 @@
 	b = &f->f_blockstack[--f->f_iblock];
 	return b;
 }
+
+/* Convert between "fast" version of locals and dictionary version */
+
+void
+fast_2_locals(f)
+	frameobject *f;
+{
+	/* Merge f->f_fastlocals into f->f_locals */
+	object *locals, *fast, *map;
+	object *error_type, *error_value;
+	int j;
+	if (f == NULL)
+		return;
+	locals = f->f_locals;
+	fast = f->f_fastlocals;
+	map = f->f_localmap;
+	if (locals == NULL || fast == NULL || map == NULL)
+		return;
+	if (!is_dictobject(locals) || !is_listobject(fast) ||
+	    !is_tupleobject(map))
+		return;
+	err_get(&error_type, &error_value);
+	for (j = gettuplesize(map); --j >= 0; ) {
+		object *key = gettupleitem(map, j);
+		object *value = getlistitem(fast, j);
+		if (value == NULL) {
+			err_clear();
+			if (dict2remove(locals, key) != 0)
+				err_clear();
+		}
+		else {
+			if (dict2insert(locals, key, value) != 0)
+				err_clear();
+		}
+	}
+	err_setval(error_type, error_value);
+}
+
+void
+locals_2_fast(f, clear)
+	frameobject *f;
+	int clear;
+{
+	/* Merge f->f_locals into f->f_fastlocals */
+	object *locals, *fast, *map;
+	object *error_type, *error_value;
+	int j;
+	if (f == NULL)
+		return;
+	locals = f->f_locals;
+	fast = f->f_fastlocals;
+	map = f->f_localmap;
+	if (locals == NULL || fast == NULL || map == NULL)
+		return;
+	if (!is_dictobject(locals) || !is_listobject(fast) ||
+	    !is_tupleobject(map))
+		return;
+	err_get(&error_type, &error_value);
+	for (j = gettuplesize(map); --j >= 0; ) {
+		object *key = gettupleitem(map, j);
+		object *value = dict2lookup(locals, key);
+		if (value == NULL)
+			err_clear();
+		else
+			INCREF(value);
+		if (value != NULL || clear)
+			if (setlistitem(fast, j, value) != 0)
+				err_clear();
+	}
+	err_setval(error_type, error_value);
+}