Skip to content

Commit b37181e

Browse files
authored
bpo-43683: Handle generator entry in bytecode (GH-25138)
* Handle check for sending None to starting generator and coroutine into bytecode. * Document new bytecode and make it fail gracefully if mis-compiled.
1 parent 489c369 commit b37181e

File tree

10 files changed

+2675
-2607
lines changed

10 files changed

+2675
-2607
lines changed

Doc/library/dis.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,6 +1247,14 @@ All of the following opcodes use their arguments.
12471247

12481248
.. versionadded:: 3.10
12491249

1250+
.. opcode:: GEN_START (kind)
1251+
1252+
Pops TOS. If TOS was not ``None``, raises an exception. The ``kind``
1253+
operand corresponds to the type of generator or coroutine and determines
1254+
the error message. The legal kinds are 0 for generator, 1 for coroutine,
1255+
and 2 for async generator.
1256+
1257+
.. versionadded:: 3.10
12501258

12511259
.. opcode:: HAVE_ARGUMENT
12521260

Include/opcode.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/importlib/_bootstrap_external.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ def _write_atomic(path, data, mode=0o666):
315315
# Python 3.10a2 3433 (RERAISE restores f_lasti if oparg != 0)
316316
# Python 3.10a6 3434 (PEP 634: Structural Pattern Matching)
317317
# Python 3.10a7 3435 Use instruction offsets (as opposed to byte offsets).
318+
# Python 3.10a7 3436 (Add GEN_START bytecode #43683)
318319

319320
#
320321
# MAGIC must change whenever the bytecode emitted by the compiler may no

Lib/opcode.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ def jabs_op(name, op):
173173
def_op('DELETE_FAST', 126) # Local variable number
174174
haslocal.append(126)
175175

176+
def_op('GEN_START', 129) # Kind of generator/coroutine
176177
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
177178
def_op('CALL_FUNCTION', 131) # #args
178179
def_op('MAKE_FUNCTION', 132) # Flags
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add GEN_START opcode. Marks start of generator, including async, or coroutine and handles
2+
sending values to a newly created generator or coroutine.

Objects/genobject.c

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -176,27 +176,12 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
176176
}
177177

178178
assert(_PyFrame_IsRunnable(f));
179-
if (f->f_lasti == -1) {
180-
if (arg && arg != Py_None) {
181-
const char *msg = "can't send non-None value to a "
182-
"just-started generator";
183-
if (PyCoro_CheckExact(gen)) {
184-
msg = NON_INIT_CORO_MSG;
185-
}
186-
else if (PyAsyncGen_CheckExact(gen)) {
187-
msg = "can't send non-None value to a "
188-
"just-started async generator";
189-
}
190-
PyErr_SetString(PyExc_TypeError, msg);
191-
return PYGEN_ERROR;
192-
}
193-
} else {
194-
/* Push arg onto the frame's value stack */
195-
result = arg ? arg : Py_None;
196-
Py_INCREF(result);
197-
gen->gi_frame->f_valuestack[gen->gi_frame->f_stackdepth] = result;
198-
gen->gi_frame->f_stackdepth++;
199-
}
179+
assert(f->f_lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(f->f_code->co_code))[0] == GEN_START);
180+
/* Push arg onto the frame's value stack */
181+
result = arg ? arg : Py_None;
182+
Py_INCREF(result);
183+
gen->gi_frame->f_valuestack[gen->gi_frame->f_stackdepth] = result;
184+
gen->gi_frame->f_stackdepth++;
200185

201186
/* Generators always return to their most recent caller, not
202187
* necessarily their creator. */

Python/ceval.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2639,6 +2639,30 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
26392639
goto exiting;
26402640
}
26412641

2642+
case TARGET(GEN_START): {
2643+
PyObject *none = POP();
2644+
Py_DECREF(none);
2645+
if (none != Py_None) {
2646+
if (oparg > 2) {
2647+
_PyErr_SetString(tstate, PyExc_SystemError,
2648+
"Illegal kind for GEN_START");
2649+
}
2650+
else {
2651+
static const char *gen_kind[3] = {
2652+
"generator",
2653+
"coroutine",
2654+
"async generator"
2655+
};
2656+
_PyErr_Format(tstate, PyExc_TypeError,
2657+
"can't send non-None value to a "
2658+
"just-started %s",
2659+
gen_kind[oparg]);
2660+
}
2661+
goto error;
2662+
}
2663+
DISPATCH();
2664+
}
2665+
26422666
case TARGET(POP_EXCEPT): {
26432667
PyObject *type, *value, *traceback;
26442668
_PyErr_StackItem *exc_info;

Python/compile.c

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,8 @@ stack_effect(int opcode, int oparg, int jump)
11401140
return 1;
11411141
case LIST_TO_TUPLE:
11421142
return 0;
1143+
case GEN_START:
1144+
return -1;
11431145
case LIST_EXTEND:
11441146
case SET_UPDATE:
11451147
case DICT_MERGE:
@@ -6169,7 +6171,11 @@ stackdepth(struct compiler *c)
61696171
}
61706172

61716173
sp = stack;
6172-
stackdepth_push(&sp, entryblock, 0);
6174+
if (c->u->u_ste->ste_generator || c->u->u_ste->ste_coroutine) {
6175+
stackdepth_push(&sp, entryblock, 1);
6176+
} else {
6177+
stackdepth_push(&sp, entryblock, 0);
6178+
}
61736179
while (sp != stack) {
61746180
b = *--sp;
61756181
int depth = b->b_startdepth;
@@ -6648,6 +6654,41 @@ optimize_cfg(struct assembler *a, PyObject *consts);
66486654
static int
66496655
ensure_exits_have_lineno(struct compiler *c);
66506656

6657+
static int
6658+
insert_generator_prefix(struct compiler *c, basicblock *entryblock) {
6659+
6660+
int flags = compute_code_flags(c);
6661+
if (flags < 0) {
6662+
return -1;
6663+
}
6664+
int kind;
6665+
if (flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
6666+
if (flags & CO_COROUTINE) {
6667+
kind = 1;
6668+
}
6669+
else if (flags & CO_ASYNC_GENERATOR) {
6670+
kind = 2;
6671+
}
6672+
else {
6673+
kind = 0;
6674+
}
6675+
}
6676+
else {
6677+
return 0;
6678+
}
6679+
if (compiler_next_instr(entryblock) < 0) {
6680+
return -1;
6681+
}
6682+
for (int i = entryblock->b_iused-1; i > 0; i--) {
6683+
entryblock->b_instr[i] = entryblock->b_instr[i-1];
6684+
}
6685+
entryblock->b_instr[0].i_opcode = GEN_START;
6686+
entryblock->b_instr[0].i_oparg = kind;
6687+
entryblock->b_instr[0].i_lineno = -1;
6688+
entryblock->b_instr[0].i_target = NULL;
6689+
return 0;
6690+
}
6691+
66516692
static PyCodeObject *
66526693
assemble(struct compiler *c, int addNone)
66536694
{
@@ -6685,6 +6726,10 @@ assemble(struct compiler *c, int addNone)
66856726
entryblock = b;
66866727
}
66876728

6729+
if (insert_generator_prefix(c, entryblock)) {
6730+
goto error;
6731+
}
6732+
66886733
/* Set firstlineno if it wasn't explicitly set. */
66896734
if (!c->u->u_firstlineno) {
66906735
if (entryblock && entryblock->b_instr && entryblock->b_instr->i_lineno)

0 commit comments

Comments
 (0)