From 004cdcebfe03d52ca9da3b2515c203910502dbba Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 9 Jan 2014 21:43:51 +0000 Subject: [PATCH] py: Implement base class lookup, issubclass, isinstance. --- py/builtin.h | 13 +--- py/mpqstrraw.h | 2 + py/obj.h | 10 +-- py/objtuple.c | 1 + py/objtype.c | 116 +++++++++++++++++++++++++++++------ py/runtime.c | 2 + stm/pybwlan.c | 2 +- tests/basics/tests/class3.py | 24 ++++++++ unix/main.c | 2 +- 9 files changed, 136 insertions(+), 36 deletions(-) create mode 100644 tests/basics/tests/class3.py diff --git a/py/builtin.h b/py/builtin.h index 2cd0ef15f2..db7d517a06 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -6,19 +6,12 @@ mp_obj_t mp_builtin___repl_print__(mp_obj_t o); mp_obj_t mp_builtin_abs(mp_obj_t o_in); mp_obj_t mp_builtin_all(mp_obj_t o_in); mp_obj_t mp_builtin_any(mp_obj_t o_in); -mp_obj_t mp_builtin_bool(int n_args, const mp_obj_t *args); mp_obj_t mp_builtin_callable(mp_obj_t o_in); -#if MICROPY_ENABLE_FLOAT -MP_DECLARE_CONST_FUN_OBJ(mp_builtin_complex_obj); -#endif mp_obj_t mp_builtin_chr(mp_obj_t o_in); -mp_obj_t mp_builtin_dict(void); mp_obj_t mp_builtin_divmod(mp_obj_t o1_in, mp_obj_t o2_in); -#if MICROPY_ENABLE_FLOAT -MP_DECLARE_CONST_FUN_OBJ(mp_builtin_float_obj); -#endif MP_DECLARE_CONST_FUN_OBJ(mp_builtin_hash_obj); -MP_DECLARE_CONST_FUN_OBJ(mp_builtin_int_obj); +MP_DECLARE_CONST_FUN_OBJ(mp_builtin_isinstance_obj); +MP_DECLARE_CONST_FUN_OBJ(mp_builtin_issubclass_obj); MP_DECLARE_CONST_FUN_OBJ(mp_builtin_iter_obj); mp_obj_t mp_builtin_len(mp_obj_t o_in); mp_obj_t mp_builtin_list(int n_args, const mp_obj_t *args); @@ -29,6 +22,4 @@ mp_obj_t mp_builtin_ord(mp_obj_t o_in); mp_obj_t mp_builtin_pow(int n_args, const mp_obj_t *args); mp_obj_t mp_builtin_print(int n_args, const mp_obj_t *args); mp_obj_t mp_builtin_range(int n_args, const mp_obj_t *args); -MP_DECLARE_CONST_FUN_OBJ(mp_builtin_set_obj); mp_obj_t mp_builtin_sum(int n_args, const mp_obj_t *args); -MP_DECLARE_CONST_FUN_OBJ(mp_builtin_type_obj); diff --git a/py/mpqstrraw.h b/py/mpqstrraw.h index eeed6f3a0a..e44df5ad18 100644 --- a/py/mpqstrraw.h +++ b/py/mpqstrraw.h @@ -44,6 +44,8 @@ Q(divmod) Q(float) Q(hash) Q(int) +Q(isinstance) +Q(issubclass) Q(iter) Q(len) Q(list) diff --git a/py/obj.h b/py/obj.h index 8643dbb91b..48080a0ec0 100644 --- a/py/obj.h +++ b/py/obj.h @@ -147,7 +147,10 @@ struct _mp_obj_type_t { mp_load_attr_fun_t load_attr; mp_store_attr_fun_t store_attr; - mp_obj_t locals; + + // these are for dynamically created types (classes) + mp_obj_t bases_tuple; + mp_obj_t locals_dict; /* What we might need to add here: @@ -180,7 +183,7 @@ extern const mp_obj_t mp_const_stop_iteration; // special object indicating end // General API for objects -mp_obj_t mp_obj_new_type(qstr name, mp_obj_t local_dict); +mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict); mp_obj_t mp_obj_new_none(void); mp_obj_t mp_obj_new_bool(bool value); mp_obj_t mp_obj_new_cell(mp_obj_t obj); @@ -308,9 +311,6 @@ void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte // generator extern const mp_obj_type_t gen_instance_type; -// class -struct _mp_map_elem_t *mp_obj_class_lookup(mp_obj_t self_in, qstr attr, enum _mp_map_lookup_kind_t lookup_kind); - // module extern const mp_obj_type_t module_type; mp_obj_t mp_obj_new_module(qstr module_name); diff --git a/py/objtuple.c b/py/objtuple.c index 0050fc5ea0..7685cc449f 100644 --- a/py/objtuple.c +++ b/py/objtuple.c @@ -136,6 +136,7 @@ mp_obj_t mp_obj_new_tuple_reverse(uint n, const mp_obj_t *items) { } void mp_obj_tuple_get(mp_obj_t self_in, uint *len, mp_obj_t **items) { + assert(MP_OBJ_IS_TYPE(self_in, &tuple_type)); mp_obj_tuple_t *self = self_in; *len = self->len; *items = &self->items[0]; diff --git a/py/objtype.c b/py/objtype.c index 58854ca98d..6c89c1ff2b 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -27,6 +27,45 @@ static mp_obj_t mp_obj_new_class(mp_obj_t class) { return o; } +static mp_map_elem_t *mp_obj_class_lookup(mp_obj_t self_in, qstr attr, mp_map_lookup_kind_t lookup_kind) { + for (;;) { + assert(MP_OBJ_IS_TYPE(self_in, &mp_const_type)); + mp_obj_type_t *self = self_in; + if (self->locals_dict == NULL) { + return NULL; + } + assert(MP_OBJ_IS_TYPE(self->locals_dict, &dict_type)); // Micro Python restriction, for now + mp_map_t *locals_map = ((void*)self->locals_dict + sizeof(mp_obj_base_t)); // XXX hack to get map object from dict object + mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), lookup_kind); + if (elem != NULL) { + return elem; + } + + // attribute not found, keep searching base classes + + // for a const struct, this entry might be NULL + if (self->bases_tuple == MP_OBJ_NULL) { + return NULL; + } + + uint len; + mp_obj_t *items; + mp_obj_tuple_get(self->bases_tuple, &len, &items); + if (len == 0) { + return NULL; + } + for (uint i = 0; i < len - 1; i++) { + elem = mp_obj_class_lookup(items[i], attr, lookup_kind); + if (elem != NULL) { + return elem; + } + } + + // search last base (simple tail recursion elimination) + self_in = items[len - 1]; + } +} + static void class_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) { print(env, "<%s object at %p>", mp_obj_get_type_str(self_in), self_in); } @@ -102,17 +141,6 @@ static bool class_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) { return true; } -mp_map_elem_t *mp_obj_class_lookup(mp_obj_t self_in, qstr attr, mp_map_lookup_kind_t lookup_kind) { - assert(MP_OBJ_IS_TYPE(self_in, &mp_const_type)); - mp_obj_type_t *self = self_in; - if (self->locals == NULL) { - return NULL; - } - assert(MP_OBJ_IS_TYPE(self->locals, &dict_type)); // Micro Python restriction, for now - mp_map_t *locals_map = ((void*)self->locals + sizeof(mp_obj_base_t)); // XXX hack to get map object from dict object - return mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), lookup_kind); -} - /******************************************************************************/ // type object // - the struct is mp_obj_type_t and is defined in obj.h so const types can be made @@ -131,13 +159,10 @@ static mp_obj_t type_make_new(mp_obj_t type_in, int n_args, const mp_obj_t *args return mp_obj_get_type(args[0]); case 3: - { // args[2] = name // args[1] = bases tuple // args[0] = locals dict - - return mp_obj_new_type(mp_obj_get_qstr(args[2]), args[0]); - } + return mp_obj_new_type(mp_obj_get_qstr(args[2]), args[1], args[0]); default: nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "type takes at 1 or 3 arguments")); @@ -192,7 +217,9 @@ const mp_obj_type_t mp_const_type = { .store_attr = type_store_attr, }; -mp_obj_t mp_obj_new_type(qstr name, mp_obj_t local_dict) { +mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) { + assert(MP_OBJ_IS_TYPE(bases_tuple, &tuple_type)); // Micro Python restriction, for now + assert(MP_OBJ_IS_TYPE(locals_dict, &dict_type)); // Micro Python restriction, for now mp_obj_type_t *o = m_new0(mp_obj_type_t, 1); o->base.type = &mp_const_type; o->name = qstr_str(name); @@ -200,7 +227,60 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t local_dict) { o->make_new = class_make_new; o->load_attr = class_load_attr; o->store_attr = class_store_attr; - o->locals = local_dict; - assert(MP_OBJ_IS_TYPE(o->locals, &dict_type)); // Micro Python restriction, for now + o->bases_tuple = bases_tuple; + o->locals_dict = locals_dict; return o; } + +/******************************************************************************/ +// built-ins specific to types + +static mp_obj_t mp_builtin_issubclass(mp_obj_t object, mp_obj_t classinfo) { + if (!MP_OBJ_IS_TYPE(object, &mp_const_type)) { + nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "issubclass() arg 1 must be a class")); + } + + // TODO support a tuple of classes for second argument + if (!MP_OBJ_IS_TYPE(classinfo, &mp_const_type)) { + nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "issubclass() arg 2 must be a class")); + } + + for (;;) { + if (object == classinfo) { + return mp_const_true; + } + + // not equivalent classes, keep searching base classes + + assert(MP_OBJ_IS_TYPE(object, &mp_const_type)); + mp_obj_type_t *self = object; + + // for a const struct, this entry might be NULL + if (self->bases_tuple == MP_OBJ_NULL) { + return mp_const_false; + } + + uint len; + mp_obj_t *items; + mp_obj_tuple_get(self->bases_tuple, &len, &items); + if (len == 0) { + return mp_const_false; + } + for (uint i = 0; i < len - 1; i++) { + if (mp_builtin_issubclass(items[i], classinfo) == mp_const_true) { + return mp_const_true; + } + } + + // search last base (simple tail recursion elimination) + object = items[len - 1]; + } +} + +MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_issubclass_obj, mp_builtin_issubclass); + +static mp_obj_t mp_builtin_isinstance(mp_obj_t object, mp_obj_t classinfo) { + return mp_builtin_issubclass(mp_obj_get_type(object), classinfo); +} + +MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_isinstance_obj, mp_builtin_isinstance); diff --git a/py/runtime.c b/py/runtime.c index 2c3ff803bc..795cc4624c 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -121,6 +121,8 @@ void rt_init(void) { mp_map_add_qstr(&map_builtins, MP_QSTR_chr, rt_make_function_1(mp_builtin_chr)); mp_map_add_qstr(&map_builtins, MP_QSTR_divmod, rt_make_function_2(mp_builtin_divmod)); mp_map_add_qstr(&map_builtins, MP_QSTR_hash, (mp_obj_t)&mp_builtin_hash_obj); + mp_map_add_qstr(&map_builtins, MP_QSTR_isinstance, (mp_obj_t)&mp_builtin_isinstance_obj); + mp_map_add_qstr(&map_builtins, MP_QSTR_issubclass, (mp_obj_t)&mp_builtin_issubclass_obj); mp_map_add_qstr(&map_builtins, MP_QSTR_iter, (mp_obj_t)&mp_builtin_iter_obj); mp_map_add_qstr(&map_builtins, MP_QSTR_len, rt_make_function_1(mp_builtin_len)); mp_map_add_qstr(&map_builtins, MP_QSTR_max, rt_make_function_var(1, mp_builtin_max)); diff --git a/stm/pybwlan.c b/stm/pybwlan.c index e890e10ca7..09d642d9c8 100644 --- a/stm/pybwlan.c +++ b/stm/pybwlan.c @@ -78,7 +78,7 @@ mp_obj_t pyb_wlan_get_ip(void) { // if it doesn't already exist, make a new empty class for NetAddress objects if (net_address_type == MP_OBJ_NULL) { - net_address_type = mp_obj_new_type(qstr_from_str_static("NetAddress"), mp_obj_new_dict(0)); + net_address_type = mp_obj_new_type(qstr_from_str_static("NetAddress"), mp_const_empty_tuple, mp_obj_new_dict(0)); } // make a new NetAddress object diff --git a/tests/basics/tests/class3.py b/tests/basics/tests/class3.py new file mode 100644 index 0000000000..3b4f0bb90f --- /dev/null +++ b/tests/basics/tests/class3.py @@ -0,0 +1,24 @@ +# inheritance + +class A: + def a(): + print('A.a() called') + +class B(A): + pass + +print(type(A)) +print(type(B)) + +print(issubclass(A, A)) +print(issubclass(A, B)) +print(issubclass(B, A)) +print(issubclass(B, B)) + +print(isinstance(A(), A)) +print(isinstance(A(), B)) +print(isinstance(B(), A)) +print(isinstance(B(), B)) + +A.a() +B.a() diff --git a/unix/main.c b/unix/main.c index caec900a52..15a4000ab5 100644 --- a/unix/main.c +++ b/unix/main.c @@ -218,7 +218,7 @@ int main(int argc, char **argv) { // test_obj = TestClass() // test_obj.attr = 42 mp_obj_t test_class_type, test_class_instance; - test_class_type = mp_obj_new_type(qstr_from_str_static("TestClass"), mp_obj_new_dict(0)); + test_class_type = mp_obj_new_type(qstr_from_str_static("TestClass"), mp_const_empty_tuple, mp_obj_new_dict(0)); rt_store_name(qstr_from_str_static("test_obj"), test_class_instance = rt_call_function_0(test_class_type)); rt_store_attr(test_class_instance, qstr_from_str_static("attr"), mp_obj_new_int(42));