circuitpython/py/objinstance.c
Damien d99b05282d Change object representation from 1 big union to individual structs.
A big change.  Micro Python objects are allocated as individual structs
with the first element being a pointer to the type information (which
is itself an object).  This scheme follows CPython.  Much more flexible,
not necessarily slower, uses same heap memory, and can allocate objects
statically.

Also change name prefix, from py_ to mp_ (mp for Micro Python).
2013-12-21 18:17:45 +00:00

110 lines
3.6 KiB
C

#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include "nlr.h"
#include "misc.h"
#include "mpconfig.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_qstr_map_lookup(self->members, attr, false);
if (elem != NULL) {
// object member, always treated as a value
return elem->value;
}
elem = mp_qstr_map_lookup(mp_obj_class_get_locals(self->class), attr, false);
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(rt_q_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_qstr_map_lookup(self->members, attr, false);
if (elem != NULL) {
// object member, always treated as a value
dest[1] = elem->value;
dest[0] = NULL;
return;
}
elem = mp_qstr_map_lookup(mp_obj_class_get_locals(self->class), attr, false);
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_qstr_map_lookup(mp_obj_class_get_locals(self->class), attr, false);
if (elem != NULL) {
elem->value = value;
} else {
mp_qstr_map_lookup(self->members, attr, true)->value = value;
}
}
const mp_obj_type_t instance_type = {
{ &mp_const_type },
"instance",
NULL, // print
NULL, // call_n
NULL, // unary_op
NULL, // binary_op
NULL, // getiter
NULL, // iternext
{{NULL, NULL},}, // method list
};
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(MP_MAP_QSTR, 0);
return o;
}