Skip to content

Commit 48caa09

Browse files
committed
objgenerator: Implement .throw() method to throw exceptions into generator.
1 parent 61fd20f commit 48caa09

4 files changed

Lines changed: 55 additions & 11 deletions

File tree

py/bc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ typedef struct _mp_exc_stack {
1515
} mp_exc_stack;
1616

1717
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);
18-
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);
18+
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);
1919
void mp_byte_code_print(const byte *code, int len);

py/objgenerator.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ mp_obj_t gen_instance_getiter(mp_obj_t self_in) {
7070
return self_in;
7171
}
7272

73-
STATIC mp_obj_t gen_next_send(mp_obj_t self_in, mp_obj_t send_value) {
73+
STATIC mp_obj_t gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value) {
7474
mp_obj_gen_instance_t *self = self_in;
7575
if (self->ip == 0) {
7676
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) {
8383
*self->sp = send_value;
8484
}
8585
mp_vm_return_kind_t vm_return_kind = mp_execute_byte_code_2(self->code_info, &self->ip,
86-
&self->state[self->n_state - 1], &self->sp, (mp_exc_stack*)(self->state + self->n_state), &self->exc_sp);
86+
&self->state[self->n_state - 1], &self->sp, (mp_exc_stack*)(self->state + self->n_state),
87+
&self->exc_sp, throw_value);
8788
switch (vm_return_kind) {
8889
case MP_VM_RETURN_NORMAL:
8990
// 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) {
113114
}
114115

115116
mp_obj_t gen_instance_iternext(mp_obj_t self_in) {
116-
return gen_next_send(self_in, mp_const_none);
117+
return gen_resume(self_in, mp_const_none, MP_OBJ_NULL);
117118
}
118119

119120
STATIC mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) {
120-
mp_obj_t ret = gen_next_send(self_in, send_value);
121+
mp_obj_t ret = gen_resume(self_in, send_value, MP_OBJ_NULL);
121122
if (ret == mp_const_stop_iteration) {
122123
nlr_jump(mp_obj_new_exception(&mp_type_StopIteration));
123124
} else {
@@ -127,8 +128,21 @@ STATIC mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) {
127128

128129
STATIC MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_send_obj, gen_instance_send);
129130

131+
STATIC mp_obj_t gen_instance_throw(uint n_args, const mp_obj_t *args) {
132+
mp_obj_t ret = gen_resume(args[0], mp_const_none, n_args == 2 ? args[1] : args[2]);
133+
if (ret == mp_const_stop_iteration) {
134+
nlr_jump(mp_obj_new_exception(&mp_type_StopIteration));
135+
} else {
136+
return ret;
137+
}
138+
}
139+
140+
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gen_instance_throw_obj, 2, 4, gen_instance_throw);
141+
142+
130143
STATIC const mp_method_t gen_type_methods[] = {
131144
{ "send", &gen_instance_send_obj },
145+
{ "throw", &gen_instance_throw_obj },
132146
{ NULL, NULL }, // end-of-list sentinel
133147
};
134148

py/vm.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ mp_vm_return_kind_t mp_execute_byte_code(const byte *code, const mp_obj_t *args,
8282
mp_exc_stack exc_stack[4];
8383
mp_exc_stack *exc_sp = &exc_stack[0] - 1;
8484
// execute the byte code
85-
mp_vm_return_kind_t vm_return_kind = mp_execute_byte_code_2(code, &ip, &state[n_state - 1], &sp, exc_stack, &exc_sp);
85+
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);
8686

8787
switch (vm_return_kind) {
8888
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,
107107
// MP_VM_RETURN_EXCEPTION, exception in fastn[0]
108108
mp_vm_return_kind_t mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out,
109109
mp_obj_t *fastn, mp_obj_t **sp_in_out,
110-
mp_exc_stack *exc_stack,
111-
mp_exc_stack **exc_sp_in_out) {
110+
mp_exc_stack *exc_stack, mp_exc_stack **exc_sp_in_out,
111+
volatile mp_obj_t inject_exc) {
112112
// careful: be sure to declare volatile any variables read in the exception handler (written is ok, I think)
113113

114114
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
118118
mp_obj_t obj1, obj2;
119119
nlr_buf_t nlr;
120120

121-
volatile machine_uint_t currently_in_except_block = 0; // 0 or 1, to detect nested exceptions
122-
mp_exc_stack *volatile exc_sp = *exc_sp_in_out; // stack grows up, exc_sp points to top of stack
121+
volatile machine_uint_t currently_in_except_block = (int)*exc_sp_in_out & 1; // 0 or 1, to detect nested exceptions
122+
mp_exc_stack *volatile exc_sp = (void*)((int)*exc_sp_in_out & ~1); // stack grows up, exc_sp points to top of stack
123123
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)
124124

125125
// outer exception handling loop
126126
for (;;) {
127127
if (nlr_push(&nlr) == 0) {
128+
// If we have exception to inject, now that we finish setting up
129+
// execution context, raise it. This works as if RAISE_VARARGS
130+
// bytecode was executed.
131+
if (inject_exc != MP_OBJ_NULL) {
132+
mp_obj_t t = inject_exc;
133+
inject_exc = MP_OBJ_NULL;
134+
nlr_jump(rt_make_raise_obj(t));
135+
}
128136
// loop to execute byte code
129137
for (;;) {
130138
dispatch_loop:
@@ -599,7 +607,7 @@ mp_vm_return_kind_t mp_execute_byte_code_2(const byte *code_info, const byte **i
599607
nlr_pop();
600608
*ip_in_out = ip;
601609
*sp_in_out = sp;
602-
*exc_sp_in_out = exc_sp;
610+
*exc_sp_in_out = (void*)((int)exc_sp | currently_in_except_block);
603611
return MP_VM_RETURN_YIELD;
604612

605613
case MP_BC_IMPORT_NAME:

tests/basics/generator-exc.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,25 @@ def gen2():
2929
print(next(g))
3030
except StopIteration:
3131
print("StopIteration")
32+
33+
34+
# Test throwing exception into generator
35+
def gen3():
36+
yield 1
37+
try:
38+
yield 2
39+
except ValueError:
40+
print("ValueError received")
41+
yield 3
42+
yield 4
43+
yield 5
44+
45+
g = gen3()
46+
print(next(g))
47+
print(next(g))
48+
print("out of throw:", g.throw(ValueError))
49+
print(next(g))
50+
try:
51+
print("out of throw2:", g.throw(ValueError))
52+
except ValueError:
53+
print("Boomerang ValueError caught")

0 commit comments

Comments
 (0)