From 14d28be344702b98f2694e245ed4a00c86f898c2 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 27 Jan 2014 01:01:37 +0200 Subject: [PATCH 1/3] gen.send(): Throw StopIteration. Also, explicitly shutdown finished gen. Otherwise, some generator statements still may be spuriously executed on subsequent calls to next()/send(). --- py/objgenerator.c | 25 +++++++++++++++++++++---- tests/basics/generator_send.py | 22 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/py/objgenerator.c b/py/objgenerator.c index 0cac34b09e..91bbbceb2f 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -73,8 +73,11 @@ mp_obj_t gen_instance_getiter(mp_obj_t self_in) { return self_in; } -static mp_obj_t gen_send(mp_obj_t self_in, mp_obj_t send_value) { +static mp_obj_t gen_next_send(mp_obj_t self_in, mp_obj_t send_value) { mp_obj_gen_instance_t *self = self_in; + if (self->ip == 0) { + return mp_const_stop_iteration; + } if (self->sp == self->state - 1) { if (send_value != mp_const_none) { nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "can't send non-None value to a just-started generator")); @@ -86,6 +89,12 @@ static mp_obj_t gen_send(mp_obj_t self_in, mp_obj_t send_value) { if (yield) { return *self->sp; } else { + // Explicitly mark generator as completed. If we don't do this, + // subsequent next() may re-execute statements after last yield + // again and again, leading to side effects. + // TODO: check how return with value behaves under such conditions + // in CPython. + self->ip = 0; if (*self->sp == mp_const_none) { return mp_const_stop_iteration; } else { @@ -94,14 +103,22 @@ static mp_obj_t gen_send(mp_obj_t self_in, mp_obj_t send_value) { } } } -static MP_DEFINE_CONST_FUN_OBJ_2(gen_send_obj, gen_send); mp_obj_t gen_instance_iternext(mp_obj_t self_in) { - return gen_send(self_in, mp_const_none); + return gen_next_send(self_in, mp_const_none); } +static mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) { + mp_obj_t ret = gen_next_send(self_in, send_value); + if (ret == mp_const_stop_iteration) { + nlr_jump(mp_obj_new_exception(MP_QSTR_StopIteration)); + } + return ret; +} +static MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_send_obj, gen_instance_send); + static const mp_method_t gen_type_methods[] = { - { "send", &gen_send_obj }, + { "send", &gen_instance_send_obj }, { NULL, NULL }, // end-of-list sentinel }; diff --git a/tests/basics/generator_send.py b/tests/basics/generator_send.py index 4158478cac..c472c31ba9 100644 --- a/tests/basics/generator_send.py +++ b/tests/basics/generator_send.py @@ -13,3 +13,25 @@ except TypeError: print(g.send(None)) print(g.send(100)) print(g.send(200)) + + +def f2(): + print("entering") + for i in range(3): + print(i) + yield + print("returning 1") + print("returning 2") + +g = f2() +g.send(None) +g.send(1) +g.send(1) +try: + g.send(1) +except StopIteration: + print("caught") +try: + g.send(1) +except StopIteration: + print("caught") From ddf1aa9223a2786c64df07ea686ecfe6ee83d1b2 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 27 Jan 2014 01:06:23 +0200 Subject: [PATCH 2/3] list.pop(): Don't allow ->alloc drop to zero, which causes unexpected behavior. --- py/objlist.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/py/objlist.c b/py/objlist.c index 2e9a8705ff..d21e16000d 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -23,6 +23,9 @@ static mp_obj_t mp_obj_new_list_iterator(mp_obj_list_t *list, int cur); static mp_obj_list_t *list_new(uint n); static mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in); +// TODO: Move to mpconfig.h +#define LIST_MIN_ALLOC 4 + /******************************************************************************/ /* list */ @@ -181,6 +184,7 @@ mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg) { mp_obj_list_t *self = self_in; if (self->len >= self->alloc) { self->items = m_renew(mp_obj_t, self->items, self->alloc, self->alloc * 2); + assert(self->items); self->alloc *= 2; } self->items[self->len++] = arg; @@ -215,7 +219,7 @@ static mp_obj_t list_pop(uint n_args, const mp_obj_t *args) { mp_obj_t ret = self->items[index]; self->len -= 1; memcpy(self->items + index, self->items + index + 1, (self->len - index) * sizeof(mp_obj_t)); - if (self->alloc > 2 * self->len) { + if (self->alloc > LIST_MIN_ALLOC && self->alloc > 2 * self->len) { self->items = m_renew(mp_obj_t, self->items, self->alloc, self->alloc/2); self->alloc /= 2; } @@ -267,8 +271,8 @@ static mp_obj_t list_clear(mp_obj_t self_in) { assert(MP_OBJ_IS_TYPE(self_in, &list_type)); mp_obj_list_t *self = self_in; self->len = 0; - self->items = m_renew(mp_obj_t, self->items, self->alloc, 4); - self->alloc = 4; + self->items = m_renew(mp_obj_t, self->items, self->alloc, LIST_MIN_ALLOC); + self->alloc = LIST_MIN_ALLOC; return mp_const_none; } @@ -403,7 +407,7 @@ const mp_obj_type_t list_type = { static mp_obj_list_t *list_new(uint n) { mp_obj_list_t *o = m_new_obj(mp_obj_list_t); o->base.type = &list_type; - o->alloc = n < 4 ? 4 : n; + o->alloc = n < LIST_MIN_ALLOC ? LIST_MIN_ALLOC : n; o->len = n; o->items = m_new(mp_obj_t, o->alloc); return o; From 9b00dad7bb0125a3459ca4f9c939c7510bd2f77f Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 27 Jan 2014 09:05:50 +0200 Subject: [PATCH 3/3] long int: Implement more operations. --- py/objint.c | 7 +++++++ py/objint.h | 1 + py/objint_longlong.c | 41 ++++++++++++++++++++++++++++++++++++---- tests/basics/int-long.py | 39 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 tests/basics/int-long.py diff --git a/py/objint.c b/py/objint.c index 1305f1900e..1a04408afd 100644 --- a/py/objint.c +++ b/py/objint.c @@ -51,6 +51,12 @@ void int_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj } } +// This is called only for non-SMALL_INT +mp_obj_t int_unary_op(int op, mp_obj_t o_in) { + assert(0); + return mp_const_none; +} + // This is called only for non-SMALL_INT mp_obj_t int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { assert(0); @@ -96,5 +102,6 @@ const mp_obj_type_t int_type = { "int", .print = int_print, .make_new = int_make_new, + .unary_op = int_unary_op, .binary_op = int_binary_op, }; diff --git a/py/objint.h b/py/objint.h index 7d43971ec3..6662be1ef3 100644 --- a/py/objint.h +++ b/py/objint.h @@ -6,4 +6,5 @@ typedef struct _mp_obj_int_t { } mp_obj_int_t; void int_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind); +mp_obj_t int_unary_op(int op, mp_obj_t o_in); mp_obj_t int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in); diff --git a/py/objint_longlong.c b/py/objint_longlong.c index 7eaee3bc9d..38a01837e1 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -32,6 +32,17 @@ void int_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj } } +mp_obj_t int_unary_op(int op, mp_obj_t o_in) { + mp_obj_int_t *o = o_in; + switch (op) { + case RT_UNARY_OP_NOT: return MP_BOOL(o->val != 0); // TODO: implements RT_UNARY_OP_BOOL + case RT_UNARY_OP_POSITIVE: return o_in; + case RT_UNARY_OP_NEGATIVE: return mp_obj_new_int_from_ll(-o->val); + case RT_UNARY_OP_INVERT: return mp_obj_new_int_from_ll(~o->val); + default: return NULL; // op not supported + } +} + mp_obj_t int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_obj_int_t *lhs = lhs_in; mp_obj_int_t *rhs = rhs_in; @@ -50,13 +61,23 @@ mp_obj_t int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { return mp_obj_new_int_from_ll(lhs->val + rhs_val); case RT_BINARY_OP_SUBTRACT: return mp_obj_new_int_from_ll(lhs->val - rhs_val); + case RT_BINARY_OP_MULTIPLY: + return mp_obj_new_int_from_ll(lhs->val * rhs_val); + case RT_BINARY_OP_FLOOR_DIVIDE: + return mp_obj_new_int_from_ll(lhs->val / rhs_val); + case RT_BINARY_OP_MODULO: + return mp_obj_new_int_from_ll(lhs->val % rhs_val); case RT_BINARY_OP_INPLACE_ADD: - lhs->val += rhs_val; - return lhs; + lhs->val += rhs_val; return lhs; case RT_BINARY_OP_INPLACE_SUBTRACT: - lhs->val -= rhs_val; - return lhs; + lhs->val -= rhs_val; return lhs; + case RT_BINARY_OP_INPLACE_MULTIPLY: + lhs->val *= rhs_val; return lhs; + case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE: + lhs->val /= rhs_val; return lhs; + case RT_BINARY_OP_INPLACE_MODULO: + lhs->val %= rhs_val; return lhs; case RT_BINARY_OP_AND: return mp_obj_new_int_from_ll(lhs->val & rhs_val); @@ -65,11 +86,23 @@ mp_obj_t int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { case RT_BINARY_OP_XOR: return mp_obj_new_int_from_ll(lhs->val ^ rhs_val); + case RT_BINARY_OP_INPLACE_AND: + lhs->val &= rhs_val; return lhs; + case RT_BINARY_OP_INPLACE_OR: + lhs->val |= rhs_val; return lhs; + case RT_BINARY_OP_INPLACE_XOR: + lhs->val ^= rhs_val; return lhs; + case RT_BINARY_OP_LSHIFT: return mp_obj_new_int_from_ll(lhs->val << (int)rhs_val); case RT_BINARY_OP_RSHIFT: return mp_obj_new_int_from_ll(lhs->val >> (int)rhs_val); + case RT_BINARY_OP_INPLACE_LSHIFT: + lhs->val <<= (int)rhs_val; return lhs; + case RT_BINARY_OP_INPLACE_RSHIFT: + lhs->val >>= (int)rhs_val; return lhs; + case RT_COMPARE_OP_LESS: return MP_BOOL(lhs->val < rhs_val); case RT_COMPARE_OP_MORE: diff --git a/tests/basics/int-long.py b/tests/basics/int-long.py new file mode 100644 index 0000000000..f867d8037d --- /dev/null +++ b/tests/basics/int-long.py @@ -0,0 +1,39 @@ +# This tests long ints for 32-bit machine + +a = 0x1ffffffff +b = 0x100000000 +print(a) +print(b) +print(a + b) +print(a - b) +print(b - a) +# overflows long long implementation +#print(a * b) +print(a // b) +print(a % b) +print(a & b) +print(a | b) +print(a ^ b) +print(a << 3) +print(a >> 1) + +a += b +print(a) +a -= 123456 +print(a) +a *= 257 +print(a) +a //= 257 +print(a) +a %= b +print(a) +a ^= b +print(a) +a |= b +print(a) +a &= b +print(a) +a <<= 5 +print(a) +a >>= 1 +print(a)