mirror of https://github.com/python/cpython
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:
parent
489c36920e
commit
b37181e692
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
|
@ -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. */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue