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.
This commit is contained in:
parent
d944a66ead
commit
062478e66d
12
py/builtin.c
12
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) {
|
||||
|
2
py/map.h
2
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,
|
||||
|
28
py/obj.h
28
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;
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
103
py/objinstance.c
103
py/objinstance.c
@ -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;
|
||||
}
|
@ -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) {
|
||||
|
171
py/objtype.c
171
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;
|
||||
}
|
||||
|
2
py/py.mk
2
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 \
|
||||
|
121
py/runtime.c
121
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) {
|
||||
|
@ -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!
|
||||
|
@ -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)
|
||||
|
12
unix/main.c
12
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());
|
||||
|
Loading…
Reference in New Issue
Block a user