One of the ways that this decompiler is different from most of the others I've seen is in its extensive testing.
After a lot of experimentation, the most effective way to do this is to write a little Python test program for a particular feature you want to test decomplation on. For example, suppose I want to see
if the Python 3 format strings of the form f'{xxx}'
and f'{xxx!s}'
are handled correctly.
First I'll write come code using asserts. Like this:
abc = 'def'
assert 'defdef' == f'{abc}{abc!s}'
Above it is pretty easy to figure out the assert statement. But in many cases it is too difficult for me to do, so I will let Python do the work for me instead:
abc = 'def'
print(f'{abc}{abc!s}')
After running this, Python will tell me what the right result is.
Next put the test somwhere under test/simple_source
. Let's say you are using Python 3.6 and this is a bug in version 3.6 or later. (Format specifiers were added around Python 3.6). Here, test/simple_source/bug36
might be a good directory to add this under.
The number at the front of the test-case names is just to influence where in the testing the test should be. Simple and fast tests start 01
and very complicated ones start with a high number like 10
. That way when testing, we do the easy cases before the difficult one.
Next cd test; ./add-test.py --run simple-source/bug36/01_fstring.py
This will compile the test and add it to the corresponding bytecode_N.N_run
directory. Here N.N
would be 3.6
.
Add the test into git using git add simple_source/bug36/01_fstring.py
and git add -f tests/bytecode-3.6-run/01_fstring.py
.
Finally to test run make check-bytecode-3.6
.
Here is what this does. Whenever you run make check
and you are running a Python 3.6 interpreter,
- the bytecode in
test/bytecode_3.6_run
is decompiled - the decompiled bytecode is run to make sure not only is the program syntactically correct, but that the code work in the ways that the asserts check.