Skip to content

Commit b1bbe96

Browse files
committed
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.
1 parent d07ccc5 commit b1bbe96

13 files changed

Lines changed: 172 additions & 127 deletions

File tree

extmod/moductypes.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -482,13 +482,17 @@ STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set
482482
return MP_OBJ_NULL;
483483
}
484484

485-
STATIC void uctypes_struct_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
486-
mp_obj_t val = uctypes_struct_attr_op(self_in, attr, MP_OBJ_NULL);
487-
*dest = val;
488-
}
489-
490-
STATIC bool uctypes_struct_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t val) {
491-
return uctypes_struct_attr_op(self_in, attr, val) != MP_OBJ_NULL;
485+
STATIC void uctypes_struct_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
486+
if (dest[0] == MP_OBJ_NULL) {
487+
// load attribute
488+
mp_obj_t val = uctypes_struct_attr_op(self_in, attr, MP_OBJ_NULL);
489+
dest[0] = val;
490+
} else {
491+
// delete/store attribute
492+
if (uctypes_struct_attr_op(self_in, attr, dest[1]) != MP_OBJ_NULL) {
493+
dest[0] = MP_OBJ_NULL; // indicate success
494+
}
495+
}
492496
}
493497

494498
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 = {
589593
.name = MP_QSTR_struct,
590594
.print = uctypes_struct_print,
591595
.make_new = uctypes_struct_make_new,
592-
.load_attr = uctypes_struct_load_attr,
593-
.store_attr = uctypes_struct_store_attr,
596+
.attr = uctypes_struct_attr,
594597
.subscr = uctypes_struct_subscr,
595598
};
596599

py/obj.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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
265265
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);
266266
typedef mp_obj_t (*mp_unary_op_fun_t)(mp_uint_t op, mp_obj_t);
267267
typedef mp_obj_t (*mp_binary_op_fun_t)(mp_uint_t op, mp_obj_t, mp_obj_t);
268-
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
269-
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
268+
typedef void (*mp_attr_fun_t)(mp_obj_t self_in, qstr attr, mp_obj_t *dest);
270269
typedef mp_obj_t (*mp_subscr_fun_t)(mp_obj_t self_in, mp_obj_t index, mp_obj_t value);
271270

272271
typedef struct _mp_method_t {
@@ -330,8 +329,18 @@ struct _mp_obj_type_t {
330329
mp_unary_op_fun_t unary_op; // can return MP_OBJ_NULL if op not supported
331330
mp_binary_op_fun_t binary_op; // can return MP_OBJ_NULL if op not supported
332331

333-
mp_load_attr_fun_t load_attr;
334-
mp_store_attr_fun_t store_attr; // if value is MP_OBJ_NULL, then delete that attribute
332+
// implements load, store and delete attribute
333+
//
334+
// dest[0] = MP_OBJ_NULL means load
335+
// return: for fail, do nothing
336+
// for attr, dest[0] = value
337+
// for method, dest[0] = method, dest[1] = self
338+
//
339+
// dest[0,1] = {MP_OBJ_SENTINEL, MP_OBJ_NULL} means delete
340+
// dest[0,1] = {MP_OBJ_SENTINEL, object} means store
341+
// return: for fail, do nothing
342+
// for success set dest[0] = MP_OBJ_NULL
343+
mp_attr_fun_t attr;
335344

336345
mp_subscr_fun_t subscr; // implements load, store, delete subscripting
337346
// value=MP_OBJ_NULL means delete, value=MP_OBJ_SENTINEL means load, else store

py/objboundmeth.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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_
7171
}
7272

7373
#if MICROPY_PY_FUNCTION_ATTRS
74-
STATIC void bound_meth_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
74+
STATIC void bound_meth_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
75+
if (dest[0] != MP_OBJ_NULL) {
76+
// not load attribute
77+
return;
78+
}
7579
if (attr == MP_QSTR___name__) {
7680
mp_obj_bound_meth_t *o = self_in;
7781
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 = {
8791
#endif
8892
.call = bound_meth_call,
8993
#if MICROPY_PY_FUNCTION_ATTRS
90-
.load_attr = bound_meth_load_attr,
94+
.attr = bound_meth_attr,
9195
#endif
9296
};
9397

py/objcomplex.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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
141141
return mp_obj_complex_binary_op(op, lhs->real, lhs->imag, rhs_in);
142142
}
143143

144-
STATIC void complex_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
144+
STATIC void complex_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
145+
if (dest[0] != MP_OBJ_NULL) {
146+
// not load attribute
147+
return;
148+
}
145149
mp_obj_complex_t *self = self_in;
146150
if (attr == MP_QSTR_real) {
147151
dest[0] = mp_obj_new_float(self->real);
@@ -157,7 +161,7 @@ const mp_obj_type_t mp_type_complex = {
157161
.make_new = complex_make_new,
158162
.unary_op = complex_unary_op,
159163
.binary_op = complex_binary_op,
160-
.load_attr = complex_load_attr,
164+
.attr = complex_attr,
161165
};
162166

163167
mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag) {

py/objexcept.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,11 @@ mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in) {
140140
}
141141
}
142142

143-
STATIC void exception_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
143+
STATIC void exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
144+
if (dest[0] != MP_OBJ_NULL) {
145+
// not load attribute
146+
return;
147+
}
144148
mp_obj_exception_t *self = self_in;
145149
if (attr == MP_QSTR_args) {
146150
dest[0] = self->args;
@@ -168,7 +172,7 @@ const mp_obj_type_t mp_type_BaseException = {
168172
.name = MP_QSTR_BaseException,
169173
.print = mp_obj_exception_print,
170174
.make_new = mp_obj_exception_make_new,
171-
.load_attr = exception_load_attr,
175+
.attr = exception_attr,
172176
.locals_dict = (mp_obj_t)&exc_locals_dict,
173177
};
174178

@@ -181,7 +185,7 @@ const mp_obj_type_t mp_type_ ## exc_name = { \
181185
.name = MP_QSTR_ ## exc_name, \
182186
.print = mp_obj_exception_print, \
183187
.make_new = mp_obj_exception_make_new, \
184-
.load_attr = exception_load_attr, \
188+
.attr = exception_attr, \
185189
.bases_tuple = (mp_obj_t)&mp_type_ ## base_name ## _base_tuple, \
186190
};
187191

py/objfun.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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,
296296
}
297297

298298
#if MICROPY_PY_FUNCTION_ATTRS
299-
STATIC void fun_bc_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
299+
STATIC void fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
300+
if (dest[0] != MP_OBJ_NULL) {
301+
// not load attribute
302+
return;
303+
}
300304
if (attr == MP_QSTR___name__) {
301305
dest[0] = MP_OBJ_NEW_QSTR(mp_obj_fun_get_name(self_in));
302306
}
@@ -311,7 +315,7 @@ const mp_obj_type_t mp_type_fun_bc = {
311315
#endif
312316
.call = fun_bc_call,
313317
#if MICROPY_PY_FUNCTION_ATTRS
314-
.load_attr = fun_bc_load_attr,
318+
.attr = fun_bc_attr,
315319
#endif
316320
};
317321

py/objmodule.c

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -51,48 +51,48 @@ STATIC void module_print(void (*print)(void *env, const char *fmt, ...), void *e
5151
print(env, "<module '%s'>", name);
5252
}
5353

54-
STATIC void module_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
54+
STATIC void module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
5555
mp_obj_module_t *self = self_in;
56-
mp_map_elem_t *elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP);
57-
if (elem != NULL) {
58-
dest[0] = elem->value;
59-
}
60-
}
61-
62-
STATIC bool module_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) {
63-
mp_obj_module_t *self = self_in;
64-
mp_obj_dict_t *dict = self->globals;
65-
if (dict->map.is_fixed) {
66-
#if MICROPY_CAN_OVERRIDE_BUILTINS
67-
if (dict == &mp_module_builtins_globals) {
68-
if (MP_STATE_VM(mp_module_builtins_override_dict) == NULL) {
69-
MP_STATE_VM(mp_module_builtins_override_dict) = mp_obj_new_dict(1);
70-
}
71-
dict = MP_STATE_VM(mp_module_builtins_override_dict);
72-
} else
73-
#endif
74-
{
75-
// can't delete or store to fixed map
76-
return false;
56+
if (dest[0] == MP_OBJ_NULL) {
57+
// load attribute
58+
mp_map_elem_t *elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP);
59+
if (elem != NULL) {
60+
dest[0] = elem->value;
7761
}
78-
}
79-
if (value == MP_OBJ_NULL) {
80-
// delete attribute
81-
mp_obj_dict_delete(dict, MP_OBJ_NEW_QSTR(attr));
8262
} else {
83-
// store attribute
84-
// TODO CPython allows STORE_ATTR to a module, but is this the correct implementation?
85-
mp_obj_dict_store(dict, MP_OBJ_NEW_QSTR(attr), value);
63+
// delete/store attribute
64+
mp_obj_dict_t *dict = self->globals;
65+
if (dict->map.is_fixed) {
66+
#if MICROPY_CAN_OVERRIDE_BUILTINS
67+
if (dict == &mp_module_builtins_globals) {
68+
if (MP_STATE_VM(mp_module_builtins_override_dict) == NULL) {
69+
MP_STATE_VM(mp_module_builtins_override_dict) = mp_obj_new_dict(1);
70+
}
71+
dict = MP_STATE_VM(mp_module_builtins_override_dict);
72+
} else
73+
#endif
74+
{
75+
// can't delete or store to fixed map
76+
return;
77+
}
78+
}
79+
if (dest[1] == MP_OBJ_NULL) {
80+
// delete attribute
81+
mp_obj_dict_delete(dict, MP_OBJ_NEW_QSTR(attr));
82+
} else {
83+
// store attribute
84+
// TODO CPython allows STORE_ATTR to a module, but is this the correct implementation?
85+
mp_obj_dict_store(dict, MP_OBJ_NEW_QSTR(attr), dest[1]);
86+
}
87+
dest[0] = MP_OBJ_NULL; // indicate success
8688
}
87-
return true;
8889
}
8990

9091
const mp_obj_type_t mp_type_module = {
9192
{ &mp_type_type },
9293
.name = MP_QSTR_module,
9394
.print = module_print,
94-
.load_attr = module_load_attr,
95-
.store_attr = module_store_attr,
95+
.attr = module_attr,
9696
};
9797

9898
mp_obj_t mp_obj_new_module(qstr module_name) {

py/objnamedtuple.c

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -68,20 +68,20 @@ STATIC void namedtuple_print(void (*print)(void *env, const char *fmt, ...), voi
6868
print(env, ")");
6969
}
7070

71-
STATIC void namedtuple_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
72-
mp_obj_namedtuple_t *self = self_in;
73-
int id = namedtuple_find_field((mp_obj_namedtuple_type_t*)self->tuple.base.type, attr);
74-
if (id == -1) {
75-
return;
71+
STATIC void namedtuple_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
72+
if (dest[0] == MP_OBJ_NULL) {
73+
// load attribute
74+
mp_obj_namedtuple_t *self = self_in;
75+
int id = namedtuple_find_field((mp_obj_namedtuple_type_t*)self->tuple.base.type, attr);
76+
if (id == -1) {
77+
return;
78+
}
79+
dest[0] = self->tuple.items[id];
80+
} else {
81+
// delete/store attribute
82+
// provide more detailed error message than we'd get by just returning
83+
nlr_raise(mp_obj_new_exception_msg(&mp_type_AttributeError, "can't set attribute"));
7684
}
77-
dest[0] = self->tuple.items[id];
78-
}
79-
80-
STATIC bool namedtuple_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) {
81-
(void)self_in;
82-
(void)attr;
83-
(void)value;
84-
nlr_raise(mp_obj_new_exception_msg(&mp_type_AttributeError, "can't set attribute"));
8585
}
8686

8787
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
154154
o->base.make_new = namedtuple_make_new;
155155
o->base.unary_op = mp_obj_tuple_unary_op;
156156
o->base.binary_op = mp_obj_tuple_binary_op;
157-
o->base.load_attr = namedtuple_load_attr;
158-
o->base.store_attr = namedtuple_store_attr;
157+
o->base.attr = namedtuple_attr;
159158
o->base.subscr = mp_obj_tuple_subscr;
160159
o->base.getiter = mp_obj_tuple_getiter;
161160
o->base.bases_tuple = (mp_obj_t)&namedtuple_base_tuple;

py/objrange.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,11 @@ STATIC mp_obj_t range_getiter(mp_obj_t o_in) {
168168

169169

170170
#if MICROPY_PY_BUILTINS_RANGE_ATTRS
171-
STATIC void range_load_attr(mp_obj_t o_in, qstr attr, mp_obj_t *dest) {
171+
STATIC void range_attr(mp_obj_t o_in, qstr attr, mp_obj_t *dest) {
172+
if (dest[0] != MP_OBJ_NULL) {
173+
// not load attribute
174+
return;
175+
}
172176
mp_obj_range_t *o = o_in;
173177
if (attr == MP_QSTR_start) {
174178
dest[0] = mp_obj_new_int(o->start);
@@ -189,6 +193,6 @@ const mp_obj_type_t mp_type_range = {
189193
.subscr = range_subscr,
190194
.getiter = range_getiter,
191195
#if MICROPY_PY_BUILTINS_RANGE_ATTRS
192-
.load_attr = range_load_attr,
196+
.attr = range_attr,
193197
#endif
194198
};

0 commit comments

Comments
 (0)