py/runtime: Check that keys in dicts passed as ** args are strings.

Prior to this patch the code would crash if a key in a ** dict was anything
other than a str or qstr.  This is because mp_setup_code_state() assumes
that keys in kwargs are qstrs (for efficiency).

Thanks to @jepler for finding the bug.
This commit is contained in:
Damien George 2018-03-30 11:09:00 +11:00
parent bc3a5f1917
commit 3280788195
4 changed files with 16 additions and 4 deletions

View File

@ -720,6 +720,7 @@ qstr mp_obj_str_get_qstr(mp_obj_t self_in); // use this if you will anyway conve
const char *mp_obj_str_get_str(mp_obj_t self_in); // use this only if you need the string to be null terminated const char *mp_obj_str_get_str(mp_obj_t self_in); // use this only if you need the string to be null terminated
const char *mp_obj_str_get_data(mp_obj_t self_in, size_t *len); const char *mp_obj_str_get_data(mp_obj_t self_in, size_t *len);
mp_obj_t mp_obj_str_intern(mp_obj_t str); mp_obj_t mp_obj_str_intern(mp_obj_t str);
mp_obj_t mp_obj_str_intern_checked(mp_obj_t obj);
void mp_str_print_quoted(const mp_print_t *print, const byte *str_data, size_t str_len, bool is_bytes); void mp_str_print_quoted(const mp_print_t *print, const byte *str_data, size_t str_len, bool is_bytes);
#if MICROPY_PY_BUILTINS_FLOAT #if MICROPY_PY_BUILTINS_FLOAT

View File

@ -2062,6 +2062,12 @@ mp_obj_t mp_obj_str_intern(mp_obj_t str) {
return mp_obj_new_str_via_qstr((const char*)data, len); return mp_obj_new_str_via_qstr((const char*)data, len);
} }
mp_obj_t mp_obj_str_intern_checked(mp_obj_t obj) {
size_t len;
const char *data = mp_obj_str_get_data(obj, &len);
return mp_obj_new_str_via_qstr((const char*)data, len);
}
mp_obj_t mp_obj_new_bytes(const byte* data, size_t len) { mp_obj_t mp_obj_new_bytes(const byte* data, size_t len) {
return mp_obj_new_str_copy(&mp_type_bytes, data, len); return mp_obj_new_str_copy(&mp_type_bytes, data, len);
} }

View File

@ -748,8 +748,8 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_
if (MP_MAP_SLOT_IS_FILLED(map, i)) { if (MP_MAP_SLOT_IS_FILLED(map, i)) {
// the key must be a qstr, so intern it if it's a string // the key must be a qstr, so intern it if it's a string
mp_obj_t key = map->table[i].key; mp_obj_t key = map->table[i].key;
if (MP_OBJ_IS_TYPE(key, &mp_type_str)) { if (!MP_OBJ_IS_QSTR(key)) {
key = mp_obj_str_intern(key); key = mp_obj_str_intern_checked(key);
} }
args2[args2_len++] = key; args2[args2_len++] = key;
args2[args2_len++] = map->table[i].value; args2[args2_len++] = map->table[i].value;
@ -778,8 +778,8 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_
} }
// the key must be a qstr, so intern it if it's a string // the key must be a qstr, so intern it if it's a string
if (MP_OBJ_IS_TYPE(key, &mp_type_str)) { if (!MP_OBJ_IS_QSTR(key)) {
key = mp_obj_str_intern(key); key = mp_obj_str_intern_checked(key);
} }
// get the value corresponding to the key // get the value corresponding to the key

View File

@ -6,6 +6,11 @@ def f(a, b):
f(1, **{'b':2}) f(1, **{'b':2})
f(1, **{'b':val for val in range(1)}) f(1, **{'b':val for val in range(1)})
try:
f(1, **{len:2})
except TypeError:
print('TypeError')
# test calling a method with keywords given by **dict # test calling a method with keywords given by **dict
class A: class A: