Merged revisions 59921-59932 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r59923 | raymond.hettinger | 2008-01-11 19:04:55 +0100 (Fri, 11 Jan 2008) | 1 line

  Speed-up and simplify code urlparse's result objects.
........
  r59924 | andrew.kuchling | 2008-01-11 20:33:24 +0100 (Fri, 11 Jan 2008) | 1 line

  Bug #1790: update link; remove outdated paragraph
........
  r59925 | thomas.heller | 2008-01-11 20:34:06 +0100 (Fri, 11 Jan 2008) | 5 lines

  Raise an error instead of crashing with a segfault when a NULL
  function pointer is called.

  Will backport to release25-maint.
........
  r59927 | thomas.heller | 2008-01-11 21:29:19 +0100 (Fri, 11 Jan 2008) | 4 lines

  Fix a potential 'SystemError: NULL result without error'.
  NULL may be a valid return value from PyLong_AsVoidPtr.

  Will backport to release25-maint.
........
  r59928 | raymond.hettinger | 2008-01-12 00:25:18 +0100 (Sat, 12 Jan 2008) | 1 line

  Update the opcode docs for STORE_MAP and BUILD_MAP
........
  r59929 | mark.dickinson | 2008-01-12 02:56:00 +0100 (Sat, 12 Jan 2008) | 4 lines

  Issue 1780: Allow leading and trailing whitespace in Decimal constructor,
  when constructing from a string. Disallow trailing newlines in
  Context.create_decimal.
........
  r59930 | georg.brandl | 2008-01-12 11:53:29 +0100 (Sat, 12 Jan 2008) | 3 lines

  Move OSError docs to exceptions doc, remove obsolete descriptions
  from os docs, rework posix docs.
........
  r59931 | georg.brandl | 2008-01-12 14:47:57 +0100 (Sat, 12 Jan 2008) | 3 lines

  Patch #1700288: Method cache optimization, by Armin Rigo, ported to
  2.6 by Kevin Jacobs.
........
  r59932 | georg.brandl | 2008-01-12 17:11:09 +0100 (Sat, 12 Jan 2008) | 2 lines

  Fix editing glitch.
........
diff --git a/Objects/object.c b/Objects/object.c
index d60ccc0..587e806 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -947,6 +947,7 @@
 			goto done;
 	}
 
+#if 0 /* XXX this is not quite _PyType_Lookup anymore */
 	/* Inline _PyType_Lookup */
 	{
 		Py_ssize_t i, n;
@@ -967,6 +968,9 @@
 				break;
 		}
 	}
+#else
+	descr = _PyType_Lookup(tp, name);
+#endif
 
 	Py_XINCREF(descr);
 
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 682c029..3342cb4 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -6,6 +6,171 @@
 
 #include <ctype.h>
 
+
+/* Support type attribute cache */
+
+/* The cache can keep references to the names alive for longer than
+   they normally would.  This is why the maximum size is limited to
+   MCACHE_MAX_ATTR_SIZE, since it might be a problem if very large
+   strings are used as attribute names. */
+#define MCACHE_MAX_ATTR_SIZE	100
+#define MCACHE_SIZE_EXP		10
+#define MCACHE_HASH(version, name_hash)					\
+		(((unsigned int)(version) * (unsigned int)(name_hash))	\
+		 >> (8*sizeof(unsigned int) - MCACHE_SIZE_EXP))
+#define MCACHE_HASH_METHOD(type, name)                                  \
+		MCACHE_HASH((type)->tp_version_tag,                     \
+		            ((PyStringObject *)(name))->ob_shash)
+#define MCACHE_CACHEABLE_NAME(name)                                     \
+		PyString_CheckExact(name) &&                            \
+		PyString_GET_SIZE(name) <= MCACHE_MAX_ATTR_SIZE
+
+struct method_cache_entry {
+	unsigned int version;
+	PyObject *name;		/* reference to exactly a str or None */
+	PyObject *value;	/* borrowed */
+};
+
+static struct method_cache_entry method_cache[1 << MCACHE_SIZE_EXP];
+static unsigned int next_version_tag = 0;
+
+static void
+type_modified(PyTypeObject *type)
+{
+	/* Invalidate any cached data for the specified type and all
+	   subclasses.  This function is called after the base
+	   classes, mro, or attributes of the type are altered.
+
+	   Invariants:
+
+	   - Py_TPFLAGS_VALID_VERSION_TAG is never set if
+	     Py_TPFLAGS_HAVE_VERSION_TAG is not set (e.g. on type
+	     objects coming from non-recompiled extension modules)
+
+	   - before Py_TPFLAGS_VALID_VERSION_TAG can be set on a type,
+	     it must first be set on all super types.
+
+	   This function clears the Py_TPFLAGS_VALID_VERSION_TAG of a
+	   type (so it must first clear it on all subclasses).  The
+	   tp_version_tag value is meaningless unless this flag is set.
+	   We don't assign new version tags eagerly, but only as
+	   needed.
+	 */
+	PyObject *raw, *ref;
+	Py_ssize_t i, n;
+
+	if(!PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
+		return;
+
+	raw = type->tp_subclasses;
+	if (raw != NULL) {
+		n = PyList_GET_SIZE(raw);
+		for (i = 0; i < n; i++) {
+			ref = PyList_GET_ITEM(raw, i);
+			ref = PyWeakref_GET_OBJECT(ref);
+			if (ref != Py_None) {
+				type_modified((PyTypeObject *)ref);
+			}
+		}
+	}
+	type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
+}
+
+static void
+type_mro_modified(PyTypeObject *type, PyObject *bases) {
+	/*
+	   Check that all base classes or elements of the mro of type are
+	   able to be cached.  This function is called after the base
+	   classes or mro of the type are altered.
+
+	   Unset HAVE_VERSION_TAG and VALID_VERSION_TAG if the type
+	   inherits from an old-style class, either directly or if it
+	   appears in the MRO of a new-style class.  No support either for
+	   custom MROs that include types that are not officially super
+	   types.
+
+	   Called from mro_internal, which will subsequently be called on
+	   each subclass when their mro is recursively updated.
+	 */
+	Py_ssize_t i, n;
+	int clear = 0;
+
+	if(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
+		return;
+
+	n = PyTuple_GET_SIZE(bases);
+	for (i = 0; i < n; i++) {
+		PyObject *b = PyTuple_GET_ITEM(bases, i);
+		PyTypeObject *cls;
+
+		if (!PyType_Check(b) ) {
+			clear = 1;
+			break;
+		}
+
+		cls = (PyTypeObject *)b;
+
+		if (!PyType_HasFeature(cls, Py_TPFLAGS_HAVE_VERSION_TAG) ||
+		    !PyType_IsSubtype(type, cls)) {
+			clear = 1;
+			break;
+		}
+	}
+
+	if (clear)
+		type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG|
+		                    Py_TPFLAGS_VALID_VERSION_TAG);
+}
+
+static int
+assign_version_tag(PyTypeObject *type)
+{
+	/* Ensure that the tp_version_tag is valid and set
+	   Py_TPFLAGS_VALID_VERSION_TAG.  To respect the invariant, this
+	   must first be done on all super classes.  Return 0 if this
+	   cannot be done, 1 if Py_TPFLAGS_VALID_VERSION_TAG.
+	*/
+	Py_ssize_t i, n;
+	PyObject *bases;
+
+	if (PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
+		return 1;
+	if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
+		return 0;
+	if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+		return 0;
+
+	type->tp_version_tag = next_version_tag++;
+	/* for stress-testing: next_version_tag &= 0xFF; */
+
+	if (type->tp_version_tag == 0) {
+		/* wrap-around or just starting Python - clear the whole
+		   cache by filling names with references to Py_None.
+		   Values are also set to NULL for added protection, as they
+		   are borrowed reference */
+		for (i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
+			method_cache[i].value = NULL;
+			Py_XDECREF(method_cache[i].name);
+			method_cache[i].name = Py_None;
+			Py_INCREF(Py_None);
+		}
+		/* mark all version tags as invalid */
+		type_modified(&PyBaseObject_Type);
+		return 1;
+	}
+	bases = type->tp_bases;
+	n = PyTuple_GET_SIZE(bases);
+	for (i = 0; i < n; i++) {
+		PyObject *b = PyTuple_GET_ITEM(bases, i);
+		assert(PyType_Check(b));
+		if (!assign_version_tag((PyTypeObject *)b))
+			return 0;
+	}
+	type->tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG;
+	return 1;
+}
+
+
 static PyMemberDef type_members[] = {
 	{"__basicsize__", T_INT, offsetof(PyTypeObject,tp_basicsize),READONLY},
 	{"__itemsize__", T_INT, offsetof(PyTypeObject, tp_itemsize), READONLY},
@@ -130,6 +295,8 @@
 		return -1;
 	}
 
+	type_modified(type);
+
 	return PyDict_SetItemString(type->tp_dict, "__module__", value);
 }
 
@@ -1299,6 +1466,14 @@
 		}
 	}
 	type->tp_mro = tuple;
+
+	type_mro_modified(type, type->tp_mro);
+	/* corner case: the old-style super class might have been hidden
+	   from the custom MRO */
+	type_mro_modified(type, type->tp_bases);
+
+	type_modified(type);
+
 	return 0;
 }
 
@@ -2026,6 +2201,16 @@
 {
 	Py_ssize_t i, n;
 	PyObject *mro, *res, *base, *dict;
+	unsigned int h;
+
+	if (MCACHE_CACHEABLE_NAME(name) &&
+	    PyType_HasFeature(type,Py_TPFLAGS_VALID_VERSION_TAG)) {
+		/* fast path */
+		h = MCACHE_HASH_METHOD(type, name);
+		if (method_cache[h].version == type->tp_version_tag &&
+		    method_cache[h].name == name)
+		    return method_cache[h].value;
+	}
 
 	/* Look in tp_dict of types in MRO */
 	mro = type->tp_mro;
@@ -2036,6 +2221,7 @@
 	if (mro == NULL)
 		return NULL;
 
+	res = NULL;
 	assert(PyTuple_Check(mro));
 	n = PyTuple_GET_SIZE(mro);
 	for (i = 0; i < n; i++) {
@@ -2045,9 +2231,18 @@
 		assert(dict && PyDict_Check(dict));
 		res = PyDict_GetItem(dict, name);
 		if (res != NULL)
-			return res;
+			break;
 	}
-	return NULL;
+
+	if (MCACHE_CACHEABLE_NAME(name) && assign_version_tag(type)) {
+		h = MCACHE_HASH_METHOD(type, name);
+		method_cache[h].version = type->tp_version_tag;
+		method_cache[h].value = res;  /* borrowed */
+		Py_INCREF(name);
+		Py_DECREF(method_cache[h].name);
+		method_cache[h].name = name;
+	}
+	return res;
 }
 
 /* This is similar to PyObject_GenericGetAttr(),
@@ -2137,10 +2332,6 @@
 			type->tp_name);
 		return -1;
 	}
-	/* XXX Example of how I expect this to be used...
-	if (update_subclasses(type, name, invalidate_cache, NULL) < 0)
-		return -1;
-	*/
 	if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
 		return -1;
 	return update_slot(type, name);
@@ -5421,6 +5612,13 @@
 	slotdef **pp;
 	int offset;
 
+	/* Clear the VALID_VERSION flag of 'type' and all its
+	   subclasses.  This could possibly be unified with the
+	   update_subclasses() recursion below, but carefully:
+	   they each have their own conditions on which to stop
+	   recursing into subclasses. */
+	type_modified(type);
+
 	init_slotdefs();
 	pp = ptrs;
 	for (p = slotdefs; p->name; p++) {