From 08c87a467a0bd1d5f9d330ea8f4d28c237733dea Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Tue, 4 Jun 2024 22:55:23 +0100 Subject: [PATCH 1/4] gh-119786: remove devguide documentation which is duplicated in InteralDocs --- internals/interpreter.rst | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/internals/interpreter.rst b/internals/interpreter.rst index a53b6283c..39a17cb4b 100644 --- a/internals/interpreter.rst +++ b/internals/interpreter.rst @@ -178,38 +178,13 @@ Then the interpreter function (``_PyEval_EvalFrameDefault()``) returns ``NULL``. However, if an exception is raised in a ``try`` block, the interpreter must jump to the corresponding ``except`` or ``finally`` block. In 3.10 and before, there was a separate "block stack" which was used to keep track of nesting ``try`` blocks. -In 3.11, this mechanism has been replaced by a statically generated table, ``code->co_exceptiontable``. -The advantage of this approach is that entering and leaving a ``try`` block normally does not execute any code, making execution faster. -But of course, this table needs to be generated by the compiler, and decoded (by ``get_exception_handler``) when an exception happens. - -Exception table format ----------------------- - -The table is conceptually a list of records, each containing four variable-length integer fields (in a unique format, see below): - -- start: start of ``try`` block, in code units from the start of the bytecode -- length: size of the ``try`` block, in code units -- target: start of the first instruction of the ``except`` or ``finally`` block, in code units from the start of the bytecode -- depth_and_lasti: the low bit gives the "lasti" flag, the remaining bits give the stack depth - -The stack depth is used to clean up evaluation stack entries above this depth. -The "lasti" flag indicates whether, after stack cleanup, the instruction offset of the raising instruction should be pushed (as a ``PyLongObject *``). -For more information on the design, see :cpy-file:`Objects/exception_handling_notes.txt`. - -Each varint is encoded as one or more bytes. -The high bit (bit 7) is reserved for random access -- it is set for the first varint of a record. -The second bit (bit 6) indicates whether this is the last byte or not -- it is set for all but the last bytes of a varint. -The low 6 bits (bits 0-5) are used for the integer value, in big-endian order. - -To find the table entry (if any) for a given instruction offset, we can use bisection without decoding the whole table. -We bisect the raw bytes, at each probe finding the start of the record by scanning back for a byte with the high bit set, and then decode the first varint. -See ``get_exception_handler()`` in :cpy-file:`Python/ceval.c` for the exact code (like all bisection algorithms, the code is a bit subtle). +In 3.11, this mechanism has been replaced by a statically generated table, ``code->co_exceptiontable``, which is described in detail [here](https://github.com/python/cpython/blob/main/InternalDocs/exception_handling.md). The locations table ------------------- Whenever an exception is raised, we add a traceback entry to the exception. -The ``tb_lineno`` field of a traceback entry must be set to the line number of the instruction that raised it. +The ``tb_lineno`` field of a traceback entry is (lazily) set to the line number of the instruction that raised it. This field is computed from the locations table, ``co_linetable`` (this name is an understatement), using :c:func:`PyCode_Addr2Line`. This table has an entry for every instruction rather than for every ``try`` block, so a compact format is very important. From 6b870d770fc3119b02832b07eb3e6c29b337921e Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Wed, 5 Jun 2024 09:02:36 +0100 Subject: [PATCH 2/4] apply comments from review --- internals/interpreter.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internals/interpreter.rst b/internals/interpreter.rst index 39a17cb4b..a364ed8d5 100644 --- a/internals/interpreter.rst +++ b/internals/interpreter.rst @@ -178,7 +178,9 @@ Then the interpreter function (``_PyEval_EvalFrameDefault()``) returns ``NULL``. However, if an exception is raised in a ``try`` block, the interpreter must jump to the corresponding ``except`` or ``finally`` block. In 3.10 and before, there was a separate "block stack" which was used to keep track of nesting ``try`` blocks. -In 3.11, this mechanism has been replaced by a statically generated table, ``code->co_exceptiontable``, which is described in detail [here](https://github.com/python/cpython/blob/main/InternalDocs/exception_handling.md). +In 3.11, this mechanism has been replaced by a statically generated table, ``code->co_exceptiontable``, which +is described in detail in the +`internals documentation`. The locations table ------------------- From 4abbba1d823dd0fd0de02094bdc1d4cbdbc87912 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 5 Jun 2024 17:02:47 +0100 Subject: [PATCH 3/4] copy-file Co-authored-by: Ezio Melotti --- internals/interpreter.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internals/interpreter.rst b/internals/interpreter.rst index a364ed8d5..e4abcffef 100644 --- a/internals/interpreter.rst +++ b/internals/interpreter.rst @@ -178,9 +178,8 @@ Then the interpreter function (``_PyEval_EvalFrameDefault()``) returns ``NULL``. However, if an exception is raised in a ``try`` block, the interpreter must jump to the corresponding ``except`` or ``finally`` block. In 3.10 and before, there was a separate "block stack" which was used to keep track of nesting ``try`` blocks. -In 3.11, this mechanism has been replaced by a statically generated table, ``code->co_exceptiontable``, which -is described in detail in the -`internals documentation`. +In 3.11, this mechanism has been replaced by a statically generated table, ``code->co_exceptiontable``, +which is described in detail in the :cpy-file:`internals documentation `. The locations table ------------------- From 213066b9453698a39772d973e5326635c16d2a66 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 5 Jun 2024 17:32:56 +0100 Subject: [PATCH 4/4] fix link Co-authored-by: Ezio Melotti --- internals/interpreter.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internals/interpreter.rst b/internals/interpreter.rst index e4abcffef..e3d881ab6 100644 --- a/internals/interpreter.rst +++ b/internals/interpreter.rst @@ -179,7 +179,8 @@ Then the interpreter function (``_PyEval_EvalFrameDefault()``) returns ``NULL``. However, if an exception is raised in a ``try`` block, the interpreter must jump to the corresponding ``except`` or ``finally`` block. In 3.10 and before, there was a separate "block stack" which was used to keep track of nesting ``try`` blocks. In 3.11, this mechanism has been replaced by a statically generated table, ``code->co_exceptiontable``, -which is described in detail in the :cpy-file:`internals documentation `. +which is described in detail in the `internals documentation +`_. The locations table -------------------