[Python-checkins] python/dist/src/Objects typeobject.c,2.171,2.172

gvanrossum@users.sourceforge.net gvanrossum@users.sourceforge.net
Mon, 12 Aug 2002 12:05:48 -0700


Update of /cvsroot/python/python/dist/src/Objects
In directory usw-pr-cvs1:/tmp/cvs-serv11963

Modified Files:
	typeobject.c 
Log Message:
Refactor how __dict__ and __weakref__ interact with __slots__.

1. You can now have __dict__ and/or __weakref__ in your __slots__
   (before only __weakref__ was supported).  This is treated
   differently than before: it merely sets a flag that the object
   should support the corresponding magic.

2. Dynamic types now always have descriptors __dict__ and __weakref__
   thrust upon them.  If the type in fact does not support one or the
   other, that descriptor's __get__ method will raise AttributeError.

3. (This is the reason for all this; it fixes SF bug 575229, reported
   by Cesar Douady.)  Given this code:
      class A(object): __slots__ = []
      class B(object): pass
      class C(A, B): __slots__ = []
   the class object for C was broken; its size was less than that of
   B, and some descriptors on B could cause a segfault.  C now
   correctly inherits __weakrefs__ and __dict__ from B, even though A
   is the "primary" base (C.__base__ is A).

4. Some code cleanup, and a few comments added.


Index: typeobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/typeobject.c,v
retrieving revision 2.171
retrieving revision 2.172
diff -C2 -d -r2.171 -r2.172
*** typeobject.c	10 Aug 2002 05:41:29 -0000	2.171
--- typeobject.c	12 Aug 2002 19:05:44 -0000	2.172
***************
*** 928,934 ****
  }
  
  static PyGetSetDef subtype_getsets[] = {
! 	{"__dict__", subtype_dict, subtype_setdict, NULL},
! 	{0},
  };
  
--- 928,963 ----
  }
  
+ static PyObject *
+ subtype_getweakref(PyObject *obj, void *context)
+ {
+ 	PyObject **weaklistptr;
+ 	PyObject *result;
+ 
+ 	if (obj->ob_type->tp_weaklistoffset == 0) {
+ 		PyErr_SetString(PyExc_AttributeError,
+ 				"This object has no __weaklist__");
+ 		return NULL;
+ 	}
+ 	assert(obj->ob_type->tp_weaklistoffset > 0);
+ 	assert(obj->ob_type->tp_weaklistoffset + sizeof(PyObject *) <=
+ 	       obj->ob_type->tp_basicsize);
+ 	weaklistptr = (PyObject **)
+ 		((void *)obj + obj->ob_type->tp_weaklistoffset);
+ 	if (*weaklistptr == NULL)
+ 		result = Py_None;
+ 	else
+ 		result = *weaklistptr;
+ 	Py_INCREF(result);
+ 	return result;
+ }
+ 
  static PyGetSetDef subtype_getsets[] = {
! 	/* Not all objects have these attributes!
! 	   The descriptor's __get__ method may raise AttributeError. */
! 	{"__dict__", subtype_dict, subtype_setdict,
! 	 "dictionary for instance variables (if defined)"},
! 	{"__weakref__", subtype_getweakref, NULL,
! 	 "list of weak references to the object (if defined)"},
! 	{0}
  };
  
***************
*** 986,989 ****
--- 1015,1019 ----
  	PyMemberDef *mp;
  	int i, nbases, nslots, slotoffset, add_dict, add_weak;
+ 	int j, may_add_dict, may_add_weak;
  
  	assert(args != NULL && PyTuple_Check(args));
***************
*** 1073,1077 ****
  	add_dict = 0;
  	add_weak = 0;
! 	if (slots != NULL) {
  		/* Make it into a tuple */
  		if (PyString_Check(slots))
--- 1103,1119 ----
  	add_dict = 0;
  	add_weak = 0;
! 	may_add_dict = base->tp_dictoffset == 0;
! 	may_add_weak = base->tp_weaklistoffset == 0 && base->tp_itemsize == 0;
! 	if (slots == NULL) {
! 		if (may_add_dict) {
! 			add_dict++;
! 		}
! 		if (may_add_weak) {
! 			add_weak++;
! 		}
! 	}
! 	else {
! 		/* Have slots */
! 
  		/* Make it into a tuple */
  		if (PyString_Check(slots))
***************
*** 1081,1084 ****
--- 1123,1129 ----
  		if (slots == NULL)
  			return NULL;
+ 		assert(PyTuple_Check(slots));
+ 
+ 		/* Are slots allowed? */
  		nslots = PyTuple_GET_SIZE(slots);
  		if (nslots > 0 && base->tp_itemsize != 0) {
***************
*** 1087,1107 ****
  				     "not supported for subtype of '%s'",
  				     base->tp_name);
  			return NULL;
  		}
  		for (i = 0; i < nslots; i++) {
! 			if (!valid_identifier(PyTuple_GET_ITEM(slots, i))) {
! 				Py_DECREF(slots);
! 				return NULL;
  			}
  		}
  
! 		newslots = PyTuple_New(nslots);
  		if (newslots == NULL)
! 			return NULL;
! 		for (i = 0; i < nslots; i++) {
  			tmp = PyTuple_GET_ITEM(slots, i);
  			if (_Py_Mangle(PyString_AS_STRING(name),
! 				PyString_AS_STRING(tmp),
! 				buffer, sizeof(buffer)))
  			{
  				tmp = PyString_FromString(buffer);
--- 1132,1183 ----
  				     "not supported for subtype of '%s'",
  				     base->tp_name);
+ 		  bad_slots:
+ 			Py_DECREF(slots);
  			return NULL;
  		}
+ 
+ 		/* Check for valid slot names and two special cases */
  		for (i = 0; i < nslots; i++) {
! 			PyObject *tmp = PyTuple_GET_ITEM(slots, i);
! 			char *s;
! 			if (!valid_identifier(tmp))
! 				goto bad_slots;
! 			assert(PyString_Check(tmp));
! 			s = PyString_AS_STRING(tmp);
! 			if (strcmp(s, "__dict__") == 0) {
! 				if (!may_add_dict || add_dict) {
! 					PyErr_SetString(PyExc_TypeError,
! 						"__dict__ slot disallowed: "
! 						"we already got one");
! 					goto bad_slots;
! 				}
! 				add_dict++;
! 			}
! 			if (strcmp(s, "__weakref__") == 0) {
! 				if (!may_add_weak || add_weak) {
! 					PyErr_SetString(PyExc_TypeError,
! 						"__weakref__ slot disallowed: "
! 						"either we already got one, "
! 						"or __itemsize__ != 0");
! 					goto bad_slots;
! 				}
! 				add_weak++;
  			}
  		}
  
! 		/* Copy slots into yet another tuple, demangling names */
! 		newslots = PyTuple_New(nslots - add_dict - add_weak);
  		if (newslots == NULL)
! 			goto bad_slots;
! 		for (i = j = 0; i < nslots; i++) {
! 			char *s;
  			tmp = PyTuple_GET_ITEM(slots, i);
+ 			s = PyString_AS_STRING(tmp);
+ 			if ((add_dict && strcmp(s, "__dict__") == 0) ||
+ 			    (add_weak && strcmp(s, "__weakref__") == 0))
+ 				continue;
  			if (_Py_Mangle(PyString_AS_STRING(name),
! 				       PyString_AS_STRING(tmp),
! 				       buffer, sizeof(buffer)))
  			{
  				tmp = PyString_FromString(buffer);
***************
*** 1109,1148 ****
  				Py_INCREF(tmp);
  			}
! 			PyTuple_SET_ITEM(newslots, i, tmp);
  		}
  		Py_DECREF(slots);
  		slots = newslots;
  
- 	}
- 	if (slots != NULL) {
  		/* See if *this* class defines __getstate__ */
! 		PyObject *getstate = PyDict_GetItemString(dict,
! 							  "__getstate__");
! 		if (getstate == NULL) {
  			/* If not, provide a bozo that raises TypeError */
  			if (bozo_obj == NULL) {
  				bozo_obj = PyCFunction_New(&bozo_ml, NULL);
! 				if (bozo_obj == NULL) {
! 					/* XXX decref various things */
! 					return NULL;
! 				}
  			}
  			if (PyDict_SetItemString(dict,
  						 "__getstate__",
! 						 bozo_obj) < 0) {
! 				/* XXX decref various things */
! 				return NULL;
  			}
  		}
- 	}
- 	if (slots == NULL && base->tp_dictoffset == 0 &&
- 	    (base->tp_setattro == PyObject_GenericSetAttr ||
- 	     base->tp_setattro == NULL)) {
- 		add_dict++;
- 	}
- 	if (slots == NULL && base->tp_weaklistoffset == 0 &&
- 	    base->tp_itemsize == 0) {
- 		nslots++;
- 		add_weak++;
  	}
  
--- 1185,1245 ----
  				Py_INCREF(tmp);
  			}
! 			PyTuple_SET_ITEM(newslots, j, tmp);
! 			j++;
  		}
+ 		assert(j == nslots - add_dict - add_weak);
+ 		nslots = j;
  		Py_DECREF(slots);
  		slots = newslots;
  
  		/* See if *this* class defines __getstate__ */
! 		if (PyDict_GetItemString(dict, "__getstate__") == NULL) {
  			/* If not, provide a bozo that raises TypeError */
  			if (bozo_obj == NULL) {
  				bozo_obj = PyCFunction_New(&bozo_ml, NULL);
! 				if (bozo_obj == NULL)
! 					goto bad_slots;
  			}
  			if (PyDict_SetItemString(dict,
  						 "__getstate__",
! 						 bozo_obj) < 0)
! 			{
! 				Py_DECREF(bozo_obj);
! 				goto bad_slots;
! 			}
! 		}
! 
! 		/* Secondary bases may provide weakrefs or dict */
! 		if (nbases > 1 &&
! 		    ((may_add_dict && !add_dict) ||
! 		     (may_add_weak && !add_weak))) {
! 			for (i = 0; i < nbases; i++) {
! 				tmp = PyTuple_GET_ITEM(bases, i);
! 				if (tmp == (PyObject *)base)
! 					continue; /* Skip primary base */
! 				if (PyClass_Check(tmp)) {
! 					/* Classic base class provides both */
! 					if (may_add_dict && !add_dict)
! 						add_dict++;
! 					if (may_add_weak && !add_weak)
! 						add_weak++;
! 					break;
! 				}
! 				assert(PyType_Check(tmp));
! 				tmptype = (PyTypeObject *)tmp;
! 				if (may_add_dict && !add_dict &&
! 				    tmptype->tp_dictoffset != 0)
! 					add_dict++;
! 				if (may_add_weak && !add_weak &&
! 				    tmptype->tp_weaklistoffset != 0)
! 					add_weak++;
! 				if (may_add_dict && !add_dict)
! 					continue;
! 				if (may_add_weak && !add_weak)
! 					continue;
! 				/* Nothing more to check */
! 				break;
  			}
  		}
  	}
  
***************
*** 1152,1157 ****
  	/* Allocate the type object */
  	type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots);
! 	if (type == NULL)
  		return NULL;
  
  	/* Keep name and slots alive in the extended type object */
--- 1249,1256 ----
  	/* Allocate the type object */
  	type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots);
! 	if (type == NULL) {
! 		Py_XDECREF(slots);
  		return NULL;
+ 	}
  
  	/* Keep name and slots alive in the extended type object */
***************
*** 1246,1249 ****
--- 1345,1349 ----
  			if (base->tp_weaklistoffset == 0 &&
  			    strcmp(mp->name, "__weakref__") == 0) {
+ 				add_weak++;
  				mp->type = T_OBJECT;
  				mp->flags = READONLY;
***************
*** 1253,1280 ****
  		}
  	}
! 	else {
! 		if (add_dict) {
! 			if (base->tp_itemsize)
! 				type->tp_dictoffset =
! 					-(long)sizeof(PyObject *);
! 			else
! 				type->tp_dictoffset = slotoffset;
! 			slotoffset += sizeof(PyObject *);
! 			type->tp_getset = subtype_getsets;
! 		}
! 		if (add_weak) {
! 			assert(!base->tp_itemsize);
! 			type->tp_weaklistoffset = slotoffset;
! 			mp->name = "__weakref__";
! 			mp->type = T_OBJECT;
! 			mp->offset = slotoffset;
! 			mp->flags = READONLY;
! 			mp++;
! 			slotoffset += sizeof(PyObject *);
! 		}
  	}
  	type->tp_basicsize = slotoffset;
  	type->tp_itemsize = base->tp_itemsize;
  	type->tp_members = et->members;
  
  	/* Special case some slots */
--- 1353,1372 ----
  		}
  	}
! 	if (add_dict) {
! 		if (base->tp_itemsize)
! 			type->tp_dictoffset = -(long)sizeof(PyObject *);
! 		else
! 			type->tp_dictoffset = slotoffset;
! 		slotoffset += sizeof(PyObject *);
! 	}
! 	if (add_weak) {
! 		assert(!base->tp_itemsize);
! 		type->tp_weaklistoffset = slotoffset;
! 		slotoffset += sizeof(PyObject *);
  	}
  	type->tp_basicsize = slotoffset;
  	type->tp_itemsize = base->tp_itemsize;
  	type->tp_members = et->members;
+ 	type->tp_getset = subtype_getsets;
  
  	/* Special case some slots */