Commit b42de323 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Add tp_subclasses so we can update slots on subclasses

ie if we set __getattribute__ on a superclass, we have to update
tp_getattro on all subclasses.  We've always been broken on this, but
this didn't really matter as much until now since we would internally
check __getattribute__ and not tp_getattro.

tp_subclasses is a list of weakrefs, which means that we need weakref
support before we can start finalizing classes.  which means we have to
add a whole slew of things into the bootstrapping section.

We should probably just do this in a more systematic way, but I'm
not sure what that would be...
parent 4b58bc3d
......@@ -25,6 +25,7 @@ namespace pyston {
static const std::string _new_str("__new__");
static const std::string _getattr_str("__getattr__");
static const std::string _getattribute_str("__getattribute__");
typedef int (*update_callback)(PyTypeObject*, void*);
extern "C" void conservativeGCHandler(GCVisitor* v, Box* b) noexcept {
v->visitPotentialRange((void* const*)b, (void* const*)((char*)b + b->cls->tp_basicsize));
......@@ -1496,18 +1497,55 @@ static const slotdef* update_one_slot(BoxedClass* type, const slotdef* p) noexce
return p;
}
bool update_slot(BoxedClass* self, const std::string& attr) noexcept {
bool updated = false;
for (const slotdef& p : slotdefs) {
if (!p.name)
continue;
if (p.name == attr) {
// TODO update subclasses;
update_one_slot(self, &p);
updated = true;
}
/* In the type, update the slots whose slotdefs are gathered in the pp array.
This is a callback for update_subclasses(). */
static int update_slots_callback(PyTypeObject* type, void* data) noexcept {
slotdef** pp = (slotdef**)data;
for (; *pp; pp++)
update_one_slot(type, *pp);
return 0;
}
static int update_subclasses(PyTypeObject* type, PyObject* name, update_callback callback, void* data) noexcept;
static int recurse_down_subclasses(PyTypeObject* type, PyObject* name, update_callback callback, void* data) noexcept;
bool update_slot(BoxedClass* type, const std::string& attr) noexcept {
slotdef* ptrs[MAX_EQUIV];
slotdef* p;
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. */
PyType_Modified(type);
init_slotdefs();
pp = ptrs;
for (p = slotdefs; p->name; p++) {
/* XXX assume name is interned! */
if (p->name == attr)
*pp++ = p;
}
return updated;
*pp = NULL;
for (pp = ptrs; *pp; pp++) {
p = *pp;
offset = p->offset;
while (p > slotdefs && (p - 1)->offset == offset)
--p;
*pp = p;
}
if (ptrs[0] == NULL)
return false; /* Not an attribute that affects any slots */
int r = update_subclasses(type, new BoxedString(attr), update_slots_callback, (void*)ptrs);
// TODO this is supposed to be a CAPI function!
if (r)
throwCAPIException();
return true;
}
void fixup_slot_dispatchers(BoxedClass* self) noexcept {
......@@ -1518,6 +1556,40 @@ void fixup_slot_dispatchers(BoxedClass* self) noexcept {
p = update_one_slot(self, p);
}
static int update_subclasses(PyTypeObject* type, PyObject* name, update_callback callback, void* data) noexcept {
if (callback(type, data) < 0)
return -1;
return recurse_down_subclasses(type, name, callback, data);
}
static int recurse_down_subclasses(PyTypeObject* type, PyObject* name, update_callback callback, void* data) noexcept {
PyTypeObject* subclass;
PyObject* ref, *subclasses, *dict;
Py_ssize_t i, n;
subclasses = type->tp_subclasses;
if (subclasses == NULL)
return 0;
assert(PyList_Check(subclasses));
n = PyList_GET_SIZE(subclasses);
for (i = 0; i < n; i++) {
ref = PyList_GET_ITEM(subclasses, i);
assert(PyWeakref_CheckRef(ref));
subclass = (PyTypeObject*)PyWeakref_GET_OBJECT(ref);
assert(subclass != NULL);
if ((PyObject*)subclass == Py_None)
continue;
assert(PyType_Check(subclass));
/* Avoid recursing down into unaffected classes */
dict = subclass->tp_dict;
if (dict != NULL && PyDict_Check(dict) && PyDict_GetItem(dict, name) != NULL)
continue;
if (update_subclasses(subclass, name, callback, data) < 0)
return -1;
}
return 0;
}
static PyObject* tp_new_wrapper(PyTypeObject* self, BoxedTuple* args, Box* kwds) noexcept {
RELEASE_ASSERT(isSubclass(self->cls, type_cls), "");
......@@ -2370,6 +2442,52 @@ static void inherit_slots(PyTypeObject* type, PyTypeObject* base) noexcept {
}
}
static int add_subclass(PyTypeObject* base, PyTypeObject* type) noexcept {
Py_ssize_t i;
int result;
PyObject* list, *ref, *newobj;
list = base->tp_subclasses;
if (list == NULL) {
base->tp_subclasses = list = PyList_New(0);
if (list == NULL)
return -1;
}
assert(PyList_Check(list));
newobj = PyWeakref_NewRef((PyObject*)type, NULL);
i = PyList_GET_SIZE(list);
while (--i >= 0) {
ref = PyList_GET_ITEM(list, i);
assert(PyWeakref_CheckRef(ref));
if (PyWeakref_GET_OBJECT(ref) == Py_None)
return PyList_SetItem(list, i, newobj);
}
result = PyList_Append(list, newobj);
Py_DECREF(newobj);
return result;
}
static void remove_subclass(PyTypeObject* base, PyTypeObject* type) noexcept {
Py_ssize_t i;
PyObject* list, *ref;
list = base->tp_subclasses;
if (list == NULL) {
return;
}
assert(PyList_Check(list));
i = PyList_GET_SIZE(list);
while (--i >= 0) {
ref = PyList_GET_ITEM(list, i);
assert(PyWeakref_CheckRef(ref));
if (PyWeakref_GET_OBJECT(ref) == (PyObject*)type) {
/* this can't fail, right? */
PySequence_DelItem(list, i);
return;
}
}
}
// commonClassSetup is for the common code between PyType_Ready (which is just for extension classes)
// and our internal type-creation endpoints (BoxedClass::BoxedClass()).
// TODO: Move more of the duplicated logic into here.
......@@ -2381,6 +2499,12 @@ void commonClassSetup(BoxedClass* cls) {
cls->tp_bases = new BoxedTuple({});
}
/* Link into each base class's list of subclasses */
for (PyObject* b : static_cast<BoxedTuple*>(cls->tp_bases)->elts) {
if (PyType_Check(b) && add_subclass((PyTypeObject*)b, cls) < 0)
throwCAPIException();
}
/* Calculate method resolution order */
if (mro_internal(cls) < 0)
throwCAPIException();
......
......@@ -93,12 +93,6 @@ void setupBool() {
bool_cls->giveAttr("__xor__", new BoxedFunction(boxRTFunction((void*)boolXor, BOXED_BOOL, 2)));
bool_cls->freeze();
True = new BoxedBool(true);
False = new BoxedBool(false);
gc::registerPermanentRoot(True);
gc::registerPermanentRoot(False);
}
void teardownBool() {
......
......@@ -194,6 +194,7 @@ public:
BoxedSysFlags() {
auto zero = boxInt(0);
assert(zero);
division_warning = zero;
bytes_warning = zero;
no_user_site = zero;
......@@ -297,8 +298,8 @@ void setupSys() {
sys_module->giveAttr("maxint", boxInt(PYSTON_INT_MAX));
sys_flags_cls = BoxedHeapClass::create(type_cls, object_cls, BoxedSysFlags::gcHandler, 0, 0, sizeof(BoxedSysFlags),
false, "flags");
sys_flags_cls = new BoxedHeapClass(object_cls, BoxedSysFlags::gcHandler, 0, 0, sizeof(BoxedSysFlags), false,
new BoxedString("flags"));
sys_flags_cls->giveAttr("__new__",
new BoxedFunction(boxRTFunction((void*)BoxedSysFlags::__new__, UNKNOWN, 1, 0, true, true)));
#define ADD(name) \
......@@ -309,6 +310,7 @@ void setupSys() {
ADD(no_user_site);
#undef ADD
sys_flags_cls->tp_mro = new BoxedTuple({ sys_flags_cls, object_cls });
sys_flags_cls->freeze();
sys_module->giveAttr("flags", new BoxedSysFlags());
......@@ -324,5 +326,6 @@ void setupSysEnd() {
PyLt());
sys_module->giveAttr("builtin_module_names", new BoxedTuple(std::move(builtin_module_names)));
sys_flags_cls->finishInitialization();
}
}
......@@ -1291,9 +1291,6 @@ static Box* methodGetDoc(Box* b, void*) {
}
void setupCAPI() {
capifunc_cls
= BoxedHeapClass::create(type_cls, object_cls, NULL, 0, 0, sizeof(BoxedCApiFunction), false, "capifunc");
capifunc_cls->giveAttr("__repr__",
new BoxedFunction(boxRTFunction((void*)BoxedCApiFunction::__repr__, UNKNOWN, 1)));
......@@ -1303,8 +1300,6 @@ void setupCAPI() {
capifunc_cls->freeze();
method_cls
= BoxedHeapClass::create(type_cls, object_cls, NULL, 0, 0, sizeof(BoxedMethodDescriptor), false, "method");
method_cls->giveAttr("__get__",
new BoxedFunction(boxRTFunction((void*)BoxedMethodDescriptor::__get__, UNKNOWN, 3)));
method_cls->giveAttr("__call__", new BoxedFunction(boxRTFunction((void*)BoxedMethodDescriptor::__call__, UNKNOWN, 2,
......@@ -1318,8 +1313,6 @@ void setupCAPI() {
UNKNOWN, 2, 0, true, true)));
wrapperdescr_cls->freeze();
wrapperobject_cls
= BoxedHeapClass::create(type_cls, object_cls, NULL, 0, 0, sizeof(BoxedWrapperObject), false, "method-wrapper");
wrapperobject_cls->giveAttr(
"__call__", new BoxedFunction(boxRTFunction((void*)BoxedWrapperObject::__call__, UNKNOWN, 1, 0, true, true)));
wrapperobject_cls->freeze();
......
......@@ -144,7 +144,11 @@ extern "C" void abort() {
// that object, _printStackTrace will hang waiting for the first construction
// to finish.)
alarm(1);
_printStacktrace();
try {
_printStacktrace();
} catch (ExcInfo) {
fprintf(stderr, "error printing stack trace during abort()");
}
// Cancel the alarm.
// This is helpful for when running in a debugger, since the debugger will catch the
......
......@@ -104,7 +104,8 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
}
#endif
if (!cls->tp_mro) {
assert(!wrapperdescr_cls); // the last class to be set up during bootstrapping
// wrapperdescr_cls is the last class to be set up during bootstrapping:
ASSERT(!wrapperdescr_cls, "looks like we need to set up the mro for %s manually", cls->tp_name);
} else {
assert(cls->tp_mro && "maybe we should just skip these checks if !mro");
assert(cls->tp_mro->cls == tuple_cls);
......@@ -432,6 +433,8 @@ extern "C" void typeGCHandler(GCVisitor* v, Box* b) {
v->visit(cls->tp_mro);
if (cls->tp_bases)
v->visit(cls->tp_bases);
if (cls->tp_subclasses)
v->visit(cls->tp_subclasses);
if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) {
BoxedHeapClass* hcls = static_cast<BoxedHeapClass*>(cls);
......@@ -1735,6 +1738,34 @@ void setupRuntime() {
= new BoxedHeapClass(object_cls, NULL, 0, 0, sizeof(BoxedGetsetDescriptor), false, boxStrConstant("getset"));
attrwrapper_cls = new BoxedHeapClass(object_cls, &AttrWrapper::gcHandler, 0, 0, sizeof(AttrWrapper), false,
new BoxedString("attrwrapper"));
dict_cls = new BoxedHeapClass(object_cls, &dictGCHandler, 0, 0, sizeof(BoxedDict), false, new BoxedString("dict"));
file_cls = new BoxedHeapClass(object_cls, NULL, 0, offsetof(BoxedFile, weakreflist), sizeof(BoxedFile), false,
new BoxedString("file"));
int_cls = new BoxedHeapClass(object_cls, NULL, 0, 0, sizeof(BoxedInt), false, new BoxedString("int"));
bool_cls = new BoxedHeapClass(int_cls, NULL, 0, 0, sizeof(BoxedBool), false, new BoxedString("bool"));
complex_cls = new BoxedHeapClass(object_cls, NULL, 0, 0, sizeof(BoxedComplex), false, new BoxedString("complex"));
long_cls = new BoxedHeapClass(object_cls, &BoxedLong::gchandler, 0, 0, sizeof(BoxedLong), false,
new BoxedString("long"));
float_cls = new BoxedHeapClass(object_cls, NULL, 0, 0, sizeof(BoxedFloat), false, new BoxedString("float"));
function_cls = new BoxedHeapClass(object_cls, &functionGCHandler, offsetof(BoxedFunction, attrs),
offsetof(BoxedFunction, in_weakreflist), sizeof(BoxedFunction), false,
new BoxedString("function"));
builtin_function_or_method_cls = new BoxedHeapClass(
object_cls, &functionGCHandler, 0, offsetof(BoxedBuiltinFunctionOrMethod, in_weakreflist),
sizeof(BoxedBuiltinFunctionOrMethod), false, new BoxedString("builtin_function_or_method"));
function_cls->simple_destructor = builtin_function_or_method_cls->simple_destructor = functionDtor;
module_cls = new BoxedHeapClass(object_cls, NULL, offsetof(BoxedModule, attrs), 0, sizeof(BoxedModule), false,
new BoxedString("module"));
member_cls
= new BoxedHeapClass(object_cls, NULL, 0, 0, sizeof(BoxedMemberDescriptor), false, new BoxedString("member"));
capifunc_cls
= new BoxedHeapClass(object_cls, NULL, 0, 0, sizeof(BoxedCApiFunction), false, new BoxedString("capifunc"));
method_cls
= new BoxedHeapClass(object_cls, NULL, 0, 0, sizeof(BoxedMethodDescriptor), false, new BoxedString("method"));
wrapperobject_cls = new BoxedHeapClass(object_cls, NULL, 0, 0, sizeof(BoxedWrapperObject), false,
new BoxedString("method-wrapper"));
wrapperdescr_cls = new BoxedHeapClass(object_cls, NULL, 0, 0, sizeof(BoxedWrapperDescriptor), false,
new BoxedString("wrapper_descriptor"));
......@@ -1748,6 +1779,47 @@ void setupRuntime() {
type_cls->tp_mro = new BoxedTuple({ type_cls, object_cls });
pyston_getset_cls->tp_mro = new BoxedTuple({ pyston_getset_cls, object_cls });
attrwrapper_cls->tp_mro = new BoxedTuple({ attrwrapper_cls, object_cls });
dict_cls->tp_mro = new BoxedTuple({ dict_cls, object_cls });
file_cls->tp_mro = new BoxedTuple({ file_cls, object_cls });
int_cls->tp_mro = new BoxedTuple({ int_cls, object_cls });
bool_cls->tp_mro = new BoxedTuple({ bool_cls, object_cls });
complex_cls->tp_mro = new BoxedTuple({ complex_cls, object_cls });
long_cls->tp_mro = new BoxedTuple({ long_cls, object_cls });
float_cls->tp_mro = new BoxedTuple({ float_cls, object_cls });
function_cls->tp_mro = new BoxedTuple({ function_cls, object_cls });
builtin_function_or_method_cls->tp_mro = new BoxedTuple({ builtin_function_or_method_cls, object_cls });
member_cls->tp_mro = new BoxedTuple({ member_cls, object_cls });
capifunc_cls->tp_mro = new BoxedTuple({ capifunc_cls, object_cls });
module_cls->tp_mro = new BoxedTuple({ module_cls, object_cls });
method_cls->tp_mro = new BoxedTuple({ method_cls, object_cls });
wrapperobject_cls->tp_mro = new BoxedTuple({ wrapperobject_cls, object_cls });
wrapperdescr_cls->tp_mro = new BoxedTuple({ wrapperdescr_cls, object_cls });
STR = typeFromClass(str_cls);
BOXED_INT = typeFromClass(int_cls);
BOXED_FLOAT = typeFromClass(float_cls);
BOXED_BOOL = typeFromClass(bool_cls);
NONE = typeFromClass(none_cls);
LIST = typeFromClass(list_cls);
MODULE = typeFromClass(module_cls);
DICT = typeFromClass(dict_cls);
BOXED_TUPLE = typeFromClass(tuple_cls);
LONG = typeFromClass(long_cls);
BOXED_COMPLEX = typeFromClass(complex_cls);
True = new BoxedBool(true);
False = new BoxedBool(false);
gc::registerPermanentRoot(True);
gc::registerPermanentRoot(False);
// Need to initialize interned_ints early:
setupInt();
// sys is the first module that needs to be set up, due to modules
// being tracked in sys.modules:
setupSys();
// Weakrefs are used for tp_subclasses:
init_weakref();
object_cls->finishInitialization();
type_cls->finishInitialization();
......@@ -1758,6 +1830,20 @@ void setupRuntime() {
list_cls->finishInitialization();
pyston_getset_cls->finishInitialization();
attrwrapper_cls->finishInitialization();
dict_cls->finishInitialization();
file_cls->finishInitialization();
int_cls->finishInitialization();
bool_cls->finishInitialization();
complex_cls->finishInitialization();
long_cls->finishInitialization();
float_cls->finishInitialization();
function_cls->finishInitialization();
builtin_function_or_method_cls->finishInitialization();
member_cls->finishInitialization();
module_cls->finishInitialization();
capifunc_cls->finishInitialization();
method_cls->finishInitialization();
wrapperobject_cls->finishInitialization();
wrapperdescr_cls->finishInitialization();
object_cls->tp_getattro = PyObject_GenericGetAttr;
......@@ -1770,42 +1856,15 @@ void setupRuntime() {
type_cls->giveAttr("__dict__", dict_descr);
module_cls = BoxedHeapClass::create(type_cls, object_cls, NULL, offsetof(BoxedModule, attrs), 0,
sizeof(BoxedModule), false, "module");
// TODO it'd be nice to be able to do these in the respective setupType methods,
// but those setup methods probably want access to these objects.
// We could have a multi-stage setup process, but that seems overkill for now.
int_cls = BoxedHeapClass::create(type_cls, object_cls, NULL, 0, 0, sizeof(BoxedInt), false, "int");
bool_cls = BoxedHeapClass::create(type_cls, int_cls, NULL, 0, 0, sizeof(BoxedBool), false, "bool");
complex_cls = BoxedHeapClass::create(type_cls, object_cls, NULL, 0, 0, sizeof(BoxedComplex), false, "complex");
long_cls
= BoxedHeapClass::create(type_cls, object_cls, &BoxedLong::gchandler, 0, 0, sizeof(BoxedLong), false, "long");
float_cls = BoxedHeapClass::create(type_cls, object_cls, NULL, 0, 0, sizeof(BoxedFloat), false, "float");
function_cls
= BoxedHeapClass::create(type_cls, object_cls, &functionGCHandler, offsetof(BoxedFunction, attrs),
offsetof(BoxedFunction, in_weakreflist), sizeof(BoxedFunction), false, "function");
builtin_function_or_method_cls = BoxedHeapClass::create(
type_cls, object_cls, &functionGCHandler, 0, offsetof(BoxedBuiltinFunctionOrMethod, in_weakreflist),
sizeof(BoxedBuiltinFunctionOrMethod), false, "builtin_function_or_method");
function_cls->simple_destructor = builtin_function_or_method_cls->simple_destructor = functionDtor;
instancemethod_cls = BoxedHeapClass::create(type_cls, object_cls, &instancemethodGCHandler, 0,
offsetof(BoxedInstanceMethod, in_weakreflist),
sizeof(BoxedInstanceMethod), false, "instancemethod");
list_cls = BoxedHeapClass::create(type_cls, object_cls, &listGCHandler, 0, 0, sizeof(BoxedList), false, "list");
slice_cls = BoxedHeapClass::create(type_cls, object_cls, &sliceGCHandler, 0, 0, sizeof(BoxedSlice), false, "slice");
dict_cls = BoxedHeapClass::create(type_cls, object_cls, &dictGCHandler, 0, 0, sizeof(BoxedDict), false, "dict");
file_cls = BoxedHeapClass::create(type_cls, object_cls, NULL, 0, offsetof(BoxedFile, weakreflist),
sizeof(BoxedFile), false, "file");
set_cls = BoxedHeapClass::create(type_cls, object_cls, &setGCHandler, 0, offsetof(BoxedSet, weakreflist),
sizeof(BoxedSet), false, "set");
frozenset_cls = BoxedHeapClass::create(type_cls, object_cls, &setGCHandler, 0, offsetof(BoxedSet, weakreflist),
sizeof(BoxedSet), false, "frozenset");
member_cls
= BoxedHeapClass::create(type_cls, object_cls, NULL, 0, 0, sizeof(BoxedMemberDescriptor), false, "member");
capi_getset_cls
= BoxedHeapClass::create(type_cls, object_cls, NULL, 0, 0, sizeof(BoxedGetsetDescriptor), false, "getset");
closure_cls = BoxedHeapClass::create(type_cls, object_cls, &closureGCHandler, offsetof(BoxedClosure, attrs), 0,
......@@ -1823,20 +1882,9 @@ void setupRuntime() {
pyston_getset_cls->freeze();
capi_getset_cls->freeze();
STR = typeFromClass(str_cls);
BOXED_INT = typeFromClass(int_cls);
BOXED_FLOAT = typeFromClass(float_cls);
BOXED_BOOL = typeFromClass(bool_cls);
NONE = typeFromClass(none_cls);
LIST = typeFromClass(list_cls);
SLICE = typeFromClass(slice_cls);
MODULE = typeFromClass(module_cls);
DICT = typeFromClass(dict_cls);
SET = typeFromClass(set_cls);
FROZENSET = typeFromClass(frozenset_cls);
BOXED_TUPLE = typeFromClass(tuple_cls);
LONG = typeFromClass(long_cls);
BOXED_COMPLEX = typeFromClass(complex_cls);
object_cls->giveAttr("__new__", new BoxedFunction(boxRTFunction((void*)objectNew, UNKNOWN, 1, 0, true, true)));
object_cls->giveAttr("__init__", new BoxedFunction(boxRTFunction((void*)objectInit, UNKNOWN, 1, 0, true, false)));
......@@ -1884,7 +1932,6 @@ void setupRuntime() {
object_cls->freeze();
setupBool();
setupInt();
setupLong();
setupFloat();
setupComplex();
......@@ -1976,10 +2023,6 @@ void setupRuntime() {
attrwrapperiter_cls->giveAttr("next", new BoxedFunction(boxRTFunction((void*)AttrWrapperIter::next, UNKNOWN, 1)));
attrwrapperiter_cls->freeze();
// sys is the first module that needs to be set up, due to modules
// being tracked in sys.modules:
setupSys();
setupBuiltins();
_PyExc_Init();
setupThread();
......@@ -2018,7 +2061,6 @@ void setupRuntime() {
init_codecs();
init_socket();
initunicodedata();
init_weakref();
initcStringIO();
init_io();
initzipimport();
......
......@@ -204,6 +204,7 @@ protected:
bool is_user_defined);
friend void setupRuntime();
friend void setupSysEnd();
};
class BoxedHeapClass : public BoxedClass {
......@@ -232,6 +233,7 @@ private:
bool is_user_defined, BoxedString* name);
friend void setupRuntime();
friend void setupSys();
DEFAULT_CLASS(type_cls);
};
......
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