diff --git a/py/bc.h b/py/bc.h index d13295b3bd..0709897750 100644 --- a/py/bc.h +++ b/py/bc.h @@ -15,5 +15,5 @@ typedef struct _mp_exc_stack { } mp_exc_stack; mp_vm_return_kind_t mp_execute_byte_code(const byte *code, const mp_obj_t *args, uint n_args, const mp_obj_t *args2, uint n_args2, uint n_state, mp_obj_t *ret); -mp_vm_return_kind_t mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out, mp_obj_t *fastn, mp_obj_t **sp_in_out, mp_exc_stack *exc_stack, mp_exc_stack **exc_sp_in_out); +mp_vm_return_kind_t mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out, mp_obj_t *fastn, mp_obj_t **sp_in_out, mp_exc_stack *exc_stack, mp_exc_stack **exc_sp_in_out, volatile mp_obj_t inject_exc); void mp_byte_code_print(const byte *code, int len); diff --git a/py/objgenerator.c b/py/objgenerator.c index fe927d84e4..2db04ad742 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -70,7 +70,7 @@ mp_obj_t gen_instance_getiter(mp_obj_t self_in) { return self_in; } -STATIC mp_obj_t gen_next_send(mp_obj_t self_in, mp_obj_t send_value) { +STATIC mp_obj_t gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value) { mp_obj_gen_instance_t *self = self_in; if (self->ip == 0) { return mp_const_stop_iteration; @@ -83,7 +83,8 @@ STATIC mp_obj_t gen_next_send(mp_obj_t self_in, mp_obj_t send_value) { *self->sp = send_value; } mp_vm_return_kind_t vm_return_kind = mp_execute_byte_code_2(self->code_info, &self->ip, - &self->state[self->n_state - 1], &self->sp, (mp_exc_stack*)(self->state + self->n_state), &self->exc_sp); + &self->state[self->n_state - 1], &self->sp, (mp_exc_stack*)(self->state + self->n_state), + &self->exc_sp, throw_value); switch (vm_return_kind) { case MP_VM_RETURN_NORMAL: // Explicitly mark generator as completed. If we don't do this, @@ -113,11 +114,11 @@ STATIC mp_obj_t gen_next_send(mp_obj_t self_in, mp_obj_t send_value) { } mp_obj_t gen_instance_iternext(mp_obj_t self_in) { - return gen_next_send(self_in, mp_const_none); + return gen_resume(self_in, mp_const_none, MP_OBJ_NULL); } 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); + mp_obj_t ret = gen_resume(self_in, send_value, MP_OBJ_NULL); if (ret == mp_const_stop_iteration) { nlr_jump(mp_obj_new_exception(&mp_type_StopIteration)); } else { @@ -127,8 +128,21 @@ STATIC mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) { STATIC MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_send_obj, gen_instance_send); +STATIC mp_obj_t gen_instance_throw(uint n_args, const mp_obj_t *args) { + mp_obj_t ret = gen_resume(args[0], mp_const_none, n_args == 2 ? args[1] : args[2]); + if (ret == mp_const_stop_iteration) { + nlr_jump(mp_obj_new_exception(&mp_type_StopIteration)); + } else { + return ret; + } +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gen_instance_throw_obj, 2, 4, gen_instance_throw); + + STATIC const mp_method_t gen_type_methods[] = { { "send", &gen_instance_send_obj }, + { "throw", &gen_instance_throw_obj }, { NULL, NULL }, // end-of-list sentinel }; diff --git a/py/vm.c b/py/vm.c index 1afd8363d1..7f4efeea0f 100644 --- a/py/vm.c +++ b/py/vm.c @@ -82,7 +82,7 @@ mp_vm_return_kind_t mp_execute_byte_code(const byte *code, const mp_obj_t *args, mp_exc_stack exc_stack[4]; mp_exc_stack *exc_sp = &exc_stack[0] - 1; // execute the byte code - mp_vm_return_kind_t vm_return_kind = mp_execute_byte_code_2(code, &ip, &state[n_state - 1], &sp, exc_stack, &exc_sp); + mp_vm_return_kind_t vm_return_kind = mp_execute_byte_code_2(code, &ip, &state[n_state - 1], &sp, exc_stack, &exc_sp, MP_OBJ_NULL); switch (vm_return_kind) { case MP_VM_RETURN_NORMAL: @@ -107,8 +107,8 @@ mp_vm_return_kind_t mp_execute_byte_code(const byte *code, const mp_obj_t *args, // MP_VM_RETURN_EXCEPTION, exception in fastn[0] mp_vm_return_kind_t mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out, mp_obj_t *fastn, mp_obj_t **sp_in_out, - mp_exc_stack *exc_stack, - mp_exc_stack **exc_sp_in_out) { + mp_exc_stack *exc_stack, mp_exc_stack **exc_sp_in_out, + volatile mp_obj_t inject_exc) { // careful: be sure to declare volatile any variables read in the exception handler (written is ok, I think) const byte *ip = *ip_in_out; @@ -118,13 +118,21 @@ mp_vm_return_kind_t mp_execute_byte_code_2(const byte *code_info, const byte **i mp_obj_t obj1, obj2; nlr_buf_t nlr; - volatile machine_uint_t currently_in_except_block = 0; // 0 or 1, to detect nested exceptions - mp_exc_stack *volatile exc_sp = *exc_sp_in_out; // stack grows up, exc_sp points to top of stack + volatile machine_uint_t currently_in_except_block = (int)*exc_sp_in_out & 1; // 0 or 1, to detect nested exceptions + mp_exc_stack *volatile exc_sp = (void*)((int)*exc_sp_in_out & ~1); // stack grows up, exc_sp points to top of stack const byte *volatile save_ip = ip; // this is so we can access ip in the exception handler without making ip volatile (which means the compiler can't keep it in a register in the main loop) // outer exception handling loop for (;;) { if (nlr_push(&nlr) == 0) { + // If we have exception to inject, now that we finish setting up + // execution context, raise it. This works as if RAISE_VARARGS + // bytecode was executed. + if (inject_exc != MP_OBJ_NULL) { + mp_obj_t t = inject_exc; + inject_exc = MP_OBJ_NULL; + nlr_jump(rt_make_raise_obj(t)); + } // loop to execute byte code for (;;) { dispatch_loop: @@ -599,7 +607,7 @@ unwind_return: nlr_pop(); *ip_in_out = ip; *sp_in_out = sp; - *exc_sp_in_out = exc_sp; + *exc_sp_in_out = (void*)((int)exc_sp | currently_in_except_block); return MP_VM_RETURN_YIELD; case MP_BC_IMPORT_NAME: diff --git a/tests/basics/generator-exc.py b/tests/basics/generator-exc.py index 601fa2c00e..09aca5d9a0 100644 --- a/tests/basics/generator-exc.py +++ b/tests/basics/generator-exc.py @@ -29,3 +29,25 @@ try: print(next(g)) except StopIteration: print("StopIteration") + + +# Test throwing exception into generator +def gen3(): + yield 1 + try: + yield 2 + except ValueError: + print("ValueError received") + yield 3 + yield 4 + yield 5 + +g = gen3() +print(next(g)) +print(next(g)) +print("out of throw:", g.throw(ValueError)) +print(next(g)) +try: + print("out of throw2:", g.throw(ValueError)) +except ValueError: + print("Boomerang ValueError caught")