Skip to content

Commit bbdacb4

Browse files
GH-94438: Handle extended arguments and conditional pops in mark_stacks (GH-95110)
(cherry picked from commit e4d3a96) Co-authored-by: Brandt Bucher <[email protected]>
1 parent 3a33e9b commit bbdacb4

File tree

3 files changed

+49
-4
lines changed

3 files changed

+49
-4
lines changed

Lib/test/test_sys_settrace.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2674,6 +2674,42 @@ def test_jump_with_null_on_stack_load_attr(output):
26742674
)
26752675
output.append(15)
26762676

2677+
@jump_test(2, 3, [1, 3])
2678+
def test_jump_extended_args_unpack_ex_simple(output):
2679+
output.append(1)
2680+
_, *_, _ = output.append(2) or "Spam"
2681+
output.append(3)
2682+
2683+
@jump_test(3, 4, [1, 4, 4, 5])
2684+
def test_jump_extended_args_unpack_ex_tricky(output):
2685+
output.append(1)
2686+
(
2687+
_, *_, _
2688+
) = output.append(4) or "Spam"
2689+
output.append(5)
2690+
2691+
def test_jump_extended_args_for_iter(self):
2692+
# In addition to failing when extended arg handling is broken, this can
2693+
# also hang for a *very* long time:
2694+
source = [
2695+
"def f(output):",
2696+
" output.append(1)",
2697+
" for _ in spam:",
2698+
*(f" output.append({i})" for i in range(3, 100_000)),
2699+
f" output.append(100_000)",
2700+
]
2701+
namespace = {}
2702+
exec("\n".join(source), namespace)
2703+
f = namespace["f"]
2704+
self.run_test(f, 2, 100_000, [1, 100_000])
2705+
2706+
@jump_test(2, 3, [1, 3])
2707+
def test_jump_or_pop(output):
2708+
output.append(1)
2709+
_ = output.append(2) and "Spam"
2710+
output.append(3)
2711+
2712+
26772713
class TestExtendedArgs(unittest.TestCase):
26782714

26792715
def setUp(self):
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix an issue that caused extended opcode arguments and some conditional pops
2+
to be ignored when calculating valid jump targets for assignments to the
3+
``f_lineno`` attribute of frame objects. In some cases, this could cause
4+
inconsistent internal state, resulting in a hard crash of the interpreter.

Objects/frameobject.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,15 @@ mark_stacks(PyCodeObject *code_obj, int len)
230230
int64_t target_stack;
231231
int j = get_arg(code, i);
232232
if (opcode == POP_JUMP_FORWARD_IF_FALSE ||
233-
opcode == POP_JUMP_FORWARD_IF_TRUE) {
233+
opcode == POP_JUMP_FORWARD_IF_TRUE ||
234+
opcode == JUMP_IF_FALSE_OR_POP ||
235+
opcode == JUMP_IF_TRUE_OR_POP)
236+
{
234237
j += i + 1;
235238
}
236-
else if (opcode == POP_JUMP_BACKWARD_IF_FALSE ||
237-
opcode == POP_JUMP_BACKWARD_IF_TRUE) {
239+
else {
240+
assert(opcode == POP_JUMP_BACKWARD_IF_FALSE ||
241+
opcode == POP_JUMP_BACKWARD_IF_TRUE);
238242
j = i + 1 - j;
239243
}
240244
assert(j < len);
@@ -334,7 +338,8 @@ mark_stacks(PyCodeObject *code_obj, int len)
334338
break;
335339
default:
336340
{
337-
int delta = PyCompile_OpcodeStackEffect(opcode, _Py_OPARG(code[i]));
341+
int delta = PyCompile_OpcodeStackEffect(opcode, get_arg(code, i));
342+
assert(delta != PY_INVALID_STACK_EFFECT);
338343
while (delta < 0) {
339344
next_stack = pop_value(next_stack);
340345
delta++;

0 commit comments

Comments
 (0)