Skip to content

Commit a893a8b

Browse files
committed
py/objtype: Eliminate __set_name__ hazard.
Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
1 parent 1b103b9 commit a893a8b

File tree

1 file changed

+33
-14
lines changed

1 file changed

+33
-14
lines changed

py/objtype.c

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,38 @@ static bool check_for_special_accessors(mp_obj_t key, mp_obj_t value) {
974974
}
975975
#endif
976976

977+
#if MICROPY_PY_DESCRIPTORS
978+
static void run_set_name_hooks(mp_obj_t locals_dict, mp_obj_t owner) {
979+
// copy the dict so we can iterate safely even while __set_name__ potentially modifies the original
980+
mp_obj_dict_t *locals_dict_copy = MP_OBJ_TO_PTR(mp_obj_dict_copy(locals_dict));
981+
mp_map_t *locals_map = mp_obj_dict_get_map(locals_dict_copy);
982+
983+
// make sure we don't leak this copy's memory
984+
nlr_buf_t nlr;
985+
bool ok = (nlr_push(&nlr) == 0);
986+
if (ok) {
987+
// use the copy to call __set_name__ on each
988+
for (size_t i = 0; i < locals_map->alloc; i++) {
989+
if (mp_map_slot_is_filled(locals_map, i)) {
990+
mp_map_elem_t *elem = &(locals_map->table[i]);
991+
mp_obj_t set_name_method[4];
992+
mp_load_method_maybe(elem->value, MP_QSTR___set_name__, set_name_method);
993+
if (set_name_method[1] != MP_OBJ_NULL) {
994+
set_name_method[2] = owner;
995+
set_name_method[3] = elem->key;
996+
mp_call_method_n_kw(2, 0, set_name_method);
997+
}
998+
}
999+
}
1000+
nlr_pop();
1001+
}
1002+
m_del_obj(locals_dict_copy->base.type, locals_dict_copy);
1003+
if (!ok) {
1004+
nlr_raise(nlr.ret_val); // TODO cpython raises a RuntimeError from this instead
1005+
}
1006+
}
1007+
#endif
1008+
9771009
static void type_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
9781010
(void)kind;
9791011
mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in);
@@ -1242,20 +1274,7 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict)
12421274
}
12431275

12441276
#if MICROPY_PY_DESCRIPTORS
1245-
// call __set_name__ on all entries (especially descriptors)
1246-
for (size_t i = 0; i < locals_map->alloc; i++) {
1247-
if (mp_map_slot_is_filled(locals_map, i)) {
1248-
elem = &(locals_map->table[i]);
1249-
1250-
mp_obj_t set_name_method[4];
1251-
mp_load_method_maybe(elem->value, MP_QSTR___set_name__, set_name_method);
1252-
if (set_name_method[1] != MP_OBJ_NULL) {
1253-
set_name_method[2] = MP_OBJ_FROM_PTR(o);
1254-
set_name_method[3] = elem->key;
1255-
mp_call_method_n_kw(2, 0, set_name_method);
1256-
}
1257-
}
1258-
}
1277+
run_set_name_hooks(locals_dict, MP_OBJ_FROM_PTR(o));
12591278
#endif
12601279

12611280
return MP_OBJ_FROM_PTR(o);

0 commit comments

Comments
 (0)