diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index a94cd6432e1232..656d801107f52b 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -389,6 +389,7 @@ def bug42562(): POP_EXCEPT RERAISE 1 ExceptionTable: +4 rows """ % (TRACEBACK_CODE.co_firstlineno, TRACEBACK_CODE.co_firstlineno + 1, TRACEBACK_CODE.co_firstlineno + 2, @@ -421,6 +422,133 @@ def _fstring(a, b, c, d): RETURN_VALUE """ % (_fstring.__code__.co_firstlineno, _fstring.__code__.co_firstlineno + 1) +def _with(c): + with c: + x = 1 + y = 2 + +dis_with = """\ +%3d RESUME 0 + +%3d LOAD_FAST 0 (c) + BEFORE_WITH + POP_TOP + +%3d LOAD_CONST 1 (1) + STORE_FAST 1 (x) + +%3d LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + CALL 2 + POP_TOP + +%3d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + LOAD_CONST 0 (None) + RETURN_VALUE + +%3d >> PUSH_EXC_INFO + WITH_EXCEPT_START + POP_JUMP_FORWARD_IF_TRUE 1 (to 46) + RERAISE 2 + >> POP_TOP + POP_EXCEPT + POP_TOP + POP_TOP + +%3d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + LOAD_CONST 0 (None) + RETURN_VALUE + >> COPY 3 + POP_EXCEPT + RERAISE 1 +ExceptionTable: +2 rows +""" % (_with.__code__.co_firstlineno, + _with.__code__.co_firstlineno + 1, + _with.__code__.co_firstlineno + 2, + _with.__code__.co_firstlineno + 1, + _with.__code__.co_firstlineno + 3, + _with.__code__.co_firstlineno + 1, + _with.__code__.co_firstlineno + 3, + ) + +async def _asyncwith(c): + async with c: + x = 1 + y = 2 + +dis_asyncwith = """\ +%3d RETURN_GENERATOR + POP_TOP + RESUME 0 + +%3d LOAD_FAST 0 (c) + BEFORE_ASYNC_WITH + GET_AWAITABLE 1 + LOAD_CONST 0 (None) + >> SEND 3 (to 22) + YIELD_VALUE 3 + RESUME 3 + JUMP_BACKWARD_NO_INTERRUPT 4 (to 14) + >> POP_TOP + +%3d LOAD_CONST 1 (1) + STORE_FAST 1 (x) + +%3d LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + CALL 2 + GET_AWAITABLE 2 + LOAD_CONST 0 (None) + >> SEND 3 (to 56) + YIELD_VALUE 2 + RESUME 3 + JUMP_BACKWARD_NO_INTERRUPT 4 (to 48) + >> POP_TOP + +%3d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + LOAD_CONST 0 (None) + RETURN_VALUE + +%3d >> PUSH_EXC_INFO + WITH_EXCEPT_START + GET_AWAITABLE 2 + LOAD_CONST 0 (None) + >> SEND 3 (to 82) + YIELD_VALUE 6 + RESUME 3 + JUMP_BACKWARD_NO_INTERRUPT 4 (to 74) + >> POP_JUMP_FORWARD_IF_TRUE 1 (to 86) + RERAISE 2 + >> POP_TOP + POP_EXCEPT + POP_TOP + POP_TOP + +%3d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + LOAD_CONST 0 (None) + RETURN_VALUE + >> COPY 3 + POP_EXCEPT + RERAISE 1 +ExceptionTable: +2 rows +""" % (_asyncwith.__code__.co_firstlineno, + _asyncwith.__code__.co_firstlineno + 1, + _asyncwith.__code__.co_firstlineno + 2, + _asyncwith.__code__.co_firstlineno + 1, + _asyncwith.__code__.co_firstlineno + 3, + _asyncwith.__code__.co_firstlineno + 1, + _asyncwith.__code__.co_firstlineno + 3, + ) + + def _tryfinally(a, b): try: return a @@ -455,6 +583,7 @@ def _tryfinallyconst(b): POP_EXCEPT RERAISE 1 ExceptionTable: +2 rows """ % (_tryfinally.__code__.co_firstlineno, _tryfinally.__code__.co_firstlineno + 1, _tryfinally.__code__.co_firstlineno + 2, @@ -484,6 +613,7 @@ def _tryfinallyconst(b): POP_EXCEPT RERAISE 1 ExceptionTable: +1 row """ % (_tryfinallyconst.__code__.co_firstlineno, _tryfinallyconst.__code__.co_firstlineno + 1, _tryfinallyconst.__code__.co_firstlineno + 2, @@ -678,6 +808,18 @@ def assert_offsets_increasing(self, text, delta): self.assertGreaterEqual(offset, expected_offset, line) expected_offset = offset + delta + def assert_exception_table_increasing(self, lines): + prev_start, prev_end = -1, -1 + count = 0 + for line in lines: + m = re.match(r' (\d+) to (\d+) -> \d+ \[\d+\]', line) + start, end = [int(g) for g in m.groups()] + self.assertGreaterEqual(end, start) + self.assertGreater(start, prev_end) + prev_start, prev_end = start, end + count += 1 + return count + def strip_offsets(self, text): lines = text.splitlines(True) start, end = self.find_offset_column(lines) @@ -691,6 +833,9 @@ def strip_offsets(self, text): res.append(line) else: res.append(line[:start] + line[end:]) + num_rows = self.assert_exception_table_increasing(lines) + if num_rows: + res.append(f"{num_rows} row{'s' if num_rows > 1 else ''}\n") return "".join(res) def do_disassembly_compare(self, got, expected, with_offsets=False): @@ -883,6 +1028,12 @@ def test_disassemble_coroutine(self): def test_disassemble_fstring(self): self.do_disassembly_test(_fstring, dis_fstring) + def test_disassemble_with(self): + self.do_disassembly_test(_with, dis_with) + + def test_disassemble_asyncwith(self): + self.do_disassembly_test(_asyncwith, dis_asyncwith) + def test_disassemble_try_finally(self): self.do_disassembly_test(_tryfinally, dis_tryfinally) self.do_disassembly_test(_tryfinallyconst, dis_tryfinallyconst) @@ -1471,16 +1622,16 @@ def _prepare_test_cases(): Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=302, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=304, starts_line=25, is_jump_target=False, positions=None), Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=306, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_FORWARD_IF_TRUE', opcode=115, arg=4, argval=318, argrepr='to 318', offset=308, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_FORWARD_IF_TRUE', opcode=115, arg=1, argval=312, argrepr='to 312', offset=308, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='RERAISE', opcode=119, arg=2, argval=2, argrepr='', offset=310, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=312, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=312, starts_line=None, is_jump_target=True, positions=None), Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=314, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=316, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=318, starts_line=None, is_jump_target=True, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=320, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=322, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=324, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=140, arg=27, argval=274, argrepr='to 274', offset=326, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=316, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=318, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=140, arg=24, argval=274, argrepr='to 274', offset=320, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=322, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=324, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=326, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=328, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=330, starts_line=22, is_jump_target=False, positions=None), Instruction(opname='CHECK_EXC_MATCH', opcode=36, arg=None, argval=None, argrepr='', offset=342, starts_line=None, is_jump_target=False, positions=None), diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-09-09-08-29.gh-issue-93621.-_Pn1d.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-09-09-08-29.gh-issue-93621.-_Pn1d.rst new file mode 100644 index 00000000000000..388540982fd1ff --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-06-09-09-08-29.gh-issue-93621.-_Pn1d.rst @@ -0,0 +1 @@ +Change order of bytecode instructions emitted for :keyword:`with` and :keyword:`async with` to reduce the number of entries in the exception table. diff --git a/Python/compile.c b/Python/compile.c index bbd71936cf3468..b922e0145451e5 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -5551,20 +5551,26 @@ compiler_visit_keyword(struct compiler *c, keyword_ty k) static int compiler_with_except_finish(struct compiler *c, basicblock * cleanup) { UNSET_LOC(c); - basicblock *exit; - exit = compiler_new_block(c); - if (exit == NULL) + basicblock *suppress = compiler_new_block(c); + if (suppress == NULL) { return 0; - ADDOP_JUMP(c, POP_JUMP_IF_TRUE, exit); + } + ADDOP_JUMP(c, POP_JUMP_IF_TRUE, suppress); ADDOP_I(c, RERAISE, 2); - compiler_use_next_block(c, cleanup); - POP_EXCEPT_AND_RERAISE(c); - compiler_use_next_block(c, exit); + compiler_use_next_block(c, suppress); ADDOP(c, POP_TOP); /* exc_value */ ADDOP(c, POP_BLOCK); ADDOP(c, POP_EXCEPT); ADDOP(c, POP_TOP); ADDOP(c, POP_TOP); + basicblock *exit = compiler_new_block(c); + if (exit == NULL) { + return 0; + } + ADDOP_JUMP(c, JUMP, exit); + compiler_use_next_block(c, cleanup); + POP_EXCEPT_AND_RERAISE(c); + compiler_use_next_block(c, exit); return 1; }