py: VM never throws an exception, instead returns a status and value.
Addresses issue #290, and hopefully sets up things to allow generators throwing exceptions, etc.
This commit is contained in:
parent
36109d246f
commit
c8f78bc280
10
py/bc.h
10
py/bc.h
@ -1,3 +1,9 @@
|
||||
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);
|
||||
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);
|
||||
typedef enum {
|
||||
MP_VM_RETURN_NORMAL,
|
||||
MP_VM_RETURN_YIELD,
|
||||
MP_VM_RETURN_EXCEPTION,
|
||||
} mp_vm_return_kind_t;
|
||||
|
||||
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);
|
||||
void mp_byte_code_print(const byte *code, int len);
|
||||
|
@ -27,6 +27,7 @@ STATIC void mp_obj_exception_print(void (*print)(void *env, const char *fmt, ...
|
||||
print(env, "%s: %s", qstr_str(o->base.type->name), vstr_str(o->msg));
|
||||
} else {
|
||||
// Yes, that's how CPython has it
|
||||
// TODO now that exceptions are classes and instances, I think this needs to be changed to match CPython
|
||||
if (kind == PRINT_REPR) {
|
||||
print(env, "%s", qstr_str(o->base.type->name));
|
||||
}
|
||||
@ -162,7 +163,7 @@ void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, machine_uint_t
|
||||
|
||||
// for traceback, we are just using the list object for convenience, it's not really a list of Python objects
|
||||
if (self->traceback == MP_OBJ_NULL) {
|
||||
self->traceback = mp_obj_new_list(3, NULL);
|
||||
self->traceback = mp_obj_new_list(0, NULL);
|
||||
}
|
||||
mp_obj_list_append(self->traceback, (mp_obj_t)(machine_uint_t)file);
|
||||
mp_obj_list_append(self->traceback, (mp_obj_t)(machine_uint_t)line);
|
||||
|
@ -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
|
||||
uint use_def_args = self->n_args - n_args;
|
||||
mp_map_t *old_globals = rt_globals_get();
|
||||
rt_globals_set(self->globals);
|
||||
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);
|
||||
mp_obj_t result;
|
||||
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);
|
||||
rt_globals_set(old_globals);
|
||||
|
||||
return result;
|
||||
if (vm_return_kind == MP_VM_RETURN_NORMAL) {
|
||||
return result;
|
||||
} else { // MP_VM_RETURN_EXCEPTION
|
||||
nlr_jump(result);
|
||||
}
|
||||
}
|
||||
|
||||
const mp_obj_type_t fun_bc_type = {
|
||||
|
@ -82,22 +82,30 @@ STATIC mp_obj_t gen_next_send(mp_obj_t self_in, mp_obj_t send_value) {
|
||||
} else {
|
||||
*self->sp = send_value;
|
||||
}
|
||||
bool yield = mp_execute_byte_code_2(self->code_info, &self->ip, &self->state[self->n_state - 1], &self->sp);
|
||||
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 {
|
||||
// TODO return StopIteration with value *self->sp
|
||||
return mp_const_stop_iteration;
|
||||
}
|
||||
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);
|
||||
switch (vm_return_kind) {
|
||||
case MP_VM_RETURN_NORMAL:
|
||||
// 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 {
|
||||
// TODO return StopIteration with value *self->sp
|
||||
return mp_const_stop_iteration;
|
||||
}
|
||||
|
||||
case MP_VM_RETURN_YIELD:
|
||||
return *self->sp;
|
||||
|
||||
case MP_VM_RETURN_EXCEPTION:
|
||||
default:
|
||||
// TODO
|
||||
assert(0);
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
|
||||
|
43
py/vm.c
43
py/vm.c
@ -47,7 +47,7 @@ typedef enum {
|
||||
#define TOP() (*sp)
|
||||
#define SET_TOP(val) *sp = (val)
|
||||
|
||||
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) {
|
||||
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) {
|
||||
// allocate state for locals and stack
|
||||
mp_obj_t temp_state[10];
|
||||
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
|
||||
}
|
||||
|
||||
// execute the byte code
|
||||
if (mp_execute_byte_code_2(code, &ip, &state[n_state - 1], &sp)) {
|
||||
// it shouldn't yield
|
||||
assert(0);
|
||||
}
|
||||
mp_vm_return_kind_t vm_return_kind = mp_execute_byte_code_2(code, &ip, &state[n_state - 1], &sp);
|
||||
|
||||
// TODO check fails if, eg, return from within for loop
|
||||
//assert(sp == &state[17]);
|
||||
return *sp;
|
||||
switch (vm_return_kind) {
|
||||
case MP_VM_RETURN_NORMAL:
|
||||
*ret = *sp;
|
||||
return MP_VM_RETURN_NORMAL;
|
||||
case MP_VM_RETURN_EXCEPTION:
|
||||
*ret = state[n_state - 1];
|
||||
return MP_VM_RETURN_EXCEPTION;
|
||||
case MP_VM_RETURN_YIELD: // byte-code shouldn't yield
|
||||
default:
|
||||
assert(0);
|
||||
*ret = mp_const_none;
|
||||
return MP_VM_RETURN_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
// fastn has items in reverse order (fastn[0] is local[0], fastn[-1] is local[1], etc)
|
||||
// sp points to bottom of stack which grows up
|
||||
// returns true if bytecode yielded
|
||||
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) {
|
||||
// returns:
|
||||
// MP_VM_RETURN_NORMAL, sp valid, return value in *sp
|
||||
// MP_VM_RETURN_YIELD, ip, sp valid, yielded value in *sp
|
||||
// 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) {
|
||||
// careful: be sure to declare volatile any variables read in the exception handler (written is ok, I think)
|
||||
|
||||
const byte *ip = *ip_in_out;
|
||||
@ -569,7 +579,7 @@ unwind_return:
|
||||
nlr_pop();
|
||||
*sp_in_out = sp;
|
||||
assert(exc_sp == &exc_stack[0] - 1);
|
||||
return false;
|
||||
return MP_VM_RETURN_NORMAL;
|
||||
|
||||
case MP_BC_RAISE_VARARGS:
|
||||
unum = *ip++;
|
||||
@ -581,7 +591,7 @@ unwind_return:
|
||||
nlr_pop();
|
||||
*ip_in_out = ip;
|
||||
*sp_in_out = sp;
|
||||
return true;
|
||||
return MP_VM_RETURN_YIELD;
|
||||
|
||||
case MP_BC_IMPORT_NAME:
|
||||
DECODE_QSTR;
|
||||
@ -603,7 +613,7 @@ unwind_return:
|
||||
printf("code %p, byte code 0x%02x not implemented\n", ip, op);
|
||||
assert(0);
|
||||
nlr_pop();
|
||||
return false;
|
||||
return MP_VM_RETURN_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -653,9 +663,10 @@ unwind_return:
|
||||
PUSH(nlr.ret_val); // TODO should be type(nlr.ret_val), I think...
|
||||
|
||||
} else {
|
||||
// re-raise exception to higher level
|
||||
// TODO what to do if this is a generator??
|
||||
nlr_jump(nlr.ret_val);
|
||||
// propagate exception to higher level
|
||||
// TODO what to do about ip and sp? they don't really make sense at this point
|
||||
fastn[0] = nlr.ret_val; // must put exception here because sp is invalid
|
||||
return MP_VM_RETURN_EXCEPTION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
import import1b
|
||||
|
||||
def func1():
|
||||
return
|
||||
print('func1')
|
||||
|
||||
def func2():
|
||||
try:
|
||||
|
Loading…
x
Reference in New Issue
Block a user