Skip to content

Commit efb8a25

Browse files
authored
GH-103488: Use return-offset, not yield-offset. (GH-103502)
* Use return-offset, not yield-offset, so that instruction pointer is correct when sending to a generator or coroutine.
1 parent 4307fea commit efb8a25

File tree

6 files changed

+455
-414
lines changed

6 files changed

+455
-414
lines changed

Include/internal/pycore_frame.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,13 @@ typedef struct _PyInterpreterFrame {
6161
// over, or (in the case of a newly-created frame) a totally invalid value:
6262
_Py_CODEUNIT *prev_instr;
6363
int stacktop; /* Offset of TOS from localsplus */
64-
uint16_t yield_offset;
64+
/* The return_offset determines where a `RETURN` should go in the caller,
65+
* relative to `prev_instr`.
66+
* It is only meaningful to the callee,
67+
* so it needs to be set in any CALL (to a Python function)
68+
* or SEND (to a coroutine or generator).
69+
* If there is no callee, then it is meaningless. */
70+
uint16_t return_offset;
6571
char owner;
6672
/* Locals and stack */
6773
PyObject *localsplus[1];
@@ -121,7 +127,7 @@ _PyFrame_Initialize(
121127
frame->stacktop = code->co_nlocalsplus;
122128
frame->frame_obj = NULL;
123129
frame->prev_instr = _PyCode_CODE(code) - 1;
124-
frame->yield_offset = 0;
130+
frame->return_offset = 0;
125131
frame->owner = FRAME_OWNED_BY_THREAD;
126132

127133
for (int i = null_locals_from; i < code->co_nlocalsplus; i++) {

Lib/test/test_generators.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,22 @@ def f():
225225
gi = f()
226226
self.assertIsNone(gi.gi_frame.f_back)
227227

228+
def test_issue103488(self):
228229

230+
def gen_raises():
231+
yield
232+
raise ValueError()
233+
234+
def loop():
235+
try:
236+
for _ in gen_raises():
237+
if True is False:
238+
return
239+
except ValueError:
240+
pass
241+
242+
#This should not raise
243+
loop()
229244

230245
class ExceptionTest(unittest.TestCase):
231246
# Tests for the issue #23353: check that the currently handled exception
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Change the internal offset distinguishing yield and return target addresses,
2+
so that the instruction pointer is correct for exception handling and other
3+
stack unwinding.

Python/bytecodes.c

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,7 @@ dummy_func(
506506
new_frame->localsplus[0] = container;
507507
new_frame->localsplus[1] = sub;
508508
JUMPBY(INLINE_CACHE_ENTRIES_BINARY_SUBSCR);
509+
frame->return_offset = 0;
509510
DISPATCH_INLINED(new_frame);
510511
}
511512

@@ -637,6 +638,7 @@ dummy_func(
637638
_PyInterpreterFrame *dying = frame;
638639
frame = cframe.current_frame = dying->previous;
639640
_PyEvalFrameClearAndPop(tstate, dying);
641+
frame->prev_instr += frame->return_offset;
640642
_PyFrame_StackPush(frame, retval);
641643
goto resume_frame;
642644
}
@@ -655,6 +657,7 @@ dummy_func(
655657
_PyInterpreterFrame *dying = frame;
656658
frame = cframe.current_frame = dying->previous;
657659
_PyEvalFrameClearAndPop(tstate, dying);
660+
frame->prev_instr += frame->return_offset;
658661
_PyFrame_StackPush(frame, retval);
659662
goto resume_frame;
660663
}
@@ -670,6 +673,7 @@ dummy_func(
670673
_PyInterpreterFrame *dying = frame;
671674
frame = cframe.current_frame = dying->previous;
672675
_PyEvalFrameClearAndPop(tstate, dying);
676+
frame->prev_instr += frame->return_offset;
673677
_PyFrame_StackPush(frame, retval);
674678
goto resume_frame;
675679
}
@@ -689,6 +693,7 @@ dummy_func(
689693
_PyInterpreterFrame *dying = frame;
690694
frame = cframe.current_frame = dying->previous;
691695
_PyEvalFrameClearAndPop(tstate, dying);
696+
frame->prev_instr += frame->return_offset;
692697
_PyFrame_StackPush(frame, retval);
693698
goto resume_frame;
694699
}
@@ -823,13 +828,13 @@ dummy_func(
823828
{
824829
PyGenObject *gen = (PyGenObject *)receiver;
825830
_PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe;
826-
frame->yield_offset = oparg;
831+
frame->return_offset = oparg;
827832
STACK_SHRINK(1);
828833
_PyFrame_StackPush(gen_frame, v);
829834
gen->gi_frame_state = FRAME_EXECUTING;
830835
gen->gi_exc_state.previous_item = tstate->exc_info;
831836
tstate->exc_info = &gen->gi_exc_state;
832-
JUMPBY(INLINE_CACHE_ENTRIES_SEND + oparg);
837+
JUMPBY(INLINE_CACHE_ENTRIES_SEND);
833838
DISPATCH_INLINED(gen_frame);
834839
}
835840
if (Py_IsNone(v) && PyIter_Check(receiver)) {
@@ -861,13 +866,13 @@ dummy_func(
861866
DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, SEND);
862867
STAT_INC(SEND, hit);
863868
_PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe;
864-
frame->yield_offset = oparg;
869+
frame->return_offset = oparg;
865870
STACK_SHRINK(1);
866871
_PyFrame_StackPush(gen_frame, v);
867872
gen->gi_frame_state = FRAME_EXECUTING;
868873
gen->gi_exc_state.previous_item = tstate->exc_info;
869874
tstate->exc_info = &gen->gi_exc_state;
870-
JUMPBY(INLINE_CACHE_ENTRIES_SEND + oparg);
875+
JUMPBY(INLINE_CACHE_ENTRIES_SEND);
871876
DISPATCH_INLINED(gen_frame);
872877
}
873878

@@ -886,7 +891,6 @@ dummy_func(
886891
_PyInterpreterFrame *gen_frame = frame;
887892
frame = cframe.current_frame = frame->previous;
888893
gen_frame->previous = NULL;
889-
frame->prev_instr -= frame->yield_offset;
890894
_PyFrame_StackPush(frame, retval);
891895
goto resume_frame;
892896
}
@@ -905,7 +909,6 @@ dummy_func(
905909
_PyInterpreterFrame *gen_frame = frame;
906910
frame = cframe.current_frame = frame->previous;
907911
gen_frame->previous = NULL;
908-
frame->prev_instr -= frame->yield_offset;
909912
_PyFrame_StackPush(frame, retval);
910913
goto resume_frame;
911914
}
@@ -1724,6 +1727,7 @@ dummy_func(
17241727
STACK_SHRINK(shrink_stack);
17251728
new_frame->localsplus[0] = owner;
17261729
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
1730+
frame->return_offset = 0;
17271731
DISPATCH_INLINED(new_frame);
17281732
}
17291733

@@ -1751,6 +1755,7 @@ dummy_func(
17511755
new_frame->localsplus[0] = owner;
17521756
new_frame->localsplus[1] = Py_NewRef(name);
17531757
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
1758+
frame->return_offset = 0;
17541759
DISPATCH_INLINED(new_frame);
17551760
}
17561761

@@ -2259,14 +2264,14 @@ dummy_func(
22592264
DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER);
22602265
STAT_INC(FOR_ITER, hit);
22612266
_PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe;
2262-
frame->yield_offset = oparg;
2267+
frame->return_offset = oparg;
22632268
_PyFrame_StackPush(gen_frame, Py_NewRef(Py_None));
22642269
gen->gi_frame_state = FRAME_EXECUTING;
22652270
gen->gi_exc_state.previous_item = tstate->exc_info;
22662271
tstate->exc_info = &gen->gi_exc_state;
2267-
JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg);
2268-
assert(next_instr->op.code == END_FOR ||
2269-
next_instr->op.code == INSTRUMENTED_END_FOR);
2272+
JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
2273+
assert(next_instr[oparg].op.code == END_FOR ||
2274+
next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
22702275
DISPATCH_INLINED(gen_frame);
22712276
}
22722277

@@ -2521,6 +2526,7 @@ dummy_func(
25212526
goto error;
25222527
}
25232528
JUMPBY(INLINE_CACHE_ENTRIES_CALL);
2529+
frame->return_offset = 0;
25242530
DISPATCH_INLINED(new_frame);
25252531
}
25262532
/* Callable is not a normal Python function */
@@ -2594,6 +2600,7 @@ dummy_func(
25942600
// Manipulate stack directly since we leave using DISPATCH_INLINED().
25952601
STACK_SHRINK(oparg + 2);
25962602
JUMPBY(INLINE_CACHE_ENTRIES_CALL);
2603+
frame->return_offset = 0;
25972604
DISPATCH_INLINED(new_frame);
25982605
}
25992606

@@ -2631,6 +2638,7 @@ dummy_func(
26312638
// Manipulate stack and cache directly since we leave using DISPATCH_INLINED().
26322639
STACK_SHRINK(oparg + 2);
26332640
JUMPBY(INLINE_CACHE_ENTRIES_CALL);
2641+
frame->return_offset = 0;
26342642
DISPATCH_INLINED(new_frame);
26352643
}
26362644

Python/ceval.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
672672
_PyCode_CODE(tstate->interp->interpreter_trampoline);
673673
entry_frame.stacktop = 0;
674674
entry_frame.owner = FRAME_OWNED_BY_CSTACK;
675-
entry_frame.yield_offset = 0;
675+
entry_frame.return_offset = 0;
676676
/* Push frame */
677677
entry_frame.previous = prev_cframe->current_frame;
678678
frame->previous = &entry_frame;
@@ -881,6 +881,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
881881
_PyInterpreterFrame *dying = frame;
882882
frame = cframe.current_frame = dying->previous;
883883
_PyEvalFrameClearAndPop(tstate, dying);
884+
frame->return_offset = 0;
884885
if (frame == &entry_frame) {
885886
/* Restore previous cframe and exit */
886887
tstate->cframe = cframe.previous;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy