From b1bbe966c408901ae64ea8c8b468694c47d05b1a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 1 Apr 2015 14:10:50 +0000 Subject: [PATCH] py: Combine load_attr and store_attr type methods into one (attr). This simplifies the API for objects and reduces code size (by around 400 bytes on Thumb2, and around 2k on x86). Performance impact was measured with Pystone score, but change was barely noticeable. --- extmod/moductypes.c | 21 +++++---- py/obj.h | 17 +++++-- py/objboundmeth.c | 8 +++- py/objcomplex.c | 8 +++- py/objexcept.c | 10 ++-- py/objfun.c | 8 +++- py/objmodule.c | 64 +++++++++++++------------- py/objnamedtuple.c | 29 ++++++------ py/objrange.c | 8 +++- py/objtype.c | 108 ++++++++++++++++++++++++-------------------- py/objtype.h | 5 +- py/runtime.c | 11 +++-- py/vm.c | 4 +- 13 files changed, 173 insertions(+), 128 deletions(-) diff --git a/extmod/moductypes.c b/extmod/moductypes.c index 7e7128a269..f9f0ca79bc 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -482,13 +482,17 @@ STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set return MP_OBJ_NULL; } -STATIC void uctypes_struct_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { - mp_obj_t val = uctypes_struct_attr_op(self_in, attr, MP_OBJ_NULL); - *dest = val; -} - -STATIC bool uctypes_struct_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t val) { - return uctypes_struct_attr_op(self_in, attr, val) != MP_OBJ_NULL; +STATIC void uctypes_struct_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] == MP_OBJ_NULL) { + // load attribute + mp_obj_t val = uctypes_struct_attr_op(self_in, attr, MP_OBJ_NULL); + dest[0] = val; + } else { + // delete/store attribute + if (uctypes_struct_attr_op(self_in, attr, dest[1]) != MP_OBJ_NULL) { + dest[0] = MP_OBJ_NULL; // indicate success + } + } } STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) { @@ -589,8 +593,7 @@ STATIC const mp_obj_type_t uctypes_struct_type = { .name = MP_QSTR_struct, .print = uctypes_struct_print, .make_new = uctypes_struct_make_new, - .load_attr = uctypes_struct_load_attr, - .store_attr = uctypes_struct_store_attr, + .attr = uctypes_struct_attr, .subscr = uctypes_struct_subscr, }; diff --git a/py/obj.h b/py/obj.h index 448db762af..a5423f0824 100644 --- a/py/obj.h +++ b/py/obj.h @@ -265,8 +265,7 @@ typedef mp_obj_t (*mp_make_new_fun_t)(mp_obj_t type_in, mp_uint_t n_args, mp_uin typedef mp_obj_t (*mp_call_fun_t)(mp_obj_t fun, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args); typedef mp_obj_t (*mp_unary_op_fun_t)(mp_uint_t op, mp_obj_t); typedef mp_obj_t (*mp_binary_op_fun_t)(mp_uint_t 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[0] = value; for method, dest[0] = method, dest[1] = self -typedef bool (*mp_store_attr_fun_t)(mp_obj_t self_in, qstr attr, mp_obj_t value); // return true if store succeeded; if value==MP_OBJ_NULL then delete +typedef void (*mp_attr_fun_t)(mp_obj_t self_in, qstr attr, mp_obj_t *dest); typedef mp_obj_t (*mp_subscr_fun_t)(mp_obj_t self_in, mp_obj_t index, mp_obj_t value); typedef struct _mp_method_t { @@ -330,8 +329,18 @@ struct _mp_obj_type_t { mp_unary_op_fun_t unary_op; // can return MP_OBJ_NULL if op not supported mp_binary_op_fun_t binary_op; // can return MP_OBJ_NULL if op not supported - mp_load_attr_fun_t load_attr; - mp_store_attr_fun_t store_attr; // if value is MP_OBJ_NULL, then delete that attribute + // implements load, store and delete attribute + // + // dest[0] = MP_OBJ_NULL means load + // return: for fail, do nothing + // for attr, dest[0] = value + // for method, dest[0] = method, dest[1] = self + // + // dest[0,1] = {MP_OBJ_SENTINEL, MP_OBJ_NULL} means delete + // dest[0,1] = {MP_OBJ_SENTINEL, object} means store + // return: for fail, do nothing + // for success set dest[0] = MP_OBJ_NULL + mp_attr_fun_t attr; mp_subscr_fun_t subscr; // implements load, store, delete subscripting // value=MP_OBJ_NULL means delete, value=MP_OBJ_SENTINEL means load, else store diff --git a/py/objboundmeth.c b/py/objboundmeth.c index 84d201277f..0f9ff08c81 100644 --- a/py/objboundmeth.c +++ b/py/objboundmeth.c @@ -71,7 +71,11 @@ STATIC mp_obj_t bound_meth_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_ } #if MICROPY_PY_FUNCTION_ATTRS -STATIC void bound_meth_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { +STATIC void bound_meth_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } if (attr == MP_QSTR___name__) { mp_obj_bound_meth_t *o = self_in; dest[0] = MP_OBJ_NEW_QSTR(mp_obj_fun_get_name(o->meth)); @@ -87,7 +91,7 @@ STATIC const mp_obj_type_t mp_type_bound_meth = { #endif .call = bound_meth_call, #if MICROPY_PY_FUNCTION_ATTRS - .load_attr = bound_meth_load_attr, + .attr = bound_meth_attr, #endif }; diff --git a/py/objcomplex.c b/py/objcomplex.c index 1415068438..f4a68851a5 100644 --- a/py/objcomplex.c +++ b/py/objcomplex.c @@ -141,7 +141,11 @@ STATIC mp_obj_t complex_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_in return mp_obj_complex_binary_op(op, lhs->real, lhs->imag, rhs_in); } -STATIC void complex_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { +STATIC void complex_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } mp_obj_complex_t *self = self_in; if (attr == MP_QSTR_real) { dest[0] = mp_obj_new_float(self->real); @@ -157,7 +161,7 @@ const mp_obj_type_t mp_type_complex = { .make_new = complex_make_new, .unary_op = complex_unary_op, .binary_op = complex_binary_op, - .load_attr = complex_load_attr, + .attr = complex_attr, }; mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag) { diff --git a/py/objexcept.c b/py/objexcept.c index 987a54fda7..2d23f160a2 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -140,7 +140,11 @@ mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in) { } } -STATIC void exception_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { +STATIC void exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } mp_obj_exception_t *self = self_in; if (attr == MP_QSTR_args) { dest[0] = self->args; @@ -168,7 +172,7 @@ const mp_obj_type_t mp_type_BaseException = { .name = MP_QSTR_BaseException, .print = mp_obj_exception_print, .make_new = mp_obj_exception_make_new, - .load_attr = exception_load_attr, + .attr = exception_attr, .locals_dict = (mp_obj_t)&exc_locals_dict, }; @@ -181,7 +185,7 @@ const mp_obj_type_t mp_type_ ## exc_name = { \ .name = MP_QSTR_ ## exc_name, \ .print = mp_obj_exception_print, \ .make_new = mp_obj_exception_make_new, \ - .load_attr = exception_load_attr, \ + .attr = exception_attr, \ .bases_tuple = (mp_obj_t)&mp_type_ ## base_name ## _base_tuple, \ }; diff --git a/py/objfun.c b/py/objfun.c index 76adfef500..f00a90a089 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -296,7 +296,11 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, } #if MICROPY_PY_FUNCTION_ATTRS -STATIC void fun_bc_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { +STATIC void fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } if (attr == MP_QSTR___name__) { dest[0] = MP_OBJ_NEW_QSTR(mp_obj_fun_get_name(self_in)); } @@ -311,7 +315,7 @@ const mp_obj_type_t mp_type_fun_bc = { #endif .call = fun_bc_call, #if MICROPY_PY_FUNCTION_ATTRS - .load_attr = fun_bc_load_attr, + .attr = fun_bc_attr, #endif }; diff --git a/py/objmodule.c b/py/objmodule.c index 02292ff785..971c7f38c5 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -51,48 +51,48 @@ STATIC void module_print(void (*print)(void *env, const char *fmt, ...), void *e print(env, "", name); } -STATIC void module_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { +STATIC void module_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->map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); - if (elem != NULL) { - dest[0] = 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; - mp_obj_dict_t *dict = self->globals; - if (dict->map.is_fixed) { - #if MICROPY_CAN_OVERRIDE_BUILTINS - if (dict == &mp_module_builtins_globals) { - if (MP_STATE_VM(mp_module_builtins_override_dict) == NULL) { - MP_STATE_VM(mp_module_builtins_override_dict) = mp_obj_new_dict(1); - } - dict = MP_STATE_VM(mp_module_builtins_override_dict); - } else - #endif - { - // can't delete or store to fixed map - return false; + if (dest[0] == MP_OBJ_NULL) { + // load attribute + mp_map_elem_t *elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); + if (elem != NULL) { + dest[0] = elem->value; } - } - if (value == MP_OBJ_NULL) { - // delete attribute - mp_obj_dict_delete(dict, MP_OBJ_NEW_QSTR(attr)); } else { - // store attribute - // TODO CPython allows STORE_ATTR to a module, but is this the correct implementation? - mp_obj_dict_store(dict, MP_OBJ_NEW_QSTR(attr), value); + // delete/store attribute + mp_obj_dict_t *dict = self->globals; + if (dict->map.is_fixed) { + #if MICROPY_CAN_OVERRIDE_BUILTINS + if (dict == &mp_module_builtins_globals) { + if (MP_STATE_VM(mp_module_builtins_override_dict) == NULL) { + MP_STATE_VM(mp_module_builtins_override_dict) = mp_obj_new_dict(1); + } + dict = MP_STATE_VM(mp_module_builtins_override_dict); + } else + #endif + { + // can't delete or store to fixed map + return; + } + } + if (dest[1] == MP_OBJ_NULL) { + // delete attribute + mp_obj_dict_delete(dict, MP_OBJ_NEW_QSTR(attr)); + } else { + // store attribute + // TODO CPython allows STORE_ATTR to a module, but is this the correct implementation? + mp_obj_dict_store(dict, MP_OBJ_NEW_QSTR(attr), dest[1]); + } + dest[0] = MP_OBJ_NULL; // indicate success } - return true; } const mp_obj_type_t mp_type_module = { { &mp_type_type }, .name = MP_QSTR_module, .print = module_print, - .load_attr = module_load_attr, - .store_attr = module_store_attr, + .attr = module_attr, }; mp_obj_t mp_obj_new_module(qstr module_name) { diff --git a/py/objnamedtuple.c b/py/objnamedtuple.c index 1996b41219..9cc6da1b7a 100644 --- a/py/objnamedtuple.c +++ b/py/objnamedtuple.c @@ -68,20 +68,20 @@ STATIC void namedtuple_print(void (*print)(void *env, const char *fmt, ...), voi print(env, ")"); } -STATIC void namedtuple_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { - mp_obj_namedtuple_t *self = self_in; - int id = namedtuple_find_field((mp_obj_namedtuple_type_t*)self->tuple.base.type, attr); - if (id == -1) { - return; +STATIC void namedtuple_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] == MP_OBJ_NULL) { + // load attribute + mp_obj_namedtuple_t *self = self_in; + int id = namedtuple_find_field((mp_obj_namedtuple_type_t*)self->tuple.base.type, attr); + if (id == -1) { + return; + } + dest[0] = self->tuple.items[id]; + } else { + // delete/store attribute + // provide more detailed error message than we'd get by just returning + nlr_raise(mp_obj_new_exception_msg(&mp_type_AttributeError, "can't set attribute")); } - dest[0] = self->tuple.items[id]; -} - -STATIC bool namedtuple_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) { - (void)self_in; - (void)attr; - (void)value; - nlr_raise(mp_obj_new_exception_msg(&mp_type_AttributeError, "can't set attribute")); } STATIC mp_obj_t namedtuple_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { @@ -154,8 +154,7 @@ STATIC mp_obj_t mp_obj_new_namedtuple_type(qstr name, mp_uint_t n_fields, mp_obj o->base.make_new = namedtuple_make_new; o->base.unary_op = mp_obj_tuple_unary_op; o->base.binary_op = mp_obj_tuple_binary_op; - o->base.load_attr = namedtuple_load_attr; - o->base.store_attr = namedtuple_store_attr; + o->base.attr = namedtuple_attr; o->base.subscr = mp_obj_tuple_subscr; o->base.getiter = mp_obj_tuple_getiter; o->base.bases_tuple = (mp_obj_t)&namedtuple_base_tuple; diff --git a/py/objrange.c b/py/objrange.c index ff62cc5b81..9f7e6591e1 100644 --- a/py/objrange.c +++ b/py/objrange.c @@ -168,7 +168,11 @@ STATIC mp_obj_t range_getiter(mp_obj_t o_in) { #if MICROPY_PY_BUILTINS_RANGE_ATTRS -STATIC void range_load_attr(mp_obj_t o_in, qstr attr, mp_obj_t *dest) { +STATIC void range_attr(mp_obj_t o_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } mp_obj_range_t *o = o_in; if (attr == MP_QSTR_start) { dest[0] = mp_obj_new_int(o->start); @@ -189,6 +193,6 @@ const mp_obj_type_t mp_type_range = { .subscr = range_subscr, .getiter = range_getiter, #if MICROPY_PY_BUILTINS_RANGE_ATTRS - .load_attr = range_load_attr, + .attr = range_attr, #endif }; diff --git a/py/objtype.c b/py/objtype.c index a5fb17f2ce..cb7110572d 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -440,7 +440,7 @@ STATIC mp_obj_t instance_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_i } } -void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { +STATIC void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { // logic: look in instance members then class locals assert(is_instance_type(mp_obj_get_type(self_in))); mp_obj_instance_t *self = self_in; @@ -512,7 +512,7 @@ void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } } -bool mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) { +STATIC bool mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) { mp_obj_instance_t *self = self_in; #if MICROPY_PY_BUILTINS_PROPERTY || MICROPY_PY_DESCRIPTORS @@ -602,6 +602,16 @@ bool mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) { } } +void mp_obj_instance_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] == MP_OBJ_NULL) { + mp_obj_instance_load_attr(self_in, attr, dest); + } else { + if (mp_obj_instance_store_attr(self_in, attr, dest[1])) { + dest[0] = MP_OBJ_NULL; // indicate success + } + } +} + STATIC mp_obj_t instance_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { mp_obj_instance_t *self = self_in; mp_obj_t member[2] = {MP_OBJ_NULL}; @@ -774,52 +784,52 @@ STATIC mp_obj_t type_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, co return o; } -// for fail, do nothing; for attr, dest[0] = value; for method, dest[0] = method, dest[1] = self -STATIC void type_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { - assert(MP_OBJ_IS_TYPE(self_in, &mp_type_type)); - mp_obj_type_t *self = self_in; -#if MICROPY_CPYTHON_COMPAT - if (attr == MP_QSTR___name__) { - dest[0] = MP_OBJ_NEW_QSTR(self->name); - return; - } -#endif - struct class_lookup_data lookup = { - .obj = self_in, - .attr = attr, - .meth_offset = 0, - .dest = dest, - .is_type = true, - }; - mp_obj_class_lookup(&lookup, self); -} - -STATIC bool type_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) { +STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { assert(MP_OBJ_IS_TYPE(self_in, &mp_type_type)); mp_obj_type_t *self = self_in; - // TODO CPython allows STORE_ATTR to a class, but is this the correct implementation? + if (dest[0] == MP_OBJ_NULL) { + // load attribute + #if MICROPY_CPYTHON_COMPAT + if (attr == MP_QSTR___name__) { + dest[0] = MP_OBJ_NEW_QSTR(self->name); + return; + } + #endif + struct class_lookup_data lookup = { + .obj = self_in, + .attr = attr, + .meth_offset = 0, + .dest = dest, + .is_type = true, + }; + mp_obj_class_lookup(&lookup, self); + } else { + // delete/store attribute - if (self->locals_dict != NULL) { - assert(MP_OBJ_IS_TYPE(self->locals_dict, &mp_type_dict)); // Micro Python restriction, for now - mp_map_t *locals_map = mp_obj_dict_get_map(self->locals_dict); - if (value == MP_OBJ_NULL) { - // delete attribute - mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_REMOVE_IF_FOUND); - // note that locals_map may be in ROM, so remove will fail in that case - return elem != NULL; - } else { - // store attribute - mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); - // note that locals_map may be in ROM, so add will fail in that case - if (elem != NULL) { - elem->value = value; - return true; + // TODO CPython allows STORE_ATTR to a class, but is this the correct implementation? + + if (self->locals_dict != NULL) { + assert(MP_OBJ_IS_TYPE(self->locals_dict, &mp_type_dict)); // Micro Python restriction, for now + mp_map_t *locals_map = mp_obj_dict_get_map(self->locals_dict); + if (dest[1] == MP_OBJ_NULL) { + // delete attribute + mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_REMOVE_IF_FOUND); + // note that locals_map may be in ROM, so remove will fail in that case + if (elem != NULL) { + dest[0] = MP_OBJ_NULL; // indicate success + } + } else { + // store attribute + mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + // note that locals_map may be in ROM, so add will fail in that case + if (elem != NULL) { + elem->value = dest[1]; + dest[0] = MP_OBJ_NULL; // indicate success + } } } } - - return false; } const mp_obj_type_t mp_type_type = { @@ -828,8 +838,7 @@ const mp_obj_type_t mp_type_type = { .print = type_print, .make_new = type_make_new, .call = type_call, - .load_attr = type_load_attr, - .store_attr = type_store_attr, + .attr = type_attr, }; mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) { @@ -865,8 +874,7 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) o->call = mp_obj_instance_call; o->unary_op = instance_unary_op; o->binary_op = instance_binary_op; - o->load_attr = mp_obj_instance_load_attr; - o->store_attr = mp_obj_instance_store_attr; + o->attr = mp_obj_instance_attr; o->subscr = instance_subscr; o->getiter = instance_getiter; //o->iternext = ; not implemented @@ -921,8 +929,12 @@ STATIC mp_obj_t super_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_k return mp_obj_new_super(args[0], args[1]); } -// for fail, do nothing; for attr, dest[0] = value; for method, dest[0] = method, dest[1] = self -STATIC void super_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { +STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } + assert(MP_OBJ_IS_TYPE(self_in, &mp_type_super)); mp_obj_super_t *self = self_in; @@ -960,7 +972,7 @@ const mp_obj_type_t mp_type_super = { .name = MP_QSTR_super, .print = super_print, .make_new = super_make_new, - .load_attr = super_load_attr, + .attr = super_attr, }; mp_obj_t mp_obj_new_super(mp_obj_t type, mp_obj_t obj) { diff --git a/py/objtype.h b/py/objtype.h index 176e802d24..de1909b1e1 100644 --- a/py/objtype.h +++ b/py/objtype.h @@ -37,9 +37,8 @@ typedef struct _mp_obj_instance_t { // TODO maybe cache __getattr__ and __setattr__ for efficient lookup of them } mp_obj_instance_t; -// these need to be exposed for MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE to work -void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest); -bool mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value); +// this needs to be exposed for MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE to work +void mp_obj_instance_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest); // these need to be exposed so mp_obj_is_callable can work correctly bool mp_obj_instance_is_callable(mp_obj_t self_in); diff --git a/py/runtime.c b/py/runtime.c index 2bb9a02641..b04a4af139 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -917,9 +917,9 @@ void mp_load_method_maybe(mp_obj_t obj, qstr attr, mp_obj_t *dest) { dest[0] = (mp_obj_t)&mp_builtin_next_obj; dest[1] = obj; - } else if (type->load_attr != NULL) { + } else if (type->attr != NULL) { // this type can do its own load, so call it - type->load_attr(obj, attr, dest); + type->attr(obj, attr, dest); } else if (type->locals_dict != NULL) { // generic method lookup @@ -961,8 +961,11 @@ void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) { void mp_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); mp_obj_type_t *type = mp_obj_get_type(base); - if (type->store_attr != NULL) { - if (type->store_attr(base, attr, value)) { + if (type->attr != NULL) { + mp_obj_t dest[2] = {MP_OBJ_SENTINEL, value}; + type->attr(base, attr, dest); + if (dest[0] == MP_OBJ_NULL) { + // success return; } } diff --git a/py/vm.c b/py/vm.c index d81f5580e2..55203b0748 100644 --- a/py/vm.c +++ b/py/vm.c @@ -315,7 +315,7 @@ dispatch_loop: MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_obj_t top = TOP(); - if (mp_obj_get_type(top)->load_attr == mp_obj_instance_load_attr) { + if (mp_obj_get_type(top)->attr == mp_obj_instance_attr) { mp_obj_instance_t *self = top; mp_uint_t x = *ip; mp_obj_t key = MP_OBJ_NEW_QSTR(qst); @@ -405,7 +405,7 @@ dispatch_loop: MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_obj_t top = TOP(); - if (mp_obj_get_type(top)->store_attr == mp_obj_instance_store_attr && sp[-1] != MP_OBJ_NULL) { + if (mp_obj_get_type(top)->attr == mp_obj_instance_attr && sp[-1] != MP_OBJ_NULL) { mp_obj_instance_t *self = top; mp_uint_t x = *ip; mp_obj_t key = MP_OBJ_NEW_QSTR(qst);