Commit a011d128 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Save+reuse attrwrappers we generate for objects

With the goal of making `assert obj.__dict__ is obj.__dict__` work.
I haven't seen people do that, but people (gflags, doctest? others?)
definitely need `assert globals() is globals()` to pass, which uses
the same mechanism.
parent 4e6bd9db
...@@ -425,7 +425,7 @@ extern "C" PyObject* PyModule_GetDict(PyObject* _m) noexcept { ...@@ -425,7 +425,7 @@ extern "C" PyObject* PyModule_GetDict(PyObject* _m) noexcept {
BoxedModule* m = static_cast<BoxedModule*>(_m); BoxedModule* m = static_cast<BoxedModule*>(_m);
assert(m->cls == module_cls); assert(m->cls == module_cls);
return makeAttrWrapper(m); return m->getAttrWrapper();
} }
extern "C" int PyModule_AddObject(PyObject* _m, const char* name, PyObject* value) noexcept { extern "C" int PyModule_AddObject(PyObject* _m, const char* name, PyObject* value) noexcept {
......
...@@ -2706,7 +2706,7 @@ extern "C" int PyType_Ready(PyTypeObject* cls) noexcept { ...@@ -2706,7 +2706,7 @@ extern "C" int PyType_Ready(PyTypeObject* cls) noexcept {
cls->giveAttr("__base__", base); cls->giveAttr("__base__", base);
assert(cls->tp_dict == NULL); assert(cls->tp_dict == NULL);
cls->tp_dict = makeAttrWrapper(cls); cls->tp_dict = cls->getAttrWrapper();
assert(cls->tp_name); assert(cls->tp_name);
// tp_name // tp_name
......
...@@ -516,7 +516,7 @@ Box* exec(Box* boxedCode, Box* globals, Box* locals) { ...@@ -516,7 +516,7 @@ Box* exec(Box* boxedCode, Box* globals, Box* locals) {
// From CPython (they set it to be f->f_builtins): // From CPython (they set it to be f->f_builtins):
Box* globals_dict = globals; Box* globals_dict = globals;
if (globals->cls == module_cls) if (globals->cls == module_cls)
globals_dict = makeAttrWrapper(globals); globals_dict = globals->getAttrWrapper();
if (PyDict_GetItemString(globals_dict, "__builtins__") == NULL) if (PyDict_GetItemString(globals_dict, "__builtins__") == NULL)
PyDict_SetItemString(globals_dict, "__builtins__", builtins_module); PyDict_SetItemString(globals_dict, "__builtins__", builtins_module);
} }
......
...@@ -596,7 +596,7 @@ Box* getGlobalsDict() { ...@@ -596,7 +596,7 @@ Box* getGlobalsDict() {
return NULL; return NULL;
if (isSubclass(globals->cls, module_cls)) if (isSubclass(globals->cls, module_cls))
return makeAttrWrapper(globals); return globals->getAttrWrapper();
return globals; return globals;
} }
...@@ -735,7 +735,7 @@ Box* PythonFrameIterator::fastLocalsToBoxedLocals() { ...@@ -735,7 +735,7 @@ Box* PythonFrameIterator::fastLocalsToBoxedLocals() {
// TODO we should cache this in frame_info->locals or something so that locals() // TODO we should cache this in frame_info->locals or something so that locals()
// (and globals() too) will always return the same dict // (and globals() too) will always return the same dict
RELEASE_ASSERT(cf->clfunc->source->scoping->areGlobalsFromModule(), ""); RELEASE_ASSERT(cf->clfunc->source->scoping->areGlobalsFromModule(), "");
return makeAttrWrapper(cf->clfunc->source->parent_module); return cf->clfunc->source->parent_module->getAttrWrapper();
} }
if (impl->getId().type == PythonFrameId::COMPILED) { if (impl->getId().type == PythonFrameId::COMPILED) {
......
...@@ -430,6 +430,12 @@ class Box { ...@@ -430,6 +430,12 @@ class Box {
private: private:
BoxedDict** getDictPtr(); BoxedDict** getDictPtr();
// Adds a new attribute to a HCAttrs-backed object. Must pass in the new hidden class object
// which must be the same as the current hidden class but with the new attribute at the end.
// Swaps the hidden class, reallocates and copies and updates the attribute array.
// The value of the current hidden class should be guarded before calling this.
void addNewHCAttr(HiddenClass* new_hcls, Box* val, SetattrRewriteArgs* rewrite_args);
public: public:
// Add a no-op constructor to make sure that we don't zero-initialize cls // Add a no-op constructor to make sure that we don't zero-initialize cls
Box() {} Box() {}
...@@ -459,6 +465,9 @@ public: ...@@ -459,6 +465,9 @@ public:
bool hasattr(const std::string& attr) { return getattr(attr) != NULL; } bool hasattr(const std::string& attr) { return getattr(attr) != NULL; }
void delattr(const std::string& attr, DelattrRewriteArgs* rewrite_args); void delattr(const std::string& attr, DelattrRewriteArgs* rewrite_args);
// Only valid for hc-backed instances:
Box* getAttrWrapper();
Box* reprIC(); Box* reprIC();
BoxedString* reprICAsString(); BoxedString* reprICAsString();
bool nonzeroIC(); bool nonzeroIC();
......
...@@ -81,7 +81,7 @@ extern "C" Box* vars(Box* obj) { ...@@ -81,7 +81,7 @@ extern "C" Box* vars(Box* obj) {
if (!obj) if (!obj)
return fastLocalsToBoxedLocals(); return fastLocalsToBoxedLocals();
return makeAttrWrapper(obj); return obj->getAttrWrapper();
} }
extern "C" Box* abs_(Box* x) { extern "C" Box* abs_(Box* x) {
...@@ -587,7 +587,7 @@ public: ...@@ -587,7 +587,7 @@ public:
RELEASE_ASSERT(isSubclass(self->cls, BaseException), ""); RELEASE_ASSERT(isSubclass(self->cls, BaseException), "");
BoxedException* exc = static_cast<BoxedException*>(self); BoxedException* exc = static_cast<BoxedException*>(self);
return BoxedTuple::create({ self->cls, EmptyTuple, makeAttrWrapper(self) }); return BoxedTuple::create({ self->cls, EmptyTuple, self->getAttrWrapper() });
} }
}; };
......
...@@ -150,7 +150,7 @@ static Box* classobjGetattribute(Box* _cls, Box* _attr) { ...@@ -150,7 +150,7 @@ static Box* classobjGetattribute(Box* _cls, Box* _attr) {
// These are special cases in CPython as well: // These are special cases in CPython as well:
if (attr->s[0] == '_' && attr->s[1] == '_') { if (attr->s[0] == '_' && attr->s[1] == '_') {
if (attr->s == "__dict__") if (attr->s == "__dict__")
return makeAttrWrapper(cls); return cls->getAttrWrapper();
if (attr->s == "__bases__") if (attr->s == "__bases__")
return cls->bases; return cls->bases;
...@@ -255,7 +255,7 @@ static Box* _instanceGetattribute(Box* _inst, Box* _attr, bool raise_on_missing) ...@@ -255,7 +255,7 @@ static Box* _instanceGetattribute(Box* _inst, Box* _attr, bool raise_on_missing)
// These are special cases in CPython as well: // These are special cases in CPython as well:
if (attr->s[0] == '_' && attr->s[1] == '_') { if (attr->s[0] == '_' && attr->s[1] == '_') {
if (attr->s == "__dict__") if (attr->s == "__dict__")
return makeAttrWrapper(inst); return inst->getAttrWrapper();
if (attr->s == "__class__") if (attr->s == "__class__")
return inst->inst_cls; return inst->inst_cls;
......
...@@ -125,7 +125,7 @@ Box* getFrame(int depth) { ...@@ -125,7 +125,7 @@ Box* getFrame(int depth) {
auto cf = it.getCF(); auto cf = it.getCF();
BoxedFrame* f = fi->frame_obj = new BoxedFrame(std::move(it)); BoxedFrame* f = fi->frame_obj = new BoxedFrame(std::move(it));
assert(cf->clfunc->source->scoping->areGlobalsFromModule()); assert(cf->clfunc->source->scoping->areGlobalsFromModule());
f->_globals = makeAttrWrapper(cf->clfunc->source->parent_module); f->_globals = cf->clfunc->source->parent_module->getAttrWrapper();
f->_code = codeForCLFunction(cf->clfunc); f->_code = codeForCLFunction(cf->clfunc);
} }
......
...@@ -400,7 +400,7 @@ void BoxedClass::finishInitialization() { ...@@ -400,7 +400,7 @@ void BoxedClass::finishInitialization() {
} }
assert(!this->tp_dict); assert(!this->tp_dict);
this->tp_dict = makeAttrWrapper(this); this->tp_dict = this->getAttrWrapper();
commonClassSetup(this); commonClassSetup(this);
} }
...@@ -490,19 +490,31 @@ HiddenClass* HiddenClass::getOrMakeChild(const std::string& attr) { ...@@ -490,19 +490,31 @@ HiddenClass* HiddenClass::getOrMakeChild(const std::string& attr) {
HiddenClass* rtn = new HiddenClass(this); HiddenClass* rtn = new HiddenClass(this);
this->children[attr] = rtn; this->children[attr] = rtn;
rtn->attr_offsets[attr] = attr_offsets.size(); rtn->attr_offsets[attr] = this->attributeArraySize();
assert(rtn->attributeArraySize() == this->attributeArraySize() + 1);
return rtn; return rtn;
} }
HiddenClass* HiddenClass::getAttrwrapperChild() {
assert(type == NORMAL);
if (!attrwrapper_child) {
attrwrapper_child = new HiddenClass(this);
attrwrapper_child->attrwrapper_offset = this->attributeArraySize();
assert(attrwrapper_child->attributeArraySize() == this->attributeArraySize() + 1);
}
return attrwrapper_child;
}
/** /**
* del attr from current HiddenClass, pertain the orders of remaining attrs * del attr from current HiddenClass, maintaining the order of the remaining attrs
*/ */
HiddenClass* HiddenClass::delAttrToMakeHC(const std::string& attr) { HiddenClass* HiddenClass::delAttrToMakeHC(const std::string& attr) {
assert(type == NORMAL); assert(type == NORMAL);
int idx = getOffset(attr); int idx = getOffset(attr);
assert(idx >= 0); assert(idx >= 0);
std::vector<std::string> new_attrs(attr_offsets.size() - 1); std::vector<std::string> new_attrs(attributeArraySize() - 1);
for (auto it = attr_offsets.begin(); it != attr_offsets.end(); ++it) { for (auto it = attr_offsets.begin(); it != attr_offsets.end(); ++it) {
if (it->second < idx) if (it->second < idx)
new_attrs[it->second] = it->first(); new_attrs[it->second] = it->first();
...@@ -511,11 +523,20 @@ HiddenClass* HiddenClass::delAttrToMakeHC(const std::string& attr) { ...@@ -511,11 +523,20 @@ HiddenClass* HiddenClass::delAttrToMakeHC(const std::string& attr) {
} }
} }
int new_attrwrapper_offset = attrwrapper_offset;
if (new_attrwrapper_offset > idx)
new_attrwrapper_offset--;
// TODO we can first locate the parent HiddenClass of the deleted // TODO we can first locate the parent HiddenClass of the deleted
// attribute and hence avoid creation of its ancestors. // attribute and hence avoid creation of its ancestors.
HiddenClass* cur = root_hcls; HiddenClass* cur = root_hcls;
int curidx = 0;
for (const auto& attr : new_attrs) { for (const auto& attr : new_attrs) {
cur = cur->getOrMakeChild(attr); if (curidx == new_attrwrapper_offset)
cur = cur->getAttrwrapperChild();
else
cur = cur->getOrMakeChild(attr);
curidx++;
} }
return cur; return cur;
} }
...@@ -648,6 +669,58 @@ Box* Box::getattr(const std::string& attr, GetattrRewriteArgs* rewrite_args) { ...@@ -648,6 +669,58 @@ Box* Box::getattr(const std::string& attr, GetattrRewriteArgs* rewrite_args) {
return NULL; return NULL;
} }
void Box::addNewHCAttr(HiddenClass* new_hcls, Box* new_attr, SetattrRewriteArgs* rewrite_args) {
assert(cls->instancesHaveHCAttrs());
HCAttrs* attrs = getHCAttrsPtr();
HiddenClass* hcls = attrs->hcls;
#ifndef NDEBUG
// make sure we don't need to rearrange the attributes
assert(new_hcls->attributeArraySize() == hcls->attributeArraySize() + 1);
for (const auto& p : hcls->getStrAttrOffsets()) {
assert(new_hcls->getStrAttrOffsets().lookup(p.first()) == p.second);
}
if (hcls->getAttrwrapperOffset() != -1)
assert(hcls->getAttrwrapperOffset() == new_hcls->getAttrwrapperOffset());
#endif
int numattrs = hcls->attributeArraySize();
RewriterVar* r_new_array2 = NULL;
int new_size = sizeof(HCAttrs::AttrList) + sizeof(Box*) * (numattrs + 1);
if (numattrs == 0) {
attrs->attr_list = (HCAttrs::AttrList*)gc_alloc(new_size, gc::GCKind::PRECISE);
if (rewrite_args) {
RewriterVar* r_newsize = rewrite_args->rewriter->loadConst(new_size, Location::forArg(0));
RewriterVar* r_kind = rewrite_args->rewriter->loadConst((int)gc::GCKind::PRECISE, Location::forArg(1));
r_new_array2 = rewrite_args->rewriter->call(true, (void*)gc::gc_alloc, r_newsize, r_kind);
}
} else {
attrs->attr_list = (HCAttrs::AttrList*)gc::gc_realloc(attrs->attr_list, new_size);
if (rewrite_args) {
RewriterVar* r_oldarray
= rewrite_args->obj->getAttr(cls->attrs_offset + HCATTRS_ATTRS_OFFSET, Location::forArg(0));
RewriterVar* r_newsize = rewrite_args->rewriter->loadConst(new_size, Location::forArg(1));
r_new_array2 = rewrite_args->rewriter->call(true, (void*)gc::gc_realloc, r_oldarray, r_newsize);
}
}
// Don't set the new hcls until after we do the allocation for the new attr_list;
// that allocation can cause a collection, and we want the collector to always
// see a consistent state between the hcls and the attr_list
attrs->hcls = new_hcls;
if (rewrite_args) {
r_new_array2->setAttr(numattrs * sizeof(Box*) + ATTRLIST_ATTRS_OFFSET, rewrite_args->attrval);
rewrite_args->obj->setAttr(cls->attrs_offset + HCATTRS_ATTRS_OFFSET, r_new_array2);
RewriterVar* r_hcls = rewrite_args->rewriter->loadConst((intptr_t)new_hcls);
rewrite_args->obj->setAttr(cls->attrs_offset + HCATTRS_HCLS_OFFSET, r_hcls);
rewrite_args->out_success = true;
}
attrs->attr_list->attrs[numattrs] = new_attr;
}
void Box::setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite_args) { void Box::setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite_args) {
assert(gc::isValidGCObject(val)); assert(gc::isValidGCObject(val));
...@@ -681,7 +754,6 @@ void Box::setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite ...@@ -681,7 +754,6 @@ void Box::setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite
} }
assert(hcls->type == HiddenClass::NORMAL); assert(hcls->type == HiddenClass::NORMAL);
int numattrs = hcls->getAttrOffsets().size();
int offset = hcls->getOffset(attr); int offset = hcls->getOffset(attr);
...@@ -691,7 +763,7 @@ void Box::setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite ...@@ -691,7 +763,7 @@ void Box::setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite
} }
if (offset >= 0) { if (offset >= 0) {
assert(offset < numattrs); assert(offset < hcls->attributeArraySize());
Box* prev = attrs->attr_list->attrs[offset]; Box* prev = attrs->attr_list->attrs[offset];
attrs->attr_list->attrs[offset] = val; attrs->attr_list->attrs[offset] = val;
...@@ -711,47 +783,11 @@ void Box::setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite ...@@ -711,47 +783,11 @@ void Box::setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite
assert(offset == -1); assert(offset == -1);
HiddenClass* new_hcls = hcls->getOrMakeChild(attr); HiddenClass* new_hcls = hcls->getOrMakeChild(attr);
// TODO need to make sure we don't need to rearrange the attributes // make sure we don't need to rearrange the attributes
assert(new_hcls->getAttrOffsets().lookup(attr) == numattrs); assert(new_hcls->getStrAttrOffsets().lookup(attr) == hcls->attributeArraySize());
#ifndef NDEBUG
for (const auto& p : hcls->getAttrOffsets()) {
assert(new_hcls->getAttrOffsets().lookup(p.first()) == p.second);
}
#endif
RewriterVar* r_new_array2 = NULL;
int new_size = sizeof(HCAttrs::AttrList) + sizeof(Box*) * (numattrs + 1);
if (numattrs == 0) {
attrs->attr_list = (HCAttrs::AttrList*)gc_alloc(new_size, gc::GCKind::PRECISE);
if (rewrite_args) {
RewriterVar* r_newsize = rewrite_args->rewriter->loadConst(new_size, Location::forArg(0));
RewriterVar* r_kind = rewrite_args->rewriter->loadConst((int)gc::GCKind::PRECISE, Location::forArg(1));
r_new_array2 = rewrite_args->rewriter->call(true, (void*)gc::gc_alloc, r_newsize, r_kind);
}
} else {
attrs->attr_list = (HCAttrs::AttrList*)gc::gc_realloc(attrs->attr_list, new_size);
if (rewrite_args) {
RewriterVar* r_oldarray
= rewrite_args->obj->getAttr(cls->attrs_offset + HCATTRS_ATTRS_OFFSET, Location::forArg(0));
RewriterVar* r_newsize = rewrite_args->rewriter->loadConst(new_size, Location::forArg(1));
r_new_array2 = rewrite_args->rewriter->call(true, (void*)gc::gc_realloc, r_oldarray, r_newsize);
}
}
// Don't set the new hcls until after we do the allocation for the new attr_list;
// that allocation can cause a collection, and we want the collector to always
// see a consistent state between the hcls and the attr_list
attrs->hcls = new_hcls;
if (rewrite_args) {
r_new_array2->setAttr(numattrs * sizeof(Box*) + ATTRLIST_ATTRS_OFFSET, rewrite_args->attrval);
rewrite_args->obj->setAttr(cls->attrs_offset + HCATTRS_ATTRS_OFFSET, r_new_array2);
RewriterVar* r_hcls = rewrite_args->rewriter->loadConst((intptr_t)new_hcls); addNewHCAttr(new_hcls, val, rewrite_args);
rewrite_args->obj->setAttr(cls->attrs_offset + HCATTRS_HCLS_OFFSET, r_hcls);
rewrite_args->out_success = true;
}
attrs->attr_list->attrs[numattrs] = val;
return; return;
} }
...@@ -3727,7 +3763,7 @@ void Box::delattr(const std::string& attr, DelattrRewriteArgs* rewrite_args) { ...@@ -3727,7 +3763,7 @@ void Box::delattr(const std::string& attr, DelattrRewriteArgs* rewrite_args) {
// The order of attributes is pertained as delAttrToMakeHC constructs // The order of attributes is pertained as delAttrToMakeHC constructs
// the new HiddenClass by invoking getOrMakeChild in the prevous order // the new HiddenClass by invoking getOrMakeChild in the prevous order
// of remaining attributes // of remaining attributes
int num_attrs = hcls->getAttrOffsets().size(); int num_attrs = hcls->attributeArraySize();
int offset = hcls->getOffset(attr); int offset = hcls->getOffset(attr);
assert(offset >= 0); assert(offset >= 0);
Box** start = attrs->attr_list->attrs; Box** start = attrs->attr_list->attrs;
...@@ -4499,7 +4535,7 @@ extern "C" Box* importStar(Box* _from_module, BoxedModule* to_module) { ...@@ -4499,7 +4535,7 @@ extern "C" Box* importStar(Box* _from_module, BoxedModule* to_module) {
} }
HCAttrs* module_attrs = from_module->getHCAttrsPtr(); HCAttrs* module_attrs = from_module->getHCAttrsPtr();
for (auto& p : module_attrs->hcls->getAttrOffsets()) { for (auto& p : module_attrs->hcls->getStrAttrOffsets()) {
if (p.first()[0] == '_') if (p.first()[0] == '_')
continue; continue;
......
...@@ -157,10 +157,11 @@ void _printStacktrace() { ...@@ -157,10 +157,11 @@ void _printStacktrace() {
extern "C" void abort() { extern "C" void abort() {
static void (*libc_abort)() = (void (*)())dlsym(RTLD_NEXT, "abort"); static void (*libc_abort)() = (void (*)())dlsym(RTLD_NEXT, "abort");
// In case something calls abort down the line: // In case displaying the traceback recursively calls abort:
static bool recursive = false; static bool recursive = false;
// If object_cls is NULL, then we somehow died early on, and won't be able to display a traceback.
if (!recursive && object_cls) { // If traceback_cls is NULL, then we somehow died early on, and won't be able to display a traceback.
if (!recursive && traceback_cls) {
recursive = true; recursive = true;
fprintf(stderr, "Someone called abort!\n"); fprintf(stderr, "Someone called abort!\n");
...@@ -178,7 +179,7 @@ extern "C" void abort() { ...@@ -178,7 +179,7 @@ extern "C" void abort() {
} }
// Cancel the alarm. // Cancel the alarm.
// This is helpful for when running in a debugger, since the debugger will catch the // This is helpful for when running in a debugger, since otherwise the debugger will catch the
// abort and let you investigate, but the alarm will still come back to kill the program. // abort and let you investigate, but the alarm will still come back to kill the program.
alarm(0); alarm(0);
} }
......
...@@ -477,7 +477,7 @@ extern "C" void typeGCHandler(GCVisitor* v, Box* b) { ...@@ -477,7 +477,7 @@ extern "C" void typeGCHandler(GCVisitor* v, Box* b) {
static Box* typeDict(Box* obj, void* context) { static Box* typeDict(Box* obj, void* context) {
if (obj->cls->instancesHaveHCAttrs()) if (obj->cls->instancesHaveHCAttrs())
return makeAttrWrapper(obj); return obj->getAttrWrapper();
if (obj->cls->instancesHaveDictAttrs()) if (obj->cls->instancesHaveDictAttrs())
return obj->getDict(); return obj->getDict();
abort(); abort();
...@@ -1311,7 +1311,7 @@ public: ...@@ -1311,7 +1311,7 @@ public:
HCAttrs* attrs = self->b->getHCAttrsPtr(); HCAttrs* attrs = self->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, ""); RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, "");
bool first = true; bool first = true;
for (const auto& p : attrs->hcls->getAttrOffsets()) { for (const auto& p : attrs->hcls->getStrAttrOffsets()) {
if (!first) if (!first)
os << ", "; os << ", ";
first = false; first = false;
...@@ -1343,7 +1343,7 @@ public: ...@@ -1343,7 +1343,7 @@ public:
HCAttrs* attrs = self->b->getHCAttrsPtr(); HCAttrs* attrs = self->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, ""); RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, "");
for (const auto& p : attrs->hcls->getAttrOffsets()) { for (const auto& p : attrs->hcls->getStrAttrOffsets()) {
listAppend(rtn, boxString(p.first())); listAppend(rtn, boxString(p.first()));
} }
return rtn; return rtn;
...@@ -1357,7 +1357,7 @@ public: ...@@ -1357,7 +1357,7 @@ public:
HCAttrs* attrs = self->b->getHCAttrsPtr(); HCAttrs* attrs = self->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, ""); RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, "");
for (const auto& p : attrs->hcls->getAttrOffsets()) { for (const auto& p : attrs->hcls->getStrAttrOffsets()) {
listAppend(rtn, attrs->attr_list->attrs[p.second]); listAppend(rtn, attrs->attr_list->attrs[p.second]);
} }
return rtn; return rtn;
...@@ -1371,7 +1371,7 @@ public: ...@@ -1371,7 +1371,7 @@ public:
HCAttrs* attrs = self->b->getHCAttrsPtr(); HCAttrs* attrs = self->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, ""); RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, "");
for (const auto& p : attrs->hcls->getAttrOffsets()) { for (const auto& p : attrs->hcls->getStrAttrOffsets()) {
BoxedTuple* t = BoxedTuple::create({ boxString(p.first()), attrs->attr_list->attrs[p.second] }); BoxedTuple* t = BoxedTuple::create({ boxString(p.first()), attrs->attr_list->attrs[p.second] });
listAppend(rtn, t); listAppend(rtn, t);
} }
...@@ -1386,7 +1386,7 @@ public: ...@@ -1386,7 +1386,7 @@ public:
HCAttrs* attrs = self->b->getHCAttrsPtr(); HCAttrs* attrs = self->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, ""); RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, "");
for (const auto& p : attrs->hcls->getAttrOffsets()) { for (const auto& p : attrs->hcls->getStrAttrOffsets()) {
rtn->d[boxString(p.first())] = attrs->attr_list->attrs[p.second]; rtn->d[boxString(p.first())] = attrs->attr_list->attrs[p.second];
} }
return rtn; return rtn;
...@@ -1398,7 +1398,7 @@ public: ...@@ -1398,7 +1398,7 @@ public:
HCAttrs* attrs = self->b->getHCAttrsPtr(); HCAttrs* attrs = self->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, ""); RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, "");
return boxInt(attrs->hcls->getAttrOffsets().size()); return boxInt(attrs->hcls->getStrAttrOffsets().size());
} }
static Box* update(Box* _self, Box* _container) { static Box* update(Box* _self, Box* _container) {
...@@ -1410,7 +1410,7 @@ public: ...@@ -1410,7 +1410,7 @@ public:
HCAttrs* attrs = container->b->getHCAttrsPtr(); HCAttrs* attrs = container->b->getHCAttrsPtr();
RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, ""); RELEASE_ASSERT(attrs->hcls->type == HiddenClass::NORMAL, "");
for (const auto& p : attrs->hcls->getAttrOffsets()) { for (const auto& p : attrs->hcls->getStrAttrOffsets()) {
self->b->setattr(p.first(), attrs->attr_list->attrs[p.second], NULL); self->b->setattr(p.first(), attrs->attr_list->attrs[p.second], NULL);
} }
} else if (_container->cls == dict_cls) { } else if (_container->cls == dict_cls) {
...@@ -1439,7 +1439,7 @@ AttrWrapperIter::AttrWrapperIter(AttrWrapper* aw) { ...@@ -1439,7 +1439,7 @@ AttrWrapperIter::AttrWrapperIter(AttrWrapper* aw) {
hcls = aw->b->getHCAttrsPtr()->hcls; hcls = aw->b->getHCAttrsPtr()->hcls;
assert(hcls); assert(hcls);
RELEASE_ASSERT(hcls->type == HiddenClass::NORMAL, ""); RELEASE_ASSERT(hcls->type == HiddenClass::NORMAL, "");
it = hcls->getAttrOffsets().begin(); it = hcls->getStrAttrOffsets().begin();
} }
Box* AttrWrapperIter::hasnext(Box* _self) { Box* AttrWrapperIter::hasnext(Box* _self) {
...@@ -1447,7 +1447,7 @@ Box* AttrWrapperIter::hasnext(Box* _self) { ...@@ -1447,7 +1447,7 @@ Box* AttrWrapperIter::hasnext(Box* _self) {
AttrWrapperIter* self = static_cast<AttrWrapperIter*>(_self); AttrWrapperIter* self = static_cast<AttrWrapperIter*>(_self);
RELEASE_ASSERT(self->hcls->type == HiddenClass::NORMAL, ""); RELEASE_ASSERT(self->hcls->type == HiddenClass::NORMAL, "");
return boxBool(self->it != self->hcls->getAttrOffsets().end()); return boxBool(self->it != self->hcls->getStrAttrOffsets().end());
} }
Box* AttrWrapperIter::next(Box* _self) { Box* AttrWrapperIter::next(Box* _self) {
...@@ -1455,19 +1455,28 @@ Box* AttrWrapperIter::next(Box* _self) { ...@@ -1455,19 +1455,28 @@ Box* AttrWrapperIter::next(Box* _self) {
AttrWrapperIter* self = static_cast<AttrWrapperIter*>(_self); AttrWrapperIter* self = static_cast<AttrWrapperIter*>(_self);
RELEASE_ASSERT(self->hcls->type == HiddenClass::NORMAL, ""); RELEASE_ASSERT(self->hcls->type == HiddenClass::NORMAL, "");
assert(self->it != self->hcls->getAttrOffsets().end()); assert(self->it != self->hcls->getStrAttrOffsets().end());
Box* r = boxString(self->it->first()); Box* r = boxString(self->it->first());
++self->it; ++self->it;
return r; return r;
} }
Box* makeAttrWrapper(Box* b) { Box* Box::getAttrWrapper() {
assert(b->cls->instancesHaveHCAttrs()); assert(cls->instancesHaveHCAttrs());
if (b->getHCAttrsPtr()->hcls->type == HiddenClass::DICT_BACKED) { HCAttrs* attrs = getHCAttrsPtr();
return b->getHCAttrsPtr()->attr_list->attrs[0]; HiddenClass* hcls = attrs->hcls;
if (hcls->type == HiddenClass::DICT_BACKED) {
return attrs->attr_list->attrs[0];
} }
return new AttrWrapper(b); int offset = hcls->getAttrwrapperOffset();
if (offset == -1) {
Box* aw = new AttrWrapper(this);
addNewHCAttr(hcls->getAttrwrapperChild(), aw, NULL);
return aw;
}
return attrs->attr_list->attrs[offset];
} }
Box* unwrapAttrWrapper(Box* b) { Box* unwrapAttrWrapper(Box* b) {
......
...@@ -283,16 +283,19 @@ public: ...@@ -283,16 +283,19 @@ public:
private: private:
HiddenClass(HCType type) : type(type) {} HiddenClass(HCType type) : type(type) {}
HiddenClass(HiddenClass* parent) : type(NORMAL), attr_offsets() { HiddenClass(HiddenClass* parent) : type(NORMAL), attr_offsets(), attrwrapper_offset(parent->attrwrapper_offset) {
assert(parent->type == NORMAL); assert(parent->type == NORMAL);
for (auto& p : parent->attr_offsets) { for (auto& p : parent->attr_offsets) {
this->attr_offsets.insert(&p); this->attr_offsets.insert(&p);
} }
} }
// Only makes sense for NORMAL hidden classes. Clients should access through getAttrOffsets(): // These fields only make sense for NORMAL hidden classes:
llvm::StringMap<int> attr_offsets; llvm::StringMap<int> attr_offsets;
ContiguousMap<llvm::StringRef, HiddenClass*, llvm::StringMap<int>> children; ContiguousMap<llvm::StringRef, HiddenClass*, llvm::StringMap<int>> children;
// If >= 0, is the offset where we stored an attrwrapper object
int attrwrapper_offset = -1;
HiddenClass* attrwrapper_child = NULL;
public: public:
static HiddenClass* makeRoot() { static HiddenClass* makeRoot() {
...@@ -315,18 +318,35 @@ public: ...@@ -315,18 +318,35 @@ public:
void gc_visit(GCVisitor* visitor) { void gc_visit(GCVisitor* visitor) {
// Visit children even for the dict-backed case, since children will just be empty // Visit children even for the dict-backed case, since children will just be empty
visitor->visitRange((void* const*)&children.vector()[0], (void* const*)&children.vector()[children.size()]); visitor->visitRange((void* const*)&children.vector()[0], (void* const*)&children.vector()[children.size()]);
if (attrwrapper_child)
visitor->visit(attrwrapper_child);
} }
// Only makes sense for NORMAL hidden classes: // The total size of the attribute array. The slots in the attribute array may not correspond 1:1 to Python
const llvm::StringMap<int>& getAttrOffsets() { // attributes.
int attributeArraySize() {
if (type == DICT_BACKED)
return 1;
ASSERT(type == NORMAL, "%d", type);
int r = attr_offsets.size();
if (attrwrapper_offset != -1)
r += 1;
return r;
}
// The mapping from string attribute names to attribute offsets. There may be other objects in the attributes
// array.
// Only valid for NORMAL hidden classes
const llvm::StringMap<int>& getStrAttrOffsets() {
assert(type == NORMAL); assert(type == NORMAL);
return attr_offsets; return attr_offsets;
} }
// Only makes sense for NORMAL hidden classes: // Only valid for NORMAL hidden classes:
HiddenClass* getOrMakeChild(const std::string& attr); HiddenClass* getOrMakeChild(const std::string& attr);
// Only makes sense for NORMAL hidden classes: // Only valid for NORMAL hidden classes:
int getOffset(const std::string& attr) { int getOffset(const std::string& attr) {
assert(type == NORMAL); assert(type == NORMAL);
auto it = attr_offsets.find(attr); auto it = attr_offsets.find(attr);
...@@ -335,7 +355,14 @@ public: ...@@ -335,7 +355,14 @@ public:
return it->second; return it->second;
} }
// Only makes sense for NORMAL hidden classes: int getAttrwrapperOffset() {
assert(type == NORMAL);
return attrwrapper_offset;
}
HiddenClass* getAttrwrapperChild();
// Only valid for NORMAL hidden classes:
HiddenClass* delAttrToMakeHC(const std::string& attr); HiddenClass* delAttrToMakeHC(const std::string& attr);
}; };
...@@ -803,7 +830,6 @@ extern "C" void boxGCHandler(GCVisitor* v, Box* b); ...@@ -803,7 +830,6 @@ extern "C" void boxGCHandler(GCVisitor* v, Box* b);
Box* objectNewNoArgs(BoxedClass* cls); Box* objectNewNoArgs(BoxedClass* cls);
Box* objectSetattr(Box* obj, Box* attr, Box* value); Box* objectSetattr(Box* obj, Box* attr, Box* value);
Box* makeAttrWrapper(Box* b);
Box* unwrapAttrWrapper(Box* b); Box* unwrapAttrWrapper(Box* b);
Box* attrwrapperKeys(Box* b); Box* attrwrapperKeys(Box* b);
void attrwrapperDel(Box* b, const std::string& attr); void attrwrapperDel(Box* b, const std::string& attr);
......
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