Skip to content

Commit

Permalink
Fix some slot handling issues
Browse files Browse the repository at this point in the history
Well, fix one of them and work around another.  They both come from having
an extension class subclass from a builtin class, since in CPython they can
expect the tp_* slots to be set, but in Pyston those are just wrappers around
the Python functions and then things can get in infinite recursion.
  • Loading branch information
kmod committed Feb 26, 2015
1 parent e16e077 commit 79b2e9c
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 4 deletions.
7 changes: 3 additions & 4 deletions src/capi/typeobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,9 @@ static PyObject* wrap_init(PyObject* self, PyObject* args, void* wrapped, PyObje
static PyObject* lookup_maybe(PyObject* self, const char* attrstr, PyObject** attrobj) noexcept {
PyObject* res;

// TODO: CPython uses the attrobj as a cache
// TODO: CPython uses the attrobj as a cache. If we want to use it, we'd have to make sure that
// they get registered as GC roots since they are usually placed into static variables.

Box* obj = typeLookup(self->cls, attrstr, NULL);
if (obj)
return processDescriptor(obj, self, self->cls);
Expand Down Expand Up @@ -1842,9 +1844,6 @@ extern "C" int PyType_Ready(PyTypeObject* cls) noexcept {
// tp_basicsize, tp_itemsize
// tp_doc

if (!cls->tp_new && base != object_cls)
cls->tp_new = base->tp_new;

try {
add_operators(cls);
} catch (ExcInfo e) {
Expand Down
35 changes: 35 additions & 0 deletions src/runtime/dict.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,27 @@ extern "C" void dictViewGCHandler(GCVisitor* v, Box* b) {
v->visit(view->d);
}

static int dict_init(PyObject* self, PyObject* args, PyObject* kwds) noexcept {
assert(isSubclass(self->cls, dict_cls));
try {
dictInit(static_cast<BoxedDict*>(self), static_cast<BoxedTuple*>(args), static_cast<BoxedDict*>(kwds));
} catch (ExcInfo e) {
setCAPIException(e);
return -1;
}
return 0;
}

static Box* dict_repr(PyObject* self) noexcept {
assert(isSubclass(self->cls, dict_cls));
try {
return dictRepr(static_cast<BoxedDict*>(self));
} catch (ExcInfo e) {
setCAPIException(e);
return NULL;
}
}

void setupDict() {
dict_iterator_cls = new BoxedHeapClass(object_cls, &dictIteratorGCHandler, 0, sizeof(BoxedDict), false,
"dictionary-itemiterator");
Expand Down Expand Up @@ -617,6 +638,20 @@ void setupDict() {

dict_iterator_cls->freeze();

// Manually set some tp_* slots *after* calling freeze() -> fixup_slot_dispatchers().
// fixup_slot_dispatchers will insert a wrapper like slot_tp_init into tp_init, which calls the python-level
// __init__ function. This is all well and good, until a C extension tries to subclass from dict and then
// creates a new tp_init function which calls Py_DictType.tp_init(). That tp_init is slot_tp_init, which calls
// self.__init__, which is the *subclasses* init function not dict's.
//
// This seems to happen pretty rarely, and only with dict, so for now let's just work around it by manually
// setting the couple functions that get used.
//
// I'm not sure if CPython has a better mechanism for this, since I assume they allow having extension classes
// subclass Python classes.
dict_cls->tp_init = dict_init;
dict_cls->tp_repr = dict_repr;

dict_keys_cls->giveAttr(
"__iter__", new BoxedFunction(boxRTFunction((void*)dictViewKeysIter, typeFromClass(dict_iterator_cls), 1)));
dict_keys_cls->freeze();
Expand Down
3 changes: 3 additions & 0 deletions test/tests/_collections_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@

while d:
print d.popleft()

d = _collections.defaultdict()
print str(d), repr(d)

0 comments on commit 79b2e9c

Please sign in to comment.