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:
Damien George 2014-01-09 20:57:50 +00:00
parent d944a66ead
commit 062478e66d
13 changed files with 288 additions and 292 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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