py: Implement base class lookup, issubclass, isinstance.

This commit is contained in:
Damien George 2014-01-09 21:43:51 +00:00
parent 062478e66d
commit 004cdcebfe
9 changed files with 136 additions and 36 deletions

View File

@ -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);

View File

@ -44,6 +44,8 @@ Q(divmod)
Q(float)
Q(hash)
Q(int)
Q(isinstance)
Q(issubclass)
Q(iter)
Q(len)
Q(list)

View File

@ -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);

View File

@ -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];

View File

@ -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);

View File

@ -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));

View File

@ -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

View 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()

View File

@ -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));