Commit 8e952a4e authored by Kevin Modzelewski's avatar Kevin Modzelewski

Very basic old-style class support

The mechanics for creating one actually seem pretty simple:
you just pick a different metaclass (ClassType).

I think the old-style classes are working ok now, but the old-style
instances very much are not.
parent 17cbc7ab
......@@ -440,6 +440,7 @@ public:
bool is_user_defined);
void freeze() {
assert(!is_constant);
assert(getattr("__name__")); // otherwise debugging will be very hard
is_constant = true;
}
};
......
// Copyright (c) 2014 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "runtime/classobj.h"
#include <sstream>
#include "codegen/compvars.h"
#include "core/types.h"
#include "gc/collector.h"
#include "runtime/objmodel.h"
#include "runtime/types.h"
namespace pyston {
BoxedClass* classobj_cls, *instance_cls;
class BoxedClassobj : public Box {
public:
HCAttrs attrs;
BoxedTuple* bases;
BoxedString* name;
BoxedClassobj(BoxedClass* metaclass, BoxedString* name, BoxedTuple* bases)
: Box(metaclass), bases(bases), name(name) {}
static void gcHandler(GCVisitor* v, Box* _o) {
assert(_o->cls == classobj_cls);
BoxedClassobj* o = static_cast<BoxedClassobj*>(_o);
boxGCHandler(v, o);
}
};
class BoxedInstance : public Box {
public:
HCAttrs attrs;
BoxedClassobj* inst_cls;
BoxedInstance(BoxedClassobj* inst_cls) : Box(instance_cls), inst_cls(inst_cls) {}
static void gcHandler(GCVisitor* v, Box* _o) {
assert(_o->cls == instance_cls);
BoxedInstance* o = static_cast<BoxedInstance*>(_o);
boxGCHandler(v, o);
}
};
Box* classobjNew(Box* _cls, Box* _name, Box* _bases, Box** _args) {
if (!isSubclass(_cls->cls, type_cls))
raiseExcHelper(TypeError, "classobj.__new__(X): X is not a type object (%s)", getTypeName(_cls)->c_str());
BoxedClass* cls = static_cast<BoxedClass*>(_cls);
if (!isSubclass(cls, classobj_cls))
raiseExcHelper(TypeError, "classobj.__new__(%s): %s is not a subtype of classobj", getNameOfClass(cls)->c_str(),
getNameOfClass(cls)->c_str());
if (_name->cls != str_cls)
raiseExcHelper(TypeError, "argument 1 must be string, not %s", getTypeName(_name));
BoxedString* name = static_cast<BoxedString*>(_name);
Box* _dict = _args[0];
if (_dict->cls != dict_cls)
raiseExcHelper(TypeError, "PyClass_New: dict must be a dictionary");
BoxedDict* dict = static_cast<BoxedDict*>(_dict);
if (_bases->cls != tuple_cls)
raiseExcHelper(TypeError, "PyClass_New: bases must be a tuple");
BoxedTuple* bases = static_cast<BoxedTuple*>(_bases);
BoxedClassobj* made = new BoxedClassobj(cls, name, bases);
made->giveAttr("__module__", boxString(getCurrentModule()->name()));
made->giveAttr("__doc__", None);
for (auto& p : dict->d) {
RELEASE_ASSERT(p.first->cls == str_cls, "");
made->setattr(static_cast<BoxedString*>(p.first)->s, p.second, NULL);
}
// Note: make sure to do this after assigning the attrs, since it will overwrite any defined __name__
made->setattr("__name__", name, NULL);
made->setattr("__bases__", bases, NULL);
return made;
}
Box* classobjCall(Box* _cls, Box* _args, Box* _kwargs) {
assert(_cls->cls == classobj_cls);
assert(_args->cls == tuple_cls);
assert(_kwargs->cls == dict_cls);
BoxedClassobj* cls = static_cast<BoxedClassobj*>(_cls);
return new BoxedInstance(cls);
}
Box* classobjStr(Box* _obj) {
if (!isSubclass(_obj->cls, classobj_cls)) {
raiseExcHelper(TypeError, "descriptor '__str__' requires a 'classobj' object but received an '%s'",
getTypeName(_obj)->c_str());
}
BoxedClassobj* cls = static_cast<BoxedClassobj*>(_obj);
Box* _mod = cls->getattr("__module__");
RELEASE_ASSERT(_mod, "");
RELEASE_ASSERT(_mod->cls == str_cls, "");
return boxString(static_cast<BoxedString*>(_mod)->s + "." + cls->name->s);
}
void setupClassobj() {
classobj_cls = new BoxedClass(type_cls, object_cls, &BoxedClassobj::gcHandler, offsetof(BoxedClassobj, attrs),
sizeof(BoxedClassobj), false);
instance_cls = new BoxedClass(type_cls, object_cls, &BoxedInstance::gcHandler, offsetof(BoxedInstance, attrs),
sizeof(BoxedInstance), false);
classobj_cls->giveAttr("__name__", boxStrConstant("classobj"));
classobj_cls->giveAttr("__new__",
new BoxedFunction(boxRTFunction((void*)classobjNew, UNKNOWN, 4, 0, false, false)));
classobj_cls->giveAttr("__call__",
new BoxedFunction(boxRTFunction((void*)classobjCall, UNKNOWN, 1, 0, true, true)));
classobj_cls->giveAttr("__str__", new BoxedFunction(boxRTFunction((void*)classobjStr, STR, 1)));
classobj_cls->freeze();
instance_cls->giveAttr("__name__", boxStrConstant("instance"));
instance_cls->freeze();
}
}
// Copyright (c) 2014 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PYSTON_RUNTIME_CLASSOBJ_H
#define PYSTON_RUNTIME_CLASSOBJ_H
namespace pyston {
void setupClassobj();
class BoxedClass;
extern BoxedClass* classobj_cls;
}
#endif
......@@ -3209,7 +3209,9 @@ Box* typeNew(Box* _cls, Box* arg1, Box* arg2, Box** _args) {
raiseExcHelper(TypeError, "type.__new__(X): X is not a type object (%s)", getTypeName(_cls)->c_str());
BoxedClass* cls = static_cast<BoxedClass*>(_cls);
RELEASE_ASSERT(isSubclass(cls, type_cls), "");
if (!isSubclass(cls, type_cls))
raiseExcHelper(TypeError, "type.__new__(%s): %s is not a subtype of type", getNameOfClass(cls)->c_str(),
getNameOfClass(cls)->c_str());
if (arg2 == NULL) {
assert(arg3 == NULL);
......@@ -3228,15 +3230,15 @@ Box* typeNew(Box* _cls, Box* arg1, Box* arg2, Box** _args) {
BoxedClass* base;
if (bases->elts.size() == 0) {
printf("Warning: old style class detected\n");
base = object_cls;
} else {
RELEASE_ASSERT(bases->elts.size() == 1, "");
Box* _base = bases->elts[0];
RELEASE_ASSERT(_base->cls == type_cls, "");
base = static_cast<BoxedClass*>(_base);
bases = new BoxedTuple({ object_cls });
}
RELEASE_ASSERT(bases->elts.size() == 1, "");
Box* _base = bases->elts[0];
RELEASE_ASSERT(_base->cls == type_cls, "");
base = static_cast<BoxedClass*>(_base);
BoxedClass* made;
......@@ -3247,13 +3249,12 @@ Box* typeNew(Box* _cls, Box* arg1, Box* arg2, Box** _args) {
made = new BoxedClass(cls, base, NULL, base->tp_basicsize, base->tp_basicsize + sizeof(HCAttrs), true);
}
made->giveAttr("__module__", boxString(getCurrentModule()->name()));
made->giveAttr("__doc__", None);
for (const auto& p : attr_dict->d) {
assert(p.first->cls == str_cls);
made->giveAttr(static_cast<BoxedString*>(p.first)->s, p.second);
}
if (made->getattr("__doc__") == NULL) {
made->giveAttr("__doc__", None);
made->setattr(static_cast<BoxedString*>(p.first)->s, p.second, NULL);
}
// Note: make sure to do this after assigning the attrs, since it will overwrite any defined __name__
......
......@@ -237,7 +237,7 @@ void raise0() {
void raise3(Box* arg0, Box* arg1, Box* arg2) {
RELEASE_ASSERT(arg2 == None, "unsupported");
if (arg0->cls == type_cls) {
if (isSubclass(arg0->cls, type_cls)) {
BoxedClass* c = static_cast<BoxedClass*>(arg0);
if (isSubclass(c, Exception)) {
Box* exc_obj;
......
......@@ -27,6 +27,7 @@
#include "core/stats.h"
#include "core/types.h"
#include "gc/collector.h"
#include "runtime/classobj.h"
#include "runtime/iterobject.h"
#include "runtime/long.h"
#include "runtime/objmodel.h"
......@@ -325,9 +326,7 @@ extern "C" Box* createUserClass(std::string* name, Box* _bases, Box* _attr_dict)
metaclass = m->getattr("__metaclass__");
if (!metaclass) {
printf("Warning: old style class detected\n");
metaclass = type_cls;
// Py_FatalError("Should default to an old-style class here");
metaclass = classobj_cls;
}
}
assert(metaclass);
......@@ -753,6 +752,7 @@ void setupRuntime() {
setupFile();
setupGenerator();
setupIter();
setupClassobj();
function_cls->giveAttr("__name__", boxStrConstant("function"));
function_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)functionRepr, STR, 1)));
......
......@@ -40,3 +40,17 @@ try:
type.__new__(1, 2, 3)
except TypeError, e:
print e
try:
type.__new__(int, 1, 2, 3)
except TypeError, e:
print e
class D():
__metaclass__ = type
print D
print D.__base__
print type("test", (), {})
print type("test", (), {"__module__":"fake"})
# __bases__ and __name__ not supported yet -- need to add a custom getattr() method for old style classes
class C():
pass
print C, type(C)
print map(str, C.__bases__), C.__name__
print type(C())
class D(C):
pass
print D, type(D)
print map(str, D.__bases__), D.__name__
print type(D())
ClassType = type(C)
try:
ClassType.__new__(int, 1, 1, 1)
except TypeError, e:
print e
try:
ClassType.__new__(1, 1, 1, 1)
except TypeError, e:
print e
print ClassType("test", (), {})
print ClassType("test", (), {"__module__":"fake"})
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