Commit 39dbe4fd authored by Kevin Modzelewski's avatar Kevin Modzelewski

Import some more TypeReady handling from CPython

parent fa2942ae
...@@ -896,27 +896,6 @@ static void** slotptr(BoxedClass* type, int offset) { ...@@ -896,27 +896,6 @@ static void** slotptr(BoxedClass* type, int offset) {
return (void**)ptr; return (void**)ptr;
} }
static void update_one_slot(BoxedClass* self, const slotdef& p) {
// TODO: CPython version is significantly more sophisticated
void** ptr = slotptr(self, p.offset);
Box* attr = typeLookup(self, p.name, NULL);
if (!ptr) {
assert(!attr && "I don't think this case should happen? CPython handles it though");
return;
}
if (attr) {
if (attr == None && ptr == (void**)&self->tp_hash) {
*ptr = (void*)&PyObject_HashNotImplemented;
} else {
*ptr = p.function;
}
} else {
*ptr = NULL;
}
}
// Copied from CPython: // Copied from CPython:
#define TPSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ #define TPSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
{ NAME, offsetof(PyTypeObject, SLOT), (void*)(FUNCTION), WRAPPER, PyDoc_STR(DOC), 0 } { NAME, offsetof(PyTypeObject, SLOT), (void*)(FUNCTION), WRAPPER, PyDoc_STR(DOC), 0 }
...@@ -940,8 +919,8 @@ static void update_one_slot(BoxedClass* self, const slotdef& p) { ...@@ -940,8 +919,8 @@ static void update_one_slot(BoxedClass* self, const slotdef& p) {
#define RBINSLOTNOTINFIX(NAME, SLOT, FUNCTION, DOC) \ #define RBINSLOTNOTINFIX(NAME, SLOT, FUNCTION, DOC) \
ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_r, "x." NAME "(y) <==> " DOC) ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_r, "x." NAME "(y) <==> " DOC)
static slotdef slotdefs[] = { static slotdef slotdefs[]
TPSLOT("__repr__", tp_repr, slot_tp_repr, wrap_unaryfunc, "x.__repr__() <==> repr(x)"), = { TPSLOT("__repr__", tp_repr, slot_tp_repr, wrap_unaryfunc, "x.__repr__() <==> repr(x)"),
TPSLOT("__hash__", tp_hash, slot_tp_hash, wrap_hashfunc, "x.__hash__() <==> hash(x)"), TPSLOT("__hash__", tp_hash, slot_tp_hash, wrap_hashfunc, "x.__hash__() <==> hash(x)"),
FLSLOT("__call__", tp_call, slot_tp_call, (wrapperfunc)wrap_call, "x.__call__(...) <==> x(...)", FLSLOT("__call__", tp_call, slot_tp_call, (wrapperfunc)wrap_call, "x.__call__(...) <==> x(...)",
PyWrapperFlag_KEYWORDS), PyWrapperFlag_KEYWORDS),
...@@ -1024,7 +1003,7 @@ static slotdef slotdefs[] = { ...@@ -1024,7 +1003,7 @@ static slotdef slotdefs[] = {
SQSLOT("__contains__", sq_contains, slot_sq_contains, wrap_objobjproc, "x.__contains__(y) <==> y in x"), SQSLOT("__contains__", sq_contains, slot_sq_contains, wrap_objobjproc, "x.__contains__(y) <==> y in x"),
SQSLOT("__iadd__", sq_inplace_concat, NULL, wrap_binaryfunc, "x.__iadd__(y) <==> x+=y"), SQSLOT("__iadd__", sq_inplace_concat, NULL, wrap_binaryfunc, "x.__iadd__(y) <==> x+=y"),
SQSLOT("__imul__", sq_inplace_repeat, NULL, wrap_indexargfunc, "x.__imul__(y) <==> x*=y"), SQSLOT("__imul__", sq_inplace_repeat, NULL, wrap_indexargfunc, "x.__imul__(y) <==> x*=y"),
}; { NULL, 0, NULL, NULL, NULL, 0 } };
static void init_slotdefs() { static void init_slotdefs() {
static bool initialized = false; static bool initialized = false;
...@@ -1033,6 +1012,9 @@ static void init_slotdefs() { ...@@ -1033,6 +1012,9 @@ static void init_slotdefs() {
for (int i = 0; i < sizeof(slotdefs) / sizeof(slotdefs[0]); i++) { for (int i = 0; i < sizeof(slotdefs) / sizeof(slotdefs[0]); i++) {
if (i > 0) { if (i > 0) {
if (!slotdefs[i].name)
continue;
#ifndef NDEBUG #ifndef NDEBUG
if (slotdefs[i - 1].offset > slotdefs[i].offset) { if (slotdefs[i - 1].offset > slotdefs[i].offset) {
printf("slotdef for %s in the wrong place\n", slotdefs[i - 1].name); printf("slotdef for %s in the wrong place\n", slotdefs[i - 1].name);
...@@ -1052,12 +1034,132 @@ static void init_slotdefs() { ...@@ -1052,12 +1034,132 @@ static void init_slotdefs() {
initialized = true; initialized = true;
} }
/* Length of array of slotdef pointers used to store slots with the
same __name__. There should be at most MAX_EQUIV-1 slotdef entries with
the same __name__, for any __name__. Since that's a static property, it is
appropriate to declare fixed-size arrays for this. */
#define MAX_EQUIV 10
/* Return a slot pointer for a given name, but ONLY if the attribute has
exactly one slot function. The name must be an interned string. */
static void** resolve_slotdups(PyTypeObject* type, const std::string& name) {
/* XXX Maybe this could be optimized more -- but is it worth it? */
/* pname and ptrs act as a little cache */
static std::string pname;
static slotdef* ptrs[MAX_EQUIV];
slotdef* p, **pp;
void** res, **ptr;
if (pname != name) {
/* Collect all slotdefs that match name into ptrs. */
pname = name;
pp = ptrs;
for (p = slotdefs; p->name; p++) {
if (p->name == name)
*pp++ = p;
}
*pp = NULL;
}
/* Look in all matching slots of the type; if exactly one of these has
a filled-in slot, return its value. Otherwise return NULL. */
res = NULL;
for (pp = ptrs; *pp; pp++) {
ptr = slotptr(type, (*pp)->offset);
if (ptr == NULL || *ptr == NULL)
continue;
if (res != NULL)
return NULL;
res = ptr;
}
return res;
}
static const slotdef* update_one_slot(BoxedClass* type, const slotdef* p) {
assert(p->name);
PyObject* descr;
BoxedWrapperDescriptor* d;
void* generic = NULL, * specific = NULL;
int use_generic = 0;
int offset = p->offset;
void** ptr = slotptr(type, offset);
if (ptr == NULL) {
do {
++p;
} while (p->offset == offset);
return p;
}
do {
descr = typeLookup(type, p->name, NULL);
if (descr == NULL) {
if (ptr == (void**)&type->tp_iternext) {
specific = (void*)_PyObject_NextNotImplemented;
}
continue;
}
if (Py_TYPE(descr) == wrapperdescr_cls
&& ((BoxedWrapperDescriptor*)descr)->wrapper->name == std::string(p->name)) {
void** tptr = resolve_slotdups(type, p->name);
if (tptr == NULL || tptr == ptr)
generic = p->function;
d = (BoxedWrapperDescriptor*)descr;
if (d->wrapper->wrapper == p->wrapper && PyType_IsSubtype(type, d->type)) {
if (specific == NULL || specific == d->wrapped)
specific = d->wrapped;
else
use_generic = 1;
}
// TODO Pyston doesn't support PyCFunction_Type yet I think?
#if 0
} else if (Py_TYPE(descr) == &PyCFunction_Type && PyCFunction_GET_FUNCTION(descr) == (PyCFunction)tp_new_wrapper
&& ptr == (void**)&type->tp_new) {
/* The __new__ wrapper is not a wrapper descriptor,
so must be special-cased differently.
If we don't do this, creating an instance will
always use slot_tp_new which will look up
__new__ in the MRO which will call tp_new_wrapper
which will look through the base classes looking
for a static base and call its tp_new (usually
PyType_GenericNew), after performing various
sanity checks and constructing a new argument
list. Cut all that nonsense short -- this speeds
up instance creation tremendously. */
specific = (void*)type->tp_new;
/* XXX I'm not 100% sure that there isn't a hole
in this reasoning that requires additional
sanity checks. I'll buy the first person to
point out a bug in this reasoning a beer. */
#endif
} else if (descr == Py_None && ptr == (void**)&type->tp_hash) {
/* We specifically allow __hash__ to be set to None
to prevent inheritance of the default
implementation from object.__hash__ */
specific = (void*)PyObject_HashNotImplemented;
} else {
use_generic = 1;
generic = p->function;
}
} while ((++p)->offset == offset);
if (specific && !use_generic)
*ptr = specific;
else
*ptr = generic;
return p;
}
bool update_slot(BoxedClass* self, const std::string& attr) { bool update_slot(BoxedClass* self, const std::string& attr) {
bool updated = false; bool updated = false;
for (const slotdef& p : slotdefs) { for (const slotdef& p : slotdefs) {
if (!p.name)
continue;
if (p.name == attr) { if (p.name == attr) {
// TODO update subclasses; // TODO update subclasses;
update_one_slot(self, p); update_one_slot(self, &p);
updated = true; updated = true;
} }
} }
...@@ -1067,9 +1169,9 @@ bool update_slot(BoxedClass* self, const std::string& attr) { ...@@ -1067,9 +1169,9 @@ bool update_slot(BoxedClass* self, const std::string& attr) {
void fixup_slot_dispatchers(BoxedClass* self) { void fixup_slot_dispatchers(BoxedClass* self) {
init_slotdefs(); init_slotdefs();
for (const slotdef& p : slotdefs) { const slotdef* p = slotdefs;
update_one_slot(self, p); while (p->name)
} p = update_one_slot(self, p);
// TODO: CPython handles this by having the __name__ attribute wrap (via a getset object) // TODO: CPython handles this by having the __name__ attribute wrap (via a getset object)
// the tp_name field, whereas we're (needlessly?) doing the opposite. // the tp_name field, whereas we're (needlessly?) doing the opposite.
......
...@@ -456,6 +456,11 @@ extern "C" long PyObject_HashNotImplemented(PyObject* self) { ...@@ -456,6 +456,11 @@ extern "C" long PyObject_HashNotImplemented(PyObject* self) {
return -1; return -1;
} }
extern "C" PyObject* _PyObject_NextNotImplemented(PyObject* self) {
PyErr_Format(PyExc_TypeError, "'%.200s' object is not iterable", Py_TYPE(self)->tp_name);
return NULL;
}
extern "C" long _Py_HashPointer(void* p) { extern "C" long _Py_HashPointer(void* p) {
long x; long x;
size_t y = (size_t)p; size_t y = (size_t)p;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment