Skip to content

Commit 7d95f0d

Browse files
[3.15] gh-130279: Fix missing exception handlers for jumps in loops (GH-159460) (#159698)
gh-130279: Fix missing exception handlers for jumps in loops (GH-159460) (cherry picked from commit 036d309) Co-authored-by: Han <180024497+LH-and-FPGA@users.noreply.github.com>
1 parent d3b46c5 commit 7d95f0d

6 files changed

Lines changed: 127 additions & 69 deletions

File tree

‎Lib/test/test_compile.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,28 @@ def f():
13801380

13811381
self.check_line_numbers(f.__code__, 'JUMP_BACKWARD')
13821382

1383+
def test_backward_jump_conditional_in_loop_in_try(self):
1384+
# gh-130279: The jumps in a loop body, including the backward jump
1385+
# for the untaken branch of a conditional at the end of the body,
1386+
# must be covered by the exception handler of the enclosing try.
1387+
def f(items):
1388+
try:
1389+
for x in items:
1390+
if x < 0:
1391+
raise ValueError(x)
1392+
except KeyboardInterrupt:
1393+
return 'handled'
1394+
1395+
bytecode = dis.Bytecode(f)
1396+
jumps = [instr for instr in bytecode
1397+
if instr.opcode in dis.hasjump]
1398+
self.assertTrue(jumps)
1399+
for instr in jumps:
1400+
with self.subTest(instr=instr):
1401+
self.assertTrue(
1402+
any(entry.start <= instr.offset < entry.end
1403+
for entry in bytecode.exception_entries))
1404+
13831405
def test_big_dict_literal(self):
13841406
# The compiler has a flushing point in "compiler_dict" that calls compiles
13851407
# a portion of the dictionary literal when the loop that iterates over the items

‎Lib/test/test_dis.py‎

Lines changed: 56 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -453,53 +453,52 @@ def foo(a: int, b: str) -> str:
453453
"""
454454

455455
dis_traceback = """\
456-
%4d RESUME 0
456+
%4d RESUME 0
457457
458-
%4d NOP
458+
%4d NOP
459459
460-
%4d L1: LOAD_SMALL_INT 1
461-
LOAD_SMALL_INT 0
462-
--> BINARY_OP 11 (/)
463-
POP_TOP
460+
%4d L1: LOAD_SMALL_INT 1
461+
LOAD_SMALL_INT 0
462+
--> BINARY_OP 11 (/)
463+
POP_TOP
464464
465-
%4d L2: LOAD_FAST_CHECK 1 (tb)
466-
RETURN_VALUE
465+
%4d L2: LOAD_FAST_CHECK 1 (tb)
466+
RETURN_VALUE
467467
468-
-- L3: PUSH_EXC_INFO
468+
-- L3: PUSH_EXC_INFO
469469
470-
%4d LOAD_GLOBAL 0 (Exception)
471-
CHECK_EXC_MATCH
472-
POP_JUMP_IF_FALSE 24 (to L9)
473-
L4: NOT_TAKEN
474-
L5: STORE_FAST 0 (e)
470+
%4d LOAD_GLOBAL 0 (Exception)
471+
CHECK_EXC_MATCH
472+
POP_JUMP_IF_FALSE 24 (to L7)
473+
NOT_TAKEN
474+
STORE_FAST 0 (e)
475475
476-
%4d L6: LOAD_FAST 0 (e)
477-
LOAD_ATTR 2 (__traceback__)
478-
STORE_FAST 1 (tb)
479-
L7: POP_EXCEPT
480-
LOAD_COMMON_CONSTANT 7 (None)
481-
STORE_FAST 0 (e)
482-
DELETE_FAST 0 (e)
476+
%4d L4: LOAD_FAST 0 (e)
477+
LOAD_ATTR 2 (__traceback__)
478+
STORE_FAST 1 (tb)
479+
L5: POP_EXCEPT
480+
LOAD_COMMON_CONSTANT 7 (None)
481+
STORE_FAST 0 (e)
482+
DELETE_FAST 0 (e)
483483
484-
%4d LOAD_FAST 1 (tb)
485-
RETURN_VALUE
484+
%4d LOAD_FAST 1 (tb)
485+
RETURN_VALUE
486486
487-
-- L8: LOAD_COMMON_CONSTANT 7 (None)
488-
STORE_FAST 0 (e)
489-
DELETE_FAST 0 (e)
490-
RERAISE 1
487+
-- L6: LOAD_COMMON_CONSTANT 7 (None)
488+
STORE_FAST 0 (e)
489+
DELETE_FAST 0 (e)
490+
RERAISE 1
491491
492-
%4d L9: RERAISE 0
492+
%4d L7: RERAISE 0
493493
494-
-- L10: COPY 3
495-
POP_EXCEPT
496-
RERAISE 1
494+
-- L8: COPY 3
495+
POP_EXCEPT
496+
RERAISE 1
497497
ExceptionTable:
498498
L1 to L2 -> L3 [0]
499-
L3 to L4 -> L10 [1] lasti
500-
L5 to L6 -> L10 [1] lasti
501-
L6 to L7 -> L8 [1] lasti
502-
L8 to L10 -> L10 [1] lasti
499+
L3 to L4 -> L8 [1] lasti
500+
L4 to L5 -> L6 [1] lasti
501+
L6 to L8 -> L8 [1] lasti
503502
""" % (TRACEBACK_CODE.co_firstlineno,
504503
TRACEBACK_CODE.co_firstlineno + 1,
505504
TRACEBACK_CODE.co_firstlineno + 2,
@@ -568,11 +567,11 @@ def _with(c):
568567
%4d L3: PUSH_EXC_INFO
569568
WITH_EXCEPT_START
570569
TO_BOOL
571-
POP_JUMP_IF_TRUE 2 (to L6)
572-
L4: NOT_TAKEN
573-
L5: RERAISE 2
574-
L6: POP_TOP
575-
L7: POP_EXCEPT
570+
POP_JUMP_IF_TRUE 2 (to L4)
571+
NOT_TAKEN
572+
RERAISE 2
573+
L4: POP_TOP
574+
L5: POP_EXCEPT
576575
POP_TOP
577576
POP_TOP
578577
POP_TOP
@@ -582,13 +581,12 @@ def _with(c):
582581
LOAD_COMMON_CONSTANT 7 (None)
583582
RETURN_VALUE
584583
585-
-- L8: COPY 3
584+
-- L6: COPY 3
586585
POP_EXCEPT
587586
RERAISE 1
588587
ExceptionTable:
589588
L1 to L2 -> L3 [2] lasti
590-
L3 to L4 -> L8 [4] lasti
591-
L5 to L7 -> L8 [4] lasti
589+
L3 to L5 -> L6 [4] lasti
592590
""" % (_with.__code__.co_firstlineno,
593591
_with.__code__.co_firstlineno + 1,
594592
_with.__code__.co_firstlineno + 2,
@@ -663,11 +661,11 @@ async def _asyncwith(c):
663661
L20: CLEANUP_THROW
664662
L21: END_SEND
665663
TO_BOOL
666-
POP_JUMP_IF_TRUE 2 (to L24)
667-
L22: NOT_TAKEN
668-
L23: RERAISE 2
669-
L24: POP_TOP
670-
L25: POP_EXCEPT
664+
POP_JUMP_IF_TRUE 2 (to L22)
665+
NOT_TAKEN
666+
RERAISE 2
667+
L22: POP_TOP
668+
L23: POP_EXCEPT
671669
POP_TOP
672670
POP_TOP
673671
POP_TOP
@@ -677,25 +675,24 @@ async def _asyncwith(c):
677675
LOAD_COMMON_CONSTANT 7 (None)
678676
RETURN_VALUE
679677
680-
-- L26: COPY 3
678+
-- L24: COPY 3
681679
POP_EXCEPT
682680
RERAISE 1
683-
L27: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
681+
L25: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
684682
RERAISE 1
685683
ExceptionTable:
686-
L1 to L3 -> L27 [0] lasti
684+
L1 to L3 -> L25 [0] lasti
687685
L3 to L4 -> L12 [5]
688-
L4 to L6 -> L27 [0] lasti
686+
L4 to L6 -> L25 [0] lasti
689687
L6 to L7 -> L16 [2] lasti
690-
L7 to L9 -> L27 [0] lasti
688+
L7 to L9 -> L25 [0] lasti
691689
L9 to L10 -> L14 [3]
692-
L10 to L13 -> L27 [0] lasti
693-
L14 to L15 -> L27 [0] lasti
694-
L16 to L18 -> L26 [4] lasti
690+
L10 to L13 -> L25 [0] lasti
691+
L14 to L15 -> L25 [0] lasti
692+
L16 to L18 -> L24 [4] lasti
695693
L18 to L19 -> L20 [8]
696-
L19 to L22 -> L26 [4] lasti
697-
L23 to L25 -> L26 [4] lasti
698-
L25 to L27 -> L27 [0] lasti
694+
L19 to L23 -> L24 [4] lasti
695+
L23 to L25 -> L25 [0] lasti
699696
""" % (_asyncwith.__code__.co_firstlineno,
700697
_asyncwith.__code__.co_firstlineno + 1,
701698
_asyncwith.__code__.co_firstlineno + 2,

‎Lib/test/test_monitoring.py‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2529,6 +2529,37 @@ def foo():
25292529
foo()
25302530
sys.monitoring.set_local_events(0, foo.__code__, 0)
25312531

2532+
def test_130279(self):
2533+
# gh-130279
2534+
# An exception raised by the BRANCH_LEFT callback of a forward
2535+
# branch must be caught by the enclosing try
2536+
class Boom(Exception):
2537+
pass
2538+
2539+
raised = False
2540+
2541+
def callback(code, src, dst):
2542+
nonlocal raised
2543+
if not raised:
2544+
raised = True
2545+
raise Boom
2546+
2547+
def foo(x):
2548+
try:
2549+
if x:
2550+
pass
2551+
except Boom:
2552+
return "caught"
2553+
return "not raised"
2554+
2555+
sys.monitoring.register_callback(TEST_TOOL, E.BRANCH_LEFT, callback)
2556+
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.BRANCH_LEFT)
2557+
try:
2558+
self.assertEqual(foo(True), "caught")
2559+
finally:
2560+
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0)
2561+
sys.monitoring.register_callback(TEST_TOOL, E.BRANCH_LEFT, None)
2562+
25322563

25332564
class TestOptimizer(MonitoringTestBase, unittest.TestCase):
25342565

‎Misc/ACKS‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,7 @@ Ivan Levkivskyi
11431143
Ben Lewis
11441144
William Lewis
11451145
Akira Li
1146+
Han Li
11461147
Jiahao Li
11471148
Robert Li
11481149
Xuanji Li
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix bug where compiler did not set the right exception handler for some jump
2+
instructions.

‎Python/flowgraph.c‎

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ cfg_builder_new_block(cfg_builder *g)
187187
}
188188

189189
static int
190-
basicblock_addop(basicblock *b, int opcode, int oparg, location loc)
190+
basicblock_addop(basicblock *b, int opcode, int oparg, location loc,
191+
basicblock *except)
191192
{
192193
assert(IS_WITHIN_OPCODE_RANGE(opcode));
193194
assert(!IS_ASSEMBLER_OPCODE(opcode));
@@ -202,23 +203,24 @@ basicblock_addop(basicblock *b, int opcode, int oparg, location loc)
202203
i->i_opcode = opcode;
203204
i->i_oparg = oparg;
204205
i->i_loc = loc;
206+
i->i_except = except;
205207
// memory is already zero initialized
206208
assert(i->i_target == NULL);
207-
assert(i->i_except == NULL);
208209

209210
return SUCCESS;
210211
}
211212

212213
static int
213-
basicblock_add_jump(basicblock *b, int opcode, basicblock *target, location loc)
214+
basicblock_add_jump(basicblock *b, int opcode, basicblock *target, location loc,
215+
basicblock *except)
214216
{
215217
cfg_instr *last = basicblock_last_instr(b);
216218
if (last && is_jump(last)) {
217219
return ERROR;
218220
}
219221

220222
RETURN_IF_ERROR(
221-
basicblock_addop(b, opcode, target->b_label.id, loc));
223+
basicblock_addop(b, opcode, target->b_label.id, loc, except));
222224
last = basicblock_last_instr(b);
223225
assert(last && last->i_opcode == opcode);
224226
last->i_target = target;
@@ -492,7 +494,7 @@ int
492494
_PyCfgBuilder_Addop(cfg_builder *g, int opcode, int oparg, location loc)
493495
{
494496
RETURN_IF_ERROR(cfg_builder_maybe_start_new_block(g));
495-
return basicblock_addop(g->g_curblock, opcode, oparg, loc);
497+
return basicblock_addop(g->g_curblock, opcode, oparg, loc, NULL);
496498
}
497499

498500

@@ -553,7 +555,7 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) {
553555
bool is_forward = last->i_target->b_visited == 0;
554556
if (is_forward) {
555557
RETURN_IF_ERROR(
556-
basicblock_addop(b, NOT_TAKEN, 0, last->i_loc));
558+
basicblock_addop(b, NOT_TAKEN, 0, last->i_loc, last->i_except));
557559
return SUCCESS;
558560
}
559561

@@ -582,9 +584,11 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) {
582584
return ERROR;
583585
}
584586
RETURN_IF_ERROR(
585-
basicblock_addop(backwards_jump, NOT_TAKEN, 0, last->i_loc));
587+
basicblock_addop(backwards_jump, NOT_TAKEN, 0, last->i_loc,
588+
last->i_except));
586589
RETURN_IF_ERROR(
587-
basicblock_add_jump(backwards_jump, JUMP, target, last->i_loc));
590+
basicblock_add_jump(backwards_jump, JUMP, target, last->i_loc,
591+
last->i_except));
588592
backwards_jump->b_startdepth = target->b_startdepth;
589593
last->i_opcode = reversed_opcode;
590594
last->i_target = b->b_next;
@@ -1292,7 +1296,8 @@ jump_thread(basicblock *bb, cfg_instr *inst, cfg_instr *target, int opcode)
12921296

12931297
RETURN_IF_ERROR(
12941298
basicblock_add_jump(
1295-
bb, opcode, target->i_target, target->i_loc));
1299+
bb, opcode, target->i_target, target->i_loc,
1300+
target->i_except));
12961301

12971302
return true;
12981303
}
@@ -3541,7 +3546,7 @@ push_cold_blocks_to_end(cfg_builder *g) {
35413546
b->b_next->b_label.id = next_lbl++;
35423547
}
35433548
basicblock_addop(explicit_jump, JUMP_NO_INTERRUPT, b->b_next->b_label.id,
3544-
NO_LOCATION);
3549+
NO_LOCATION, NULL);
35453550
explicit_jump->b_cold = 1;
35463551
explicit_jump->b_next = b->b_next;
35473552
explicit_jump->b_predecessors = 1;

0 commit comments

Comments
 (0)