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