Skip to content

[3.11] GH-94438: Handle extended arguments and conditional pops in mark_stacks (GH-95110) #95154

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 22, 2022
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
36 changes: 36 additions & 0 deletions Lib/test/test_sys_settrace.py
Original file line number Diff line number Diff line change
Expand Up @@ -2674,6 +2674,42 @@ def test_jump_with_null_on_stack_load_attr(output):
)
output.append(15)

@jump_test(2, 3, [1, 3])
def test_jump_extended_args_unpack_ex_simple(output):
output.append(1)
_, *_, _ = output.append(2) or "Spam"
output.append(3)

@jump_test(3, 4, [1, 4, 4, 5])
def test_jump_extended_args_unpack_ex_tricky(output):
output.append(1)
(
_, *_, _
) = output.append(4) or "Spam"
output.append(5)

def test_jump_extended_args_for_iter(self):
# In addition to failing when extended arg handling is broken, this can
# also hang for a *very* long time:
source = [
"def f(output):",
" output.append(1)",
" for _ in spam:",
*(f" output.append({i})" for i in range(3, 100_000)),
f" output.append(100_000)",
]
namespace = {}
exec("\n".join(source), namespace)
f = namespace["f"]
self.run_test(f, 2, 100_000, [1, 100_000])

@jump_test(2, 3, [1, 3])
def test_jump_or_pop(output):
output.append(1)
_ = output.append(2) and "Spam"
output.append(3)


class TestExtendedArgs(unittest.TestCase):

def setUp(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix an issue that caused extended opcode arguments and some conditional pops
to be ignored when calculating valid jump targets for assignments to the
``f_lineno`` attribute of frame objects. In some cases, this could cause
inconsistent internal state, resulting in a hard crash of the interpreter.
13 changes: 9 additions & 4 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,15 @@ mark_stacks(PyCodeObject *code_obj, int len)
int64_t target_stack;
int j = get_arg(code, i);
if (opcode == POP_JUMP_FORWARD_IF_FALSE ||
opcode == POP_JUMP_FORWARD_IF_TRUE) {
opcode == POP_JUMP_FORWARD_IF_TRUE ||
opcode == JUMP_IF_FALSE_OR_POP ||
opcode == JUMP_IF_TRUE_OR_POP)
{
j += i + 1;
}
else if (opcode == POP_JUMP_BACKWARD_IF_FALSE ||
opcode == POP_JUMP_BACKWARD_IF_TRUE) {
else {
assert(opcode == POP_JUMP_BACKWARD_IF_FALSE ||
opcode == POP_JUMP_BACKWARD_IF_TRUE);
j = i + 1 - j;
}
assert(j < len);
Expand Down Expand Up @@ -334,7 +338,8 @@ mark_stacks(PyCodeObject *code_obj, int len)
break;
default:
{
int delta = PyCompile_OpcodeStackEffect(opcode, _Py_OPARG(code[i]));
int delta = PyCompile_OpcodeStackEffect(opcode, get_arg(code, i));
assert(delta != PY_INVALID_STACK_EFFECT);
while (delta < 0) {
next_stack = pop_value(next_stack);
delta++;
Expand Down