Commit b5f06903 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge commit 'pr/466'

Conflicts:
	src/runtime/objmodel.cpp

Closes #466
parents 87b8302d b332f703
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "core/common.h" #include "core/common.h"
#include "core/types.h" #include "core/types.h"
#include "core/util.h" #include "core/util.h"
#include "runtime/types.h"
namespace pyston { namespace pyston {
...@@ -52,6 +53,30 @@ bool containsYield(AST* ast) { ...@@ -52,6 +53,30 @@ bool containsYield(AST* ast) {
return visitor.containsYield; 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, static void mangleNameInPlace(InternedString& id, const std::string* private_name,
InternedStringPool& interned_strings) { InternedStringPool& interned_strings) {
if (!private_name) if (!private_name)
......
...@@ -178,6 +178,9 @@ public: ...@@ -178,6 +178,9 @@ public:
}; };
bool containsYield(AST* ast); bool containsYield(AST* ast);
class BoxedString;
BoxedString* mangleNameBoxedString(BoxedString* id, BoxedString* private_name);
} }
#endif #endif
...@@ -443,10 +443,12 @@ public: ...@@ -443,10 +443,12 @@ public:
llvm::iterator_range<BoxIterator> pyElements(); llvm::iterator_range<BoxIterator> pyElements();
size_t getHCAttrsOffset();
HCAttrs* getHCAttrsPtr(); HCAttrs* getHCAttrsPtr();
void setDict(BoxedDict* d); void setDict(BoxedDict* d);
BoxedDict* getDict(); BoxedDict* getDict();
void setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite_args); void setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite_args);
void giveAttr(const std::string& attr, Box* val) { void giveAttr(const std::string& attr, Box* val) {
assert(!this->hasattr(attr)); assert(!this->hasattr(attr));
...@@ -476,9 +478,11 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) ...@@ -476,9 +478,11 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
#define DEFAULT_CLASS(default_cls) \ #define DEFAULT_CLASS(default_cls) \
void* operator new(size_t size, BoxedClass * cls) __attribute__((visibility("default"))) { \ void* operator new(size_t size, BoxedClass * cls) __attribute__((visibility("default"))) { \
assert(cls->tp_itemsize == 0); \
return Box::operator new(size, cls); \ return Box::operator new(size, cls); \
} \ } \
void* operator new(size_t size) __attribute__((visibility("default"))) { \ void* operator new(size_t size) __attribute__((visibility("default"))) { \
assert(default_cls->tp_itemsize == 0); \
return Box::operator new(size, default_cls); \ return Box::operator new(size, default_cls); \
} }
...@@ -488,8 +492,27 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) ...@@ -488,8 +492,27 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
std::string per_name_allocsize_name = "allocsize." + std::string(cls->tp_name); \ std::string per_name_allocsize_name = "allocsize." + std::string(cls->tp_name); \
Stats::log(Stats::getStatId(per_name_alloc_name)); \ Stats::log(Stats::getStatId(per_name_alloc_name)); \
Stats::log(Stats::getStatId(per_name_allocsize_name), size); Stats::log(Stats::getStatId(per_name_allocsize_name), size);
#define ALLOC_STATS_VAR(cls) \
if (cls->tp_name) { \
std::string per_name_alloc_name = "alloc." + std::string(cls->tp_name); \
std::string per_name_alloc_name0 = "alloc." + std::string(cls->tp_name) + "(0)"; \
std::string per_name_allocsize_name = "allocsize." + std::string(cls->tp_name); \
std::string per_name_allocsize_name0 = "allocsize." + std::string(cls->tp_name) + "(0)"; \
static StatCounter alloc_name(per_name_alloc_name); \
static StatCounter alloc_name0(per_name_alloc_name0); \
static StatCounter allocsize_name(per_name_allocsize_name); \
static StatCounter allocsize_name0(per_name_allocsize_name0); \
if (nitems == 0) { \
alloc_name0.log(); \
allocsize_name0.log(_PyObject_VAR_SIZE(cls, nitems)); \
} else { \
alloc_name.log(); \
allocsize_name.log(_PyObject_VAR_SIZE(cls, nitems)); \
} \
}
#else #else
#define ALLOC_STATS(cls) #define ALLOC_STATS(cls)
#define ALLOC_STATS_VAR(cls)
#endif #endif
...@@ -525,12 +548,59 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) ...@@ -525,12 +548,59 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
/* TODO: there should be a way to not have to do this nested inlining by hand */ \ /* TODO: there should be a way to not have to do this nested inlining by hand */ \
} }
#define DEFAULT_CLASS_VAR(default_cls, itemsize) \
static_assert(itemsize > 0, ""); \
/* asserts that the class in question is a subclass of BoxVar */ \
inline void _base_check() { \
static_assert(std::is_base_of<BoxVar, std::remove_pointer<decltype(this)>::type>::value, ""); \
} \
\
void* operator new(size_t size, BoxedClass * cls, size_t nitems) __attribute__((visibility("default"))) { \
assert(cls->tp_itemsize == itemsize); \
return BoxVar::operator new(size, cls, nitems); \
} \
void* operator new(size_t size, size_t nitems) __attribute__((visibility("default"))) { \
assert(default_cls->tp_itemsize == itemsize); \
return BoxVar::operator new(size, default_cls, nitems); \
}
#define DEFAULT_CLASS_VAR_SIMPLE(default_cls, itemsize) \
static_assert(itemsize > 0, ""); \
inline void _base_check() { \
static_assert(std::is_base_of<BoxVar, std::remove_pointer<decltype(this)>::type>::value, ""); \
} \
\
void* operator new(size_t size, BoxedClass * cls, size_t nitems) __attribute__((visibility("default"))) { \
ALLOC_STATS_VAR(default_cls) \
assert(cls->tp_itemsize == itemsize); \
return BoxVar::operator new(size, cls, nitems); \
} \
void* operator new(size_t size, size_t nitems) __attribute__((visibility("default"))) { \
ALLOC_STATS_VAR(default_cls) \
assert(default_cls->tp_alloc == PystonType_GenericAlloc); \
assert(default_cls->tp_itemsize == itemsize); \
assert(default_cls->tp_basicsize == size); \
assert(default_cls->is_pyston_class); \
assert(default_cls->attrs_offset == 0); \
\
void* mem = gc_alloc(size + nitems * itemsize, gc::GCKind::PYTHON); \
assert(mem); \
\
BoxVar* rtn = static_cast<BoxVar*>(mem); \
rtn->cls = default_cls; \
rtn->ob_size = nitems; \
return rtn; \
}
// CPython C API compatibility class: // CPython C API compatibility class:
class BoxVar : public Box { class BoxVar : public Box {
public: public:
// This field gets initialized in operator new.
Py_ssize_t ob_size; Py_ssize_t ob_size;
BoxVar(Py_ssize_t ob_size) : ob_size(ob_size) {} BoxVar() {}
void* operator new(size_t size, BoxedClass* cls, size_t nitems) __attribute__((visibility("default")));
}; };
static_assert(offsetof(BoxVar, ob_size) == offsetof(struct _varobject, ob_size), ""); static_assert(offsetof(BoxVar, ob_size) == offsetof(struct _varobject, ob_size), "");
......
...@@ -461,8 +461,8 @@ void setupSys() { ...@@ -461,8 +461,8 @@ void setupSys() {
sys_module->giveAttr("maxint", boxInt(PYSTON_INT_MAX)); sys_module->giveAttr("maxint", boxInt(PYSTON_INT_MAX));
sys_module->giveAttr("maxsize", boxInt(PY_SSIZE_T_MAX)); sys_module->giveAttr("maxsize", boxInt(PY_SSIZE_T_MAX));
sys_flags_cls = new BoxedHeapClass(object_cls, BoxedSysFlags::gcHandler, 0, 0, sizeof(BoxedSysFlags), false, sys_flags_cls = new (0) BoxedHeapClass(object_cls, BoxedSysFlags::gcHandler, 0, 0, sizeof(BoxedSysFlags), false,
static_cast<BoxedString*>(boxString("flags"))); static_cast<BoxedString*>(boxString("flags")));
sys_flags_cls->giveAttr("__new__", sys_flags_cls->giveAttr("__new__",
new BoxedFunction(boxRTFunction((void*)BoxedSysFlags::__new__, UNKNOWN, 1, 0, true, true))); new BoxedFunction(boxRTFunction((void*)BoxedSysFlags::__new__, UNKNOWN, 1, 0, true, true)));
#define ADD(name) \ #define ADD(name) \
......
This diff is collapsed.
This diff is collapsed.
...@@ -177,7 +177,10 @@ public: ...@@ -177,7 +177,10 @@ public:
void (*simple_destructor)(Box*); void (*simple_destructor)(Box*);
// Offset of the HCAttrs object or 0 if there are no hcattrs. // 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 // 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; const int attrs_offset;
bool instancesHaveHCAttrs() { return attrs_offset != 0; } bool instancesHaveHCAttrs() { return attrs_offset != 0; }
...@@ -231,12 +234,16 @@ public: ...@@ -231,12 +234,16 @@ public:
PyBufferProcs as_buffer; PyBufferProcs as_buffer;
BoxedString* ht_name; BoxedString* ht_name;
PyObject** ht_slots; PyObject* ht_slots;
typedef size_t SlotOffset;
SlotOffset* slotOffsets() { return (BoxedHeapClass::SlotOffset*)((char*)this + this->cls->tp_basicsize); }
size_t nslots() { return this->ob_size; }
// These functions are the preferred way to construct new types: // These functions are the preferred way to construct new types:
static BoxedHeapClass* create(BoxedClass* metatype, BoxedClass* base, gcvisit_func gc_visit, int attrs_offset, 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, 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, static BoxedHeapClass* create(BoxedClass* metatype, BoxedClass* base, gcvisit_func gc_visit, int attrs_offset,
int weaklist_offset, int instance_size, bool is_user_defined, int weaklist_offset, int instance_size, bool is_user_defined,
const std::string& name); const std::string& name);
...@@ -251,7 +258,7 @@ private: ...@@ -251,7 +258,7 @@ private:
friend void setupRuntime(); friend void setupRuntime();
friend void setupSys(); friend void setupSys();
DEFAULT_CLASS(type_cls); DEFAULT_CLASS_VAR(type_cls, sizeof(SlotOffset));
}; };
static_assert(sizeof(pyston::Box) == sizeof(struct _object), ""); static_assert(sizeof(pyston::Box) == sizeof(struct _object), "");
...@@ -417,7 +424,7 @@ public: ...@@ -417,7 +424,7 @@ public:
DEFAULT_CLASS_SIMPLE(bool_cls); DEFAULT_CLASS_SIMPLE(bool_cls);
}; };
class BoxedString : public Box { class BoxedString : public BoxVar {
public: public:
llvm::StringRef s; llvm::StringRef s;
char interned_state; char interned_state;
...@@ -425,40 +432,28 @@ public: ...@@ -425,40 +432,28 @@ public:
char* data() { return const_cast<char*>(s.data()); } char* data() { return const_cast<char*>(s.data()); }
size_t size() { return s.size(); } size_t size() { return s.size(); }
void* operator new(size_t size, size_t ssize) __attribute__((visibility("default"))) { // DEFAULT_CLASS_VAR_SIMPLE doesn't work because of the +1 for the null byte
#if STAT_ALLOCATIONS void* operator new(size_t size, BoxedClass* cls, size_t nitems) __attribute__((visibility("default"))) {
static StatCounter alloc_str("alloc.str"); ALLOC_STATS_VAR(str_cls)
static StatCounter alloc_str1("alloc.str(1)");
static StatCounter allocsize_str("allocsize.str");
if (ssize == 1)
alloc_str1.log();
else
alloc_str.log();
allocsize_str.log(str_cls->tp_basicsize + ssize + 1); assert(cls->tp_itemsize == sizeof(char));
#endif return BoxVar::operator new(size, cls, nitems);
Box* rtn = static_cast<Box*>(gc_alloc(str_cls->tp_basicsize + ssize + 1, gc::GCKind::PYTHON));
rtn->cls = str_cls;
return rtn;
} }
void* operator new(size_t size, size_t nitems) __attribute__((visibility("default"))) {
ALLOC_STATS_VAR(str_cls)
void* operator new(size_t size, BoxedClass* cls, size_t ssize) __attribute__((visibility("default"))) { assert(str_cls->tp_alloc == PystonType_GenericAlloc);
#if STAT_ALLOCATIONS assert(str_cls->tp_itemsize == 1);
static StatCounter alloc_str("alloc.str"); assert(str_cls->tp_basicsize == sizeof(BoxedString) + 1);
static StatCounter alloc_str1("alloc.str(1)"); assert(str_cls->is_pyston_class);
assert(str_cls->attrs_offset == 0);
static StatCounter allocsize_str("allocsize.str");
if (ssize == 1) void* mem = gc_alloc(sizeof(BoxedString) + 1 + nitems, gc::GCKind::PYTHON);
alloc_str1.log(); assert(mem);
else
alloc_str.log();
allocsize_str.log(cls->tp_basicsize + ssize + 1); BoxVar* rtn = static_cast<BoxVar*>(mem);
#endif rtn->cls = str_cls;
Box* rtn = static_cast<Box*>(cls->tp_alloc(cls, ssize + 1)); rtn->ob_size = nitems;
rtn->cls = cls;
return rtn; return rtn;
} }
...@@ -470,7 +465,8 @@ public: ...@@ -470,7 +465,8 @@ public:
private: private:
// used only in ctors to give our llvm::StringRef the proper pointer // used only in ctors to give our llvm::StringRef the proper pointer
char* storage() { return (char*)this + cls->tp_basicsize; } // Note: sizeof(BoxedString) = str_cls->tp_basicsize - 1
char* storage() { return (char*)this + sizeof(BoxedString); }
void* operator new(size_t size) = delete; void* operator new(size_t size) = delete;
}; };
...@@ -551,54 +547,13 @@ public: ...@@ -551,54 +547,13 @@ public:
DEFAULT_CLASS_SIMPLE(list_cls); DEFAULT_CLASS_SIMPLE(list_cls);
}; };
class BoxedTuple : public Box { class BoxedTuple : public BoxVar {
public: public:
typedef std::vector<Box*, StlCompatAllocator<Box*>> GCVector; typedef std::vector<Box*, StlCompatAllocator<Box*>> GCVector;
Box** elts; Box** elts;
void* operator new(size_t size, size_t nelts) __attribute__((visibility("default"))) { DEFAULT_CLASS_VAR_SIMPLE(tuple_cls, sizeof(Box*));
#if STAT_ALLOCATIONS
static StatCounter alloc_tuple("alloc.tuple");
static StatCounter alloc_tuple0("alloc.tuple(0)");
static StatCounter allocsize_tuple("allocsize.tuple");
static StatCounter allocsize_tuple0("allocsize.tuple(0)");
if (nelts == 0) {
alloc_tuple0.log();
allocsize_tuple0.log(_PyObject_VAR_SIZE(tuple_cls, nelts + 1));
} else {
alloc_tuple.log();
allocsize_tuple.log(_PyObject_VAR_SIZE(tuple_cls, nelts + 1));
}
#endif
Box* rtn = static_cast<Box*>(gc_alloc(_PyObject_VAR_SIZE(tuple_cls, nelts + 1), gc::GCKind::PYTHON));
rtn->cls = tuple_cls;
return rtn;
}
void* operator new(size_t size, BoxedClass* cls, size_t nelts) __attribute__((visibility("default"))) {
#if STAT_ALLOCATIONS
static StatCounter alloc_tuple("alloc.tuple");
static StatCounter alloc_tuple0("alloc.tuple(0)");
static StatCounter allocsize_tuple("allocsize.tuple");
static StatCounter allocsize_tuple0("allocsize.tuple(0)");
if (nelts == 0) {
alloc_tuple0.log();
allocsize_tuple0.log(_PyObject_VAR_SIZE(cls, nelts + 1));
} else {
alloc_tuple.log();
allocsize_tuple.log(_PyObject_VAR_SIZE(cls, nelts + 1));
}
#endif
Box* rtn = static_cast<Box*>(cls->tp_alloc(cls, nelts));
rtn->cls = cls;
return rtn;
}
static BoxedTuple* create(int64_t size) { return new (size) BoxedTuple(size); } static BoxedTuple* create(int64_t size) { return new (size) BoxedTuple(size); }
static BoxedTuple* create(int64_t nelts, Box** elts) { static BoxedTuple* create(int64_t nelts, Box** elts) {
...@@ -640,18 +595,19 @@ public: ...@@ -640,18 +595,19 @@ public:
Box** begin() const { return &elts[0]; } Box** begin() const { return &elts[0]; }
Box** end() const { return &elts[nelts]; } Box** end() const { return &elts[nelts]; }
Box*& operator[](size_t index) { return elts[index]; }
size_t size() const { return nelts; } size_t size() const { return nelts; }
private: private:
size_t nelts; 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); memset(elts, 0, sizeof(Box*) * size);
} }
BoxedTuple(std::initializer_list<Box*>& members) 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 // by the time we make it here elts[] is big enough to contain members
Box** p = &elts[0]; Box** p = &elts[0];
for (auto b : members) { 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
print 'testing a lot of slots'
class C(object):
__slots__ = ['a' + str(i) for i in xrange(1000)]
c = C()
c.a0 = -8
print c.a0
for i in xrange(1000):
setattr(c, 'a' + str(i), i)
for i in xrange(1000):
print getattr(c, 'a' + str(i))
print 'slots on a subclass of str'
try:
class C(str):
__slots__ = ['a']
except TypeError as e:
print e.message
print 'slots on a class with a metaclass'
class M(type):
def __new__(*args):
print "M.__new__", args[:3]
return type.__new__(*args)
def __call__(*args):
print "M.__call__", args[:3]
return type.__call__(*args)
class C(object):
__metaclass__ = M
__slots__ = ['a', 'b']
c = C()
c.a = 5
print c.a
c.b = 6
print c.b
...@@ -8,3 +8,8 @@ def f(): ...@@ -8,3 +8,8 @@ def f():
for i in xrange(100): for i in xrange(100):
s = S(base) s = S(base)
f() 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