diff --git a/py/builtin.c b/py/builtin.c index 5dbf47ab84..50e9c02df5 100644 --- a/py/builtin.c +++ b/py/builtin.c @@ -23,8 +23,8 @@ mp_obj_t mp_builtin___build_class__(int n_args, const mp_obj_t *args) { // we differ from CPython: we set the new __locals__ object here mp_map_t *old_locals = rt_locals_get(); - mp_map_t *class_locals = mp_map_new(0); - rt_locals_set(class_locals); + mp_obj_t class_locals = mp_obj_new_dict(0); + rt_locals_set(mp_obj_dict_get_map(class_locals)); // call the class code mp_obj_t cell = rt_call_function_1(args[0], (mp_obj_t)0xdeadbeef); @@ -32,7 +32,6 @@ mp_obj_t mp_builtin___build_class__(int n_args, const mp_obj_t *args) { // restore old __locals__ object rt_locals_set(old_locals); - /* // get the class type (meta object) from the base objects mp_obj_t meta; if (n_args == 2) { @@ -42,21 +41,16 @@ mp_obj_t mp_builtin___build_class__(int n_args, const mp_obj_t *args) { // use type of first base object meta = mp_obj_get_type(args[2]); } - */ // TODO do proper metaclass resolution for multiple base objects - /* // create the new class using a call to the meta object // (arguments must be backwards in the array) mp_obj_t meta_args[3]; meta_args[2] = args[1]; // class name meta_args[1] = mp_obj_new_tuple(n_args - 2, args + 2); // tuple of bases - meta_args[0] = class_locals; // dict of members TODO, currently is a map + meta_args[0] = class_locals; // dict of members mp_obj_t new_class = rt_call_function_n(meta, 3, meta_args); - */ - // create the new class - mp_obj_t new_class = mp_obj_new_class(class_locals); // store into cell if neede if (cell != mp_const_none) { diff --git a/py/map.h b/py/map.h index 4905f5bc18..5ce4e835b6 100644 --- a/py/map.h +++ b/py/map.h @@ -18,7 +18,7 @@ typedef struct _mp_set_t { mp_obj_t *table; } mp_set_t; -typedef enum { +typedef enum _mp_map_lookup_kind_t { MP_MAP_LOOKUP, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND, MP_MAP_LOOKUP_REMOVE_IF_FOUND, diff --git a/py/obj.h b/py/obj.h index 5eb9fee75a..8643dbb91b 100644 --- a/py/obj.h +++ b/py/obj.h @@ -61,6 +61,8 @@ typedef struct _mp_obj_base_t mp_obj_base_t; // Need to declare this here so we are not dependent on map.h struct _mp_map_t; +struct _mp_map_elem_t; +enum _mp_map_lookup_kind_t; // Type definitions for methods @@ -78,6 +80,8 @@ typedef mp_obj_t (*mp_call_n_fun_t)(mp_obj_t fun, int n_args, const mp_obj_t *ar typedef mp_obj_t (*mp_call_n_kw_fun_t)(mp_obj_t fun, int n_args, int n_kw, const mp_obj_t *args); // args are in reverse order in the array typedef mp_obj_t (*mp_unary_op_fun_t)(int op, mp_obj_t); typedef mp_obj_t (*mp_binary_op_fun_t)(int op, mp_obj_t, mp_obj_t); +typedef void (*mp_load_attr_fun_t)(mp_obj_t self_in, qstr attr, mp_obj_t *dest); // for fail, do nothing; for attr, dest[1] = value; for method, dest[0] = self, dest[1] = method +typedef bool (*mp_store_attr_fun_t)(mp_obj_t self_in, qstr attr, mp_obj_t value); // return true if store succeeded typedef struct _mp_method_t { const char *name; @@ -141,15 +145,14 @@ struct _mp_obj_type_t { const mp_method_t *methods; + mp_load_attr_fun_t load_attr; + mp_store_attr_fun_t store_attr; + mp_obj_t locals; + /* What we might need to add here: - dynamic_type instance - compare_op - load_attr module instance class list - load_method instance str gen list user - store_attr module instance class store_subscr list dict len str tuple list map @@ -160,7 +163,6 @@ struct _mp_obj_type_t { get_array_n tuple list unpack seq list tuple - __next__ gen-instance */ }; @@ -178,6 +180,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_none(void); mp_obj_t mp_obj_new_bool(bool value); mp_obj_t mp_obj_new_cell(mp_obj_t obj); @@ -207,8 +210,6 @@ mp_obj_t mp_obj_new_dict(int n_args); mp_obj_t mp_obj_new_set(int n_args, mp_obj_t *items); mp_obj_t mp_obj_new_slice(mp_obj_t start, mp_obj_t stop, mp_obj_t step); mp_obj_t mp_obj_new_bound_meth(mp_obj_t self, mp_obj_t meth); -mp_obj_t mp_obj_new_class(struct _mp_map_t *class_locals); -mp_obj_t mp_obj_new_instance(mp_obj_t clas); mp_obj_t mp_obj_new_module(qstr module_name); mp_obj_t mp_obj_get_type(mp_obj_t o_in); @@ -278,6 +279,7 @@ void mp_obj_list_store(mp_obj_t self_in, mp_obj_t index, mp_obj_t value); extern const mp_obj_type_t dict_type; uint mp_obj_dict_len(mp_obj_t self_in); mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value); +struct _mp_map_t *mp_obj_dict_get_map(mp_obj_t self_in); // set extern const mp_obj_type_t set_type; @@ -307,15 +309,7 @@ void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte extern const mp_obj_type_t gen_instance_type; // class -extern const mp_obj_type_t class_type; -extern const mp_obj_t gen_instance_next_obj; -struct _mp_map_t *mp_obj_class_get_locals(mp_obj_t self_in); - -// instance -extern const mp_obj_type_t instance_type; -mp_obj_t mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr); -void mp_obj_instance_load_method(mp_obj_t self_in, qstr attr, mp_obj_t *dest); -void mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value); +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; diff --git a/py/objclass.c b/py/objclass.c deleted file mode 100644 index 3ecce54739..0000000000 --- a/py/objclass.c +++ /dev/null @@ -1,74 +0,0 @@ -#include -#include -#include -#include - -#include "nlr.h" -#include "misc.h" -#include "mpconfig.h" -#include "mpqstr.h" -#include "obj.h" -#include "runtime.h" -#include "map.h" - -typedef struct _mp_obj_class_t { - mp_obj_base_t base; - mp_map_t *locals; -} mp_obj_class_t; - -// args are in reverse order in the array -mp_obj_t class_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) { - // instantiate an instance of a class - - mp_obj_class_t *self = self_in; - - // make instance - mp_obj_t o = mp_obj_new_instance(self_in); - - // look for __init__ function - mp_map_elem_t *init_fn = mp_map_lookup(self->locals, MP_OBJ_NEW_QSTR(MP_QSTR___init__), MP_MAP_LOOKUP); - - if (init_fn != NULL) { - // call __init__ function - mp_obj_t init_ret; - if (n_args == 0) { - init_ret = rt_call_function_n(init_fn->value, 1, (mp_obj_t*)&o); - } else { - mp_obj_t *args2 = m_new(mp_obj_t, n_args + 1); - memcpy(args2, args, n_args * sizeof(mp_obj_t)); - args2[n_args] = o; - init_ret = rt_call_function_n(init_fn->value, n_args + 1, args2); - m_del(mp_obj_t, args2, n_args + 1); - } - if (init_ret != mp_const_none) { - nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "__init__() should return None, not '%s'", mp_obj_get_type_str(init_ret))); - } - - } else { - // TODO - if (n_args != 0) { - nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "function takes 0 positional arguments but %d were given", (void*)(machine_int_t)n_args)); - } - } - - return o; -} - -mp_map_t *mp_obj_class_get_locals(mp_obj_t self_in) { - assert(MP_OBJ_IS_TYPE(self_in, &class_type)); - mp_obj_class_t *self = self_in; - return self->locals; -} - -const mp_obj_type_t class_type = { - { &mp_const_type }, - "class", - .call_n = class_call_n, -}; - -mp_obj_t mp_obj_new_class(mp_map_t *class_locals) { - mp_obj_class_t *o = m_new_obj(mp_obj_class_t); - o->base.type = &class_type; - o->locals = class_locals; - return o; -} diff --git a/py/objdict.c b/py/objdict.c index 20e3e570d5..1c90998585 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -287,3 +287,9 @@ mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) { mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; return self_in; } + +mp_map_t *mp_obj_dict_get_map(mp_obj_t self_in) { + assert(MP_OBJ_IS_TYPE(self_in, &dict_type)); + mp_obj_dict_t *self = self_in; + return &self->map; +} diff --git a/py/objinstance.c b/py/objinstance.c deleted file mode 100644 index 9bb9acbd72..0000000000 --- a/py/objinstance.c +++ /dev/null @@ -1,103 +0,0 @@ -#include -#include -#include -#include - -#include "nlr.h" -#include "misc.h" -#include "mpconfig.h" -#include "mpqstr.h" -#include "obj.h" -#include "runtime.h" -#include "map.h" - -typedef struct _mp_obj_instance_t { - mp_obj_base_t base; - mp_obj_base_t *class; // points to a "class" object - mp_map_t *members; -} mp_obj_instance_t; - -/* -type needs to be specified dynamically - case O_OBJ: - { - py_map_elem_t *qn = py_qstr_map_lookup(o->u_obj.class->u_class.locals, qstr_from_str_static("__qualname__"), false); assert(qn != NULL); - assert(IS_O(qn->value, O_STR)); - return qstr_str(((py_obj_base_t*)qn->value)->u_str); - } - */ - -mp_obj_t mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr) { - // logic: look in obj members then class locals (TODO check this against CPython) - mp_obj_instance_t *self = self_in; - mp_map_elem_t *elem = mp_map_lookup(self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); - if (elem != NULL) { - // object member, always treated as a value - return elem->value; - } - elem = mp_map_lookup(mp_obj_class_get_locals(self->class), MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); - if (elem != NULL) { - if (mp_obj_is_callable(elem->value)) { - // class member is callable so build a bound method - return mp_obj_new_bound_meth(self_in, elem->value); - } else { - // class member is a value, so just return that value - return elem->value; - } - } - nlr_jump(mp_obj_new_exception_msg_2_args(MP_QSTR_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(self_in), qstr_str(attr))); -} - -void mp_obj_instance_load_method(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { - // logic: look in obj members then class locals (TODO check this against CPython) - mp_obj_instance_t *self = self_in; - mp_map_elem_t *elem = mp_map_lookup(self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); - if (elem != NULL) { - // object member, always treated as a value - dest[1] = elem->value; - dest[0] = NULL; - return; - } - elem = mp_map_lookup(mp_obj_class_get_locals(self->class), MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); - if (elem != NULL) { - if (mp_obj_is_callable(elem->value)) { - // class member is callable so build a bound method - dest[1] = elem->value; - dest[0] = self_in; - return; - } else { - // class member is a value, so just return that value - dest[1] = elem->value; - dest[0] = NULL; - return; - } - } - - // no such method, so fall back to load attr - dest[1] = rt_load_attr(self_in, attr); - dest[0] = NULL; -} - -void mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) { - // logic: look in class locals (no add) then obj members (add) (TODO check this against CPython) - mp_obj_instance_t *self = self_in; - mp_map_elem_t *elem = mp_map_lookup(mp_obj_class_get_locals(self->class), MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); - if (elem != NULL) { - elem->value = value; - } else { - mp_map_lookup(self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; - } -} - -const mp_obj_type_t instance_type = { - { &mp_const_type }, - "instance", -}; - -mp_obj_t mp_obj_new_instance(mp_obj_t class) { - mp_obj_instance_t *o = m_new_obj(mp_obj_instance_t); - o->base.type = &instance_type; - o->class = class; - o->members = mp_map_new(0); - return o; -} diff --git a/py/objmodule.c b/py/objmodule.c index a5183b51e3..ade9369176 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -17,15 +17,32 @@ typedef struct _mp_obj_module_t { mp_map_t *globals; } mp_obj_module_t; -void module_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) { +static void module_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) { mp_obj_module_t *self = self_in; print(env, "", qstr_str(self->name)); } +static void module_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + mp_obj_module_t *self = self_in; + mp_map_elem_t *elem = mp_map_lookup(self->globals, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); + if (elem != NULL) { + dest[1] = elem->value; + } +} + +static bool module_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) { + mp_obj_module_t *self = self_in; + // TODO CPython allows STORE_ATTR to a module, but is this the correct implementation? + mp_map_lookup(self->globals, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; + return true; +} + const mp_obj_type_t module_type = { { &mp_const_type }, "module", .print = module_print, + .load_attr = module_load_attr, + .store_attr = module_store_attr, }; mp_obj_t mp_obj_new_module(qstr module_name) { diff --git a/py/objtype.c b/py/objtype.c index 4a6025a70b..58854ca98d 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -1,11 +1,123 @@ #include #include +#include +#include #include "nlr.h" #include "misc.h" #include "mpconfig.h" #include "mpqstr.h" #include "obj.h" +#include "map.h" +#include "runtime.h" + +/******************************************************************************/ +// class object +// creating an instance of a class makes one of these objects + +typedef struct _mp_obj_class_t { + mp_obj_base_t base; + mp_map_t members; +} mp_obj_class_t; + +static mp_obj_t mp_obj_new_class(mp_obj_t class) { + mp_obj_class_t *o = m_new_obj(mp_obj_class_t); + o->base.type = class; + mp_map_init(&o->members, 0); + return o; +} + +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); +} + +// args are reverse in the array +static mp_obj_t class_make_new(mp_obj_t self_in, int n_args, const mp_obj_t *args) { + assert(MP_OBJ_IS_TYPE(self_in, &mp_const_type)); + + mp_obj_t o = mp_obj_new_class(self_in); + + // look for __init__ function + mp_map_elem_t *init_fn = mp_obj_class_lookup(self_in, MP_QSTR___init__, MP_MAP_LOOKUP); + + if (init_fn != NULL) { + // call __init__ function + mp_obj_t init_ret; + if (n_args == 0) { + init_ret = rt_call_function_n(init_fn->value, 1, (mp_obj_t*)&o); + } else { + mp_obj_t *args2 = m_new(mp_obj_t, n_args + 1); + memcpy(args2, args, n_args * sizeof(mp_obj_t)); + args2[n_args] = o; + init_ret = rt_call_function_n(init_fn->value, n_args + 1, args2); + m_del(mp_obj_t, args2, n_args + 1); + } + if (init_ret != mp_const_none) { + nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "__init__() should return None, not '%s'", mp_obj_get_type_str(init_ret))); + } + + } else { + // TODO + if (n_args != 0) { + nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "function takes 0 positional arguments but %d were given", (void*)(machine_int_t)n_args)); + } + } + + return o; +} + +static void class_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + // logic: look in obj members then class locals (TODO check this against CPython) + mp_obj_class_t *self = self_in; + mp_map_elem_t *elem = mp_map_lookup(&self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); + if (elem != NULL) { + // object member, always treated as a value + dest[1] = elem->value; + return; + } + elem = mp_obj_class_lookup((mp_obj_t)self->base.type, attr, MP_MAP_LOOKUP); + if (elem != NULL) { + if (mp_obj_is_callable(elem->value)) { + // class member is callable so build a bound method + dest[1] = elem->value; + dest[0] = self_in; + return; + } else { + // class member is a value, so just return that value + dest[1] = elem->value; + return; + } + } +} + +static bool class_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) { + // logic: look in class locals (no add) then obj members (add) (TODO check this against CPython) + mp_obj_class_t *self = self_in; + mp_map_elem_t *elem = mp_obj_class_lookup((mp_obj_t)self->base.type, attr, MP_MAP_LOOKUP); + if (elem != NULL) { + elem->value = value; + } else { + mp_map_lookup(&self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = 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 +// - there is a constant mp_obj_type_t (called mp_const_type) for the 'type' object +// - creating a new class (a new type) creates a new mp_obj_type_t static void type_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) { mp_obj_type_t *self = self_in; @@ -24,13 +136,7 @@ static mp_obj_t type_make_new(mp_obj_t type_in, int n_args, const mp_obj_t *args // args[1] = bases tuple // args[0] = locals dict - mp_obj_type_t *new_type = m_new0(mp_obj_type_t, 1); - new_type->base.type = &mp_const_type; - new_type->name = qstr_str(mp_obj_get_qstr(args[2])); - return new_type; - - //mp_obj_t new_class = mp_obj_new_class(mp_obj_get_qstr(args[2]), args[0]); - //return new_class; + return mp_obj_new_type(mp_obj_get_qstr(args[2]), args[0]); } default: @@ -38,14 +144,42 @@ static mp_obj_t type_make_new(mp_obj_t type_in, int n_args, const mp_obj_t *args } } +// args are in reverse order in the array static mp_obj_t type_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) { + // instantiate an instance of a class + mp_obj_type_t *self = self_in; - if (self->make_new != NULL) { - // TODO we need to init the object if it's an instance of a type - return self->make_new(self, n_args, args); - } else { + + if (self->make_new == NULL) { nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "cannot create '%s' instances", self->name)); } + + // make new instance + mp_obj_t o = self->make_new(self, n_args, args); + + // return new instance + return o; +} + +// for fail, do nothing; for attr, dest[1] = value; for method, dest[0] = self, dest[1] = method +static void type_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + mp_map_elem_t *elem = mp_obj_class_lookup(self_in, attr, MP_MAP_LOOKUP); + if (elem != NULL) { + dest[1] = elem->value; + return; + } +} + +static bool type_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) { + // TODO CPython allows STORE_ATTR to a class, but is this the correct implementation? + + mp_map_elem_t *elem = mp_obj_class_lookup(self_in, attr, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + if (elem != NULL) { + elem->value = value; + return true; + } else { + return false; + } } const mp_obj_type_t mp_const_type = { @@ -54,4 +188,19 @@ const mp_obj_type_t mp_const_type = { .print = type_print, .make_new = type_make_new, .call_n = type_call_n, + .load_attr = type_load_attr, + .store_attr = type_store_attr, }; + +mp_obj_t mp_obj_new_type(qstr name, mp_obj_t local_dict) { + mp_obj_type_t *o = m_new0(mp_obj_type_t, 1); + o->base.type = &mp_const_type; + o->name = qstr_str(name); + o->print = class_print; + 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 + return o; +} diff --git a/py/py.mk b/py/py.mk index c6c4e97058..a44a8bad02 100644 --- a/py/py.mk +++ b/py/py.mk @@ -74,7 +74,6 @@ PY_O_BASENAME = \ objbool.o \ objboundmeth.o \ objcell.o \ - objclass.o \ objclosure.o \ objcomplex.o \ objdict.o \ @@ -82,7 +81,6 @@ PY_O_BASENAME = \ objfloat.o \ objfun.o \ objgenerator.o \ - objinstance.o \ objint.o \ objlist.o \ objmodule.o \ diff --git a/py/runtime.c b/py/runtime.c index 3c92ca68cb..2c3ff803bc 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -188,8 +188,10 @@ void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, i DEBUG_printf(" %02x", code[i]); } DEBUG_printf("\n"); +#if MICROPY_SHOW_BC extern void mp_show_byte_code(const byte *code, int len); mp_show_byte_code(code, len); +#endif #ifdef WRITE_CODE if (fp_write_code != NULL) { @@ -775,85 +777,74 @@ mp_obj_t rt_store_map(mp_obj_t map, mp_obj_t key, mp_obj_t value) { } mp_obj_t rt_load_attr(mp_obj_t base, qstr attr) { - DEBUG_OP_printf("load attr %s\n", qstr_str(attr)); - if (MP_OBJ_IS_TYPE(base, &class_type)) { - mp_map_elem_t *elem = mp_map_lookup(mp_obj_class_get_locals(base), MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); - if (elem == NULL) { - // TODO what about generic method lookup? - goto no_attr; - } - return elem->value; - } else if (MP_OBJ_IS_TYPE(base, &instance_type)) { - return mp_obj_instance_load_attr(base, attr); - } else if (MP_OBJ_IS_TYPE(base, &module_type)) { - DEBUG_OP_printf("lookup module map %p\n", mp_obj_module_get_globals(base)); - mp_map_elem_t *elem = mp_map_lookup(mp_obj_module_get_globals(base), MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); - if (elem == NULL) { - // TODO what about generic method lookup? - goto no_attr; - } - return elem->value; - } else if (MP_OBJ_IS_OBJ(base)) { - // generic method lookup - mp_obj_base_t *o = base; - const mp_method_t *meth = o->type->methods; - if (meth != NULL) { - for (; meth->name != NULL; meth++) { - if (strcmp(meth->name, qstr_str(attr)) == 0) { - return mp_obj_new_bound_meth(base, (mp_obj_t)meth->fun); - } - } - } + DEBUG_OP_printf("load attr %p.%s\n", base, qstr_str(attr)); + // use load_method + mp_obj_t dest[2]; + rt_load_method(base, attr, dest); + if (dest[0] == NULL) { + // load_method returned just a normal attribute + return dest[1]; + } else { + // load_method returned a method, so build a bound method object + return mp_obj_new_bound_meth(dest[0], dest[1]); } - -no_attr: - nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(base), qstr_str(attr))); } void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) { - DEBUG_OP_printf("load method %s\n", qstr_str(attr)); - if (MP_OBJ_IS_TYPE(base, &gen_instance_type) && attr == MP_QSTR___next__) { - dest[1] = (mp_obj_t)&mp_builtin_next_obj; - dest[0] = base; - return; - } else if (MP_OBJ_IS_TYPE(base, &instance_type)) { - mp_obj_instance_load_method(base, attr, dest); - return; - } else if (MP_OBJ_IS_OBJ(base)) { - // generic method lookup - mp_obj_base_t *o = base; - const mp_method_t *meth = o->type->methods; - if (meth != NULL) { - for (; meth->name != NULL; meth++) { - if (strcmp(meth->name, qstr_str(attr)) == 0) { - dest[1] = (mp_obj_t)meth->fun; - dest[0] = base; - return; + DEBUG_OP_printf("load method %p.%s\n", base, qstr_str(attr)); + + // clear output to indicate no attribute/method found yet + dest[0] = MP_OBJ_NULL; + dest[1] = MP_OBJ_NULL; + + // get the type + mp_obj_type_t *type = mp_obj_get_type(base); + + // if this type can do its own load, then call it + if (type->load_attr != NULL) { + type->load_attr(base, attr, dest); + } + + // if nothing found yet, look for built-in and generic names + if (dest[1] == NULL) { + if (attr == MP_QSTR___next__ && type->iternext != NULL) { + dest[1] = (mp_obj_t)&mp_builtin_next_obj; + dest[0] = base; + } else { + // generic method lookup + const mp_method_t *meth = type->methods; + if (meth != NULL) { + for (; meth->name != NULL; meth++) { + if (strcmp(meth->name, qstr_str(attr)) == 0) { + dest[1] = (mp_obj_t)meth->fun; + dest[0] = base; + break; + } } } } } - // no method; fallback to load_attr - dest[1] = rt_load_attr(base, attr); - dest[0] = NULL; + if (dest[1] == NULL) { + // no attribute/method called attr + // following CPython, we give a more detailed error message for type objects + if (MP_OBJ_IS_TYPE(base, &mp_const_type)) { + nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_AttributeError, "type object '%s' has no attribute '%s'", ((mp_obj_type_t*)base)->name, qstr_str(attr))); + } else { + nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(base), qstr_str(attr))); + } + } } void rt_store_attr(mp_obj_t base, qstr attr, mp_obj_t value) { DEBUG_OP_printf("store attr %p.%s <- %p\n", base, qstr_str(attr), value); - if (MP_OBJ_IS_TYPE(base, &class_type)) { - // TODO CPython allows STORE_ATTR to a class, but is this the correct implementation? - mp_map_t *locals = mp_obj_class_get_locals(base); - mp_map_lookup(locals, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; - } else if (MP_OBJ_IS_TYPE(base, &instance_type)) { - mp_obj_instance_store_attr(base, attr, value); - } else if (MP_OBJ_IS_TYPE(base, &module_type)) { - // TODO CPython allows STORE_ATTR to a module, but is this the correct implementation? - mp_map_t *globals = mp_obj_module_get_globals(base); - mp_map_lookup(globals, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; - } else { - nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(base), qstr_str(attr))); + mp_obj_type_t *type = mp_obj_get_type(base); + if (type->store_attr != NULL) { + if (type->store_attr(base, attr, value)) { + return; + } } + nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(base), qstr_str(attr))); } void rt_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) { diff --git a/stm/pybwlan.c b/stm/pybwlan.c index 8da7bb937b..e890e10ca7 100644 --- a/stm/pybwlan.c +++ b/stm/pybwlan.c @@ -65,6 +65,8 @@ void decode_addr_and_store(mp_obj_t object, qstr q_attr, unsigned char *ip, int rt_store_attr(object, q_attr, decode_addr(ip, n_bytes)); } +static mp_obj_t net_address_type = MP_OBJ_NULL; + mp_obj_t pyb_wlan_get_ip(void) { tNetappIpconfigRetArgs ipconfig; netapp_ipconfig(&ipconfig); @@ -74,16 +76,24 @@ mp_obj_t pyb_wlan_get_ip(void) { return mp_const_none; } - mp_obj_t data = mp_obj_new_class(mp_map_new(0)); // TODO should this be an instance of a class? - decode_addr_and_store(data, qstr_from_str_static("ip"), &ipconfig.aucIP[0], 4); - decode_addr_and_store(data, qstr_from_str_static("subnet"), &ipconfig.aucSubnetMask[0], 4); - decode_addr_and_store(data, qstr_from_str_static("gateway"), &ipconfig.aucDefaultGateway[0], 4); - decode_addr_and_store(data, qstr_from_str_static("dhcp"), &ipconfig.aucDHCPServer[0], 4); - decode_addr_and_store(data, qstr_from_str_static("dns"), &ipconfig.aucDNSServer[0], 4); - decode_addr_and_store(data, qstr_from_str_static("mac"), &ipconfig.uaMacAddr[0], 6); - decode_addr_and_store(data, qstr_from_str_static("ssid"), &ipconfig.uaSSID[0], 32); + // 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)); + } - return data; + // make a new NetAddress object + mp_obj_t net_addr = rt_call_function_0(net_address_type); + + // fill the NetAddress object with data + decode_addr_and_store(net_addr, qstr_from_str_static("ip"), &ipconfig.aucIP[0], 4); + decode_addr_and_store(net_addr, qstr_from_str_static("subnet"), &ipconfig.aucSubnetMask[0], 4); + decode_addr_and_store(net_addr, qstr_from_str_static("gateway"), &ipconfig.aucDefaultGateway[0], 4); + decode_addr_and_store(net_addr, qstr_from_str_static("dhcp"), &ipconfig.aucDHCPServer[0], 4); + decode_addr_and_store(net_addr, qstr_from_str_static("dns"), &ipconfig.aucDNSServer[0], 4); + decode_addr_and_store(net_addr, qstr_from_str_static("mac"), &ipconfig.uaMacAddr[0], 6); + decode_addr_and_store(net_addr, qstr_from_str_static("ssid"), &ipconfig.uaSSID[0], 32); + + return net_addr; } uint32_t last_ip = 0; // XXX such a hack! diff --git a/tests/basics/tests/class2.py b/tests/basics/tests/class2.py index 0b3b218672..64f1f62b96 100644 --- a/tests/basics/tests/class2.py +++ b/tests/basics/tests/class2.py @@ -5,6 +5,7 @@ class C1: self.x = 1 c1 = C1() +print(type(c1) == C1) print(c1.x) class C2: @@ -12,4 +13,5 @@ class C2: self.x = x c2 = C2(4) +print(type(c2) == C2) print(c2.x) diff --git a/unix/main.c b/unix/main.c index b29a99f4fa..caec900a52 100644 --- a/unix/main.c +++ b/unix/main.c @@ -210,6 +210,18 @@ int main(int argc, char **argv) { rt_store_name(qstr_from_str_static("test"), test_obj_new(42)); rt_store_name(qstr_from_str_static("open"), (mp_obj_t)&mp_builtin_open_obj); + // Here is some example code to create a class and instance of that class. + // First is the Python, then the C code. + // + // class TestClass: + // pass + // 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)); + 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)); + /* printf("bytes:\n"); printf(" total %d\n", m_get_total_bytes_allocated());