diff --git a/py/objobject.c b/py/objobject.c index 49d2ec62ee..265fcfbf2b 100644 --- a/py/objobject.c +++ b/py/objobject.c @@ -52,9 +52,12 @@ STATIC mp_obj_t object___new__(mp_obj_t cls) { if (!MP_OBJ_IS_TYPE(cls, &mp_type_type) || !mp_obj_is_instance_type((mp_obj_type_t*)MP_OBJ_TO_PTR(cls))) { mp_raise_TypeError("__new__ arg must be a user-type"); } - mp_obj_t o = MP_OBJ_SENTINEL; - mp_obj_t res = mp_obj_instance_make_new(MP_OBJ_TO_PTR(cls), 1, 0, &o); - return res; + // This executes only "__new__" part of instance creation. + // TODO: This won't work well for classes with native bases. + // TODO: This is a hack, should be resolved along the lines of + // https://github.com/micropython/micropython/issues/606#issuecomment-43685883 + const mp_obj_type_t *native_base; + return MP_OBJ_FROM_PTR(mp_obj_new_instance(MP_OBJ_TO_PTR(cls), &native_base)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(object___new___fun_obj, object___new__); STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(object___new___obj, MP_ROM_PTR(&object___new___fun_obj)); diff --git a/py/objtype.c b/py/objtype.c index 22c8f01bcb..33757287ac 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -90,17 +90,22 @@ STATIC mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(native_base_init_wrapper_obj, 1, MP_OBJ_FUN_ARGS_MAX, native_base_init_wrapper); -STATIC mp_obj_t mp_obj_new_instance(const mp_obj_type_t *class, size_t subobjs) { - mp_obj_instance_t *o = m_new_obj_var(mp_obj_instance_t, mp_obj_t, subobjs); +#if !MICROPY_CPYTHON_COMPAT +STATIC +#endif +mp_obj_instance_t *mp_obj_new_instance(const mp_obj_type_t *class, const mp_obj_type_t **native_base) { + size_t num_native_bases = instance_count_native_bases(class, native_base); + assert(num_native_bases < 2); + mp_obj_instance_t *o = m_new_obj_var(mp_obj_instance_t, mp_obj_t, num_native_bases); o->base.type = class; mp_map_init(&o->members, 0); // Initialise the native base-class slot (should be 1 at most) with a valid // object. It doesn't matter which object, so long as it can be uniquely // distinguished from a native class that is initialised. - if (subobjs != 0) { + if (num_native_bases != 0) { o->subobj[0] = MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj); } - return MP_OBJ_FROM_PTR(o); + return o; } // TODO @@ -267,20 +272,6 @@ STATIC void instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args) { assert(mp_obj_is_instance_type(self)); - const mp_obj_type_t *native_base = NULL; - size_t num_native_bases = instance_count_native_bases(self, &native_base); - assert(num_native_bases < 2); - - mp_obj_instance_t *o = MP_OBJ_TO_PTR(mp_obj_new_instance(self, num_native_bases)); - - // This executes only "__new__" part of instance creation. - // TODO: This won't work well for classes with native bases. - // TODO: This is a hack, should be resolved along the lines of - // https://github.com/micropython/micropython/issues/606#issuecomment-43685883 - if (n_args == 1 && *args == MP_OBJ_SENTINEL) { - return MP_OBJ_FROM_PTR(o); - } - // look for __new__ function mp_obj_t init_fn[2] = {MP_OBJ_NULL}; struct class_lookup_data lookup = { @@ -292,10 +283,12 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size }; mp_obj_class_lookup(&lookup, self); - mp_obj_t new_ret = MP_OBJ_FROM_PTR(o); - if (init_fn[0] == MP_OBJ_SENTINEL) { - // Native type's constructor is what wins - it gets all our arguments, - // and none Python classes are initialized at all. + const mp_obj_type_t *native_base = NULL; + mp_obj_instance_t *o; + if (init_fn[0] == MP_OBJ_NULL || init_fn[0] == MP_OBJ_SENTINEL) { + // Either there is no __new__() method defined or there is a native + // constructor. In both cases create a blank instance. + o = mp_obj_new_instance(self, &native_base); // Since type->make_new() implements both __new__() and __init__() in // one go, of which the latter may be overridden by the Python subclass, @@ -303,8 +296,9 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size // constructor to give a chance for the Python __init__() method to call // said native constructor. - } else if (init_fn[0] != MP_OBJ_NULL) { - // now call Python class __new__ function with all args + } else { + // Call Python class __new__ function with all args to create an instance + mp_obj_t new_ret; if (n_args == 0 && n_kw == 0) { mp_obj_t args2[1] = {MP_OBJ_FROM_PTR(self)}; new_ret = mp_call_function_n_kw(init_fn[0], 1, 0, args2); @@ -316,16 +310,17 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size m_del(mp_obj_t, args2, 1 + n_args + 2 * n_kw); } - } + // https://docs.python.org/3.4/reference/datamodel.html#object.__new__ + // "If __new__() does not return an instance of cls, then the new + // instance's __init__() method will not be invoked." + if (mp_obj_get_type(new_ret) != self) { + return new_ret; + } - // https://docs.python.org/3.4/reference/datamodel.html#object.__new__ - // "If __new__() does not return an instance of cls, then the new instance's __init__() method will not be invoked." - if (mp_obj_get_type(new_ret) != self) { - return new_ret; + // The instance returned by __new__() becomes the new object + o = MP_OBJ_TO_PTR(new_ret); } - o = MP_OBJ_TO_PTR(new_ret); - // now call Python class __init__ function with all args // This method has a chance to call super().__init__() to construct a // possible native base class. diff --git a/py/objtype.h b/py/objtype.h index 52419f3cdc..1f43130845 100644 --- a/py/objtype.h +++ b/py/objtype.h @@ -37,6 +37,11 @@ typedef struct _mp_obj_instance_t { // TODO maybe cache __getattr__ and __setattr__ for efficient lookup of them } mp_obj_instance_t; +#if MICROPY_CPYTHON_COMPAT +// this is needed for object.__new__ +mp_obj_instance_t *mp_obj_new_instance(const mp_obj_type_t *cls, const mp_obj_type_t **native_base); +#endif + // this needs to be exposed for MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE to work void mp_obj_instance_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest);