d99b05282d
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).
110 lines
3.6 KiB
C
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;
|
|
}
|