Skip to content

Commit c1fd52e

Browse files
committed
Add co_branches iterator
1 parent 0959666 commit c1fd52e

File tree

3 files changed

+94
-0
lines changed

3 files changed

+94
-0
lines changed

Include/internal/pycore_code.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,8 @@ extern int _Py_GetBaseOpcode(PyCodeObject *code, int offset);
590590

591591
extern int _PyInstruction_GetLength(PyCodeObject *code, int offset);
592592

593+
extern PyObject *_PyInstrumentation_BranchesIterator(PyCodeObject *code);
594+
593595
#ifdef __cplusplus
594596
}
595597
#endif

Objects/codeobject.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,6 +1277,7 @@ typedef struct {
12771277
} lineiterator;
12781278

12791279

1280+
12801281
static void
12811282
lineiter_dealloc(lineiterator *li)
12821283
{
@@ -2162,6 +2163,12 @@ code_linesiterator(PyCodeObject *code, PyObject *Py_UNUSED(args))
21622163
return (PyObject *)new_linesiterator(code);
21632164
}
21642165

2166+
static PyObject *
2167+
code_branchesiterator(PyCodeObject *code, PyObject *Py_UNUSED(args))
2168+
{
2169+
return _PyInstrumentation_BranchesIterator(code);
2170+
}
2171+
21652172
/*[clinic input]
21662173
@text_signature "($self, /, **changes)"
21672174
code.replace
@@ -2302,6 +2309,7 @@ code__varname_from_oparg_impl(PyCodeObject *self, int oparg)
23022309
static struct PyMethodDef code_methods[] = {
23032310
{"__sizeof__", (PyCFunction)code_sizeof, METH_NOARGS},
23042311
{"co_lines", (PyCFunction)code_linesiterator, METH_NOARGS},
2312+
{"co_branches", (PyCFunction)code_branchesiterator, METH_NOARGS},
23052313
{"co_positions", (PyCFunction)code_positionsiterator, METH_NOARGS},
23062314
CODE_REPLACE_METHODDEF
23072315
CODE__VARNAME_FROM_OPARG_METHODDEF

Python/instrumentation.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2877,3 +2877,87 @@ _PyMonitoring_RegisterCallback(int tool_id, int event_id, PyObject *obj)
28772877
}
28782878
return res;
28792879
}
2880+
2881+
/* Branch Iterator */
2882+
2883+
typedef struct {
2884+
PyObject_HEAD
2885+
PyCodeObject *bi_code;
2886+
int bi_offset;
2887+
} branchesiterator;
2888+
2889+
PyObject *int_triple(int a, int b, int c) {
2890+
PyObject *obja = PyLong_FromLong(a);
2891+
PyObject *objb = NULL;
2892+
PyObject *objc = NULL;
2893+
if (obja == NULL) {
2894+
goto error;
2895+
}
2896+
objb = PyLong_FromLong(b);
2897+
if (objb == NULL) {
2898+
goto error;
2899+
}
2900+
objc = PyLong_FromLong(c);
2901+
if (objc == NULL) {
2902+
goto error;
2903+
}
2904+
PyObject *array[3] = { obja, objb, objc };
2905+
return _PyTuple_FromArraySteal(array, 3);
2906+
error:
2907+
Py_XDECREF(obja);
2908+
Py_XDECREF(objb);
2909+
Py_XDECREF(objc);
2910+
return NULL;
2911+
}
2912+
2913+
static PyObject *
2914+
branchesiter_next(branchesiterator *bi)
2915+
{
2916+
int offset = bi->bi_offset;
2917+
while (offset < Py_SIZE(bi->bi_code)) {
2918+
_Py_CODEUNIT inst = _PyCode_CODE(bi->bi_code)[offset];
2919+
int next_offset = offset + _PyInstruction_GetLength(bi->bi_code, offset);
2920+
int event = EVENT_FOR_OPCODE[inst.op.code];
2921+
if (event == PY_MONITORING_EVENT_BRANCH_TAKEN) {
2922+
/* Skip NOT_TAKEN */
2923+
int not_taken = next_offset + 1;
2924+
bi->bi_offset = not_taken;
2925+
return int_triple(offset*2, not_taken*2, (next_offset + inst.op.arg)*2);
2926+
}
2927+
offset = next_offset;
2928+
}
2929+
return NULL;
2930+
}
2931+
2932+
static void
2933+
branchesiter_dealloc(branchesiterator *bi)
2934+
{
2935+
Py_DECREF(bi->bi_code);
2936+
PyObject_Free(bi);
2937+
}
2938+
2939+
PyTypeObject _PyBranchesIterator = {
2940+
PyVarObject_HEAD_INIT(&PyType_Type, 0)
2941+
"line_iterator", /* tp_name */
2942+
sizeof(branchesiterator), /* tp_basicsize */
2943+
0, /* tp_itemsize */
2944+
/* methods */
2945+
.tp_dealloc = (destructor)branchesiter_dealloc,
2946+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
2947+
.tp_iter = PyObject_SelfIter,
2948+
.tp_iternext = (iternextfunc)branchesiter_next,
2949+
.tp_free = PyObject_Del,
2950+
};
2951+
2952+
PyObject *
2953+
_PyInstrumentation_BranchesIterator(PyCodeObject *code)
2954+
{
2955+
2956+
branchesiterator *bi = (branchesiterator *)PyType_GenericAlloc(&_PyBranchesIterator, 0);
2957+
if (bi == NULL) {
2958+
return NULL;
2959+
}
2960+
bi->bi_code = (PyCodeObject*)Py_NewRef(code);
2961+
bi->bi_offset = 0;
2962+
return (PyObject *)bi;
2963+
}

0 commit comments

Comments
 (0)