Skip to content

Commit 25c8464

Browse files
committed
py: Fix break from within a for loop.
Needed to pop the iterator object when breaking out of a for loop. Need also to be careful to unwind exception handler before popping iterator. Addresses issue adafruit#635.
1 parent 8827682 commit 25c8464

5 files changed

Lines changed: 20 additions & 9 deletions

File tree

py/compile.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ typedef struct _compiler_t {
7373

7474
uint next_label;
7575

76-
uint break_label;
77-
uint continue_label;
76+
uint16_t break_label; // highest bit set indicates we are breaking out of a for loop
77+
uint16_t continue_label;
7878
int break_continue_except_level;
7979
uint16_t cur_except_level; // increased for SETUP_EXCEPT, SETUP_FINALLY; decreased for POP_BLOCK, POP_EXCEPT
8080

@@ -1745,6 +1745,7 @@ void compile_while_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
17451745
// And, if the loop never runs, the loop variable should never be assigned
17461746
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) {
17471747
START_BREAK_CONTINUE_BLOCK
1748+
comp->break_label |= MP_EMIT_BREAK_FROM_FOR;
17481749

17491750
uint top_label = comp_next_label(comp);
17501751
uint entry_label = comp_next_label(comp);
@@ -1843,6 +1844,7 @@ void compile_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
18431844
#endif
18441845

18451846
START_BREAK_CONTINUE_BLOCK
1847+
comp->break_label |= MP_EMIT_BREAK_FROM_FOR;
18461848

18471849
uint pop_label = comp_next_label(comp);
18481850
uint end_label = comp_next_label(comp);

py/emit.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ typedef enum {
4444
#define MP_EMIT_STAR_FLAG_SINGLE (0x01)
4545
#define MP_EMIT_STAR_FLAG_DOUBLE (0x02)
4646

47+
#define MP_EMIT_BREAK_FROM_FOR (0x8000)
48+
4749
typedef struct _emit_t emit_t;
4850

4951
typedef struct _emit_method_table_t {

py/emitbc.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -617,11 +617,15 @@ STATIC void emit_bc_jump_if_false_or_pop(emit_t *emit, uint label) {
617617

618618
STATIC void emit_bc_unwind_jump(emit_t *emit, uint label, int except_depth) {
619619
if (except_depth == 0) {
620-
emit_bc_jump(emit, label);
621-
} else {
622620
emit_bc_pre(emit, 0);
623-
emit_write_bytecode_byte_signed_label(emit, MP_BC_UNWIND_JUMP, label);
624-
emit_write_bytecode_byte(emit, except_depth);
621+
if (label & MP_EMIT_BREAK_FROM_FOR) {
622+
// need to pop the iterator if we are breaking out of a for loop
623+
emit_write_bytecode_byte(emit, MP_BC_POP_TOP);
624+
}
625+
emit_write_bytecode_byte_signed_label(emit, MP_BC_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR);
626+
} else {
627+
emit_write_bytecode_byte_signed_label(emit, MP_BC_UNWIND_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR);
628+
emit_write_bytecode_byte(emit, ((label & MP_EMIT_BREAK_FROM_FOR) ? 0x80 : 0) | except_depth);
625629
}
626630
}
627631

py/emitnative.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,7 @@ STATIC void emit_native_jump_if_false_or_pop(emit_t *emit, uint label) {
10431043
}
10441044

10451045
STATIC void emit_native_break_loop(emit_t *emit, uint label, int except_depth) {
1046-
emit_native_jump(emit, label); // TODO properly
1046+
emit_native_jump(emit, label & ~MP_EMIT_BREAK_FROM_FOR); // TODO properly
10471047
}
10481048

10491049
STATIC void emit_native_continue_loop(emit_t *emit, uint label, int except_depth) {

py/vm.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -618,10 +618,10 @@ mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_
618618
ENTRY(MP_BC_UNWIND_JUMP):
619619
DECODE_SLABEL;
620620
PUSH((void*)(ip + unum)); // push destination ip for jump
621-
PUSH((void*)(machine_uint_t)(*ip)); // push number of exception handlers to unwind
621+
PUSH((void*)(machine_uint_t)(*ip)); // push number of exception handlers to unwind (0x80 bit set if we also need to pop stack)
622622
unwind_jump:
623623
unum = (machine_uint_t)POP(); // get number of exception handlers to unwind
624-
while (unum > 0) {
624+
while ((unum & 0x7f) > 0) {
625625
unum -= 1;
626626
assert(exc_sp >= exc_stack);
627627
if (exc_sp->opcode == MP_BC_SETUP_FINALLY || exc_sp->opcode == MP_BC_SETUP_WITH) {
@@ -638,6 +638,9 @@ mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_
638638
exc_sp--;
639639
}
640640
ip = (const byte*)POP(); // pop destination ip for jump
641+
if (unum != 0) {
642+
sp--;
643+
}
641644
DISPATCH();
642645

643646
// matched against: POP_BLOCK or POP_EXCEPT (anything else?)

0 commit comments

Comments
 (0)