Skip to content

Commit cbddb27

Browse files
committed
py: Implement break/continue from an exception with finally.
Still todo: break/continue from within the finally block itself.
1 parent a908202 commit cbddb27

8 files changed

Lines changed: 103 additions & 81 deletions

File tree

py/bc0.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@
5252
#define MP_BC_JUMP_IF_TRUE_OR_POP (0x48) // rel byte code offset, 16-bit signed, in excess
5353
#define MP_BC_JUMP_IF_FALSE_OR_POP (0x49) // rel byte code offset, 16-bit signed, in excess
5454
#define MP_BC_SETUP_LOOP (0x4a) // rel byte code offset, 16-bit unsigned
55-
#define MP_BC_BREAK_LOOP (0x4b) // rel byte code offset, 16-bit unsigned
56-
#define MP_BC_CONTINUE_LOOP (0x4c) // rel byte code offset, 16-bit unsigned
5755
#define MP_BC_SETUP_WITH (0x4d) // rel byte code offset, 16-bit unsigned
5856
#define MP_BC_WITH_CLEANUP (0x4e)
5957
#define MP_BC_SETUP_EXCEPT (0x4f) // rel byte code offset, 16-bit unsigned
@@ -63,6 +61,7 @@
6361
#define MP_BC_FOR_ITER (0x53) // rel byte code offset, 16-bit unsigned
6462
#define MP_BC_POP_BLOCK (0x54)
6563
#define MP_BC_POP_EXCEPT (0x55)
64+
#define MP_BC_UNWIND_JUMP (0x56) // rel byte code offset, 16-bit signed, in excess; then a byte
6665

6766
#define MP_BC_UNARY_OP (0x60) // byte
6867
#define MP_BC_BINARY_OP (0x61) // byte

py/compile.c

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ typedef struct _compiler_t {
5050

5151
int break_label;
5252
int continue_label;
53-
int except_nest_level;
53+
int break_continue_except_level;
54+
int cur_except_level; // increased for SETUP_EXCEPT, SETUP_FINALLY; decreased for POP_BLOCK, POP_EXCEPT
5455

5556
int n_arg_keyword;
5657
bool have_star_arg;
@@ -1080,18 +1081,14 @@ void compile_break_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
10801081
if (comp->break_label == 0) {
10811082
printf("ERROR: cannot break from here\n");
10821083
}
1083-
EMIT_ARG(break_loop, comp->break_label);
1084+
EMIT_ARG(break_loop, comp->break_label, comp->cur_except_level - comp->break_continue_except_level);
10841085
}
10851086

10861087
void compile_continue_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
10871088
if (comp->continue_label == 0) {
10881089
printf("ERROR: cannot continue from here\n");
10891090
}
1090-
if (comp->except_nest_level > 0) {
1091-
EMIT_ARG(continue_loop, comp->continue_label);
1092-
} else {
1093-
EMIT_ARG(jump, comp->continue_label);
1094-
}
1091+
EMIT_ARG(continue_loop, comp->continue_label, comp->cur_except_level - comp->break_continue_except_level);
10951092
}
10961093

10971094
void compile_return_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
@@ -1387,15 +1384,22 @@ void compile_if_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
13871384
EMIT_ARG(label_assign, l_end);
13881385
}
13891386

1390-
void compile_while_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
1391-
int old_break_label = comp->break_label;
1392-
int old_continue_label = comp->continue_label;
1387+
#define START_BREAK_CONTINUE_BLOCK \
1388+
int old_break_label = comp->break_label; \
1389+
int old_continue_label = comp->continue_label; \
1390+
int break_label = comp_next_label(comp); \
1391+
int continue_label = comp_next_label(comp); \
1392+
comp->break_label = break_label; \
1393+
comp->continue_label = continue_label; \
1394+
comp->break_continue_except_level = comp->cur_except_level;
13931395

1394-
int break_label = comp_next_label(comp);
1395-
int continue_label = comp_next_label(comp);
1396+
#define END_BREAK_CONTINUE_BLOCK \
1397+
comp->break_label = old_break_label; \
1398+
comp->continue_label = old_continue_label; \
1399+
comp->break_continue_except_level = comp->cur_except_level;
13961400

1397-
comp->break_label = break_label;
1398-
comp->continue_label = continue_label;
1401+
void compile_while_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
1402+
START_BREAK_CONTINUE_BLOCK
13991403

14001404
// compared to CPython, we have an optimised version of while loops
14011405
#if MICROPY_EMIT_CPYTHON
@@ -1423,8 +1427,7 @@ void compile_while_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
14231427
#endif
14241428

14251429
// break/continue apply to outer loop (if any) in the else block
1426-
comp->break_label = old_break_label;
1427-
comp->continue_label = old_continue_label;
1430+
END_BREAK_CONTINUE_BLOCK
14281431

14291432
compile_node(comp, pns->nodes[2]); // else
14301433

@@ -1434,14 +1437,7 @@ void compile_while_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
14341437
// TODO preload end and step onto stack if they are not constants
14351438
// TODO check if step is negative and do opposite test
14361439
void compile_for_stmt_optimised_range(compiler_t *comp, mp_parse_node_t pn_var, mp_parse_node_t pn_start, mp_parse_node_t pn_end, mp_parse_node_t pn_step, mp_parse_node_t pn_body, mp_parse_node_t pn_else) {
1437-
int old_break_label = comp->break_label;
1438-
int old_continue_label = comp->continue_label;
1439-
1440-
int break_label = comp_next_label(comp);
1441-
int continue_label = comp_next_label(comp);
1442-
1443-
comp->break_label = break_label;
1444-
comp->continue_label = continue_label;
1440+
START_BREAK_CONTINUE_BLOCK
14451441

14461442
int top_label = comp_next_label(comp);
14471443
int entry_label = comp_next_label(comp);
@@ -1477,8 +1473,7 @@ void compile_for_stmt_optimised_range(compiler_t *comp, mp_parse_node_t pn_var,
14771473
EMIT_ARG(pop_jump_if_true, top_label);
14781474

14791475
// break/continue apply to outer loop (if any) in the else block
1480-
comp->break_label = old_break_label;
1481-
comp->continue_label = old_continue_label;
1476+
END_BREAK_CONTINUE_BLOCK
14821477

14831478
compile_node(comp, pn_else);
14841479

@@ -1531,38 +1526,30 @@ void compile_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
15311526
}
15321527
#endif
15331528

1534-
int old_break_label = comp->break_label;
1535-
int old_continue_label = comp->continue_label;
1529+
START_BREAK_CONTINUE_BLOCK
15361530

1537-
int for_label = comp_next_label(comp);
15381531
int pop_label = comp_next_label(comp);
15391532
int end_label = comp_next_label(comp);
15401533

1541-
int break_label = comp_next_label(comp);
1542-
1543-
comp->continue_label = for_label;
1544-
comp->break_label = break_label;
1545-
15461534
// I don't think our implementation needs SETUP_LOOP/POP_BLOCK for for-statements
15471535
#if MICROPY_EMIT_CPYTHON
15481536
EMIT_ARG(setup_loop, end_label);
15491537
#endif
15501538

15511539
compile_node(comp, pns->nodes[1]); // iterator
15521540
EMIT(get_iter);
1553-
EMIT_ARG(label_assign, for_label);
1541+
EMIT_ARG(label_assign, continue_label);
15541542
EMIT_ARG(for_iter, pop_label);
15551543
c_assign(comp, pns->nodes[0], ASSIGN_STORE); // variable
15561544
compile_node(comp, pns->nodes[2]); // body
15571545
if (!EMIT(last_emit_was_return_value)) {
1558-
EMIT_ARG(jump, for_label);
1546+
EMIT_ARG(jump, continue_label);
15591547
}
15601548
EMIT_ARG(label_assign, pop_label);
15611549
EMIT(for_iter_end);
15621550

15631551
// break/continue apply to outer loop (if any) in the else block
1564-
comp->break_label = old_break_label;
1565-
comp->continue_label = old_continue_label;
1552+
END_BREAK_CONTINUE_BLOCK
15661553

15671554
#if MICROPY_EMIT_CPYTHON
15681555
EMIT(pop_block);
@@ -1582,8 +1569,10 @@ void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_except,
15821569
int stack_size = EMIT(get_stack_size);
15831570
int l1 = comp_next_label(comp);
15841571
int success_label = comp_next_label(comp);
1585-
comp->except_nest_level += 1; // for correct handling of continue
1572+
15861573
EMIT_ARG(setup_except, l1);
1574+
comp->cur_except_level += 1;
1575+
15871576
compile_node(comp, pn_body); // body
15881577
EMIT(pop_block);
15891578
EMIT_ARG(jump, success_label);
@@ -1634,6 +1623,7 @@ void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_except,
16341623
if (qstr_exception_local != 0) {
16351624
l3 = comp_next_label(comp);
16361625
EMIT_ARG(setup_finally, l3);
1626+
comp->cur_except_level += 1;
16371627
}
16381628
compile_node(comp, pns_except->nodes[1]);
16391629
if (qstr_exception_local != 0) {
@@ -1646,15 +1636,18 @@ void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_except,
16461636
EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
16471637
EMIT_ARG(store_id, qstr_exception_local);
16481638
EMIT_ARG(delete_id, qstr_exception_local);
1639+
1640+
comp->cur_except_level -= 1;
16491641
EMIT(end_finally);
16501642
}
16511643
EMIT_ARG(jump, l2);
16521644
EMIT_ARG(label_assign, end_finally_label);
16531645
}
16541646

1647+
comp->cur_except_level -= 1;
16551648
EMIT(end_finally);
1649+
16561650
EMIT_ARG(label_assign, success_label);
1657-
comp->except_nest_level -= 1;
16581651
compile_node(comp, pn_else); // else block, can be null
16591652
EMIT_ARG(label_assign, l2);
16601653
EMIT_ARG(set_stack_size, stack_size);
@@ -1664,7 +1657,10 @@ void compile_try_finally(compiler_t *comp, mp_parse_node_t pn_body, int n_except
16641657
// don't understand how the stack works with exceptions, so we force it to return to the correct value
16651658
int stack_size = EMIT(get_stack_size);
16661659
int l_finally_block = comp_next_label(comp);
1660+
16671661
EMIT_ARG(setup_finally, l_finally_block);
1662+
comp->cur_except_level += 1;
1663+
16681664
if (n_except == 0) {
16691665
assert(MP_PARSE_NODE_IS_NULL(pn_else));
16701666
compile_node(comp, pn_body);
@@ -1675,7 +1671,10 @@ void compile_try_finally(compiler_t *comp, mp_parse_node_t pn_body, int n_except
16751671
EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
16761672
EMIT_ARG(label_assign, l_finally_block);
16771673
compile_node(comp, pn_finally);
1674+
1675+
comp->cur_except_level -= 1;
16781676
EMIT(end_finally);
1677+
16791678
EMIT_ARG(set_stack_size, stack_size);
16801679
}
16811680

@@ -3056,7 +3055,9 @@ mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, bool is_repl) {
30563055

30573056
comp->break_label = 0;
30583057
comp->continue_label = 0;
3059-
comp->except_nest_level = 0;
3058+
comp->break_continue_except_level = 0;
3059+
comp->cur_except_level = 0;
3060+
30603061
comp->scope_head = NULL;
30613062
comp->scope_cur = NULL;
30623063

py/emit.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ typedef struct _emit_method_table_t {
7272
void (*jump_if_true_or_pop)(emit_t *emit, int label);
7373
void (*jump_if_false_or_pop)(emit_t *emit, int label);
7474
void (*setup_loop)(emit_t *emit, int label);
75-
void (*break_loop)(emit_t *emit, int label);
76-
void (*continue_loop)(emit_t *emit, int label);
75+
void (*break_loop)(emit_t *emit, int label, int except_depth);
76+
void (*continue_loop)(emit_t *emit, int label, int except_depth);
7777
void (*setup_with)(emit_t *emit, int label);
7878
void (*with_cleanup)(emit_t *emit);
7979
void (*setup_except)(emit_t *emit, int label);

py/emitbc.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -540,14 +540,14 @@ static void emit_bc_setup_loop(emit_t *emit, int label) {
540540
emit_write_byte_code_byte_unsigned_label(emit, MP_BC_SETUP_LOOP, label);
541541
}
542542

543-
static void emit_bc_break_loop(emit_t *emit, int label) {
544-
emit_pre(emit, 0);
545-
emit_write_byte_code_byte_unsigned_label(emit, MP_BC_BREAK_LOOP, label);
546-
}
547-
548-
static void emit_bc_continue_loop(emit_t *emit, int label) {
549-
emit_pre(emit, 0);
550-
emit_write_byte_code_byte_unsigned_label(emit, MP_BC_CONTINUE_LOOP, label);
543+
static void emit_bc_unwind_jump(emit_t *emit, int label, int except_depth) {
544+
if (except_depth == 0) {
545+
emit_bc_jump(emit, label);
546+
} else {
547+
emit_pre(emit, 0);
548+
emit_write_byte_code_byte_signed_label(emit, MP_BC_UNWIND_JUMP, label);
549+
emit_write_byte_code_byte(emit, except_depth);
550+
}
551551
}
552552

553553
static void emit_bc_setup_with(emit_t *emit, int label) {
@@ -828,8 +828,8 @@ const emit_method_table_t emit_bc_method_table = {
828828
emit_bc_jump_if_true_or_pop,
829829
emit_bc_jump_if_false_or_pop,
830830
emit_bc_setup_loop,
831-
emit_bc_break_loop,
832-
emit_bc_continue_loop,
831+
emit_bc_unwind_jump,
832+
emit_bc_unwind_jump,
833833
emit_bc_setup_with,
834834
emit_bc_with_cleanup,
835835
emit_bc_setup_except,

py/emitnative.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -931,10 +931,10 @@ static void emit_native_setup_loop(emit_t *emit, int label) {
931931
emit_post(emit);
932932
}
933933

934-
static void emit_native_break_loop(emit_t *emit, int label) {
934+
static void emit_native_break_loop(emit_t *emit, int label, int except_depth) {
935935
emit_native_jump(emit, label); // TODO properly
936936
}
937-
static void emit_native_continue_loop(emit_t *emit, int label) {
937+
static void emit_native_continue_loop(emit_t *emit, int label, int except_depth) {
938938
assert(0);
939939
}
940940
static void emit_native_setup_with(emit_t *emit, int label) {

py/showbc.c

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -220,14 +220,10 @@ void mp_byte_code_print(const byte *ip, int len) {
220220
printf("SETUP_LOOP " UINT_FMT, ip + unum - ip_start);
221221
break;
222222

223-
case MP_BC_BREAK_LOOP:
224-
DECODE_ULABEL; // loop labels are always forward
225-
printf("BREAK_LOOP " UINT_FMT, ip + unum - ip_start);
226-
break;
227-
228-
case MP_BC_CONTINUE_LOOP:
229-
DECODE_ULABEL; // loop labels are always forward
230-
printf("CONTINUE_LOOP " UINT_FMT, ip + unum - ip_start);
223+
case MP_BC_UNWIND_JUMP:
224+
DECODE_SLABEL;
225+
printf("UNWIND_JUMP " UINT_FMT " %d", ip + unum - ip_start, *ip);
226+
ip += 1;
231227
break;
232228

233229
case MP_BC_SETUP_EXCEPT:

py/vm.c

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ typedef struct _mp_exc_stack {
3131
} mp_exc_stack;
3232

3333
// Exception stack unwind reasons (WHY_* in CPython-speak)
34+
// TODO perhaps compress this to RETURN=0, JUMP>0, with number of unwinds
35+
// left to do encoded in the JUMP number
3436
typedef enum {
3537
UNWIND_RETURN = 1,
36-
UNWIND_BREAK,
37-
UNWIND_CONTINUE,
38+
UNWIND_JUMP,
3839
} mp_unwind_reason_t;
3940

4041
#define DECODE_UINT do { unum = *ip++; if (unum > 127) { unum = ((unum & 0x3f) << 8) | (*ip++); } } while (0)
@@ -328,16 +329,29 @@ bool mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out, mp_ob
328329
break;
329330
*/
330331

331-
// TODO this might need more sophisticated handling when breaking from within an except
332-
case MP_BC_BREAK_LOOP:
333-
DECODE_ULABEL;
334-
ip += unum;
335-
break;
336-
337-
// TODO this might need more sophisticated handling when breaking from within an except
338-
case MP_BC_CONTINUE_LOOP:
339-
DECODE_ULABEL;
340-
ip += unum;
332+
case MP_BC_UNWIND_JUMP:
333+
DECODE_SLABEL;
334+
PUSH((void*)(ip + unum)); // push destination ip for jump
335+
PUSH((void*)(machine_uint_t)(*ip)); // push number of exception handlers to unwind
336+
unwind_jump:
337+
unum = (machine_uint_t)POP(); // get number of exception handlers to unwind
338+
while (unum > 0) {
339+
unum -= 1;
340+
assert(exc_sp >= exc_stack);
341+
if (exc_sp->opcode == MP_BC_SETUP_FINALLY) {
342+
// We're going to run "finally" code as a coroutine
343+
// (not calling it recursively). Set up a sentinel
344+
// on a stack so it can return back to us when it is
345+
// done (when END_FINALLY reached).
346+
PUSH((void*)unum); // push number of exception handlers left to unwind
347+
PUSH(MP_OBJ_NEW_SMALL_INT(UNWIND_JUMP)); // push sentinel
348+
ip = exc_sp->handler; // get exception handler byte code address
349+
exc_sp--; // pop exception handler
350+
goto dispatch_loop; // run the exception handler
351+
}
352+
exc_sp--;
353+
}
354+
ip = (const byte*)POP(); // pop destination ip for jump
341355
break;
342356

343357
// matched against: POP_BLOCK or POP_EXCEPT (anything else?)
@@ -369,10 +383,8 @@ bool mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out, mp_ob
369383
switch (reason) {
370384
case UNWIND_RETURN:
371385
goto unwind_return;
372-
// TODO
373-
case UNWIND_BREAK:
374-
case UNWIND_CONTINUE:
375-
;
386+
case UNWIND_JUMP:
387+
goto unwind_jump;
376388
}
377389
assert(0);
378390
} else {

tests/basics/try-finally-break.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
for i in range(4):
2+
print(i)
3+
try:
4+
while True:
5+
try:
6+
try:
7+
break
8+
finally:
9+
print('finally 1')
10+
finally:
11+
print('finally 2')
12+
print('here')
13+
finally:
14+
print('finnaly 3')

0 commit comments

Comments
 (0)