Nuitka decompiler eval

We evaluate once a day how well Python source code can be recovered from programs compiled with Nuitka.

Nuitka
It is a compiler that turns Python into C, which a C compiler (GCC) then compiles into a binary.
Our decompiler
It extends the decompiler angr to recover Python source code from binaries 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.

Correctness evaluation

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.

dev / holdout
The functions are split into two sets. We develop the decompiler against dev; holdout is kept unseen during development, to show how it does on code not seen in development.
pass
The tests pass.
fail
A test fails or times out, or the output lacks the function.
error
The decompiler failed, timed out, or produced no Python.
datasetpassfailerrortotal
dev/cruxeval725063788
dev/humaneval_plus15808166
dev/thealgorithms169703982095
dev total258004693049
holdout/mbpp_plus166037203
holdout total166037203
passfailerror

Readability evaluation

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.

Python

We compare the original source code with the recovered source code, over the passed functions only.

datasetpassavg lines (original -> ours)avg cognitive complexity (original -> ours)
dev/cruxeval7255.3 -> 6.21.8 -> 1.9
dev/humaneval_plus15817.1 -> 18.93.1 -> 3.9
dev/thealgorithms169720.1 -> 21.93.3 -> 4.4
dev total258015.8 -> 17.32.9 -> 3.7
holdout/mbpp_plus1663.5 -> 4.91.3 -> 1.5
holdout total1663.5 -> 4.91.3 -> 1.5

Pseudo-C

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.

boilerplate
Calls for bookkeeping that CPython does and Python source code never shows (reference counting, frames, error propagation), plus memory writes.
python ops
Python operations (such as additions and attribute accesses) still left as calls in C form.
boilerplate share of angr
boilerplate / (boilerplate + python ops) in angr's pseudo-C.
datasetnumber of functionsboilerplate share of angrsum boilerplate (angr -> ours)sum python ops (angr -> ours)avg lines (angr -> ours)avg cognitive complexity (angr -> ours)
dev/cruxeval78785.4%45705 -> 2367843 -> 63269.4 -> 28.2179.6 -> 3.8
dev/humaneval_plus16683.4%10802 -> 52157 -> 11305.3 -> 32.4232.9 -> 5.3
dev/thealgorithms208580.4%168099 -> 139540856 -> 625421.4 -> 44.7446.6 -> 8.4
dev total303881.5%224606 -> 163650856 -> 699375.7 -> 39.7365.8 -> 7.0
holdout/mbpp_plus20386.2%9289 -> 21484 -> 32202.2 -> 22.0145.0 -> 2.3
holdout total20386.2%9289 -> 21484 -> 32202.2 -> 22.0145.0 -> 2.3

Original and recovered source code compared

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.

lines

devlines of the original and recovered functionsmedian 11 -> 13
holdoutlines of the original and recovered functionsmedian 2 -> 3

cognitive complexity

devcognitive complexity of the original and recovered functionsmedian 2 -> 2
holdoutcognitive complexity of the original and recovered functionsmedian 0 -> 0

Limitations

Datasets

The original Python source code belongs to the authors of each dataset and is under its licenses.

datasetsourcelicenses
cruxevalhttps://github.com/facebookresearch/cruxeval/tree/10d71b4ca99f2b6624bef9533ade5a0594ab7716MIT
humaneval_plushttps://github.com/evalplus/humanevalplus_release/tree/68cd26d53a0dec69f85eafe1f82a2a74155a2bd6Apache-2.0, MIT
mbpp_plushttps://github.com/evalplus/mbppplus_release/tree/64fc4195b858a17cdfdb3324f0baf37939144e14Apache-2.0, CC-BY-4.0
thealgorithmshttps://github.com/TheAlgorithms/Python/tree/f8084d92b2df4faecad57e0cd8c5ab2adbbf8b19MIT

Environment

dev

holdout