From 5e34a113eaaf736fb4f703a3ee0892e1705d0a63 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 24 Nov 2017 13:04:24 +1100 Subject: py/runtime: Add MP_BINARY_OP_CONTAINS as reverse of MP_BINARY_OP_IN. Before this patch MP_BINARY_OP_IN had two meanings: coming from bytecode it meant that the args needed to be swapped, but coming from within the runtime meant that the args were already in the correct order. This lead to some confusion in the code and comments stating how args were reversed. It also lead to 2 bugs: 1) containment for a subclass of a native type didn't work; 2) the expression "{True} in True" would illegally succeed and return True. In both of these cases it was because the args to MP_BINARY_OP_IN ended up being reversed twice. To fix these things this patch introduces MP_BINARY_OP_CONTAINS which corresponds exactly to the __contains__ special method, and this is the operator that built-in types should implement. MP_BINARY_OP_IN is now only emitted by the compiler and is converted to MP_BINARY_OP_CONTAINS by swapping the arguments. --- py/objdict.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'py/objdict.c') diff --git a/py/objdict.c b/py/objdict.c index 1553a83b4..d0f95e41a 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -115,7 +115,7 @@ STATIC mp_obj_t dict_unary_op(mp_unary_op_t op, mp_obj_t self_in) { STATIC mp_obj_t dict_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_obj_dict_t *o = MP_OBJ_TO_PTR(lhs_in); switch (op) { - case MP_BINARY_OP_IN: { + case MP_BINARY_OP_CONTAINS: { mp_map_elem_t *elem = mp_map_lookup(&o->map, rhs_in, MP_MAP_LOOKUP); return mp_obj_new_bool(elem != NULL); } @@ -485,7 +485,7 @@ STATIC mp_obj_t dict_view_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t if (o->kind != MP_DICT_VIEW_KEYS) { return MP_OBJ_NULL; // op not supported } - if (op != MP_BINARY_OP_IN) { + if (op != MP_BINARY_OP_CONTAINS) { return MP_OBJ_NULL; // op not supported } return dict_binary_op(op, o->dict, rhs_in); -- cgit v1.2.3 From 2161d6b603e2dfc444a597c4346f7e2301f9c05e Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 27 Nov 2017 23:40:31 +1100 Subject: py/objdict: Reuse dict-view key iterator for standard dict iterator. It has equivalent behaviour and reusing it saves some code bytes. --- py/objdict.c | 45 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) (limited to 'py/objdict.c') diff --git a/py/objdict.c b/py/objdict.c index d0f95e41a..f6fb594b3 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -192,37 +192,6 @@ STATIC mp_obj_t dict_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { } } -/******************************************************************************/ -/* dict iterator */ - -typedef struct _mp_obj_dict_it_t { - mp_obj_base_t base; - mp_fun_1_t iternext; - mp_obj_t dict; - size_t cur; -} mp_obj_dict_it_t; - -STATIC mp_obj_t dict_it_iternext(mp_obj_t self_in) { - mp_obj_dict_it_t *self = MP_OBJ_TO_PTR(self_in); - mp_map_elem_t *next = dict_iter_next(MP_OBJ_TO_PTR(self->dict), &self->cur); - - if (next == NULL) { - return MP_OBJ_STOP_ITERATION; - } else { - return next->key; - } -} - -STATIC mp_obj_t dict_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { - assert(sizeof(mp_obj_dict_it_t) <= sizeof(mp_obj_iter_buf_t)); - mp_obj_dict_it_t *o = (mp_obj_dict_it_t*)iter_buf; - o->base.type = &mp_type_polymorph_iter; - o->iternext = dict_it_iternext; - o->dict = self_in; - o->cur = 0; - return MP_OBJ_FROM_PTR(o); -} - /******************************************************************************/ /* dict methods */ @@ -527,6 +496,20 @@ STATIC mp_obj_t dict_values(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_values_obj, dict_values); +/******************************************************************************/ +/* dict iterator */ + +STATIC mp_obj_t dict_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(mp_obj_dict_view_it_t) <= sizeof(mp_obj_iter_buf_t)); + mp_check_self(MP_OBJ_IS_DICT_TYPE(self_in)); + mp_obj_dict_view_it_t *o = (mp_obj_dict_view_it_t*)iter_buf; + o->base.type = &dict_view_it_type; + o->kind = MP_DICT_VIEW_KEYS; + o->dict = self_in; + o->cur = 0; + return MP_OBJ_FROM_PTR(o); +} + /******************************************************************************/ /* dict constructors & public C API */ -- cgit v1.2.3 From a3e01d3642d6c287bf2d0c14b3d75bf305327819 Mon Sep 17 00:00:00 2001 From: Mike Wadsten Date: Sun, 18 Feb 2018 21:51:04 -0600 Subject: py/objdict: Disallow possible modifications to fixed dicts. --- py/objdict.c | 13 ++++++++++++ tests/basics/dict_fixed.py | 48 ++++++++++++++++++++++++++++++++++++++++++ tests/basics/dict_fixed.py.exp | 7 ++++++ 3 files changed, 68 insertions(+) create mode 100644 tests/basics/dict_fixed.py create mode 100644 tests/basics/dict_fixed.py.exp (limited to 'py/objdict.c') diff --git a/py/objdict.c b/py/objdict.c index f6fb594b3..c0647067a 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -195,9 +195,16 @@ STATIC mp_obj_t dict_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { /******************************************************************************/ /* dict methods */ +STATIC void mp_ensure_not_fixed(const mp_obj_dict_t *dict) { + if (dict->map.is_fixed) { + mp_raise_TypeError(NULL); + } +} + STATIC mp_obj_t dict_clear(mp_obj_t self_in) { mp_check_self(MP_OBJ_IS_DICT_TYPE(self_in)); mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); + mp_ensure_not_fixed(self); mp_map_clear(&self->map); @@ -253,6 +260,9 @@ STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(dict_fromkeys_obj, MP_ROM_PTR(&dict_fromk STATIC mp_obj_t dict_get_helper(size_t n_args, const mp_obj_t *args, mp_map_lookup_kind_t lookup_kind) { mp_check_self(MP_OBJ_IS_DICT_TYPE(args[0])); mp_obj_dict_t *self = MP_OBJ_TO_PTR(args[0]); + if (lookup_kind != MP_MAP_LOOKUP) { + mp_ensure_not_fixed(self); + } mp_map_elem_t *elem = mp_map_lookup(&self->map, args[1], lookup_kind); mp_obj_t value; if (elem == NULL || elem->value == MP_OBJ_NULL) { @@ -295,6 +305,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_setdefault_obj, 2, 3, dict_setde STATIC mp_obj_t dict_popitem(mp_obj_t self_in) { mp_check_self(MP_OBJ_IS_DICT_TYPE(self_in)); mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); + mp_ensure_not_fixed(self); size_t cur = 0; mp_map_elem_t *next = dict_iter_next(self, &cur); if (next == NULL) { @@ -313,6 +324,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_popitem_obj, dict_popitem); STATIC mp_obj_t dict_update(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { mp_check_self(MP_OBJ_IS_DICT_TYPE(args[0])); mp_obj_dict_t *self = MP_OBJ_TO_PTR(args[0]); + mp_ensure_not_fixed(self); mp_arg_check_num(n_args, kwargs->used, 1, 2, true); @@ -578,6 +590,7 @@ size_t mp_obj_dict_len(mp_obj_t self_in) { mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) { mp_check_self(MP_OBJ_IS_DICT_TYPE(self_in)); mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); + mp_ensure_not_fixed(self); mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; return self_in; } diff --git a/tests/basics/dict_fixed.py b/tests/basics/dict_fixed.py new file mode 100644 index 000000000..4261a0655 --- /dev/null +++ b/tests/basics/dict_fixed.py @@ -0,0 +1,48 @@ +# test that fixed dictionaries cannot be modified + +try: + import uerrno +except ImportError: + print("SKIP") + raise SystemExit + +# Save a copy of uerrno.errorcode, so we can check later +# that it hasn't been modified. +errorcode_copy = uerrno.errorcode.copy() + +try: + uerrno.errorcode.popitem() +except TypeError: + print("TypeError") + +try: + uerrno.errorcode.pop(0) +except TypeError: + print("TypeError") + +try: + uerrno.errorcode.setdefault(0, 0) +except TypeError: + print("TypeError") + +try: + uerrno.errorcode.update([(1, 2)]) +except TypeError: + print("TypeError") + +try: + del uerrno.errorcode[1] +except TypeError: + print("TypeError") + +try: + uerrno.errorcode[1] = 'foo' +except TypeError: + print("TypeError") + +try: + uerrno.errorcode.clear() +except TypeError: + print("TypeError") + +assert uerrno.errorcode == errorcode_copy diff --git a/tests/basics/dict_fixed.py.exp b/tests/basics/dict_fixed.py.exp new file mode 100644 index 000000000..ffaeb4085 --- /dev/null +++ b/tests/basics/dict_fixed.py.exp @@ -0,0 +1,7 @@ +TypeError +TypeError +TypeError +TypeError +TypeError +TypeError +TypeError -- cgit v1.2.3