Skip to main content

Objects/codeobject.c (part 3)

Source:

cpython 3.14 @ ab2d84fe1023/Objects/codeobject.c

This annotation covers line number mapping and position tables. See objects_codeobject2_detail for PyCode_New, constants/names/varnames layout, and objects_codeobject_detail for the code object struct and dis disassembly.

Map

LinesSymbolRole
1-100PyCode_Addr2LineMap bytecode offset to source line number
101-220_PyLineTable_*Decode the co_linetable delta-encoded line table
221-360co_positions()Iterator over (line, endline, col, endcol) for each instruction
361-500_PyCode_InitAddressRangeInitialize a PyCodeAddressRange for efficient sequential scanning
501-600_PyCode_CODEAccess the instruction array

Reading

PyCode_Addr2Line

// CPython: Objects/codeobject.c:680 PyCode_Addr2Line
int
PyCode_Addr2Line(PyCodeObject *co, int addrq)
{
/* Binary search or sequential scan of co_linetable to find
the line number for bytecode offset addrq. */
if (addrq < 0) return co->co_firstlineno;
PyCodeAddressRange bounds;
_PyCode_InitAddressRange(co, &bounds);
return _PyLineTable_ScanRange(&bounds, addrq) ? bounds.ar_line : -1;
}

PyCode_Addr2Line is used by the traceback machinery to show the source line number. It decodes the compressed co_linetable — a sequence of (bytecode_delta, line_delta) pairs.

co_linetable encoding

// CPython: Objects/codeobject.c:240 line table format
/* co_linetable is a sequence of entries, each 2 bytes:
byte 0: bytecode_delta (units of 2 bytes = 1 word)
byte 1: line_delta
Special values:
line_delta = 0x80: no line info (generated code)
bytecode_delta = 0: end-of-table
This is the PEP 626 "positions" table format from Python 3.10+.
*/

The table is compact: a typical function with 50 instructions compresses to ~100 bytes. The sequential scan _PyCode_InitAddressRange caches the current position for forward scans (common in traceback generation).

co_positions()

// CPython: Objects/codeobject.c:380 code_co_positions
static PyObject *
code_co_positions(PyCodeObject *co, PyObject *noargs)
{
/* Return an iterator yielding (line, endline, col, endcol)
for each instruction. Uses the positions table (co_linetable
in Python 3.11+, a separate table in 3.10). */
return new_lnotab_iterator(co);
}

code.co_positions() is used by the interpreter for SyntaxError caret display and by traceback for fine-grained highlighting. Each entry covers one instruction word (2 bytes).

_PyCode_CODE

// CPython: Objects/codeobject.c:540 _PyCode_CODE
static inline _Py_CODEUNIT *
_PyCode_CODE(PyCodeObject *co)
{
/* Return a pointer to the first instruction word.
co_code_adaptive is a mutable copy for specialization;
original opcodes are preserved in co_code_bytes. */
return (_Py_CODEUNIT *)co->co_code_adaptive;
}

_PyCode_CODE returns the mutable bytecode array. The adaptive specialization engine modifies this array in place. co_code_bytes (or co_code in older versions) holds the original unspecialized bytecode for dis.dis.

gopy notes

PyCode_Addr2Line is objects.CodeAddr2Line in objects/code.go. The line table is stored as objects.Code.LineTable []byte using the same delta encoding. co_positions() returns objects.CodePositionsIterator. _PyCode_CODE maps to objects.Code.Instructions []uint16.