Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1583,6 +1583,28 @@ def f():

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

def test_backward_jump_conditional_in_loop_in_try(self):
# gh-130279: The jumps in a loop body, including the backward jump
# for the untaken branch of a conditional at the end of the body,
# must be covered by the exception handler of the enclosing try.
def f(items):
try:
for x in items:
if x < 0:
raise ValueError(x)
except KeyboardInterrupt:
return 'handled'

bytecode = dis.Bytecode(f)
jumps = [instr for instr in bytecode
if instr.opcode in dis.hasjump]
self.assertTrue(jumps)
for instr in jumps:
with self.subTest(instr=instr):
self.assertTrue(
any(entry.start <= instr.offset < entry.end
for entry in bytecode.exception_entries))

def test_big_dict_literal(self):
# The compiler has a flushing point in "compiler_dict" that calls compiles
# a portion of the dictionary literal when the loop that iterates over the items
Expand Down
115 changes: 56 additions & 59 deletions Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,53 +454,52 @@ def foo(a: int, b: str) -> str:
"""

dis_traceback = """\
%4d RESUME 0
%4d RESUME 0
%4d NOP
%4d NOP
%4d L1: LOAD_SMALL_INT 1
LOAD_SMALL_INT 0
--> BINARY_OP 11 (/)
POP_TOP
%4d L1: LOAD_SMALL_INT 1
LOAD_SMALL_INT 0
--> BINARY_OP 11 (/)
POP_TOP
%4d L2: LOAD_FAST_CHECK 1 (tb)
RETURN_VALUE
%4d L2: LOAD_FAST_CHECK 1 (tb)
RETURN_VALUE
-- L3: PUSH_EXC_INFO
-- L3: PUSH_EXC_INFO
%4d LOAD_GLOBAL 0 (Exception)
CHECK_EXC_MATCH
POP_JUMP_IF_FALSE 24 (to L9)
L4: NOT_TAKEN
L5: STORE_FAST 0 (e)
%4d LOAD_GLOBAL 0 (Exception)
CHECK_EXC_MATCH
POP_JUMP_IF_FALSE 24 (to L7)
NOT_TAKEN
STORE_FAST 0 (e)
%4d L6: LOAD_FAST 0 (e)
LOAD_ATTR 2 (__traceback__)
STORE_FAST 1 (tb)
L7: POP_EXCEPT
LOAD_COMMON_CONSTANT 7 (None)
STORE_FAST 0 (e)
DELETE_FAST 0 (e)
%4d L4: LOAD_FAST 0 (e)
LOAD_ATTR 2 (__traceback__)
STORE_FAST 1 (tb)
L5: POP_EXCEPT
LOAD_COMMON_CONSTANT 7 (None)
STORE_FAST 0 (e)
DELETE_FAST 0 (e)
%4d LOAD_FAST 1 (tb)
RETURN_VALUE
%4d LOAD_FAST 1 (tb)
RETURN_VALUE
-- L8: LOAD_COMMON_CONSTANT 7 (None)
STORE_FAST 0 (e)
DELETE_FAST 0 (e)
RERAISE 1
-- L6: LOAD_COMMON_CONSTANT 7 (None)
STORE_FAST 0 (e)
DELETE_FAST 0 (e)
RERAISE 1
%4d L9: RERAISE 0
%4d L7: RERAISE 0
-- L10: COPY 3
POP_EXCEPT
RERAISE 1
-- L8: COPY 3
POP_EXCEPT
RERAISE 1
ExceptionTable:
L1 to L2 -> L3 [0]
L3 to L4 -> L10 [1] lasti
L5 to L6 -> L10 [1] lasti
L6 to L7 -> L8 [1] lasti
L8 to L10 -> L10 [1] lasti
L3 to L4 -> L8 [1] lasti
L4 to L5 -> L6 [1] lasti
L6 to L8 -> L8 [1] lasti
""" % (TRACEBACK_CODE.co_firstlineno,
TRACEBACK_CODE.co_firstlineno + 1,
TRACEBACK_CODE.co_firstlineno + 2,
Expand Down Expand Up @@ -569,11 +568,11 @@ def _with(c):
%4d L3: PUSH_EXC_INFO
WITH_EXCEPT_START
TO_BOOL
POP_JUMP_IF_TRUE 2 (to L6)
L4: NOT_TAKEN
L5: RERAISE 2
L6: POP_TOP
L7: POP_EXCEPT
POP_JUMP_IF_TRUE 2 (to L4)
NOT_TAKEN
RERAISE 2
L4: POP_TOP
L5: POP_EXCEPT
POP_TOP
POP_TOP
POP_TOP
Expand All @@ -583,13 +582,12 @@ def _with(c):
LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
-- L8: COPY 3
-- L6: COPY 3
POP_EXCEPT
RERAISE 1
ExceptionTable:
L1 to L2 -> L3 [2] lasti
L3 to L4 -> L8 [4] lasti
L5 to L7 -> L8 [4] lasti
L3 to L5 -> L6 [4] lasti
""" % (_with.__code__.co_firstlineno,
_with.__code__.co_firstlineno + 1,
_with.__code__.co_firstlineno + 2,
Expand Down Expand Up @@ -664,11 +662,11 @@ async def _asyncwith(c):
L20: CLEANUP_THROW
L21: END_SEND
TO_BOOL
POP_JUMP_IF_TRUE 2 (to L24)
L22: NOT_TAKEN
L23: RERAISE 2
L24: POP_TOP
L25: POP_EXCEPT
POP_JUMP_IF_TRUE 2 (to L22)
NOT_TAKEN
RERAISE 2
L22: POP_TOP
L23: POP_EXCEPT
POP_TOP
POP_TOP
POP_TOP
Expand All @@ -678,25 +676,24 @@ async def _asyncwith(c):
LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
-- L26: COPY 3
-- L24: COPY 3
POP_EXCEPT
RERAISE 1
L27: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
L25: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
RERAISE 1
ExceptionTable:
L1 to L3 -> L27 [0] lasti
L1 to L3 -> L25 [0] lasti
L3 to L4 -> L12 [5]
L4 to L6 -> L27 [0] lasti
L4 to L6 -> L25 [0] lasti
L6 to L7 -> L16 [2] lasti
L7 to L9 -> L27 [0] lasti
L7 to L9 -> L25 [0] lasti
L9 to L10 -> L14 [3]
L10 to L13 -> L27 [0] lasti
L14 to L15 -> L27 [0] lasti
L16 to L18 -> L26 [4] lasti
L10 to L13 -> L25 [0] lasti
L14 to L15 -> L25 [0] lasti
L16 to L18 -> L24 [4] lasti
L18 to L19 -> L20 [8]
L19 to L22 -> L26 [4] lasti
L23 to L25 -> L26 [4] lasti
L25 to L27 -> L27 [0] lasti
L19 to L23 -> L24 [4] lasti
L23 to L25 -> L25 [0] lasti
""" % (_asyncwith.__code__.co_firstlineno,
_asyncwith.__code__.co_firstlineno + 1,
_asyncwith.__code__.co_firstlineno + 2,
Expand Down
31 changes: 31 additions & 0 deletions Lib/test/test_monitoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -2529,6 +2529,37 @@ def foo():
foo()
sys.monitoring.set_local_events(0, foo.__code__, 0)

def test_130279(self):
# gh-130279
# An exception raised by the BRANCH_LEFT callback of a forward
# branch must be caught by the enclosing try
class Boom(Exception):
pass

raised = False

def callback(code, src, dst):
nonlocal raised
if not raised:
raised = True
raise Boom

def foo(x):
try:
if x:
pass
except Boom:
return "caught"
return "not raised"

sys.monitoring.register_callback(TEST_TOOL, E.BRANCH_LEFT, callback)
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.BRANCH_LEFT)
try:
self.assertEqual(foo(True), "caught")
finally:
sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0)
sys.monitoring.register_callback(TEST_TOOL, E.BRANCH_LEFT, None)


class TestOptimizer(MonitoringTestBase, unittest.TestCase):

Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,7 @@ Ivan Levkivskyi
Ben Lewis
William Lewis
Akira Li
Han Li
Jiahao Li
Robert Li
Xuanji Li
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix bug where compiler did not set the right exception handler for some jump
instructions.
25 changes: 15 additions & 10 deletions Python/flowgraph.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ cfg_builder_new_block(cfg_builder *g)
}

static int
basicblock_addop(basicblock *b, int opcode, int oparg, location loc)
basicblock_addop(basicblock *b, int opcode, int oparg, location loc,
basicblock *except)
{
assert(IS_WITHIN_OPCODE_RANGE(opcode));
assert(!IS_ASSEMBLER_OPCODE(opcode));
Expand All @@ -203,23 +204,24 @@ basicblock_addop(basicblock *b, int opcode, int oparg, location loc)
i->i_opcode = opcode;
i->i_oparg = oparg;
i->i_loc = loc;
i->i_except = except;
// memory is already zero initialized
assert(i->i_target == NULL);
assert(i->i_except == NULL);

return SUCCESS;
}

static int
basicblock_add_jump(basicblock *b, int opcode, basicblock *target, location loc)
basicblock_add_jump(basicblock *b, int opcode, basicblock *target, location loc,
basicblock *except)
{
cfg_instr *last = basicblock_last_instr(b);
if (last && is_jump(last)) {
return ERROR;
}

RETURN_IF_ERROR(
basicblock_addop(b, opcode, target->b_label.id, loc));
basicblock_addop(b, opcode, target->b_label.id, loc, except));
last = basicblock_last_instr(b);
assert(last && last->i_opcode == opcode);
last->i_target = target;
Expand Down Expand Up @@ -493,7 +495,7 @@ int
_PyCfgBuilder_Addop(cfg_builder *g, int opcode, int oparg, location loc)
{
RETURN_IF_ERROR(cfg_builder_maybe_start_new_block(g));
return basicblock_addop(g->g_curblock, opcode, oparg, loc);
return basicblock_addop(g->g_curblock, opcode, oparg, loc, NULL);
}


Expand Down Expand Up @@ -554,7 +556,7 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) {
bool is_forward = last->i_target->b_visited == 0;
if (is_forward) {
RETURN_IF_ERROR(
basicblock_addop(b, NOT_TAKEN, 0, last->i_loc));
basicblock_addop(b, NOT_TAKEN, 0, last->i_loc, last->i_except));
return SUCCESS;
}

Expand Down Expand Up @@ -583,9 +585,11 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) {
return ERROR;
}
RETURN_IF_ERROR(
basicblock_addop(backwards_jump, NOT_TAKEN, 0, last->i_loc));
basicblock_addop(backwards_jump, NOT_TAKEN, 0, last->i_loc,
last->i_except));
RETURN_IF_ERROR(
basicblock_add_jump(backwards_jump, JUMP, target, last->i_loc));
basicblock_add_jump(backwards_jump, JUMP, target, last->i_loc,
last->i_except));
backwards_jump->b_startdepth = target->b_startdepth;
last->i_opcode = reversed_opcode;
last->i_target = b->b_next;
Expand Down Expand Up @@ -1293,7 +1297,8 @@ jump_thread(basicblock *bb, cfg_instr *inst, cfg_instr *target, int opcode)

RETURN_IF_ERROR(
basicblock_add_jump(
bb, opcode, target->i_target, target->i_loc));
bb, opcode, target->i_target, target->i_loc,
target->i_except));

return true;
}
Expand Down Expand Up @@ -3547,7 +3552,7 @@ push_cold_blocks_to_end(cfg_builder *g) {
b->b_next->b_label.id = next_lbl++;
}
basicblock_addop(explicit_jump, JUMP_NO_INTERRUPT, b->b_next->b_label.id,
NO_LOCATION);
NO_LOCATION, NULL);
explicit_jump->b_cold = 1;
explicit_jump->b_next = b->b_next;
explicit_jump->b_predecessors = 1;
Expand Down
Loading