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.
This commit is contained in:
Mark Shannon 2021-04-06 11:48:59 +01:00 committed by GitHub
parent 489c36920e
commit b37181e692
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 2680 additions and 2612 deletions

View File

@ -1247,6 +1247,14 @@ All of the following opcodes use their arguments.
.. versionadded:: 3.10 .. versionadded:: 3.10
.. opcode:: GEN_START (kind)
Pops TOS. If TOS was not ``None``, raises an exception. The ``kind``
operand corresponds to the type of generator or coroutine and determines
the error message. The legal kinds are 0 for generator, 1 for coroutine,
and 2 for async generator.
.. versionadded:: 3.10
.. opcode:: HAVE_ARGUMENT .. opcode:: HAVE_ARGUMENT

1
Include/opcode.h generated
View File

@ -106,6 +106,7 @@ extern "C" {
#define LOAD_FAST 124 #define LOAD_FAST 124
#define STORE_FAST 125 #define STORE_FAST 125
#define DELETE_FAST 126 #define DELETE_FAST 126
#define GEN_START 129
#define RAISE_VARARGS 130 #define RAISE_VARARGS 130
#define CALL_FUNCTION 131 #define CALL_FUNCTION 131
#define MAKE_FUNCTION 132 #define MAKE_FUNCTION 132

View File

@ -315,6 +315,7 @@ _code_type = type(_write_atomic.__code__)
# Python 3.10a2 3433 (RERAISE restores f_lasti if oparg != 0) # Python 3.10a2 3433 (RERAISE restores f_lasti if oparg != 0)
# Python 3.10a6 3434 (PEP 634: Structural Pattern Matching) # Python 3.10a6 3434 (PEP 634: Structural Pattern Matching)
# Python 3.10a7 3435 Use instruction offsets (as opposed to byte offsets). # Python 3.10a7 3435 Use instruction offsets (as opposed to byte offsets).
# Python 3.10a7 3436 (Add GEN_START bytecode #43683)
# #
# MAGIC must change whenever the bytecode emitted by the compiler may no # MAGIC must change whenever the bytecode emitted by the compiler may no

View File

@ -173,6 +173,7 @@ haslocal.append(125)
def_op('DELETE_FAST', 126) # Local variable number def_op('DELETE_FAST', 126) # Local variable number
haslocal.append(126) haslocal.append(126)
def_op('GEN_START', 129) # Kind of generator/coroutine
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
def_op('CALL_FUNCTION', 131) # #args def_op('CALL_FUNCTION', 131) # #args
def_op('MAKE_FUNCTION', 132) # Flags def_op('MAKE_FUNCTION', 132) # Flags

View File

@ -0,0 +1,2 @@
Add GEN_START opcode. Marks start of generator, including async, or coroutine and handles
sending values to a newly created generator or coroutine.

View File

@ -176,27 +176,12 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
} }
assert(_PyFrame_IsRunnable(f)); assert(_PyFrame_IsRunnable(f));
if (f->f_lasti == -1) { assert(f->f_lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(f->f_code->co_code))[0] == GEN_START);
if (arg && arg != Py_None) {
const char *msg = "can't send non-None value to a "
"just-started generator";
if (PyCoro_CheckExact(gen)) {
msg = NON_INIT_CORO_MSG;
}
else if (PyAsyncGen_CheckExact(gen)) {
msg = "can't send non-None value to a "
"just-started async generator";
}
PyErr_SetString(PyExc_TypeError, msg);
return PYGEN_ERROR;
}
} else {
/* Push arg onto the frame's value stack */ /* Push arg onto the frame's value stack */
result = arg ? arg : Py_None; result = arg ? arg : Py_None;
Py_INCREF(result); Py_INCREF(result);
gen->gi_frame->f_valuestack[gen->gi_frame->f_stackdepth] = result; gen->gi_frame->f_valuestack[gen->gi_frame->f_stackdepth] = result;
gen->gi_frame->f_stackdepth++; gen->gi_frame->f_stackdepth++;
}
/* Generators always return to their most recent caller, not /* Generators always return to their most recent caller, not
* necessarily their creator. */ * necessarily their creator. */

View File

@ -2639,6 +2639,30 @@ main_loop:
goto exiting; goto exiting;
} }
case TARGET(GEN_START): {
PyObject *none = POP();
Py_DECREF(none);
if (none != Py_None) {
if (oparg > 2) {
_PyErr_SetString(tstate, PyExc_SystemError,
"Illegal kind for GEN_START");
}
else {
static const char *gen_kind[3] = {
"generator",
"coroutine",
"async generator"
};
_PyErr_Format(tstate, PyExc_TypeError,
"can't send non-None value to a "
"just-started %s",
gen_kind[oparg]);
}
goto error;
}
DISPATCH();
}
case TARGET(POP_EXCEPT): { case TARGET(POP_EXCEPT): {
PyObject *type, *value, *traceback; PyObject *type, *value, *traceback;
_PyErr_StackItem *exc_info; _PyErr_StackItem *exc_info;

View File

@ -1140,6 +1140,8 @@ stack_effect(int opcode, int oparg, int jump)
return 1; return 1;
case LIST_TO_TUPLE: case LIST_TO_TUPLE:
return 0; return 0;
case GEN_START:
return -1;
case LIST_EXTEND: case LIST_EXTEND:
case SET_UPDATE: case SET_UPDATE:
case DICT_MERGE: case DICT_MERGE:
@ -6169,7 +6171,11 @@ stackdepth(struct compiler *c)
} }
sp = stack; sp = stack;
if (c->u->u_ste->ste_generator || c->u->u_ste->ste_coroutine) {
stackdepth_push(&sp, entryblock, 1);
} else {
stackdepth_push(&sp, entryblock, 0); stackdepth_push(&sp, entryblock, 0);
}
while (sp != stack) { while (sp != stack) {
b = *--sp; b = *--sp;
int depth = b->b_startdepth; int depth = b->b_startdepth;
@ -6648,6 +6654,41 @@ optimize_cfg(struct assembler *a, PyObject *consts);
static int static int
ensure_exits_have_lineno(struct compiler *c); ensure_exits_have_lineno(struct compiler *c);
static int
insert_generator_prefix(struct compiler *c, basicblock *entryblock) {
int flags = compute_code_flags(c);
if (flags < 0) {
return -1;
}
int kind;
if (flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
if (flags & CO_COROUTINE) {
kind = 1;
}
else if (flags & CO_ASYNC_GENERATOR) {
kind = 2;
}
else {
kind = 0;
}
}
else {
return 0;
}
if (compiler_next_instr(entryblock) < 0) {
return -1;
}
for (int i = entryblock->b_iused-1; i > 0; i--) {
entryblock->b_instr[i] = entryblock->b_instr[i-1];
}
entryblock->b_instr[0].i_opcode = GEN_START;
entryblock->b_instr[0].i_oparg = kind;
entryblock->b_instr[0].i_lineno = -1;
entryblock->b_instr[0].i_target = NULL;
return 0;
}
static PyCodeObject * static PyCodeObject *
assemble(struct compiler *c, int addNone) assemble(struct compiler *c, int addNone)
{ {
@ -6685,6 +6726,10 @@ assemble(struct compiler *c, int addNone)
entryblock = b; entryblock = b;
} }
if (insert_generator_prefix(c, entryblock)) {
goto error;
}
/* Set firstlineno if it wasn't explicitly set. */ /* Set firstlineno if it wasn't explicitly set. */
if (!c->u->u_firstlineno) { if (!c->u->u_firstlineno) {
if (entryblock && entryblock->b_instr && entryblock->b_instr->i_lineno) if (entryblock && entryblock->b_instr && entryblock->b_instr->i_lineno)

File diff suppressed because it is too large Load Diff

View File

@ -128,7 +128,7 @@ static void *opcode_targets[256] = {
&&TARGET_DELETE_FAST, &&TARGET_DELETE_FAST,
&&_unknown_opcode, &&_unknown_opcode,
&&_unknown_opcode, &&_unknown_opcode,
&&_unknown_opcode, &&TARGET_GEN_START,
&&TARGET_RAISE_VARARGS, &&TARGET_RAISE_VARARGS,
&&TARGET_CALL_FUNCTION, &&TARGET_CALL_FUNCTION,
&&TARGET_MAKE_FUNCTION, &&TARGET_MAKE_FUNCTION,