https://github.com/python/cpython/commit/89df62c12093bfa079860a93032468ebece3774d
commit: 89df62c12093bfa079860a93032468ebece3774d
branch: main
author: Mark Shannon <[email protected]>
committer: markshannon <[email protected]>
date: 2025-03-07T14:30:31Z
summary:
GH-128534: Fix behavior of branch monitoring for `async for` (GH-130847)
* Both branches in a pair now have a common source and are included in
co_branches
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2025-03-04-15-12-32.gh-issue-128534.3A0K3D.rst
M Include/internal/pycore_magic_number.h
M Include/internal/pycore_opcode_metadata.h
M Include/opcode_ids.h
M Lib/_opcode_metadata.py
M Lib/dis.py
M Lib/test/test_code.py
M Lib/test/test_dis.py
M Lib/test/test_monitoring.py
M Programs/test_frozenmain.h
M Python/assemble.c
M Python/bytecodes.c
M Python/codegen.c
M Python/flowgraph.c
M Python/generated_cases.c.h
M Python/instrumentation.c
M Python/opcode_targets.h
diff --git a/Include/internal/pycore_magic_number.h
b/Include/internal/pycore_magic_number.h
index 642f3bcec407ea..839a86e5830e78 100644
--- a/Include/internal/pycore_magic_number.h
+++ b/Include/internal/pycore_magic_number.h
@@ -270,7 +270,8 @@ Known values:
Python 3.14a5 3615 (CALL_FUNCTION_EX always take a kwargs argument)
Python 3.14a5 3616 (Remove BINARY_SUBSCR and family. Make them BINARY_OPs)
Python 3.14a6 3617 (Branch monitoring for async for loops)
- Python 3.14a6 3618 (Renumber RESUME opcode from 149 to 128)
+ Python 3.14a6 3618 (Add oparg to END_ASYNC_FOR)
+ Python 3.14a6 3619 (Renumber RESUME opcode from 149 to 128)
Python 3.15 will start with 3650
@@ -283,7 +284,7 @@ PC/launcher.c must also be updated.
*/
-#define PYC_MAGIC_NUMBER 3618
+#define PYC_MAGIC_NUMBER 3619
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
(little-endian) and then appending b'\r\n'. */
#define PYC_MAGIC_NUMBER_TOKEN \
diff --git a/Include/internal/pycore_opcode_metadata.h
b/Include/internal/pycore_opcode_metadata.h
index ae5f6fddafd2c5..d236206e295e1e 100644
--- a/Include/internal/pycore_opcode_metadata.h
+++ b/Include/internal/pycore_opcode_metadata.h
@@ -2084,7 +2084,7 @@ const struct opcode_metadata
_PyOpcode_opcode_metadata[266] = {
[DELETE_SUBSCR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG
},
[DICT_MERGE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG |
HAS_ESCAPES_FLAG },
[DICT_UPDATE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG |
HAS_ESCAPES_FLAG },
- [END_ASYNC_FOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG |
HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
+ [END_ASYNC_FOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG |
HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
[END_FOR] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | HAS_NO_SAVE_IP_FLAG },
[END_SEND] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | HAS_PURE_FLAG },
[ENTER_EXECUTOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG },
@@ -2108,7 +2108,7 @@ const struct opcode_metadata
_PyOpcode_opcode_metadata[266] = {
[INSTRUMENTED_CALL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG |
HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG
},
[INSTRUMENTED_CALL_FUNCTION_EX] = { true, INSTR_FMT_IX,
HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG
},
[INSTRUMENTED_CALL_KW] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG |
HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
- [INSTRUMENTED_END_ASYNC_FOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG |
HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
+ [INSTRUMENTED_END_ASYNC_FOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG |
HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
[INSTRUMENTED_END_FOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG |
HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG | HAS_NO_SAVE_IP_FLAG },
[INSTRUMENTED_END_SEND] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG |
HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
[INSTRUMENTED_FOR_ITER] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG |
HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
diff --git a/Include/opcode_ids.h b/Include/opcode_ids.h
index 0a669f2daf49d8..e4e6a88276655e 100644
--- a/Include/opcode_ids.h
+++ b/Include/opcode_ids.h
@@ -18,65 +18,65 @@ extern "C" {
#define CHECK_EXC_MATCH 5
#define CLEANUP_THROW 6
#define DELETE_SUBSCR 7
-#define END_ASYNC_FOR 8
-#define END_FOR 9
-#define END_SEND 10
-#define EXIT_INIT_CHECK 11
-#define FORMAT_SIMPLE 12
-#define FORMAT_WITH_SPEC 13
-#define GET_AITER 14
-#define GET_ANEXT 15
-#define GET_ITER 16
+#define END_FOR 8
+#define END_SEND 9
+#define EXIT_INIT_CHECK 10
+#define FORMAT_SIMPLE 11
+#define FORMAT_WITH_SPEC 12
+#define GET_AITER 13
+#define GET_ANEXT 14
+#define GET_ITER 15
+#define GET_LEN 16
#define RESERVED 17
-#define GET_LEN 18
-#define GET_YIELD_FROM_ITER 19
-#define INTERPRETER_EXIT 20
-#define LOAD_BUILD_CLASS 21
-#define LOAD_LOCALS 22
-#define MAKE_FUNCTION 23
-#define MATCH_KEYS 24
-#define MATCH_MAPPING 25
-#define MATCH_SEQUENCE 26
-#define NOP 27
-#define NOT_TAKEN 28
-#define POP_EXCEPT 29
-#define POP_ITER 30
-#define POP_TOP 31
-#define PUSH_EXC_INFO 32
-#define PUSH_NULL 33
-#define RETURN_GENERATOR 34
-#define RETURN_VALUE 35
-#define SETUP_ANNOTATIONS 36
-#define STORE_SLICE 37
-#define STORE_SUBSCR 38
-#define TO_BOOL 39
-#define UNARY_INVERT 40
-#define UNARY_NEGATIVE 41
-#define UNARY_NOT 42
-#define WITH_EXCEPT_START 43
-#define BINARY_OP 44
-#define BUILD_LIST 45
-#define BUILD_MAP 46
-#define BUILD_SET 47
-#define BUILD_SLICE 48
-#define BUILD_STRING 49
-#define BUILD_TUPLE 50
-#define CALL 51
-#define CALL_INTRINSIC_1 52
-#define CALL_INTRINSIC_2 53
-#define CALL_KW 54
-#define COMPARE_OP 55
-#define CONTAINS_OP 56
-#define CONVERT_VALUE 57
-#define COPY 58
-#define COPY_FREE_VARS 59
-#define DELETE_ATTR 60
-#define DELETE_DEREF 61
-#define DELETE_FAST 62
-#define DELETE_GLOBAL 63
-#define DELETE_NAME 64
-#define DICT_MERGE 65
-#define DICT_UPDATE 66
+#define GET_YIELD_FROM_ITER 18
+#define INTERPRETER_EXIT 19
+#define LOAD_BUILD_CLASS 20
+#define LOAD_LOCALS 21
+#define MAKE_FUNCTION 22
+#define MATCH_KEYS 23
+#define MATCH_MAPPING 24
+#define MATCH_SEQUENCE 25
+#define NOP 26
+#define NOT_TAKEN 27
+#define POP_EXCEPT 28
+#define POP_ITER 29
+#define POP_TOP 30
+#define PUSH_EXC_INFO 31
+#define PUSH_NULL 32
+#define RETURN_GENERATOR 33
+#define RETURN_VALUE 34
+#define SETUP_ANNOTATIONS 35
+#define STORE_SLICE 36
+#define STORE_SUBSCR 37
+#define TO_BOOL 38
+#define UNARY_INVERT 39
+#define UNARY_NEGATIVE 40
+#define UNARY_NOT 41
+#define WITH_EXCEPT_START 42
+#define BINARY_OP 43
+#define BUILD_LIST 44
+#define BUILD_MAP 45
+#define BUILD_SET 46
+#define BUILD_SLICE 47
+#define BUILD_STRING 48
+#define BUILD_TUPLE 49
+#define CALL 50
+#define CALL_INTRINSIC_1 51
+#define CALL_INTRINSIC_2 52
+#define CALL_KW 53
+#define COMPARE_OP 54
+#define CONTAINS_OP 55
+#define CONVERT_VALUE 56
+#define COPY 57
+#define COPY_FREE_VARS 58
+#define DELETE_ATTR 59
+#define DELETE_DEREF 60
+#define DELETE_FAST 61
+#define DELETE_GLOBAL 62
+#define DELETE_NAME 63
+#define DICT_MERGE 64
+#define DICT_UPDATE 65
+#define END_ASYNC_FOR 66
#define EXTENDED_ARG 67
#define FOR_ITER 68
#define GET_AWAITABLE 69
@@ -243,7 +243,7 @@ extern "C" {
#define SETUP_WITH 264
#define STORE_FAST_MAYBE_NULL 265
-#define HAVE_ARGUMENT 43
+#define HAVE_ARGUMENT 42
#define MIN_SPECIALIZED_OPCODE 129
#define MIN_INSTRUMENTED_OPCODE 234
diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py
index 9e381f936e8820..2c399d2f7f4631 100644
--- a/Lib/_opcode_metadata.py
+++ b/Lib/_opcode_metadata.py
@@ -220,64 +220,64 @@
'CHECK_EXC_MATCH': 5,
'CLEANUP_THROW': 6,
'DELETE_SUBSCR': 7,
- 'END_ASYNC_FOR': 8,
- 'END_FOR': 9,
- 'END_SEND': 10,
- 'EXIT_INIT_CHECK': 11,
- 'FORMAT_SIMPLE': 12,
- 'FORMAT_WITH_SPEC': 13,
- 'GET_AITER': 14,
- 'GET_ANEXT': 15,
- 'GET_ITER': 16,
- 'GET_LEN': 18,
- 'GET_YIELD_FROM_ITER': 19,
- 'INTERPRETER_EXIT': 20,
- 'LOAD_BUILD_CLASS': 21,
- 'LOAD_LOCALS': 22,
- 'MAKE_FUNCTION': 23,
- 'MATCH_KEYS': 24,
- 'MATCH_MAPPING': 25,
- 'MATCH_SEQUENCE': 26,
- 'NOP': 27,
- 'NOT_TAKEN': 28,
- 'POP_EXCEPT': 29,
- 'POP_ITER': 30,
- 'POP_TOP': 31,
- 'PUSH_EXC_INFO': 32,
- 'PUSH_NULL': 33,
- 'RETURN_GENERATOR': 34,
- 'RETURN_VALUE': 35,
- 'SETUP_ANNOTATIONS': 36,
- 'STORE_SLICE': 37,
- 'STORE_SUBSCR': 38,
- 'TO_BOOL': 39,
- 'UNARY_INVERT': 40,
- 'UNARY_NEGATIVE': 41,
- 'UNARY_NOT': 42,
- 'WITH_EXCEPT_START': 43,
- 'BINARY_OP': 44,
- 'BUILD_LIST': 45,
- 'BUILD_MAP': 46,
- 'BUILD_SET': 47,
- 'BUILD_SLICE': 48,
- 'BUILD_STRING': 49,
- 'BUILD_TUPLE': 50,
- 'CALL': 51,
- 'CALL_INTRINSIC_1': 52,
- 'CALL_INTRINSIC_2': 53,
- 'CALL_KW': 54,
- 'COMPARE_OP': 55,
- 'CONTAINS_OP': 56,
- 'CONVERT_VALUE': 57,
- 'COPY': 58,
- 'COPY_FREE_VARS': 59,
- 'DELETE_ATTR': 60,
- 'DELETE_DEREF': 61,
- 'DELETE_FAST': 62,
- 'DELETE_GLOBAL': 63,
- 'DELETE_NAME': 64,
- 'DICT_MERGE': 65,
- 'DICT_UPDATE': 66,
+ 'END_FOR': 8,
+ 'END_SEND': 9,
+ 'EXIT_INIT_CHECK': 10,
+ 'FORMAT_SIMPLE': 11,
+ 'FORMAT_WITH_SPEC': 12,
+ 'GET_AITER': 13,
+ 'GET_ANEXT': 14,
+ 'GET_ITER': 15,
+ 'GET_LEN': 16,
+ 'GET_YIELD_FROM_ITER': 18,
+ 'INTERPRETER_EXIT': 19,
+ 'LOAD_BUILD_CLASS': 20,
+ 'LOAD_LOCALS': 21,
+ 'MAKE_FUNCTION': 22,
+ 'MATCH_KEYS': 23,
+ 'MATCH_MAPPING': 24,
+ 'MATCH_SEQUENCE': 25,
+ 'NOP': 26,
+ 'NOT_TAKEN': 27,
+ 'POP_EXCEPT': 28,
+ 'POP_ITER': 29,
+ 'POP_TOP': 30,
+ 'PUSH_EXC_INFO': 31,
+ 'PUSH_NULL': 32,
+ 'RETURN_GENERATOR': 33,
+ 'RETURN_VALUE': 34,
+ 'SETUP_ANNOTATIONS': 35,
+ 'STORE_SLICE': 36,
+ 'STORE_SUBSCR': 37,
+ 'TO_BOOL': 38,
+ 'UNARY_INVERT': 39,
+ 'UNARY_NEGATIVE': 40,
+ 'UNARY_NOT': 41,
+ 'WITH_EXCEPT_START': 42,
+ 'BINARY_OP': 43,
+ 'BUILD_LIST': 44,
+ 'BUILD_MAP': 45,
+ 'BUILD_SET': 46,
+ 'BUILD_SLICE': 47,
+ 'BUILD_STRING': 48,
+ 'BUILD_TUPLE': 49,
+ 'CALL': 50,
+ 'CALL_INTRINSIC_1': 51,
+ 'CALL_INTRINSIC_2': 52,
+ 'CALL_KW': 53,
+ 'COMPARE_OP': 54,
+ 'CONTAINS_OP': 55,
+ 'CONVERT_VALUE': 56,
+ 'COPY': 57,
+ 'COPY_FREE_VARS': 58,
+ 'DELETE_ATTR': 59,
+ 'DELETE_DEREF': 60,
+ 'DELETE_FAST': 61,
+ 'DELETE_GLOBAL': 62,
+ 'DELETE_NAME': 63,
+ 'DICT_MERGE': 64,
+ 'DICT_UPDATE': 65,
+ 'END_ASYNC_FOR': 66,
'EXTENDED_ARG': 67,
'FOR_ITER': 68,
'GET_AWAITABLE': 69,
@@ -360,5 +360,5 @@
'STORE_FAST_MAYBE_NULL': 265,
}
-HAVE_ARGUMENT = 43
+HAVE_ARGUMENT = 42
MIN_INSTRUMENTED_OPCODE = 234
diff --git a/Lib/dis.py b/Lib/dis.py
index 72cc2d19456467..c0a25dea2a9a95 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -52,6 +52,7 @@
STORE_FAST_STORE_FAST = opmap['STORE_FAST_STORE_FAST']
IS_OP = opmap['IS_OP']
CONTAINS_OP = opmap['CONTAINS_OP']
+END_ASYNC_FOR = opmap['END_ASYNC_FOR']
CACHE = opmap["CACHE"]
@@ -605,7 +606,8 @@ def get_argval_argrepr(self, op, arg, offset):
argval = self.offset_from_jump_arg(op, arg, offset)
lbl = self.get_label_for_offset(argval)
assert lbl is not None
- argrepr = f"to L{lbl}"
+ preposition = "from" if deop == END_ASYNC_FOR else "to"
+ argrepr = f"{preposition} L{lbl}"
elif deop in (LOAD_FAST_LOAD_FAST, STORE_FAST_LOAD_FAST,
STORE_FAST_STORE_FAST):
arg1 = arg >> 4
arg2 = arg & 15
@@ -745,7 +747,8 @@ def _parse_exception_table(code):
def _is_backward_jump(op):
return opname[op] in ('JUMP_BACKWARD',
- 'JUMP_BACKWARD_NO_INTERRUPT')
+ 'JUMP_BACKWARD_NO_INTERRUPT',
+ 'END_ASYNC_FOR') # Not really a jump, but it has a
"target"
def _get_instructions_bytes(code, linestarts=None, line_offset=0,
co_positions=None,
original_code=None, arg_resolver=None):
diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py
index 0f6220efa0482b..2f459a46b5ad70 100644
--- a/Lib/test/test_code.py
+++ b/Lib/test/test_code.py
@@ -953,6 +953,15 @@ def with_extended_args(x):
get_line_branches(with_extended_args),
[(1,2,8)])
+ async def afunc():
+ async for letter in async_iter1:
+ 2
+ 3
+
+ self.assertEqual(
+ get_line_branches(afunc),
+ [(1,1,3)])
+
if check_impl_detail(cpython=True) and ctypes is not None:
py = ctypes.pythonapi
freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp)
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index 25e0b003172dbd..6e1d94bd535663 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -1256,6 +1256,23 @@ def test__try_compile_no_context_exc_on_error(self):
except Exception as e:
self.assertIsNone(e.__context__)
+ def test_async_for_presentation(self):
+
+ async def afunc():
+ async for letter in async_iter1:
+ l2
+ l3
+
+ disassembly = self.get_disassembly(afunc)
+ for line in disassembly.split("\n"):
+ if "END_ASYNC_FOR" in line:
+ break
+ else:
+ self.fail("No END_ASYNC_FOR in disassembly of async for")
+ self.assertNotIn("to", line)
+ self.assertIn("from", line)
+
+
@staticmethod
def code_quicken(f):
_testinternalcapi = import_helper.import_module("_testinternalcapi")
diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py
index 305ba6fdeed96b..fc98b9eee383e4 100644
--- a/Lib/test/test_monitoring.py
+++ b/Lib/test/test_monitoring.py
@@ -1683,7 +1683,9 @@ async def foo():
class TestBranchConsistency(MonitoringTestBase, unittest.TestCase):
- def check_branches(self, func, tool=TEST_TOOL,
recorders=BRANCH_OFFSET_RECORDERS):
+ def check_branches(self, run_func, test_func=None, tool=TEST_TOOL,
recorders=BRANCH_OFFSET_RECORDERS):
+ if test_func is None:
+ test_func = run_func
try:
self.assertEqual(sys.monitoring._all_events(), {})
event_list = []
@@ -1692,16 +1694,17 @@ def check_branches(self, func, tool=TEST_TOOL,
recorders=BRANCH_OFFSET_RECORDERS
ev = recorder.event_type
sys.monitoring.register_callback(tool, ev,
recorder(event_list))
all_events |= ev
- sys.monitoring.set_local_events(tool, func.__code__, all_events)
- func()
- sys.monitoring.set_local_events(tool, func.__code__, 0)
+ sys.monitoring.set_local_events(tool, test_func.__code__,
all_events)
+ run_func()
+ sys.monitoring.set_local_events(tool, test_func.__code__, 0)
for recorder in recorders:
sys.monitoring.register_callback(tool, recorder.event_type,
None)
lefts = set()
rights = set()
- for (src, left, right) in func.__code__.co_branches():
+ for (src, left, right) in test_func.__code__.co_branches():
lefts.add((src, left))
rights.add((src, right))
+ print(event_list)
for event in event_list:
way, _, src, dest = event
if "left" in way:
@@ -1710,7 +1713,7 @@ def check_branches(self, func, tool=TEST_TOOL,
recorders=BRANCH_OFFSET_RECORDERS
self.assertIn("right", way)
self.assertIn((src, dest), rights)
finally:
- sys.monitoring.set_local_events(tool, func.__code__, 0)
+ sys.monitoring.set_local_events(tool, test_func.__code__, 0)
for recorder in recorders:
sys.monitoring.register_callback(tool, recorder.event_type,
None)
@@ -1762,6 +1765,25 @@ def foo(n=0):
self.check_branches(foo)
+ def test_async_for(self):
+
+ async def gen():
+ yield 2
+ yield 3
+
+ async def foo():
+ async for y in gen():
+ 2
+ pass # line 3
+
+ def func():
+ try:
+ foo().send(None)
+ except StopIteration:
+ pass
+
+ self.check_branches(func, foo)
+
class TestLoadSuperAttr(CheckEvents):
RECORDERS = CallRecorder, LineRecorder, CRaiseRecorder, CReturnRecorder
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2025-03-04-15-12-32.gh-issue-128534.3A0K3D.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2025-03-04-15-12-32.gh-issue-128534.3A0K3D.rst
new file mode 100644
index 00000000000000..025847fdd6620b
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2025-03-04-15-12-32.gh-issue-128534.3A0K3D.rst
@@ -0,0 +1,2 @@
+Ensure that both left and right branches have the same source for ``async
for`` loops.
+Add these branches to the ``co_branches()`` iterator.
diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h
index 9bcb4a4634782d..55bf4fe26e967a 100644
--- a/Programs/test_frozenmain.h
+++ b/Programs/test_frozenmain.h
@@ -2,18 +2,18 @@
unsigned char M_test_frozenmain[] = {
227,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,
0,0,0,0,0,243,184,0,0,0,128,0,90,0,80,0,
- 71,0,112,0,90,0,80,0,71,1,112,1,89,2,33,0,
- 80,1,51,1,0,0,0,0,0,0,31,0,89,2,33,0,
+ 71,0,112,0,90,0,80,0,71,1,112,1,89,2,32,0,
+ 80,1,50,1,0,0,0,0,0,0,30,0,89,2,32,0,
80,2,89,0,78,6,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,51,2,0,0,0,0,0,0,
- 31,0,89,1,78,8,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,33,0,51,0,0,0,0,0,
- 0,0,80,3,44,26,0,0,0,0,0,0,0,0,0,0,
- 112,5,80,4,16,0,68,24,0,0,112,6,89,2,33,0,
- 80,5,89,6,12,0,80,6,89,5,89,6,44,26,0,0,
- 0,0,0,0,0,0,0,0,12,0,49,4,51,1,0,0,
- 0,0,0,0,31,0,73,26,0,0,9,0,30,0,80,0,
- 35,0,41,7,78,122,18,70,114,111,122,101,110,32,72,101,
+ 0,0,0,0,0,0,0,0,50,2,0,0,0,0,0,0,
+ 30,0,89,1,78,8,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,32,0,50,0,0,0,0,0,
+ 0,0,80,3,43,26,0,0,0,0,0,0,0,0,0,0,
+ 112,5,80,4,15,0,68,24,0,0,112,6,89,2,32,0,
+ 80,5,89,6,11,0,80,6,89,5,89,6,43,26,0,0,
+ 0,0,0,0,0,0,0,0,11,0,48,4,50,1,0,0,
+ 0,0,0,0,30,0,73,26,0,0,8,0,29,0,80,0,
+ 34,0,41,7,78,122,18,70,114,111,122,101,110,32,72,101,
108,108,111,32,87,111,114,108,100,122,8,115,121,115,46,97,
114,103,118,218,6,99,111,110,102,105,103,41,5,218,12,112,
114,111,103,114,97,109,95,110,97,109,101,218,10,101,120,101,
diff --git a/Python/assemble.c b/Python/assemble.c
index 070be1ca54e3ea..e33918edf8e4b9 100644
--- a/Python/assemble.c
+++ b/Python/assemble.c
@@ -632,6 +632,10 @@ makecode(_PyCompile_CodeUnitMetadata *umd, struct
assembler *a, PyObject *const_
return co;
}
+
+// The offset (in code units) of the END_SEND from the SEND in the `yield
from` sequence.
+#define END_SEND_OFFSET 5
+
static int
resolve_jump_offsets(instr_sequence *instrs)
{
@@ -670,7 +674,12 @@ resolve_jump_offsets(instr_sequence *instrs)
if (OPCODE_HAS_JUMP(instr->i_opcode)) {
instruction *target = &instrs->s_instrs[instr->i_target];
instr->i_oparg = target->i_offset;
- if (instr->i_oparg < offset) {
+ if (instr->i_opcode == END_ASYNC_FOR) {
+ // sys.monitoring needs to be able to find the matching
END_SEND
+ // but the target is the SEND, so we adjust it here.
+ instr->i_oparg = offset - instr->i_oparg - END_SEND_OFFSET;
+ }
+ else if (instr->i_oparg < offset) {
assert(IS_BACKWARDS_JUMP_OPCODE(instr->i_opcode));
instr->i_oparg = offset - instr->i_oparg;
}
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 718c8f5607c55b..5191b5785f7654 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -1341,6 +1341,8 @@ dummy_func(
}
tier1 op(_END_ASYNC_FOR, (awaitable_st, exc_st -- )) {
+ JUMPBY(0); // Pretend jump as we need source offset for monitoring
+ (void)oparg;
PyObject *exc = PyStackRef_AsPyObjectBorrow(exc_st);
assert(exc && PyExceptionInstance_Check(exc));
@@ -1356,12 +1358,13 @@ dummy_func(
}
}
- tier1 op(_MONITOR_BRANCH_RIGHT, ( -- )) {
- INSTRUMENTED_JUMP(prev_instr, this_instr+1,
PY_MONITORING_EVENT_BRANCH_RIGHT);
+ tier1 op(_MONITOR_END_ASYNC_FOR, ( -- )) {
+ assert((next_instr-oparg)->op.code == END_SEND ||
(next_instr-oparg)->op.code >= MIN_INSTRUMENTED_OPCODE);
+ INSTRUMENTED_JUMP(next_instr-oparg, this_instr+1,
PY_MONITORING_EVENT_BRANCH_RIGHT);
}
macro(INSTRUMENTED_END_ASYNC_FOR) =
- _MONITOR_BRANCH_RIGHT +
+ _MONITOR_END_ASYNC_FOR +
_END_ASYNC_FOR;
macro(END_ASYNC_FOR) = _END_ASYNC_FOR;
diff --git a/Python/codegen.c b/Python/codegen.c
index 8f1a2983007ce4..7a3f787aec0d2d 100644
--- a/Python/codegen.c
+++ b/Python/codegen.c
@@ -2019,13 +2019,13 @@ codegen_for(compiler *c, stmt_ty s)
return SUCCESS;
}
-
static int
codegen_async_for(compiler *c, stmt_ty s)
{
location loc = LOC(s);
NEW_JUMP_TARGET_LABEL(c, start);
+ NEW_JUMP_TARGET_LABEL(c, send);
NEW_JUMP_TARGET_LABEL(c, except);
NEW_JUMP_TARGET_LABEL(c, end);
@@ -2039,6 +2039,7 @@ codegen_async_for(compiler *c, stmt_ty s)
ADDOP_JUMP(c, loc, SETUP_FINALLY, except);
ADDOP(c, loc, GET_ANEXT);
ADDOP_LOAD_CONST(c, loc, Py_None);
+ USE_LABEL(c, send);
ADD_YIELD_FROM(c, loc, 1);
ADDOP(c, loc, POP_BLOCK); /* for SETUP_FINALLY */
ADDOP(c, loc, NOT_TAKEN);
@@ -2057,7 +2058,7 @@ codegen_async_for(compiler *c, stmt_ty s)
/* Use same line number as the iterator,
* as the END_ASYNC_FOR succeeds the `for`, not the body. */
loc = LOC(s->v.AsyncFor.iter);
- ADDOP(c, loc, END_ASYNC_FOR);
+ ADDOP_JUMP(c, loc, END_ASYNC_FOR, send);
/* `else` block */
VISIT_SEQ(c, stmt, s->v.AsyncFor.orelse);
@@ -4252,6 +4253,7 @@ codegen_async_comprehension_generator(compiler *c,
location loc,
int iter_on_stack)
{
NEW_JUMP_TARGET_LABEL(c, start);
+ NEW_JUMP_TARGET_LABEL(c, send);
NEW_JUMP_TARGET_LABEL(c, except);
NEW_JUMP_TARGET_LABEL(c, if_cleanup);
@@ -4279,6 +4281,7 @@ codegen_async_comprehension_generator(compiler *c,
location loc,
ADDOP_JUMP(c, loc, SETUP_FINALLY, except);
ADDOP(c, loc, GET_ANEXT);
ADDOP_LOAD_CONST(c, loc, Py_None);
+ USE_LABEL(c, send);
ADD_YIELD_FROM(c, loc, 1);
ADDOP(c, loc, POP_BLOCK);
VISIT(c, expr, gen->target);
@@ -4338,7 +4341,7 @@ codegen_async_comprehension_generator(compiler *c,
location loc,
USE_LABEL(c, except);
- ADDOP(c, loc, END_ASYNC_FOR);
+ ADDOP_JUMP(c, loc, END_ASYNC_FOR, send);
return SUCCESS;
}
diff --git a/Python/flowgraph.c b/Python/flowgraph.c
index fb3c73a059a589..6ba60d4312e56c 100644
--- a/Python/flowgraph.c
+++ b/Python/flowgraph.c
@@ -849,7 +849,7 @@ calculate_stackdepth(cfg_builder *g)
goto error;
}
maxdepth = Py_MAX(maxdepth, depth + effects.max);
- if (HAS_TARGET(instr->i_opcode)) {
+ if (HAS_TARGET(instr->i_opcode) && instr->i_opcode !=
END_ASYNC_FOR) {
if (get_stack_effects(instr->i_opcode, instr->i_oparg, 1,
&effects) < 0) {
PyErr_Format(PyExc_SystemError,
"Invalid stack effect for opcode=%d, arg=%i",
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 33d00afea18e1c..c9371f77e1d0bb 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -5183,6 +5183,8 @@
_PyStackRef exc_st;
exc_st = stack_pointer[-1];
awaitable_st = stack_pointer[-2];
+ JUMPBY(0); // Pretend jump as we need source offset for monitoring
+ (void)oparg;
PyObject *exc = PyStackRef_AsPyObjectBorrow(exc_st);
assert(exc && PyExceptionInstance_Check(exc));
_PyFrame_SetStackPointer(frame, stack_pointer);
@@ -6607,7 +6609,6 @@
int opcode = INSTRUMENTED_END_ASYNC_FOR;
(void)(opcode);
#endif
- _Py_CODEUNIT* const prev_instr = frame->instr_ptr;
_Py_CODEUNIT* const this_instr = next_instr;
(void)this_instr;
frame->instr_ptr = next_instr;
@@ -6615,14 +6616,17 @@
INSTRUCTION_STATS(INSTRUMENTED_END_ASYNC_FOR);
_PyStackRef awaitable_st;
_PyStackRef exc_st;
- // _MONITOR_BRANCH_RIGHT
+ // _MONITOR_END_ASYNC_FOR
{
- INSTRUMENTED_JUMP(prev_instr, this_instr+1,
PY_MONITORING_EVENT_BRANCH_RIGHT);
+ assert((next_instr-oparg)->op.code == END_SEND ||
(next_instr-oparg)->op.code >= MIN_INSTRUMENTED_OPCODE);
+ INSTRUMENTED_JUMP(next_instr-oparg, this_instr+1,
PY_MONITORING_EVENT_BRANCH_RIGHT);
}
// _END_ASYNC_FOR
{
exc_st = stack_pointer[-1];
awaitable_st = stack_pointer[-2];
+ JUMPBY(0); // Pretend jump as we need source offset for
monitoring
+ (void)oparg;
PyObject *exc = PyStackRef_AsPyObjectBorrow(exc_st);
assert(exc && PyExceptionInstance_Check(exc));
_PyFrame_SetStackPointer(frame, stack_pointer);
diff --git a/Python/instrumentation.c b/Python/instrumentation.c
index 4e7ca808b3c3e6..f871d1949db322 100644
--- a/Python/instrumentation.c
+++ b/Python/instrumentation.c
@@ -3109,6 +3109,14 @@ branchesiter_next(branchesiterator *bi)
int not_taken = next_offset + 1;
bi->bi_offset = not_taken;
return int_triple(offset*2, not_taken*2, (next_offset +
oparg)*2);
+ case END_ASYNC_FOR:
+ oparg = (oparg << 8) | inst.op.arg;
+ int src_offset = next_offset - oparg;
+ bi->bi_offset = next_offset;
+ assert(_Py_GetBaseCodeUnit(bi->bi_code, src_offset).op.code ==
END_SEND);
+ assert(_Py_GetBaseCodeUnit(bi->bi_code, src_offset+1).op.code
== NOT_TAKEN);
+ not_taken = src_offset + 2;
+ return int_triple(src_offset *2, not_taken*2, next_offset*2);
default:
oparg = 0;
}
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index 5208025cbf3edf..c0dac90aebd458 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -8,7 +8,6 @@ static void *opcode_targets[256] = {
&&TARGET_CHECK_EXC_MATCH,
&&TARGET_CLEANUP_THROW,
&&TARGET_DELETE_SUBSCR,
- &&TARGET_END_ASYNC_FOR,
&&TARGET_END_FOR,
&&TARGET_END_SEND,
&&TARGET_EXIT_INIT_CHECK,
@@ -17,8 +16,8 @@ static void *opcode_targets[256] = {
&&TARGET_GET_AITER,
&&TARGET_GET_ANEXT,
&&TARGET_GET_ITER,
- &&TARGET_RESERVED,
&&TARGET_GET_LEN,
+ &&TARGET_RESERVED,
&&TARGET_GET_YIELD_FROM_ITER,
&&TARGET_INTERPRETER_EXIT,
&&TARGET_LOAD_BUILD_CLASS,
@@ -67,6 +66,7 @@ static void *opcode_targets[256] = {
&&TARGET_DELETE_NAME,
&&TARGET_DICT_MERGE,
&&TARGET_DICT_UPDATE,
+ &&TARGET_END_ASYNC_FOR,
&&TARGET_EXTENDED_ARG,
&&TARGET_FOR_ITER,
&&TARGET_GET_AWAITABLE,
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]