rocky / python-uncompyle6

A cross-version Python bytecode decompiler
GNU General Public License v3.0
3.74k stars 408 forks source link

Error when decompiling loop inside if-else #322

Open ghost opened 4 years ago

ghost commented 4 years ago

I was trying to decompile a pyc that I had, but it had a few errors. I managed to manually decompile it though and narrowed it down to code that looks like this:

def func(val):
    if val == 0:
        while True:
            pass
    else:
        pass

When I compile this with python3 -m compileall . and then attempt to decompile, I get a 'parse error':

# uncompyle6 version 3.7.2
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.7.7 (default, Jun 28 2020, 13:00:53) 
# [GCC 9.3.0]
# Embedded file name: ./code.py
# Compiled at: 2020-06-28 16:30:43
# Size of source mod 2**32: 92 bytes
Instruction context:

 L.   4         8  JUMP_BACK             8  'to 8'
->                10  JUMP_FORWARD         12  'to 12'
                12_0  COME_FROM            10  '10'

def func--- This code section failed: ---

 L.   2         0  LOAD_FAST                'val'
                2  LOAD_CONST               0
                4  COMPARE_OP               ==
                6  POP_JUMP_IF_FALSE    12  'to 12'

 L.   4         8  JUMP_BACK             8  'to 8'
               10  JUMP_FORWARD         12  'to 12'
             12_0  COME_FROM            10  '10'
             12_1  COME_FROM             6  '6'

Parse error at or near `JUMP_FORWARD' instruction at offset 10

# file __pycache__/code.cpython-38.pyc
# Deparsing stopped due to parse error

Here is the disassembly of that code:

  1           0 LOAD_CONST               0 (<code object func at 0x7ffac2d20b30, file "code.py", line 1>)
              2 LOAD_CONST               1 ('func')
              4 MAKE_FUNCTION            0
              6 STORE_NAME               0 (func)
              8 LOAD_CONST               2 (None)
             10 RETURN_VALUE

Disassembly of <code object func at 0x7ffac2d20b30, file "code.py", line 1>:
  2           0 LOAD_FAST                0 (val)
              2 LOAD_CONST               1 (0)
              4 COMPARE_OP               2 (==)
              6 POP_JUMP_IF_FALSE       12

  4     >>    8 JUMP_ABSOLUTE            8
             10 JUMP_FORWARD             0 (to 12)

  6     >>   12 LOAD_CONST               0 (None)
             14 RETURN_VALUE

Hopefully this issue can be fixed!

rocky commented 4 years ago

This has been addressed by https://github.com/rocky/python-decompile3/commit/a34740ad9b7f20d6bcf58234e1611240e14ecdfb however the change there needs to be applied here. I'm hoping someone else will put in a PR for that.

Handling on 3.8 will need additional work.

byehack commented 4 years ago
while True:
    pass

also this is not work. without inside function or if-else

rocky commented 3 years ago
while True:
    pass

is a little bit silly,

  2:     >>    0 JUMP_ABSOLUTE        (to 0)

but if one wanted to handle that, you could probably write a parse custom rule for this.

It's not something though that I feel particularly motivated to work on. However should I need to do so in the future, maybe this will serve as a reminder for what approach to take.