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) {
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:
#define TPSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
{ NAME, offsetof(PyTypeObject, SLOT), (void*)(FUNCTION), WRAPPER, PyDoc_STR(DOC), 0 }
......@@ -940,91 +919,91 @@ static void update_one_slot(BoxedClass* self, const slotdef& p) {
#define RBINSLOTNOTINFIX(NAME, SLOT, FUNCTION, DOC) \
ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_r, "x." NAME "(y) <==> " DOC)
static slotdef slotdefs[] = {
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)"),
FLSLOT("__call__", tp_call, slot_tp_call, (wrapperfunc)wrap_call, "x.__call__(...) <==> x(...)",
PyWrapperFlag_KEYWORDS),
TPSLOT("__str__", tp_str, slot_tp_str, wrap_unaryfunc, "x.__str__() <==> str(x)"),
TPSLOT("__lt__", tp_richcompare, slot_tp_richcompare, richcmp_lt, "x.__lt__(y) <==> x<y"),
TPSLOT("__le__", tp_richcompare, slot_tp_richcompare, richcmp_le, "x.__le__(y) <==> x<=y"),
TPSLOT("__eq__", tp_richcompare, slot_tp_richcompare, richcmp_eq, "x.__eq__(y) <==> x==y"),
TPSLOT("__ne__", tp_richcompare, slot_tp_richcompare, richcmp_ne, "x.__ne__(y) <==> x!=y"),
TPSLOT("__gt__", tp_richcompare, slot_tp_richcompare, richcmp_gt, "x.__gt__(y) <==> x>y"),
TPSLOT("__ge__", tp_richcompare, slot_tp_richcompare, richcmp_ge, "x.__ge__(y) <==> x>=y"),
FLSLOT("__init__", tp_init, slot_tp_init, (wrapperfunc)wrap_init, "x.__init__(...) initializes x; "
"see help(type(x)) for signature",
PyWrapperFlag_KEYWORDS),
TPSLOT("__new__", tp_new, slot_tp_new, NULL, ""),
BINSLOT("__add__", nb_add, slot_nb_add, "+"), // [force clang-format to line break]
RBINSLOT("__radd__", nb_add, slot_nb_add, "+"), //
BINSLOT("__sub__", nb_subtract, slot_nb_subtract, "-"), //
RBINSLOT("__rsub__", nb_subtract, slot_nb_subtract, "-"), //
BINSLOT("__mul__", nb_multiply, slot_nb_multiply, "*"), //
RBINSLOT("__rmul__", nb_multiply, slot_nb_multiply, "*"), //
BINSLOT("__div__", nb_divide, slot_nb_divide, "/"), //
RBINSLOT("__rdiv__", nb_divide, slot_nb_divide, "/"), //
BINSLOT("__mod__", nb_remainder, slot_nb_remainder, "%"), //
RBINSLOT("__rmod__", nb_remainder, slot_nb_remainder, "%"), //
BINSLOTNOTINFIX("__divmod__", nb_divmod, slot_nb_divmod, "divmod(x, y)"),
RBINSLOTNOTINFIX("__rdivmod__", nb_divmod, slot_nb_divmod, "divmod(y, x)"),
NBSLOT("__pow__", nb_power, slot_nb_power, wrap_ternaryfunc, "x.__pow__(y[, z]) <==> pow(x, y[, z])"),
NBSLOT("__rpow__", nb_power, slot_nb_power, wrap_ternaryfunc_r, "y.__rpow__(x[, z]) <==> pow(x, y[, z])"),
UNSLOT("__neg__", nb_negative, slot_nb_negative, wrap_unaryfunc, "-x"), //
UNSLOT("__pos__", nb_positive, slot_nb_positive, wrap_unaryfunc, "+x"), //
UNSLOT("__abs__", nb_absolute, slot_nb_absolute, wrap_unaryfunc, "abs(x)"), //
UNSLOT("__nonzero__", nb_nonzero, slot_nb_nonzero, wrap_inquirypred, "x != 0"), //
UNSLOT("__invert__", nb_invert, slot_nb_invert, wrap_unaryfunc, "~x"), //
BINSLOT("__lshift__", nb_lshift, slot_nb_lshift, "<<"), //
RBINSLOT("__rlshift__", nb_lshift, slot_nb_lshift, "<<"), //
BINSLOT("__rshift__", nb_rshift, slot_nb_rshift, ">>"), //
RBINSLOT("__rrshift__", nb_rshift, slot_nb_rshift, ">>"), //
BINSLOT("__and__", nb_and, slot_nb_and, "&"), //
RBINSLOT("__rand__", nb_and, slot_nb_and, "&"), //
BINSLOT("__xor__", nb_xor, slot_nb_xor, "^"), //
RBINSLOT("__rxor__", nb_xor, slot_nb_xor, "^"), //
BINSLOT("__or__", nb_or, slot_nb_or, "|"), //
RBINSLOT("__ror__", nb_or, slot_nb_or, "|"), //
UNSLOT("__int__", nb_int, slot_nb_int, wrap_unaryfunc, "int(x)"), //
UNSLOT("__long__", nb_long, slot_nb_long, wrap_unaryfunc, "long(x)"), //
UNSLOT("__float__", nb_float, slot_nb_float, wrap_unaryfunc, "float(x)"), //
UNSLOT("__oct__", nb_oct, slot_nb_oct, wrap_unaryfunc, "oct(x)"), //
UNSLOT("__hex__", nb_hex, slot_nb_hex, wrap_unaryfunc, "hex(x)"), //
MPSLOT("__len__", mp_length, slot_mp_length, wrap_lenfunc, "x.__len__() <==> len(x)"),
MPSLOT("__getitem__", mp_subscript, slot_mp_subscript, wrap_binaryfunc, "x.__getitem__(y) <==> x[y]"),
MPSLOT("__setitem__", mp_ass_subscript, slot_mp_ass_subscript, wrap_objobjargproc,
"x.__setitem__(i, y) <==> x[i]=y"),
MPSLOT("__delitem__", mp_ass_subscript, slot_mp_ass_subscript, wrap_delitem, "x.__delitem__(y) <==> del x[y]"),
SQSLOT("__len__", sq_length, slot_sq_length, wrap_lenfunc, "x.__len__() <==> len(x)"),
/* Heap types defining __add__/__mul__ have sq_concat/sq_repeat == NULL.
The logic in abstract.c always falls back to nb_add/nb_multiply in
this case. Defining both the nb_* and the sq_* slots to call the
user-defined methods has unexpected side-effects, as shown by
test_descr.notimplemented() */
SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc, "x.__add__(y) <==> x+y"),
SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc, "x.__mul__(n) <==> x*n"),
SQSLOT("__rmul__", sq_repeat, NULL, wrap_indexargfunc, "x.__rmul__(n) <==> n*x"),
SQSLOT("__getitem__", sq_item, slot_sq_item, wrap_sq_item, "x.__getitem__(y) <==> x[y]"),
SQSLOT("__getslice__", sq_slice, slot_sq_slice, wrap_ssizessizeargfunc, "x.__getslice__(i, j) <==> x[i:j]\n\
static slotdef slotdefs[]
= { 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)"),
FLSLOT("__call__", tp_call, slot_tp_call, (wrapperfunc)wrap_call, "x.__call__(...) <==> x(...)",
PyWrapperFlag_KEYWORDS),
TPSLOT("__str__", tp_str, slot_tp_str, wrap_unaryfunc, "x.__str__() <==> str(x)"),
TPSLOT("__lt__", tp_richcompare, slot_tp_richcompare, richcmp_lt, "x.__lt__(y) <==> x<y"),
TPSLOT("__le__", tp_richcompare, slot_tp_richcompare, richcmp_le, "x.__le__(y) <==> x<=y"),
TPSLOT("__eq__", tp_richcompare, slot_tp_richcompare, richcmp_eq, "x.__eq__(y) <==> x==y"),
TPSLOT("__ne__", tp_richcompare, slot_tp_richcompare, richcmp_ne, "x.__ne__(y) <==> x!=y"),
TPSLOT("__gt__", tp_richcompare, slot_tp_richcompare, richcmp_gt, "x.__gt__(y) <==> x>y"),
TPSLOT("__ge__", tp_richcompare, slot_tp_richcompare, richcmp_ge, "x.__ge__(y) <==> x>=y"),
FLSLOT("__init__", tp_init, slot_tp_init, (wrapperfunc)wrap_init, "x.__init__(...) initializes x; "
"see help(type(x)) for signature",
PyWrapperFlag_KEYWORDS),
TPSLOT("__new__", tp_new, slot_tp_new, NULL, ""),
BINSLOT("__add__", nb_add, slot_nb_add, "+"), // [force clang-format to line break]
RBINSLOT("__radd__", nb_add, slot_nb_add, "+"), //
BINSLOT("__sub__", nb_subtract, slot_nb_subtract, "-"), //
RBINSLOT("__rsub__", nb_subtract, slot_nb_subtract, "-"), //
BINSLOT("__mul__", nb_multiply, slot_nb_multiply, "*"), //
RBINSLOT("__rmul__", nb_multiply, slot_nb_multiply, "*"), //
BINSLOT("__div__", nb_divide, slot_nb_divide, "/"), //
RBINSLOT("__rdiv__", nb_divide, slot_nb_divide, "/"), //
BINSLOT("__mod__", nb_remainder, slot_nb_remainder, "%"), //
RBINSLOT("__rmod__", nb_remainder, slot_nb_remainder, "%"), //
BINSLOTNOTINFIX("__divmod__", nb_divmod, slot_nb_divmod, "divmod(x, y)"),
RBINSLOTNOTINFIX("__rdivmod__", nb_divmod, slot_nb_divmod, "divmod(y, x)"),
NBSLOT("__pow__", nb_power, slot_nb_power, wrap_ternaryfunc, "x.__pow__(y[, z]) <==> pow(x, y[, z])"),
NBSLOT("__rpow__", nb_power, slot_nb_power, wrap_ternaryfunc_r, "y.__rpow__(x[, z]) <==> pow(x, y[, z])"),
UNSLOT("__neg__", nb_negative, slot_nb_negative, wrap_unaryfunc, "-x"), //
UNSLOT("__pos__", nb_positive, slot_nb_positive, wrap_unaryfunc, "+x"), //
UNSLOT("__abs__", nb_absolute, slot_nb_absolute, wrap_unaryfunc, "abs(x)"), //
UNSLOT("__nonzero__", nb_nonzero, slot_nb_nonzero, wrap_inquirypred, "x != 0"), //
UNSLOT("__invert__", nb_invert, slot_nb_invert, wrap_unaryfunc, "~x"), //
BINSLOT("__lshift__", nb_lshift, slot_nb_lshift, "<<"), //
RBINSLOT("__rlshift__", nb_lshift, slot_nb_lshift, "<<"), //
BINSLOT("__rshift__", nb_rshift, slot_nb_rshift, ">>"), //
RBINSLOT("__rrshift__", nb_rshift, slot_nb_rshift, ">>"), //
BINSLOT("__and__", nb_and, slot_nb_and, "&"), //
RBINSLOT("__rand__", nb_and, slot_nb_and, "&"), //
BINSLOT("__xor__", nb_xor, slot_nb_xor, "^"), //
RBINSLOT("__rxor__", nb_xor, slot_nb_xor, "^"), //
BINSLOT("__or__", nb_or, slot_nb_or, "|"), //
RBINSLOT("__ror__", nb_or, slot_nb_or, "|"), //
UNSLOT("__int__", nb_int, slot_nb_int, wrap_unaryfunc, "int(x)"), //
UNSLOT("__long__", nb_long, slot_nb_long, wrap_unaryfunc, "long(x)"), //
UNSLOT("__float__", nb_float, slot_nb_float, wrap_unaryfunc, "float(x)"), //
UNSLOT("__oct__", nb_oct, slot_nb_oct, wrap_unaryfunc, "oct(x)"), //
UNSLOT("__hex__", nb_hex, slot_nb_hex, wrap_unaryfunc, "hex(x)"), //
MPSLOT("__len__", mp_length, slot_mp_length, wrap_lenfunc, "x.__len__() <==> len(x)"),
MPSLOT("__getitem__", mp_subscript, slot_mp_subscript, wrap_binaryfunc, "x.__getitem__(y) <==> x[y]"),
MPSLOT("__setitem__", mp_ass_subscript, slot_mp_ass_subscript, wrap_objobjargproc,
"x.__setitem__(i, y) <==> x[i]=y"),
MPSLOT("__delitem__", mp_ass_subscript, slot_mp_ass_subscript, wrap_delitem, "x.__delitem__(y) <==> del x[y]"),
SQSLOT("__len__", sq_length, slot_sq_length, wrap_lenfunc, "x.__len__() <==> len(x)"),
/* Heap types defining __add__/__mul__ have sq_concat/sq_repeat == NULL.
The logic in abstract.c always falls back to nb_add/nb_multiply in
this case. Defining both the nb_* and the sq_* slots to call the
user-defined methods has unexpected side-effects, as shown by
test_descr.notimplemented() */
SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc, "x.__add__(y) <==> x+y"),
SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc, "x.__mul__(n) <==> x*n"),
SQSLOT("__rmul__", sq_repeat, NULL, wrap_indexargfunc, "x.__rmul__(n) <==> n*x"),
SQSLOT("__getitem__", sq_item, slot_sq_item, wrap_sq_item, "x.__getitem__(y) <==> x[y]"),
SQSLOT("__getslice__", sq_slice, slot_sq_slice, wrap_ssizessizeargfunc, "x.__getslice__(i, j) <==> x[i:j]\n\
\n\
Use of negative indices is not supported."),
SQSLOT("__setitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_setitem, "x.__setitem__(i, y) <==> x[i]=y"),
SQSLOT("__delitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_delitem, "x.__delitem__(y) <==> del x[y]"),
SQSLOT("__setslice__", sq_ass_slice, slot_sq_ass_slice, wrap_ssizessizeobjargproc,
"x.__setslice__(i, j, y) <==> x[i:j]=y\n\
SQSLOT("__setitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_setitem, "x.__setitem__(i, y) <==> x[i]=y"),
SQSLOT("__delitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_delitem, "x.__delitem__(y) <==> del x[y]"),
SQSLOT("__setslice__", sq_ass_slice, slot_sq_ass_slice, wrap_ssizessizeobjargproc,
"x.__setslice__(i, j, y) <==> x[i:j]=y\n\
\n\
Use of negative indices is not supported."),
SQSLOT("__delslice__", sq_ass_slice, slot_sq_ass_slice, wrap_delslice, "x.__delslice__(i, j) <==> del x[i:j]\n\
SQSLOT("__delslice__", sq_ass_slice, slot_sq_ass_slice, wrap_delslice, "x.__delslice__(i, j) <==> del x[i:j]\n\
\n\
Use of negative indices is not supported."),
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("__imul__", sq_inplace_repeat, NULL, wrap_indexargfunc, "x.__imul__(y) <==> x*=y"),
};
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("__imul__", sq_inplace_repeat, NULL, wrap_indexargfunc, "x.__imul__(y) <==> x*=y"),
{ NULL, 0, NULL, NULL, NULL, 0 } };
static void init_slotdefs() {
static bool initialized = false;
......@@ -1033,6 +1012,9 @@ static void init_slotdefs() {
for (int i = 0; i < sizeof(slotdefs) / sizeof(slotdefs[0]); i++) {
if (i > 0) {
if (!slotdefs[i].name)
continue;
#ifndef NDEBUG
if (slotdefs[i - 1].offset > slotdefs[i].offset) {
printf("slotdef for %s in the wrong place\n", slotdefs[i - 1].name);
......@@ -1052,12 +1034,132 @@ static void init_slotdefs() {
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 updated = false;
for (const slotdef& p : slotdefs) {
if (!p.name)
continue;
if (p.name == attr) {
// TODO update subclasses;
update_one_slot(self, p);
update_one_slot(self, &p);
updated = true;
}
}
......@@ -1067,9 +1169,9 @@ bool update_slot(BoxedClass* self, const std::string& attr) {
void fixup_slot_dispatchers(BoxedClass* self) {
init_slotdefs();
for (const slotdef& p : slotdefs) {
update_one_slot(self, p);
}
const slotdef* p = slotdefs;
while (p->name)
p = update_one_slot(self, p);
// 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.
......
......@@ -456,6 +456,11 @@ extern "C" long PyObject_HashNotImplemented(PyObject* self) {
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) {
long x;
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