summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2014-01-09 20:57:50 +0000
committerDamien George <damien.p.george@gmail.com>2014-01-09 20:57:50 +0000
commit062478e66d03cc59ed73484a3032ef5f08db12a4 (patch)
tree6d3beb59ade6fe3d1ab7a5f05ff876e63062717f
parentd944a66ead38b15b5a3c7a4ae8b054451fd5c077 (diff)
downloadmicropython-062478e66d03cc59ed73484a3032ef5f08db12a4.tar.gz
micropython-062478e66d03cc59ed73484a3032ef5f08db12a4.zip
Improved type/class/instance code; mp_obj_type_t now has load_attr, store_attr.
Creating of classes (types) and instances is much more like CPython now. You can use "type('name', (), {...})" to create classes.
-rw-r--r--py/builtin.c12
-rw-r--r--py/map.h2
-rw-r--r--py/obj.h28
-rw-r--r--py/objclass.c74
-rw-r--r--py/objdict.c6
-rw-r--r--py/objinstance.c103
-rw-r--r--py/objmodule.c19
-rw-r--r--py/objtype.c171
-rw-r--r--py/py.mk2
-rw-r--r--py/runtime.c121
-rw-r--r--stm/pybwlan.c28
-rw-r--r--tests/basics/tests/class2.py2
-rw-r--r--unix/main.c12
13 files changed, 288 insertions, 292 deletions
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 <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <assert.h>
-
-#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 <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <assert.h>
-
-#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, "<module '%s' from '-unknown-file-'>", 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 <stdlib.h>
#include <stdint.h>
+#include <string.h>
+#include <assert.h>
#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));
+ }
+
+ // 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 data;
+ 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());