Commit c8bb686f authored by Travis Hance's avatar Travis Hance

slots

parent ea1c4746
......@@ -479,6 +479,7 @@ typedef struct _heaptypeobject {
see add_operators() in typeobject.c . */
PyBufferProcs as_buffer;
PyObject *ht_name, *ht_slots;
Py_ssize_t nslots;
/* here are optional user slots, followed by the members. */
} PyHeapTypeObject;
......
......@@ -20,6 +20,7 @@
#include "core/common.h"
#include "core/types.h"
#include "core/util.h"
#include "runtime/types.h"
namespace pyston {
......@@ -52,6 +53,30 @@ bool containsYield(AST* ast) {
return visitor.containsYield;
}
// TODO
// Combine this with the below? Basically the same logic with different string types...
// Also should this go in this file?
BoxedString* mangleNameBoxedString(BoxedString* id, BoxedString* private_name) {
assert(id);
assert(private_name);
int len = id->s.size();
if (len < 2 || id->s[0] != '_' || id->s[1] != '_')
return id;
if ((id->s[len - 2] == '_' && id->s[len - 1] == '_') || id->s.find('.') != llvm::StringRef::npos)
return id;
const char* p = private_name->s.data();
while (*p == '_') {
p++;
len--;
}
if (*p == '\0')
return id;
return static_cast<BoxedString*>(boxStringTwine("_" + (p + id->s)));
}
static void mangleNameInPlace(InternedString& id, const std::string* private_name,
InternedStringPool& interned_strings) {
if (!private_name)
......
......@@ -178,6 +178,9 @@ public:
};
bool containsYield(AST* ast);
class BoxedString;
BoxedString* mangleNameBoxedString(BoxedString* id, BoxedString* private_name);
}
#endif
......@@ -440,6 +440,7 @@ public:
// Add a no-op constructor to make sure that we don't zero-initialize cls
Box() {}
void* operator new(size_t size, BoxedClass* cls, size_t nitems) __attribute__((visibility("default")));
void* operator new(size_t size, BoxedClass* cls) __attribute__((visibility("default")));
void operator delete(void* ptr) __attribute__((visibility("default"))) { abort(); }
......@@ -448,10 +449,12 @@ public:
llvm::iterator_range<BoxIterator> pyElements();
size_t getHCAttrsOffset();
HCAttrs* getHCAttrsPtr();
void setDict(BoxedDict* d);
BoxedDict* getDict();
void setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite_args);
void giveAttr(const std::string& attr, Box* val) {
assert(!this->hasattr(attr));
......@@ -481,10 +484,15 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
#define DEFAULT_CLASS(default_cls) \
void* operator new(size_t size, BoxedClass * cls) __attribute__((visibility("default"))) { \
return Box::operator new(size, cls); \
return Box::operator new(size, cls, 0); \
} \
void* operator new(size_t size) __attribute__((visibility("default"))) { \
return Box::operator new(size, default_cls); \
return Box::operator new(size, default_cls, 0); \
}
#define ALLOCATABLE_WITH_ITEMS \
void* operator new(size_t size, BoxedClass * cls, size_t nitems) __attribute__((visibility("default"))) { \
return Box::operator new(size, cls, nitems); \
}
// The restrictions on when you can use the SIMPLE (ie fast) variant are encoded as
......@@ -522,6 +530,7 @@ class BoxVar : public Box {
public:
Py_ssize_t ob_size;
BoxVar() {}
BoxVar(Py_ssize_t ob_size) : ob_size(ob_size) {}
};
static_assert(offsetof(BoxVar, ob_size) == offsetof(struct _varobject, ob_size), "");
......
This diff is collapsed.
......@@ -132,7 +132,10 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
Box* rtn = static_cast<Box*>(mem);
PyObject_Init(rtn, cls);
if (cls->tp_itemsize != 0)
static_cast<BoxVar*>(rtn)->ob_size = nitems + 1;
PyObject_INIT(rtn, cls);
assert(rtn->cls);
return rtn;
......@@ -175,16 +178,20 @@ extern "C" PyObject* _PyObject_New(PyTypeObject* tp) noexcept {
}
// Analogue of PyType_GenericNew
void* Box::operator new(size_t size, BoxedClass* cls) {
void* Box::operator new(size_t size, BoxedClass* cls, size_t nitems) {
assert(cls);
ASSERT(cls->tp_basicsize >= size, "%s", cls->tp_name);
assert(cls->tp_alloc);
void* mem = cls->tp_alloc(cls, 0);
void* mem = cls->tp_alloc(cls, nitems);
RELEASE_ASSERT(mem, "");
return mem;
}
void* Box::operator new(size_t size, BoxedClass* cls) {
return Box::operator new(size, cls, 0);
}
Box* BoxedClass::callHasnextIC(Box* obj, bool null_on_nonexistent) {
assert(obj->cls == this);
......@@ -448,6 +455,14 @@ extern "C" void boxGCHandler(GCVisitor* v, Box* b) {
if (b->cls->instancesHaveDictAttrs()) {
RELEASE_ASSERT(0, "Shouldn't all of these objects be conservatively scanned?");
}
if (b->cls->tp_flags & Py_TPFLAGS_HEAPTYPE) {
BoxedHeapClass* heap_cls = static_cast<BoxedHeapClass*>(b->cls);
BoxedHeapClass::SlotOffset* slotOffsets = heap_cls->slotOffsets();
for (int i = 0; i < heap_cls->nslots; i++) {
v->visit(*((Box**)((char*)b + slotOffsets[i])));
}
}
} else {
assert(type_cls == NULL || b == type_cls);
}
......@@ -1958,6 +1973,17 @@ extern "C" PyObject* PyObject_Init(PyObject* op, PyTypeObject* tp) noexcept {
// initUserAttrs themselves, though.
initUserAttrs(op, tp);
// Initialize the variables declared in __slots__ to NULL.
if (tp->tp_flags & Py_TPFLAGS_HEAPTYPE) {
BoxedHeapClass* heap_cls = static_cast<BoxedHeapClass*>(tp);
if (heap_cls->nslots > 0) {
BoxedHeapClass::SlotOffset* slotOffsets = heap_cls->slotOffsets();
for (int i = 0; i < heap_cls->nslots; i++) {
*(Box**)((char*)op + slotOffsets[i]) = NULL;
}
}
}
return op;
}
......@@ -1987,6 +2013,7 @@ void setupRuntime() {
type_cls = ::new (mem) BoxedHeapClass(object_cls, &typeGCHandler, offsetof(BoxedClass, attrs),
offsetof(BoxedClass, tp_weaklist), sizeof(BoxedHeapClass), false, NULL);
type_cls->tp_flags |= Py_TPFLAGS_TYPE_SUBCLASS;
type_cls->tp_itemsize = sizeof(BoxedHeapClass::SlotOffset);
PyObject_Init(object_cls, type_cls);
PyObject_Init(type_cls, type_cls);
......
......@@ -177,7 +177,10 @@ public:
void (*simple_destructor)(Box*);
// Offset of the HCAttrs object or 0 if there are no hcattrs.
// Negative offset is from the end of the class (useful for variable-size objects with the attrs at the end)
// Analogous to tp_dictoffset
// A class should have at most of one attrs_offset and tp_dictoffset be nonzero.
// (But having nonzero attrs_offset here would map to having nonzero tp_dictoffset in CPython)
const int attrs_offset;
bool instancesHaveHCAttrs() { return attrs_offset != 0; }
......@@ -231,12 +234,16 @@ public:
PyBufferProcs as_buffer;
BoxedString* ht_name;
PyObject** ht_slots;
PyObject* ht_slots;
size_t nslots;
typedef size_t SlotOffset;
SlotOffset* slotOffsets() { return (BoxedHeapClass::SlotOffset*)((char*)this + this->cls->tp_basicsize); }
// These functions are the preferred way to construct new types:
static BoxedHeapClass* create(BoxedClass* metatype, BoxedClass* base, gcvisit_func gc_visit, int attrs_offset,
int weaklist_offset, int instance_size, bool is_user_defined, BoxedString* name,
BoxedTuple* bases);
BoxedTuple* bases, size_t nslots);
static BoxedHeapClass* create(BoxedClass* metatype, BoxedClass* base, gcvisit_func gc_visit, int attrs_offset,
int weaklist_offset, int instance_size, bool is_user_defined,
const std::string& name);
......@@ -246,12 +253,13 @@ private:
// by BoxedHeapClass::create(), but setupRuntime() also needs to do some manual class
// creation due to bootstrapping issues.
BoxedHeapClass(BoxedClass* base, gcvisit_func gc_visit, int attrs_offset, int weaklist_offset, int instance_size,
bool is_user_defined, BoxedString* name);
bool is_user_defined, BoxedString* name, size_t nslots = 0);
friend void setupRuntime();
friend void setupSys();
DEFAULT_CLASS(type_cls);
ALLOCATABLE_WITH_ITEMS;
};
static_assert(sizeof(pyston::Box) == sizeof(struct _object), "");
......@@ -402,7 +410,7 @@ public:
DEFAULT_CLASS_SIMPLE(bool_cls);
};
class BoxedString : public Box {
class BoxedString : public BoxVar {
public:
llvm::StringRef s;
......@@ -410,13 +418,15 @@ public:
size_t size() { return s.size(); }
void* operator new(size_t size, size_t ssize) __attribute__((visibility("default"))) {
Box* rtn = static_cast<Box*>(gc_alloc(str_cls->tp_basicsize + ssize + 1, gc::GCKind::PYTHON));
BoxVar* rtn = static_cast<BoxVar*>(gc_alloc(str_cls->tp_basicsize + ssize + 1, gc::GCKind::PYTHON));
// TODO need to initialize ob_size for other objects as well
rtn->ob_size = ssize + 1;
rtn->cls = str_cls;
return rtn;
}
void* operator new(size_t size, BoxedClass* cls, size_t ssize) __attribute__((visibility("default"))) {
Box* rtn = static_cast<Box*>(cls->tp_alloc(cls, ssize + 1));
BoxVar* rtn = static_cast<BoxVar*>(cls->tp_alloc(cls, ssize + 1));
rtn->cls = cls;
return rtn;
}
......@@ -429,7 +439,7 @@ public:
private:
// used only in ctors to give our llvm::StringRef the proper pointer
char* storage() { return (char*)this + cls->tp_basicsize; }
char* storage() { return (char*)this + sizeof(BoxedString); }
void* operator new(size_t size) = delete;
};
......@@ -514,20 +524,21 @@ public:
DEFAULT_CLASS_SIMPLE(list_cls);
};
class BoxedTuple : public Box {
class BoxedTuple : public BoxVar {
public:
typedef std::vector<Box*, StlCompatAllocator<Box*>> GCVector;
Box** elts;
void* operator new(size_t size, size_t nelts) __attribute__((visibility("default"))) {
Box* rtn = static_cast<Box*>(gc_alloc(_PyObject_VAR_SIZE(tuple_cls, nelts + 1), gc::GCKind::PYTHON));
BoxVar* rtn = static_cast<BoxVar*>(gc_alloc(_PyObject_VAR_SIZE(tuple_cls, nelts + 1), gc::GCKind::PYTHON));
rtn->ob_size = nelts;
rtn->cls = tuple_cls;
return rtn;
}
void* operator new(size_t size, BoxedClass* cls, size_t nelts) __attribute__((visibility("default"))) {
Box* rtn = static_cast<Box*>(cls->tp_alloc(cls, nelts));
BoxVar* rtn = static_cast<BoxVar*>(cls->tp_alloc(cls, nelts));
rtn->cls = cls;
return rtn;
}
......@@ -572,18 +583,19 @@ public:
Box** begin() const { return &elts[0]; }
Box** end() const { return &elts[nelts]; }
Box*& operator[](size_t index) { return elts[index]; }
size_t size() const { return nelts; }
private:
size_t nelts;
BoxedTuple(size_t size) : elts(reinterpret_cast<Box**>((char*)this + this->cls->tp_basicsize)), nelts(size) {
BoxedTuple(size_t size) : elts(reinterpret_cast<Box**>((char*)this + tuple_cls->tp_basicsize)), nelts(size) {
memset(elts, 0, sizeof(Box*) * size);
}
BoxedTuple(std::initializer_list<Box*>& members)
: elts(reinterpret_cast<Box**>((char*)this + this->cls->tp_basicsize)), nelts(members.size()) {
: elts(reinterpret_cast<Box**>((char*)this + tuple_cls->tp_basicsize)), nelts(members.size()) {
// by the time we make it here elts[] is big enough to contain members
Box** p = &elts[0];
for (auto b : members) {
......
print 'basic test'
class C(object):
__slots__ = ['a', 'b', '__private_var']
c = C()
try:
print c.a
except AttributeError as e:
print e.message
c.a = 5
print c.a
print c.__slots__
c._C__private_var = 6
print c._C__private_var
try:
c.x = 12
except AttributeError as e:
print e.message
print 'testing __dict__'
class C(object):
__slots__ = ['d', 'e', '__dict__']
c = C()
c.d = 5
print c.d
c.r = 6
print c.r
print c.__dict__.items() # dict should contain only r (not d)
print 'testing inheritance'
class C(object):
__slots__ = ['a', 'b']
class D(object):
__slots__ = ['c', 'd']
class E(object):
pass
class G(C):
__slots__ = ['k', 'l']
g = G()
g.a = 5
print g.a
g.k = 12
print g.k
class G(C, E):
__slots__ = ['k', 'l']
g = G()
g.a = 5
print g.a
g.k = 12
print g.k
class G(E, C):
__slots__ = ['k', 'l']
g = G()
g.a = 5
print g.a
g.k = 12
print g.k
try:
class G(C, D):
pass
except TypeError as e:
print e.message
......@@ -8,3 +8,8 @@ def f():
for i in xrange(100):
s = S(base)
f()
# make sure it has attrs
s = S("blah")
s.blah = 2
print s.blah
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