We evaluate once a day how well Python source code can be recovered from programs compiled with Nuitka.
We measure whether the recovered source code passes the original's tests, and how its lines and cognitive complexity differ from the original's. We also compare the pseudo-C output of our decompiler with that of angr alone.
We replace the original function in its program with the recovered one, and check that it returns the same results as the original on the dataset's test inputs. The tests of each function must finish within 30 seconds. A dataset's name links to the list of its functions.
| dataset | pass | fail | error | total |
|---|---|---|---|---|
| dev/cruxeval | 725 | 0 | 63 | 788 |
| dev/humaneval_plus | 158 | 0 | 8 | 166 |
| dev/thealgorithms | 1697 | 0 | 398 | 2095 |
| dev total | 2580 | 0 | 469 | 3049 |
| holdout/mbpp_plus | 166 | 0 | 37 | 203 |
| holdout total | 166 | 0 | 37 | 203 |
For lines and cognitive complexity, a smaller value means code that is easier to read. avg: per function. sum: over the functions of the dataset. Cognitive complexity counts branches and loops, weighted by how deeply they nest.
We compare the original source code with the recovered source code, over the passed functions only.
| dataset | pass | avg lines (original -> ours) | avg cognitive complexity (original -> ours) |
|---|---|---|---|
| dev/cruxeval | 725 | 5.3 -> 6.2 | 1.8 -> 1.9 |
| dev/humaneval_plus | 158 | 17.1 -> 18.9 | 3.1 -> 3.9 |
| dev/thealgorithms | 1697 | 20.1 -> 21.9 | 3.3 -> 4.4 |
| dev total | 2580 | 15.8 -> 17.3 | 2.9 -> 3.7 |
| holdout/mbpp_plus | 166 | 3.5 -> 4.9 | 1.3 -> 1.5 |
| holdout total | 166 | 3.5 -> 4.9 | 1.3 -> 1.5 |
We generate pseudo-C source code from the intermediate representation our decompiler uses internally, and compare it with angr's output, over every function that has both, passed or not. angr has no handling for Nuitka, so this measures how much boilerplate our additions remove and how many Python operations they turn back from C calls.
| dataset | number of functions | boilerplate share of angr | sum boilerplate (angr -> ours) | sum python ops (angr -> ours) | avg lines (angr -> ours) | avg cognitive complexity (angr -> ours) |
|---|---|---|---|---|---|---|
| dev/cruxeval | 787 | 85.4% | 45705 -> 236 | 7843 -> 63 | 269.4 -> 28.2 | 179.6 -> 3.8 |
| dev/humaneval_plus | 166 | 83.4% | 10802 -> 5 | 2157 -> 11 | 305.3 -> 32.4 | 232.9 -> 5.3 |
| dev/thealgorithms | 2085 | 80.4% | 168099 -> 1395 | 40856 -> 625 | 421.4 -> 44.7 | 446.6 -> 8.4 |
| dev total | 3038 | 81.5% | 224606 -> 1636 | 50856 -> 699 | 375.7 -> 39.7 | 365.8 -> 7.0 |
| holdout/mbpp_plus | 203 | 86.2% | 9289 -> 2 | 1484 -> 32 | 202.2 -> 22.0 | 145.0 -> 2.3 |
| holdout total | 203 | 86.2% | 9289 -> 2 | 1484 -> 32 | 202.2 -> 22.0 | 145.0 -> 2.3 |
Each dot stands for one passed function. The horizontal axis is the value of the original function and the vertical axis that of the recovered one. The ticks are not evenly spaced: a square-root scale makes changes among small values easier to see. For example, a dot on "same" means the recovered value equals the original, and a dot on "2x" means it doubled. Click a dot to see the details of the function.
The original Python source code belongs to the authors of each dataset and is under its licenses.