Commit 3e8f2520 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Switch to cpython's object_init and object_new

parent 0c0a7da1
......@@ -4513,23 +4513,10 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa
// Notably, "type" itself does not. For instance, assuming M is a subclass of
// type, type.__new__(M, 1) will return the int class, which is not an instance of M.
static Box* object_new = NULL;
static Box* object_init = NULL;
// this is ok with not using StlCompatAllocator since we will manually register these objects with the GC
static std::vector<Box*> allowable_news;
if (!object_new) {
object_new = typeLookup(object_cls, new_str, NULL);
assert(object_new);
// I think this is unnecessary, but good form:
gc::registerPermanentRoot(object_new);
object_init = typeLookup(object_cls, init_str, NULL);
assert(object_init);
gc::registerPermanentRoot(object_init);
allowable_news.push_back(object_new);
for (BoxedClass* allowed_cls : { enumerate_cls, xrange_cls }) {
if (allowable_news.empty()) {
for (BoxedClass* allowed_cls : { object_cls, enumerate_cls, xrange_cls }) {
auto new_obj = typeLookup(allowed_cls, new_str, NULL);
gc::registerPermanentRoot(new_obj);
allowable_news.push_back(new_obj);
......@@ -4585,16 +4572,9 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa
RewriterVar* r_made = NULL;
ArgPassSpec new_argspec = argspec;
if (npassed_args > 1 && new_attr == object_new) {
if (init_attr == object_init) {
raiseExcHelper(TypeError, objectNewParameterTypeErrorMsg());
} else {
new_argspec = ArgPassSpec(1);
}
}
if (rewrite_args) {
if (new_attr == object_new && init_attr != object_init) {
if (cls->tp_new == object_cls->tp_new && cls->tp_init != object_cls->tp_init) {
// Fast case: if we are calling object_new, we normally doesn't look at the arguments at all.
// (Except in the case when init_attr != object_init, in which case object_new looks at the number
// of arguments and throws an exception.)
......@@ -4660,7 +4640,7 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa
}
}
if (init_attr && init_attr != object_init) {
if (init_attr && made->cls->tp_init != object_cls->tp_init) {
// TODO apply the same descriptor special-casing as in callattr?
Box* initrtn;
......@@ -4722,8 +4702,10 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa
Box* typeCall(Box* obj, BoxedTuple* vararg, BoxedDict* kwargs) {
assert(vararg->cls == tuple_cls);
bool pass_kwargs = (kwargs && kwargs->d.size());
int n = vararg->size();
int args_to_pass = n + 2; // 1 for obj, 1 for kwargs
int args_to_pass = n + 1 + (pass_kwargs ? 1 : 0); // 1 for obj, 1 for kwargs
Box** args = NULL;
if (args_to_pass > 3)
......@@ -4734,9 +4716,11 @@ Box* typeCall(Box* obj, BoxedTuple* vararg, BoxedDict* kwargs) {
for (int i = 0; i < n; i++) {
getArg(i + 1, arg1, arg2, arg3, args) = vararg->elts[i];
}
getArg(n + 1, arg1, arg2, arg3, args) = kwargs;
return typeCallInternal(NULL, NULL, ArgPassSpec(n + 1, 0, false, true), arg1, arg2, arg3, args, NULL);
if (pass_kwargs)
getArg(n + 1, arg1, arg2, arg3, args) = kwargs;
return typeCallInternal(NULL, NULL, ArgPassSpec(n + 1, 0, false, pass_kwargs), arg1, arg2, arg3, args, NULL);
}
extern "C" void delGlobal(Box* globals, const std::string* name) {
......
......@@ -1731,26 +1731,45 @@ Box* objectNewNoArgs(BoxedClass* cls) {
return new (cls) Box();
}
Box* objectNew(BoxedClass* cls, BoxedTuple* args, BoxedDict* kwargs) {
assert(isSubclass(cls->cls, type_cls));
assert(args->cls == tuple_cls);
assert(kwargs->cls == dict_cls);
static int excess_args(PyObject* args, PyObject* kwds) noexcept {
return PyTuple_GET_SIZE(args) || (kwds && PyDict_Check(kwds) && PyDict_Size(kwds));
}
// We use a different strategy from CPython: we let object.__new__ take extra
// arguments, but raise an error if they wouldn't be handled by the corresponding init.
// TODO switch to the CPython approach?
if (args->size() != 0 || kwargs->d.size() != 0) {
// TODO slow (We already cache these in typeCall -- should use that here too?)
if (typeLookup(cls, "__new__", NULL) != typeLookup(object_cls, "__new__", NULL)
|| typeLookup(cls, "__init__", NULL) == typeLookup(object_cls, "__init__", NULL))
raiseExcHelper(TypeError, objectNewParameterTypeErrorMsg());
}
static PyObject* object_new(PyTypeObject* type, PyObject* args, PyObject* kwds) noexcept;
return new (cls) Box();
static int object_init(PyObject* self, PyObject* args, PyObject* kwds) noexcept {
int err = 0;
if (excess_args(args, kwds)) {
PyTypeObject* type = Py_TYPE(self);
if (type->tp_init != object_init && type->tp_new != object_new) {
err = PyErr_WarnEx(PyExc_DeprecationWarning, "object.__init__() takes no parameters", 1);
} else if (type->tp_init != object_init || type->tp_new == object_new) {
PyErr_SetString(PyExc_TypeError, "object.__init__() takes no parameters");
err = -1;
}
}
return err;
}
Box* objectInit(Box* b, BoxedTuple* args) {
return None;
static PyObject* object_new(PyTypeObject* type, PyObject* args, PyObject* kwds) noexcept {
int err = 0;
if (excess_args(args, kwds)) {
if (type->tp_new != object_new && type->tp_init != object_init) {
err = PyErr_WarnEx(PyExc_DeprecationWarning, "object() takes no parameters", 1);
} else if (type->tp_new != object_new || type->tp_init == object_init) {
PyErr_SetString(PyExc_TypeError, "object() takes no parameters");
err = -1;
}
}
if (err < 0)
return NULL;
if (type->tp_flags & Py_TPFLAGS_IS_ABSTRACT) {
// I don't know what this is or when it happens, but
// CPython does something special with it
Py_FatalError("unimplemented");
}
return type->tp_alloc(type, 0);
}
Box* objectRepr(Box* obj) {
......@@ -2430,6 +2449,8 @@ void setupRuntime() {
object_cls->tp_getattro = PyObject_GenericGetAttr;
object_cls->tp_setattro = PyObject_GenericSetAttr;
object_cls->tp_init = object_init;
object_cls->tp_new = object_new;
add_operators(object_cls);
object_cls->finishInitialization();
......@@ -2494,8 +2515,6 @@ void setupRuntime() {
SET = typeFromClass(set_cls);
FROZENSET = typeFromClass(frozenset_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)));
object_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)objectRepr, UNKNOWN, 1, 0, false, false)));
object_cls->giveAttr("__str__", new BoxedFunction(boxRTFunction((void*)objectStr, UNKNOWN, 1, 0, false, false)));
object_cls->giveAttr("__hash__",
......@@ -2548,6 +2567,8 @@ void setupRuntime() {
}
object_cls->giveAttr("__class__", new (pyston_getset_cls) BoxedGetsetDescriptor(objectClass, objectSetClass, NULL));
object_cls->freeze();
assert(object_cls->tp_init == object_init);
assert(object_cls->tp_new == object_new);
setupBool();
setupLong();
......
# should_error
# skip-if: sys.version_info < (2, 7, 4)
# - Error message changed in 2.7.4
# Regression test:
# If the init function doesn't exist, shouldn't just silently ignore any args
# that got passed
......@@ -16,5 +12,8 @@ print "This should have worked"
class D(object):
pass
d = D(1)
print "This should have failed"
try:
d = D(1)
print "This should have failed"
except TypeError as e:
print "expected exception" # the message got changed in 2.7.4
# should_error
# skip-if: sys.version_info < (2, 7, 4)
# - Error message changed in 2.7.4
# object.__new__ doesn't complain if __init__ is overridden:
class C1(object):
......@@ -16,4 +12,12 @@ object.__new__(C1, 1)
object.__new__(C1, a=1)
print "Trying C2"
object.__new__(C2, 1)
try:
object.__new__(C2, 1)
except TypeError as e:
print "caught TypeError"
# These are some tricky cases, since they can potentially look like arguments
# are being passed, but really they are not.
type.__call__(*[C2])
type.__call__(C2, **{})
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