summaryrefslogtreecommitdiffstatshomepage
path: root/py/objtype.c
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2017-12-12 15:22:03 +1100
committerDamien George <damien.p.george@gmail.com>2017-12-12 16:53:44 +1100
commitc78ef92d787d7bab8acbec69e978037ec2b20d21 (patch)
treed539242f509ac167ee78f790b8e5424b06f801d8 /py/objtype.c
parent3c28df16586157b6b80e7437559c36f05a309e24 (diff)
downloadmicropython-c78ef92d787d7bab8acbec69e978037ec2b20d21.tar.gz
micropython-c78ef92d787d7bab8acbec69e978037ec2b20d21.zip
py/objtype: Refactor object's handling of __new__ to not create 2 objs.
Before this patch, if a user defined the __new__() function for a class then two instances of that class would be created: once before __new__ is called and once during the __new__ call (assuming the user creates some instance, eg using super().__new__, which is most of the time). The first one was then discarded. This refactor makes it so that a new instance is only created if the user __new__ function doesn't exist.
Diffstat (limited to 'py/objtype.c')
-rw-r--r--py/objtype.c57
1 files changed, 26 insertions, 31 deletions
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.