summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorPaul Sokolovsky <pfalcon@users.sourceforge.net>2014-03-30 00:48:21 +0200
committerPaul Sokolovsky <pfalcon@users.sourceforge.net>2014-03-30 01:01:35 +0200
commit0c904df8e6c4cf9123a837861b97585a61b3d8df (patch)
tree9ff8dbdbb02b300f6b08a3570ea9e3e408bcb4b7
parent69975df3fffaae5f11caa6663f01f4a876d3ab41 (diff)
downloadmicropython-0c904df8e6c4cf9123a837861b97585a61b3d8df.tar.gz
micropython-0c904df8e6c4cf9123a837861b97585a61b3d8df.zip
vm: Save current active exception on opening new try block.
Required to reraise correct exceptions in except block, regardless if more try blocks with active exceptions happen in the same except block. P.S. This "automagic reraise" appears to be quite wasteful feature of Python - we need to save pending exception just in case it *might* be reraised. Instead, programmer could explcitly capture exception to a variable using "except ... as var", and reraise that. So, consider disabling argless raise support as an optimization.
-rw-r--r--py/bc.h2
-rw-r--r--py/vm.c2
-rw-r--r--tests/basics/try-reraise2.py23
3 files changed, 27 insertions, 0 deletions
diff --git a/py/bc.h b/py/bc.h
index 065daece47..d7e6aa70eb 100644
--- a/py/bc.h
+++ b/py/bc.h
@@ -9,6 +9,8 @@ typedef struct _mp_exc_stack {
const byte *handler;
// bit 0 is saved currently_in_except_block value
mp_obj_t *val_sp;
+ // Saved exception, valid if currently_in_except_block bit is 1
+ mp_obj_t prev_exc;
// We might only have 2 interesting cases here: SETUP_EXCEPT & SETUP_FINALLY,
// consider storing it in bit 1 of val_sp. TODO: SETUP_WITH?
byte opcode;
diff --git a/py/vm.c b/py/vm.c
index 7316742665..b96cca1425 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -51,10 +51,12 @@ typedef enum {
exc_sp->opcode = op; \
exc_sp->handler = ip + unum; \
exc_sp->val_sp = MP_TAGPTR_MAKE(sp, currently_in_except_block); \
+ exc_sp->prev_exc = nlr.ret_val; \
currently_in_except_block = 0; /* in a try block now */
#define POP_EXC_BLOCK() \
currently_in_except_block = MP_TAGPTR_TAG(exc_sp->val_sp); /* restore previous state */ \
+ if (currently_in_except_block) { nlr.ret_val = exc_sp->prev_exc; } \
exc_sp--; /* pop back to previous exception handler */
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, mp_obj_t *ret) {
diff --git a/tests/basics/try-reraise2.py b/tests/basics/try-reraise2.py
new file mode 100644
index 0000000000..9ab8d9c099
--- /dev/null
+++ b/tests/basics/try-reraise2.py
@@ -0,0 +1,23 @@
+# Reraise not the latest occured exception
+def f():
+ try:
+ raise ValueError("val", 3)
+ except:
+ try:
+ raise TypeError
+ except:
+ try:
+ try:
+ raise AttributeError
+ except:
+ pass
+ raise
+ except TypeError:
+ pass
+ # This should raise original ValueError, not the most recently occurred AttributeError
+ raise
+
+try:
+ f()
+except ValueError as e:
+ print(repr(e))