py: Implement base class lookup, issubclass, isinstance.
This commit is contained in:
parent
062478e66d
commit
004cdcebfe
13
py/builtin.h
13
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);
|
||||
|
@ -44,6 +44,8 @@ Q(divmod)
|
||||
Q(float)
|
||||
Q(hash)
|
||||
Q(int)
|
||||
Q(isinstance)
|
||||
Q(issubclass)
|
||||
Q(iter)
|
||||
Q(len)
|
||||
Q(list)
|
||||
|
10
py/obj.h
10
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);
|
||||
|
@ -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];
|
||||
|
116
py/objtype.c
116
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);
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
24
tests/basics/tests/class3.py
Normal file
24
tests/basics/tests/class3.py
Normal file
@ -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()
|
@ -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));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user