From 973e56662190dbec954d9e3cacb6adabe5395585 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Tue, 22 Mar 2022 10:34:46 -0700 Subject: [PATCH 1/6] Perform throws in the bytecode --- Include/opcode.h | 36 +++++++------ Lib/importlib/_bootstrap_external.py | 3 +- Lib/opcode.py | 1 + Lib/test/test_asyncgen.py | 1 + Objects/frameobject.c | 1 + Objects/genobject.c | 81 +++------------------------- Python/ceval.c | 50 +++++++++++++++++ Python/compile.c | 29 +++++++--- Python/opcode_targets.h | 16 +++--- 9 files changed, 113 insertions(+), 105 deletions(-) diff --git a/Include/opcode.h b/Include/opcode.h index dfc7b72e3cdc68..b5524e1ded7c9a 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -94,6 +94,7 @@ extern "C" { #define LOAD_DEREF 137 #define STORE_DEREF 138 #define DELETE_DEREF 139 +#define THROW 140 #define CALL_FUNCTION_EX 142 #define EXTENDED_ARG 144 #define LIST_APPEND 145 @@ -168,21 +169,21 @@ extern "C" { #define PRECALL_NO_KW_STR_1 79 #define PRECALL_NO_KW_TUPLE_1 80 #define PRECALL_NO_KW_TYPE_1 81 -#define PRECALL_PYFUNC 140 -#define RESUME_QUICK 141 -#define STORE_ATTR_ADAPTIVE 143 -#define STORE_ATTR_INSTANCE_VALUE 150 -#define STORE_ATTR_SLOT 153 -#define STORE_ATTR_WITH_HINT 154 -#define STORE_FAST__LOAD_FAST 158 -#define STORE_FAST__STORE_FAST 159 -#define STORE_SUBSCR_ADAPTIVE 161 -#define STORE_SUBSCR_DICT 167 -#define STORE_SUBSCR_LIST_INT 168 -#define UNPACK_SEQUENCE_ADAPTIVE 169 -#define UNPACK_SEQUENCE_LIST 170 -#define UNPACK_SEQUENCE_TUPLE 173 -#define UNPACK_SEQUENCE_TWO_TUPLE 174 +#define PRECALL_PYFUNC 141 +#define RESUME_QUICK 143 +#define STORE_ATTR_ADAPTIVE 150 +#define STORE_ATTR_INSTANCE_VALUE 153 +#define STORE_ATTR_SLOT 154 +#define STORE_ATTR_WITH_HINT 158 +#define STORE_FAST__LOAD_FAST 159 +#define STORE_FAST__STORE_FAST 161 +#define STORE_SUBSCR_ADAPTIVE 167 +#define STORE_SUBSCR_DICT 168 +#define STORE_SUBSCR_LIST_INT 169 +#define UNPACK_SEQUENCE_ADAPTIVE 170 +#define UNPACK_SEQUENCE_LIST 173 +#define UNPACK_SEQUENCE_TUPLE 174 +#define UNPACK_SEQUENCE_TWO_TUPLE 175 #define DO_TRACING 255 extern const uint8_t _PyOpcode_Caches[256]; @@ -195,7 +196,7 @@ static const uint32_t _PyOpcode_RelativeJump[8] = { 0U, 536870912U, 134234112U, - 0U, + 4096U, 0U, 0U, 0U, @@ -205,7 +206,7 @@ static const uint32_t _PyOpcode_Jump[8] = { 0U, 536870912U, 2316288000U, - 67U, + 4163U, 0U, 0U, 0U, @@ -389,6 +390,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [STORE_SUBSCR_DICT] = STORE_SUBSCR, [STORE_SUBSCR_LIST_INT] = STORE_SUBSCR, [SWAP] = SWAP, + [THROW] = THROW, [UNARY_INVERT] = UNARY_INVERT, [UNARY_NEGATIVE] = UNARY_NEGATIVE, [UNARY_NOT] = UNARY_NOT, diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 48b55bb821f8bb..0dac8589604f9b 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -396,6 +396,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.11a6 3486 (Use inline caching for PRECALL and CALL) # Python 3.11a6 3487 (Remove the adaptive "oparg counter" mechanism) # Python 3.11a6 3488 (LOAD_GLOBAL can push additional NULL) +# Python 3.11a6 3489 (Add THROW) # Python 3.12 will start with magic number 3500 @@ -410,7 +411,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3488).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3489).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/opcode.py b/Lib/opcode.py index 7a52c13579af7c..a060e63a28e075 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -166,6 +166,7 @@ def jabs_op(name, op, entries=0): hasfree.append(138) def_op('DELETE_DEREF', 139) hasfree.append(139) +jrel_op('THROW', 140) def_op('CALL_FUNCTION_EX', 142) # Flags diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py index 473bce484b47b0..2df5b6564ebee7 100644 --- a/Lib/test/test_asyncgen.py +++ b/Lib/test/test_asyncgen.py @@ -669,6 +669,7 @@ def test3(anext): agen = agenfn() with contextlib.closing(anext(agen, "default").__await__()) as g: self.assertEqual(g.send(None), 1) + breakpoint() g.close() with self.assertRaisesRegex(RuntimeError, 'cannot reuse'): self.assertEqual(g.send(None), 1) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 7ccd300e2b4548..be44028f5532e3 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -252,6 +252,7 @@ mark_stacks(PyCodeObject *code_obj, int len) stacks[i+1] = next_stack; break; case SEND: + case THROW: j = get_arg(code, i) + i + 1; assert(j < len); assert(stacks[j] == UNINITIALIZED || stacks[j] == pop_value(next_stack)); diff --git a/Objects/genobject.c b/Objects/genobject.c index 3ad8dc1c459420..4887c46ff299d2 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -417,93 +417,28 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, PyObject *typ, PyObject *val, PyObject *tb) { PyObject *yf = _PyGen_yf(gen); - if (yf) { - _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe; - PyObject *ret; - int err; - if (PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit) && - close_on_genexit - ) { + if (close_on_genexit && + PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit)) + { /* Asynchronous generators *should not* be closed right away. We have to allow some awaits to work it through, hence the `close_on_genexit` parameter here. */ + _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe; PyFrameState state = frame->f_state; frame->f_state = FRAME_EXECUTING; - err = gen_close_iter(yf); + int err = gen_close_iter(yf); frame->f_state = state; Py_DECREF(yf); - if (err < 0) + if (err < 0) { return gen_send_ex(gen, Py_None, 1, 0); - goto throw_here; - } - if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) { - /* `yf` is a generator or a coroutine. */ - PyThreadState *tstate = _PyThreadState_GET(); - /* Since we are fast-tracking things by skipping the eval loop, - we need to update the current frame so the stack trace - will be reported correctly to the user. */ - /* XXX We should probably be updating the current frame - somewhere in ceval.c. */ - _PyInterpreterFrame *prev = tstate->cframe->current_frame; - frame->previous = prev; - tstate->cframe->current_frame = frame; - /* Close the generator that we are currently iterating with - 'yield from' or awaiting on with 'await'. */ - PyFrameState state = frame->f_state; - frame->f_state = FRAME_EXECUTING; - ret = _gen_throw((PyGenObject *)yf, close_on_genexit, - typ, val, tb); - frame->f_state = state; - tstate->cframe->current_frame = prev; - frame->previous = NULL; - } else { - /* `yf` is an iterator or a coroutine-like object. */ - PyObject *meth; - if (_PyObject_LookupAttr(yf, &_Py_ID(throw), &meth) < 0) { - Py_DECREF(yf); - return NULL; - } - if (meth == NULL) { - Py_DECREF(yf); - goto throw_here; } - PyFrameState state = frame->f_state; - frame->f_state = FRAME_EXECUTING; - ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL); - frame->f_state = state; - Py_DECREF(meth); } - Py_DECREF(yf); - if (!ret) { - PyObject *val; - /* Pop subiterator from stack */ - assert(gen->gi_frame_valid); - ret = _PyFrame_StackPop((_PyInterpreterFrame *)gen->gi_iframe); - assert(ret == yf); - Py_DECREF(ret); - // XXX: Performing this jump ourselves is awkward and problematic. - // See https://github.com/python/cpython/pull/31968. - /* Termination repetition of SEND loop */ - assert(frame->f_lasti >= 0); - _Py_CODEUNIT *code = _PyCode_CODE(gen->gi_code); - /* Backup to SEND */ - frame->f_lasti--; - assert(_Py_OPCODE(code[frame->f_lasti]) == SEND); - int jump = _Py_OPARG(code[frame->f_lasti]); - frame->f_lasti += jump; - if (_PyGen_FetchStopIterationValue(&val) == 0) { - ret = gen_send(gen, val); - Py_DECREF(val); - } else { - ret = gen_send_ex(gen, Py_None, 1, 0); - } + else { + Py_DECREF(yf); } - return ret; } - -throw_here: /* First, check the traceback argument, replacing None with NULL. */ if (tb == Py_None) { diff --git a/Python/ceval.c b/Python/ceval.c index 7c6bfd46900e6a..9410477dbac5a3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2570,6 +2570,54 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int DISPATCH(); } + TARGET(THROW) { + assert(frame->is_entry); + assert(throwflag); + PyObject *exc_value = POP(); + PyObject *exc_type = Py_NewRef(Py_TYPE(exc_value)); + PyObject *exc_traceback = PyException_GetTraceback(exc_value); + PyErr_NormalizeException(&exc_type, &exc_value, &exc_traceback); + PyObject *last_send = POP(); + Py_DECREF(last_send); + PyObject *receiver = TOP(); + PyObject *throw; + int found = _PyObject_LookupAttr(receiver, &_Py_ID(throw), &throw); + if (found < 0) { + if (PyErr_GivenExceptionMatches(exc_value, PyExc_GeneratorExit)) + { + PyErr_Clear(); + _PyErr_Restore(tstate, exc_type, exc_value, exc_traceback); + } + goto error; + } + if (found == 0) { + _PyErr_Restore(tstate, exc_type, exc_value, exc_traceback); + goto error; + } + PyObject *retval = PyObject_CallFunctionObjArgs( + throw, exc_type, exc_value, exc_traceback, NULL); + Py_DECREF(throw); + Py_DECREF(exc_type); + Py_DECREF(exc_value); + Py_XDECREF(exc_traceback); + if (retval) { + PUSH(retval); + DISPATCH(); + } + if (tstate->c_tracefunc && + _PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) + { + call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, + frame); + } + if (_PyGen_FetchStopIterationValue(&TOP())) { + goto error; + } + Py_DECREF(receiver); + JUMPBY(oparg); + DISPATCH(); + } + TARGET(ASYNC_GEN_WRAP) { PyObject *v = TOP(); assert(frame->f_code->co_flags & CO_ASYNC_GENERATOR); @@ -2583,6 +2631,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(YIELD_VALUE) { + // NOTE: It's important that YIELD_VALUE never raises an exception! + // The compiler treats *anything* raised here as a throw() call. assert(frame->is_entry); PyObject *retval = POP(); frame->f_state = FRAME_SUSPENDED; diff --git a/Python/compile.c b/Python/compile.c index e24f425229b6af..46b837f1ce6257 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -947,6 +947,8 @@ stack_effect(int opcode, int oparg, int jump) return jump > 0 ? -1 : 1; case SEND: return jump > 0 ? -1 : 0; + case THROW: + return jump > 0 ? -2 : -1; case STORE_ATTR: return -2; case DELETE_ATTR: @@ -1875,19 +1877,34 @@ compiler_call_exit_with_nones(struct compiler *c) { static int compiler_add_yield_from(struct compiler *c, int await) { - basicblock *start, *resume, *exit; - start = compiler_new_block(c); - resume = compiler_new_block(c); + + basicblock *body, *except, *exit; + basicblock *start = compiler_new_block(c); + body = compiler_new_block(c); + except = compiler_new_block(c); exit = compiler_new_block(c); - if (start == NULL || resume == NULL || exit == NULL) { + if (body == NULL || except == NULL || exit == NULL) return 0; - } + compiler_use_next_block(c, start); ADDOP_JUMP(c, SEND, exit); - compiler_use_next_block(c, resume); + + + compiler_use_next_block(c, body); + ADDOP_JUMP(c, SETUP_FINALLY, except); + RETURN_IF_FALSE(compiler_push_fblock(c, TRY_EXCEPT, body, NULL, NULL)); + // The only way YIELD_VALUE can raise is if throw() is called: ADDOP(c, YIELD_VALUE); + compiler_pop_fblock(c, TRY_EXCEPT, body); + ADDOP_NOLINE(c, POP_BLOCK); + ADDOP_I(c, RESUME, await ? 3 : 2); ADDOP_JUMP(c, JUMP_NO_INTERRUPT, start); + + compiler_use_next_block(c, except); + ADDOP_JUMP(c, THROW, exit); + ADDOP_JUMP(c, JUMP_NO_INTERRUPT, body); + compiler_use_next_block(c, exit); return 1; } diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 2aa6471abf99a1..889709f5cd2ad8 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -139,39 +139,40 @@ static void *opcode_targets[256] = { &&TARGET_LOAD_DEREF, &&TARGET_STORE_DEREF, &&TARGET_DELETE_DEREF, + &&TARGET_THROW, &&TARGET_PRECALL_PYFUNC, - &&TARGET_RESUME_QUICK, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_STORE_ATTR_ADAPTIVE, + &&TARGET_RESUME_QUICK, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, &&TARGET_MAP_ADD, &&TARGET_LOAD_CLASSDEREF, &&TARGET_COPY_FREE_VARS, - &&TARGET_STORE_ATTR_INSTANCE_VALUE, + &&TARGET_STORE_ATTR_ADAPTIVE, &&TARGET_RESUME, &&TARGET_MATCH_CLASS, + &&TARGET_STORE_ATTR_INSTANCE_VALUE, &&TARGET_STORE_ATTR_SLOT, - &&TARGET_STORE_ATTR_WITH_HINT, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, + &&TARGET_STORE_ATTR_WITH_HINT, &&TARGET_STORE_FAST__LOAD_FAST, - &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_LOAD_METHOD, - &&TARGET_STORE_SUBSCR_ADAPTIVE, + &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_LIST_EXTEND, &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, &&TARGET_DICT_UPDATE, &&TARGET_PRECALL, + &&TARGET_STORE_SUBSCR_ADAPTIVE, &&TARGET_STORE_SUBSCR_DICT, &&TARGET_STORE_SUBSCR_LIST_INT, &&TARGET_UNPACK_SEQUENCE_ADAPTIVE, - &&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_CALL, &&TARGET_KW_NAMES, + &&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_UNPACK_SEQUENCE_TUPLE, &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, &&_unknown_opcode, @@ -253,6 +254,5 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_DO_TRACING }; From 7a23b5ea0a5971c6ba92b3cf745c50cf760758bb Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Thu, 5 May 2022 20:40:48 -0700 Subject: [PATCH 2/6] Cleanup --- Lib/importlib/_bootstrap_external.py | 2 +- Python/compile.c | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 3e8ddb8c527b6b..34f46e56dfe19b 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -403,7 +403,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.11a7 3492 (make POP_JUMP_IF_NONE/NOT_NONE/TRUE/FALSE relative) # Python 3.11a7 3493 (Make JUMP_IF_TRUE_OR_POP/JUMP_IF_FALSE_OR_POP relative) # Python 3.11a7 3494 (New location info table) -# Python 3.11a6 3495 (Add THROW) +# Python 3.11a7 3495 (Add THROW) # Python 3.12 will start with magic number 3500 diff --git a/Python/compile.c b/Python/compile.c index aea28a3776217b..5660968cf2ddeb 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1962,10 +1962,8 @@ compiler_add_yield_from(struct compiler *c, int await) RETURN_IF_FALSE(body = compiler_new_block(c)); RETURN_IF_FALSE(except = compiler_new_block(c)); RETURN_IF_FALSE(exit = compiler_new_block(c)); - compiler_use_next_block(c, start); ADDOP_JUMP(c, SEND, exit); - compiler_use_next_block(c, body); ADDOP_JUMP(c, SETUP_FINALLY, except); RETURN_IF_FALSE(compiler_push_fblock(c, TRY_EXCEPT, body, NULL, NULL)); @@ -1975,12 +1973,10 @@ compiler_add_yield_from(struct compiler *c, int await) ADDOP_NOLINE(c, POP_BLOCK); ADDOP_I(c, RESUME, await ? 3 : 2); ADDOP_JUMP(c, JUMP_NO_INTERRUPT, start); - compiler_use_next_block(c, except); ADDOP_JUMP(c, THROW, exit); ADDOP_I(c, RESUME, await ? 3 : 2); ADDOP_JUMP(c, JUMP_NO_INTERRUPT, body); - compiler_use_next_block(c, exit); return 1; } From 9e1f52175559115f5f6e03d83ac8ed6119731222 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Thu, 11 Aug 2022 11:22:14 -0700 Subject: [PATCH 3/6] fixup --- Python/compile.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index a15653740f080d..75d4fdec2be09a 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -9366,9 +9366,6 @@ mark_reachable(basicblock *entryblock) { if (is_jump(instr) || is_block_push(instr)) { target = instr->i_target; if (!target->b_visited) { - if (!(target->b_predecessors == 0 || target == b->b_next)) { - printf("%s\n", _PyOpcode_OpName[instr->i_opcode]); - } assert(target->b_predecessors == 0 || target == b->b_next); *sp++ = target; } From ea0ecaa05e8b6c826099de83c361be3a2faa8831 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 12 Aug 2022 11:37:05 -0700 Subject: [PATCH 4/6] Deduplicate the THROW code --- Python/ceval.c | 84 +++++++++++++++++--------------------------------- 1 file changed, 29 insertions(+), 55 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 90789fd6e2f0e2..9e8aeb1658a025 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1124,35 +1124,6 @@ match_class(PyThreadState *tstate, PyObject *subject, PyObject *type, return NULL; } -static PyObject * -throw(PyThreadState *tstate, PyObject *exc_value, PyObject *receiver) -{ - PyObject *exc_type = Py_NewRef(Py_TYPE(exc_value)); - PyObject *exc_traceback = PyException_GetTraceback(exc_value); - PyErr_NormalizeException(&exc_type, &exc_value, &exc_traceback); - PyObject *throw; - int found = _PyObject_LookupAttr(receiver, &_Py_ID(throw), &throw); - if (found < 0) { - if (PyErr_GivenExceptionMatches(exc_value, PyExc_GeneratorExit)) - { - PyErr_Clear(); - _PyErr_Restore(tstate, exc_type, exc_value, exc_traceback); - } - return NULL; - } - if (found == 0) { - _PyErr_Restore(tstate, exc_type, exc_value, exc_traceback); - return NULL; - } - PyObject *retval = PyObject_CallFunctionObjArgs(throw, exc_type, exc_value, - exc_traceback, NULL); - Py_DECREF(throw); - Py_DECREF(exc_type); - Py_DECREF(exc_value); - Py_XDECREF(exc_traceback); - return retval; -} - static int do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause); static int exception_group_match( @@ -2685,13 +2656,36 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(THROW_FORWARD) { + PREDICTED(THROW_FORWARD); assert(frame->is_entry); assert(throwflag); PyObject *exc_value = POP(); + PyObject *exc_type = Py_NewRef(Py_TYPE(exc_value)); + PyObject *exc_traceback = PyException_GetTraceback(exc_value); + PyErr_NormalizeException(&exc_type, &exc_value, &exc_traceback); PyObject *last_send = POP(); Py_DECREF(last_send); - PyObject *receiver = TOP(); - PyObject *retval = throw(tstate, exc_value, receiver); + PyObject *yieldfrom = TOP(); + PyObject *throw; + int found = _PyObject_LookupAttr(yieldfrom, &_Py_ID(throw), &throw); + if (found < 0) { + if (PyErr_GivenExceptionMatches(exc_value, PyExc_GeneratorExit)) + { + PyErr_Clear(); + _PyErr_Restore(tstate, exc_type, exc_value, exc_traceback); + } + goto error; + } + if (found == 0) { + _PyErr_Restore(tstate, exc_type, exc_value, exc_traceback); + goto error; + } + PyObject *retval = PyObject_CallFunctionObjArgs( + throw, exc_type, exc_value, exc_traceback, NULL); + Py_DECREF(throw); + Py_DECREF(exc_type); + Py_DECREF(exc_value); + Py_XDECREF(exc_traceback); if (retval) { PUSH(retval); DISPATCH(); @@ -2705,35 +2699,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int if (_PyGen_FetchStopIterationValue(&TOP())) { goto error; } - Py_DECREF(receiver); + Py_DECREF(yieldfrom); JUMPBY(oparg); DISPATCH(); } TARGET(THROW_BACKWARD) { - assert(frame->is_entry); - assert(throwflag); - PyObject *exc_value = POP(); - PyObject *last_send = POP(); - Py_DECREF(last_send); - PyObject *receiver = TOP(); - PyObject *retval = throw(tstate, exc_value, receiver); - if (retval) { - PUSH(retval); - DISPATCH(); - } - if (tstate->c_tracefunc && - _PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) - { - call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, - frame); - } - if (_PyGen_FetchStopIterationValue(&TOP())) { - goto error; - } - Py_DECREF(receiver); - JUMPBY(-oparg); - DISPATCH(); + // No interrupts! + oparg = -oparg; + JUMP_TO_INSTRUCTION(THROW_FORWARD); } TARGET(ASYNC_GEN_WRAP) { From cdf7fc4bd13f2fb3cc8de3013969b6ee8143bc8c Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Mon, 15 Aug 2022 08:53:34 -0700 Subject: [PATCH 5/6] Fix some yield from and await tracebacks --- Python/ceval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 9e8aeb1658a025..1f4efe294cf84e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2674,11 +2674,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int PyErr_Clear(); _PyErr_Restore(tstate, exc_type, exc_value, exc_traceback); } - goto error; + goto exception_unwind; } if (found == 0) { _PyErr_Restore(tstate, exc_type, exc_value, exc_traceback); - goto error; + goto exception_unwind; } PyObject *retval = PyObject_CallFunctionObjArgs( throw, exc_type, exc_value, exc_traceback, NULL); @@ -2697,7 +2697,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int frame); } if (_PyGen_FetchStopIterationValue(&TOP())) { - goto error; + goto exception_unwind; } Py_DECREF(yieldfrom); JUMPBY(oparg); From 5b13d12d8218e2e57c24a1d7ae379135e150fd95 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Tue, 16 Aug 2022 22:55:39 -0700 Subject: [PATCH 6/6] Don't RESUME after throw --- Lib/test/test_dis.py | 21 +++++++++------------ Python/ceval.c | 2 +- Python/compile.c | 1 - 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 935780788efb33..7d6ef5621e3cf4 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -508,29 +508,26 @@ async def _asyncwith(c): RETURN_VALUE %3d >> THROW_BACKWARD 23 (to 114) - RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 28 (to 16) - >> THROW_BACKWARD 9 (to 92) - RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 14 (to 50) + JUMP_BACKWARD_NO_INTERRUPT 27 (to 16) + >> THROW_BACKWARD 8 (to 88) + JUMP_BACKWARD_NO_INTERRUPT 12 (to 50) >> PUSH_EXC_INFO WITH_EXCEPT_START GET_AWAITABLE 2 LOAD_CONST 0 (None) - >> SEND 6 (to 100) + >> SEND 5 (to 94) >> YIELD_VALUE 6 RESUME 3 - >> JUMP_BACKWARD_NO_INTERRUPT 4 (to 86) - >> THROW_FORWARD 2 (to 100) - RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 6 (to 88) - >> POP_JUMP_FORWARD_IF_TRUE 1 (to 104) + >> JUMP_BACKWARD_NO_INTERRUPT 4 (to 82) + >> THROW_FORWARD 1 (to 94) + JUMP_BACKWARD_NO_INTERRUPT 5 (to 84) + >> POP_JUMP_FORWARD_IF_TRUE 1 (to 98) RERAISE 2 >> POP_TOP POP_EXCEPT POP_TOP POP_TOP - JUMP_BACKWARD 28 (to 58) + JUMP_BACKWARD 25 (to 58) >> COPY 3 POP_EXCEPT RERAISE 1 diff --git a/Python/ceval.c b/Python/ceval.c index 1f4efe294cf84e..a0a532ce83b903 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2671,7 +2671,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int if (found < 0) { if (PyErr_GivenExceptionMatches(exc_value, PyExc_GeneratorExit)) { - PyErr_Clear(); + _PyErr_Clear(tstate); _PyErr_Restore(tstate, exc_type, exc_value, exc_traceback); } goto exception_unwind; diff --git a/Python/compile.c b/Python/compile.c index 75d4fdec2be09a..cd7be2e885cbd7 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1984,7 +1984,6 @@ compiler_add_yield_from(struct compiler *c, int await) USE_LABEL(c, thrown); ADDOP_JUMP(c, THROW, stop); - ADDOP_I(c, RESUME, await ? 3 : 2); ADDOP_JUMP(c, JUMP_NO_INTERRUPT, yield); USE_LABEL(c, stop);