summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorChristian Heimes <christian@cheimes.de>2008-01-12 19:39:10 (GMT)
committerChristian Heimes <christian@cheimes.de>2008-01-12 19:39:10 (GMT)
commita62da1daafc63075c08088f45750152a4974e7b8 (patch)
tree3b70d95989fcae7e012ff32d523863d9e6eef5a5 /Objects
parent25bb783c030cd1c4f13297f3e2e1b6246d3f0a0c (diff)
downloadcpython-a62da1daafc63075c08088f45750152a4974e7b8.zip
cpython-a62da1daafc63075c08088f45750152a4974e7b8.tar.gz
cpython-a62da1daafc63075c08088f45750152a4974e7b8.tar.bz2
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. ........
Diffstat (limited to 'Objects')
-rw-r--r--Objects/object.c4
-rw-r--r--Objects/typeobject.c210
2 files changed, 208 insertions, 6 deletions
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 @@ PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
goto done;
}
+#if 0 /* XXX this is not quite _PyType_Lookup anymore */
/* Inline _PyType_Lookup */
{
Py_ssize_t i, n;
@@ -967,6 +968,9 @@ PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
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 @@ type_set_module(PyTypeObject *type, PyObject *value, void *context)
return -1;
}
+ type_modified(type);
+
return PyDict_SetItemString(type->tp_dict, "__module__", value);
}
@@ -1299,6 +1466,14 @@ mro_internal(PyTypeObject *type)
}
}
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 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
{
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 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
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 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
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_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
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 @@ update_slot(PyTypeObject *type, PyObject *name)
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++) {