Skip to content
R. Bernstein edited this page Aug 9, 2019 · 3 revisions

I'm going to start documenting how I fix bugs in case others want to also do so. Issue 150 wasn't all that interesting, but still you might be able to get a sense of the tools and techniques used...

Here are the steps I have took to address issue #150

Step one is get a small test case and compile it. The test case provided in the issue was:

def func(a):
    if a:
        return True
        something_never_run()

To compile it one can use the compile-file.py program in python-uncompyle6/test/stdlib

   $ cd test/stdlib
   $ pyenv 2.7.14
   $ ./compile-file.py /tmp/issue-150.py
compiling /tmp/issue-150.py to /tmp/issue-150-2.7.pyc

Let's get detailed information in decomplation: assembly code, grammar reductions and the AST tree (which will fail below):

   $ ../../bin/uncompyle6 -agT /tmp/issue-150-2.7.pyc > /tmp/issue-150-bad.log 2>&1

Looking at /tmp/issue-150-bad.log we see:

L.  4:  10     assert_expr ::= expr (6)
L.  4:  10-16  call_stmt ::= expr POP_TOP (7)
L.  4:  10     stmt ::= call_stmt (7)
L.  3:   6-16  _stmts ::= _stmts stmt (7)
Reduce _stmts invalid by check
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- Stacks of completed symbols:
LOAD_FAST .
LOAD_FAST . FOR_ITER store comp_iter JUMP_BACK
LOAD_GLOBAL .

It so happens that uncompyle2 decompiles correctly. So let's run that and compare the grammar. The option that corresponds to t in uncompyle2 is --showast:

    $ uncompyle2 --showast /tmp/issue-150-2.7.pyc > /tmp/issue-150-good.log

Looking at the tree for that we see:

            c_stmts_opt
               c_stmts
                  _stmts
                     stmt

So see if that "invalid reduction" rule is right. I removed the check from uncompyle6/parsers/parse2.py and this then worked.

Looking at the git log for the change with SHA 1e324e0e8db0d97c1ea63c577805a7b00720b83d I read

scanner2.py: check that return stmt is last in list. (May change)

It looks to me then that this change was erroneous. In this commit you'll find that two Python 2.6 tests were added. To make sure those still work with the change. I ran inside test:

  make check-bytecode-2.6

But a full make check from the root uncompyle6 directory is done for final measure.

Lastly, we add a new test case for this particular code. Run inside test directory::

$ cp /tmp/issue-150.py simple_source/bug27+/05_deadcode.py
$ git add simple_source/bug27+/05_deadcode.py
$ ./add-test.py simple_source/bug27+/05_deadcode.py
$ git add -f bytecode_2.7/05_deadcode.pyc