Skip to content

Commit c8f78bc

Browse files
committed
py: VM never throws an exception, instead returns a status and value.
Addresses issue adafruit#290, and hopefully sets up things to allow generators throwing exceptions, etc.
1 parent 36109d2 commit c8f78bc

File tree

6 files changed

+69
-38
lines changed

6 files changed

+69
-38
lines changed

py/bc.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1-
mp_obj_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);
2-
bool mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out, mp_obj_t *fastn, mp_obj_t **sp_in_out);
1+
typedef enum {
2+
MP_VM_RETURN_NORMAL,
3+
MP_VM_RETURN_YIELD,
4+
MP_VM_RETURN_EXCEPTION,
5+
} mp_vm_return_kind_t;
6+
7+
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);
8+
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);
39
void mp_byte_code_print(const byte *code, int len);

py/objexcept.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ STATIC void mp_obj_exception_print(void (*print)(void *env, const char *fmt, ...
2727
print(env, "%s: %s", qstr_str(o->base.type->name), vstr_str(o->msg));
2828
} else {
2929
// Yes, that's how CPython has it
30+
// TODO now that exceptions are classes and instances, I think this needs to be changed to match CPython
3031
if (kind == PRINT_REPR) {
3132
print(env, "%s", qstr_str(o->base.type->name));
3233
}
@@ -162,7 +163,7 @@ void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, machine_uint_t
162163

163164
// for traceback, we are just using the list object for convenience, it's not really a list of Python objects
164165
if (self->traceback == MP_OBJ_NULL) {
165-
self->traceback = mp_obj_new_list(3, NULL);
166+
self->traceback = mp_obj_new_list(0, NULL);
166167
}
167168
mp_obj_list_append(self->traceback, (mp_obj_t)(machine_uint_t)file);
168169
mp_obj_list_append(self->traceback, (mp_obj_t)(machine_uint_t)line);

py/objfun.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,15 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o
152152
uint use_def_args = self->n_args - n_args;
153153
mp_map_t *old_globals = rt_globals_get();
154154
rt_globals_set(self->globals);
155-
mp_obj_t result = mp_execute_byte_code(self->bytecode, args, n_args, self->def_args + self->n_def_args - use_def_args, use_def_args, self->n_state);
155+
mp_obj_t result;
156+
mp_vm_return_kind_t vm_return_kind = mp_execute_byte_code(self->bytecode, args, n_args, self->def_args + self->n_def_args - use_def_args, use_def_args, self->n_state, &result);
156157
rt_globals_set(old_globals);
157158

158-
return result;
159+
if (vm_return_kind == MP_VM_RETURN_NORMAL) {
160+
return result;
161+
} else { // MP_VM_RETURN_EXCEPTION
162+
nlr_jump(result);
163+
}
159164
}
160165

161166
const mp_obj_type_t fun_bc_type = {

py/objgenerator.c

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,22 +82,30 @@ STATIC mp_obj_t gen_next_send(mp_obj_t self_in, mp_obj_t send_value) {
8282
} else {
8383
*self->sp = send_value;
8484
}
85-
bool yield = mp_execute_byte_code_2(self->code_info, &self->ip, &self->state[self->n_state - 1], &self->sp);
86-
if (yield) {
87-
return *self->sp;
88-
} else {
89-
// Explicitly mark generator as completed. If we don't do this,
90-
// subsequent next() may re-execute statements after last yield
91-
// again and again, leading to side effects.
92-
// TODO: check how return with value behaves under such conditions
93-
// in CPython.
94-
self->ip = 0;
95-
if (*self->sp == mp_const_none) {
96-
return mp_const_stop_iteration;
97-
} else {
98-
// TODO return StopIteration with value *self->sp
99-
return mp_const_stop_iteration;
100-
}
85+
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);
86+
switch (vm_return_kind) {
87+
case MP_VM_RETURN_NORMAL:
88+
// Explicitly mark generator as completed. If we don't do this,
89+
// subsequent next() may re-execute statements after last yield
90+
// again and again, leading to side effects.
91+
// TODO: check how return with value behaves under such conditions
92+
// in CPython.
93+
self->ip = 0;
94+
if (*self->sp == mp_const_none) {
95+
return mp_const_stop_iteration;
96+
} else {
97+
// TODO return StopIteration with value *self->sp
98+
return mp_const_stop_iteration;
99+
}
100+
101+
case MP_VM_RETURN_YIELD:
102+
return *self->sp;
103+
104+
case MP_VM_RETURN_EXCEPTION:
105+
default:
106+
// TODO
107+
assert(0);
108+
return mp_const_none;
101109
}
102110
}
103111

py/vm.c

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ typedef enum {
4747
#define TOP() (*sp)
4848
#define SET_TOP(val) *sp = (val)
4949

50-
mp_obj_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) {
50+
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) {
5151
// allocate state for locals and stack
5252
mp_obj_t temp_state[10];
5353
mp_obj_t *state = &temp_state[0];
@@ -83,20 +83,30 @@ mp_obj_t mp_execute_byte_code(const byte *code, const mp_obj_t *args, uint n_arg
8383
}
8484

8585
// execute the byte code
86-
if (mp_execute_byte_code_2(code, &ip, &state[n_state - 1], &sp)) {
87-
// it shouldn't yield
88-
assert(0);
86+
mp_vm_return_kind_t vm_return_kind = mp_execute_byte_code_2(code, &ip, &state[n_state - 1], &sp);
87+
88+
switch (vm_return_kind) {
89+
case MP_VM_RETURN_NORMAL:
90+
*ret = *sp;
91+
return MP_VM_RETURN_NORMAL;
92+
case MP_VM_RETURN_EXCEPTION:
93+
*ret = state[n_state - 1];
94+
return MP_VM_RETURN_EXCEPTION;
95+
case MP_VM_RETURN_YIELD: // byte-code shouldn't yield
96+
default:
97+
assert(0);
98+
*ret = mp_const_none;
99+
return MP_VM_RETURN_NORMAL;
89100
}
90-
91-
// TODO check fails if, eg, return from within for loop
92-
//assert(sp == &state[17]);
93-
return *sp;
94101
}
95102

96103
// fastn has items in reverse order (fastn[0] is local[0], fastn[-1] is local[1], etc)
97104
// sp points to bottom of stack which grows up
98-
// returns true if bytecode yielded
99-
bool mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out, mp_obj_t *fastn, mp_obj_t **sp_in_out) {
105+
// returns:
106+
// MP_VM_RETURN_NORMAL, sp valid, return value in *sp
107+
// MP_VM_RETURN_YIELD, ip, sp valid, yielded value in *sp
108+
// MP_VM_RETURN_EXCEPTION, exception in fastn[0]
109+
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) {
100110
// careful: be sure to declare volatile any variables read in the exception handler (written is ok, I think)
101111

102112
const byte *ip = *ip_in_out;
@@ -569,7 +579,7 @@ bool mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out, mp_ob
569579
nlr_pop();
570580
*sp_in_out = sp;
571581
assert(exc_sp == &exc_stack[0] - 1);
572-
return false;
582+
return MP_VM_RETURN_NORMAL;
573583

574584
case MP_BC_RAISE_VARARGS:
575585
unum = *ip++;
@@ -581,7 +591,7 @@ bool mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out, mp_ob
581591
nlr_pop();
582592
*ip_in_out = ip;
583593
*sp_in_out = sp;
584-
return true;
594+
return MP_VM_RETURN_YIELD;
585595

586596
case MP_BC_IMPORT_NAME:
587597
DECODE_QSTR;
@@ -603,7 +613,7 @@ bool mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out, mp_ob
603613
printf("code %p, byte code 0x%02x not implemented\n", ip, op);
604614
assert(0);
605615
nlr_pop();
606-
return false;
616+
return MP_VM_RETURN_NORMAL;
607617
}
608618
}
609619

@@ -653,9 +663,10 @@ bool mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out, mp_ob
653663
PUSH(nlr.ret_val); // TODO should be type(nlr.ret_val), I think...
654664

655665
} else {
656-
// re-raise exception to higher level
657-
// TODO what to do if this is a generator??
658-
nlr_jump(nlr.ret_val);
666+
// propagate exception to higher level
667+
// TODO what to do about ip and sp? they don't really make sense at this point
668+
fastn[0] = nlr.ret_val; // must put exception here because sp is invalid
669+
return MP_VM_RETURN_EXCEPTION;
659670
}
660671
}
661672
}

tests/basics/try-module.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import import1b
44

55
def func1():
6-
return
6+
print('func1')
77

88
def func2():
99
try:

0 commit comments

Comments
 (0)