Skip to content

Commit

Permalink
Name unmangle; docstring fix; try-except-finally fix; syntax error fix
Browse files Browse the repository at this point in the history
- Unmangles names mangled by python's double-underscore name mangling
- Rewrote the docstring printing
- Improves printing of try-except-finally
- Fixes a syntax error from listcomprehensions inside conditionals like:
     [a for b in c] if d else e
  • Loading branch information
wibiti committed Jan 24, 2012
1 parent 9b0c0da commit 63c927b
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 146 deletions.
6 changes: 3 additions & 3 deletions README
@@ -1,7 +1,7 @@

uncompyle2 -- A Python byte-code decompiler
0.11
2011-1-9
uncompyle2 -- A Python 2.7 byte-code decompiler
0.12
2012-1-23

Introduction
------------
Expand Down
22 changes: 11 additions & 11 deletions uncompyle2/Parser.py
Expand Up @@ -95,7 +95,7 @@ def p_list_comprehension(self, args):
_come_from ::=
list_for ::= expr _for designator list_iter JUMP_BACK COME_FROM
list_for ::= expr _for designator list_iter JUMP_BACK
list_if ::= expr jmp_false list_iter
list_if_not ::= expr jmp_true list_iter
Expand All @@ -111,7 +111,7 @@ def p_setcomp(self, args):
stmt ::= setcomp_func
setcomp_func ::= BUILD_SET_0 LOAD_FAST FOR_ITER designator comp_iter
JUMP_BACK COME_FROM RETURN_VALUE RETURN_LAST
JUMP_BACK RETURN_VALUE RETURN_LAST
comp_iter ::= comp_if
comp_iter ::= comp_ifnot
Expand All @@ -126,7 +126,7 @@ def p_setcomp(self, args):
comp_if ::= expr jmp_false comp_iter
comp_ifnot ::= expr jmp_true comp_iter
comp_for ::= expr _for designator comp_iter JUMP_BACK COME_FROM
comp_for ::= expr _for designator comp_iter JUMP_BACK
'''


Expand All @@ -138,7 +138,7 @@ def p_genexpr(self, args):
stmt ::= genexpr_func
genexpr_func ::= LOAD_FAST FOR_ITER designator comp_iter JUMP_BACK COME_FROM
genexpr_func ::= LOAD_FAST FOR_ITER designator comp_iter JUMP_BACK
'''


Expand All @@ -149,7 +149,7 @@ def p_dictcomp(self, args):
stmt ::= dictcomp_func
dictcomp_func ::= BUILD_MAP LOAD_FAST FOR_ITER designator
comp_iter JUMP_BACK COME_FROM RETURN_VALUE RETURN_LAST
comp_iter JUMP_BACK RETURN_VALUE RETURN_LAST
'''

Expand Down Expand Up @@ -587,24 +587,24 @@ def p_grammar(self, args):
forstmt ::= SETUP_LOOP expr _for designator
l_stmts_opt _jump_back
COME_FROM POP_BLOCK COME_FROM
POP_BLOCK COME_FROM
forstmt ::= SETUP_LOOP expr _for designator
return_stmts
COME_FROM POP_BLOCK COME_FROM
POP_BLOCK COME_FROM
forelsestmt ::= SETUP_LOOP expr _for designator
l_stmts_opt _jump_back
COME_FROM POP_BLOCK stmts COME_FROM
POP_BLOCK stmts COME_FROM
forelsestmt ::= SETUP_LOOP expr _for designator
return_stmts _come_from
COME_FROM POP_BLOCK stmts COME_FROM
POP_BLOCK stmts COME_FROM
forelselaststmt ::= SETUP_LOOP expr _for designator
l_stmts_opt _jump_back
COME_FROM POP_BLOCK c_stmts COME_FROM
POP_BLOCK c_stmts COME_FROM
forelselaststmt ::= SETUP_LOOP expr _for designator
return_stmts _come_from
COME_FROM POP_BLOCK c_stmts COME_FROM
POP_BLOCK c_stmts COME_FROM
return_stmts ::= return_stmt
return_stmts ::= _stmts return_stmt
Expand Down
112 changes: 24 additions & 88 deletions uncompyle2/Scanner.py
Expand Up @@ -48,11 +48,11 @@ class Code:
This is similar to the original code object, but additionally
the diassembled code is stored in the attribute '_tokens'.
"""
def __init__(self, co, scanner):
def __init__(self, co, scanner, classname=None):
for i in dir(co):
if i.startswith('co_'):
setattr(self, i, getattr(co, i))
self._tokens, self._customize = scanner.disassemble(co)
self._tokens, self._customize = scanner.disassemble(co, classname)

class Scanner:
def __init__(self, version):
Expand Down Expand Up @@ -83,7 +83,7 @@ def setTokenClass(self, tokenClass):
def resetTokenClass(self):
self.setTokenClass(Token)

def disassemble(self, co):
def disassemble(self, co, classname=None):
"""
Disassemble a code object, returning a list of 'Token'.
Expand Down Expand Up @@ -131,9 +131,23 @@ def disassemble(self, co):

cf = self.find_jump_targets(code)

if classname:
classname = '_' + classname.lstrip('_') + '__'
def unmangle(name):
if name.startswith(classname) and name[-2:] != '__':
return name[len(classname) - 2:]
return name

free = [ unmangle(name) for name in (co.co_cellvars + co.co_freevars) ]
names = [ unmangle(name) for name in co.co_names ]
varnames = [ unmangle(name) for name in co.co_varnames ]
else:
free = co.co_cellvars + co.co_freevars
names = co.co_names
varnames = co.co_varnames

i = 0
extended_arg = 0
free = None
while i < n:
offset = i
k = 0
Expand All @@ -143,9 +157,6 @@ def disassemble(self, co):
offset="%s_%d" % (offset, k) ))
k += 1

# if self.__end_if_line.has_key(offset):
# rv.append(Token('END_IF_LINE', None, repr(self.__end_if_line[offset]),
# offset="%s_" % offset ))
c = code[i]
op = ord(c)
opname = dis.opname[op]
Expand Down Expand Up @@ -179,23 +190,19 @@ def disassemble(self, co):
else:
pattr = const
elif op in dis.hasname:
pattr = co.co_names[oparg]
pattr = names[oparg]
elif op in dis.hasjrel:
pattr = repr(i + oparg)
elif op in dis.hasjabs:
pattr = repr(oparg)
elif op in dis.haslocal:
pattr = co.co_varnames[oparg]
pattr = varnames[oparg]
elif op in dis.hascompare:
pattr = dis.cmp_op[oparg]
elif op in dis.hasfree:
if free is None:
free = co.co_cellvars + co.co_freevars
pattr = free[oparg]

if opname == 'SET_LINENO':
continue
elif opname in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET', 'BUILD_SLICE',
if opname in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET', 'BUILD_SLICE',
'UNPACK_LIST', 'UNPACK_TUPLE', 'UNPACK_SEQUENCE',
'MAKE_FUNCTION', 'CALL_FUNCTION', 'MAKE_CLOSURE',
'CALL_FUNCTION_VAR', 'CALL_FUNCTION_KW',
Expand All @@ -211,10 +218,6 @@ def disassemble(self, co):
opname = '%s_%d' % (opname, oparg)
if opname not in ('BUILD_SLICE_2', 'BUILD_SLICE_3'):
customize[opname] = oparg
# elif opname == 'POP_JUMP_IF_TRUE':
# target = self.__get_target(code, offset)
# if (dis.opname[ord(code[target])] == 'FOR_ITER') and (self.lines[offset] != self.lines[offset+3]):
# opname = 'CONTINUE_IF_TRUE'
elif opname == 'JUMP_ABSOLUTE':
target = self.__get_target(code, offset)
if target < offset:
Expand Down Expand Up @@ -406,48 +409,11 @@ def __next_except_jump(self, code, start, end, target):
start = jmp + 3
return None

def __list_comprehension(self, code, pos, op=None):
"""
Determine if there is a list comprehension structure starting at pos
"""
if op is None:
op = ord(code[pos])
if op != BUILD_LIST:
return 0
try:
elems = ord(code[pos+1]) + ord(code[pos+2])*256
codes = (op, elems, ord(code[pos+3]), ord(code[pos+4]))
except IndexError:
return 0
return (codes==(BUILD_LIST, 0, DUP_TOP, LOAD_ATTR))

def __ignore_if(self, code, pos):
"""
Return true if this 'if' is to be ignored.
"""

## If that was added by a while loop
if pos in self.__ignored_ifs:
return 1

# Check if we can test only for POP_TOP for this -Dan
# Maybe need to be done as above (skip SET_LINENO's)
if (ord(code[pos-3])==COMPARE_OP and
(ord(code[pos-2]) + ord(code[pos-1])*256)==EXCEPT_MATCH and
ord(code[pos+3])==POP_TOP and
ord(code[pos+4])==POP_TOP and
ord(code[pos+5])==POP_TOP and
ord(code[pos+6])==POP_TOP):
return 1 ## Exception match
return 0

def __fix_parent(self, code, target, parent):
"""Fix parent boundaries if needed"""
start = parent['start']
end = parent['end']
## Map the second start point for 'while 1:' in python 2.3+ to start
try: target = self.__while1[target]
except: pass

if target >= start or end-start < 3 or target not in self.__loops:
return
if ord(code[end-3])==JUMP_ABSOLUTE:
Expand Down Expand Up @@ -491,11 +457,6 @@ def __detect_structure(self, code, pos, op=None):

if op == SETUP_LOOP:
start = pos+3
# if ord(code[start])==JUMP_FORWARD:
## This is a while 1 (has a particular structure)
# start = self.__get_target(code, start, JUMP_FORWARD)
# start = self.__restrict_to_parent(start, parent)
# self.__while1[pos+3] = start ## map between the 2 start points
target = self.__get_target(code, pos, op)
end = self.__restrict_to_parent(target, parent)
if target != end:
Expand Down Expand Up @@ -530,7 +491,6 @@ def __detect_structure(self, code, pos, op=None):
(line_no, next_line_byte) = self.lines[pos]
test = self.prev[next_line_byte]
assert(test is not None)
self.__ignored_ifs.append(test)
test_target = self.__get_target(code, test)
if test_target > (jump_back+3):
jump_back = test_target
Expand All @@ -542,21 +502,6 @@ def __detect_structure(self, code, pos, op=None):
self.__structs.append({'type': loop_type + '-else',
'start': jump_back+3,
'end': end})
elif self.__list_comprehension(code, pos, op):
get_iter = self.__first_instr(code, pos+7, end, GET_ITER)
for_iter = self.__first_instr(code, get_iter, end, FOR_ITER)
assert(get_iter is not None and for_iter is not None)
start = get_iter+1
target = self.__get_target(code, for_iter, FOR_ITER)
end = self.__restrict_to_parent(target, parent)
jump_back = self.__last_instr(code, start, end, JUMP_ABSOLUTE,
start, False)
assert(jump_back is not None)
target = self.__get_target(code, jump_back, JUMP_ABSOLUTE)
start = self.__restrict_to_parent(target, parent)
self.__structs.append({'type': 'list-comprehension',
'start': start,
'end': jump_back})
elif op == SETUP_EXCEPT:
start = pos+3
target = self.__get_target(code, pos, op)
Expand Down Expand Up @@ -602,10 +547,8 @@ def __detect_structure(self, code, pos, op=None):
# if target != end:
# self.__fixed_jumps[jmp] = end
i = jmp+3
elif op in (POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE):

# if self.__ignore_if(code, pos):
# return
elif op in (POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE):
start = pos+3
target = self.__get_target(code, pos, op)
rtarget = self.__restrict_to_parent(target, parent)
Expand All @@ -624,8 +567,6 @@ def __detect_structure(self, code, pos, op=None):
if (ord(code[self.prev[target]]) in (JUMP_IF_FALSE_OR_POP, JUMP_IF_TRUE_OR_POP,
POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE)) and (target > pos):
self.__fixed_jumps[pos] = self.prev[target]
if op == POP_JUMP_IF_TRUE:
self.pjit_tgt[self.prev[target]] = True
return

#is this not at the end of a line
Expand Down Expand Up @@ -828,13 +769,8 @@ def find_jump_targets(self, code):
'start': 0,
'end': n-1}]
self.__loops = [] ## All loop entry points
self.__while1 = {} ## 'while 1:' in python 2.3+ has another start point
self.__fixed_jumps = {} ## Map fixed jumps to their real destination
self.__ignored_ifs = [] ## JUMP_IF_XXXX's we should ignore
# self.__end_if_line = {}
self.__jump_back_else = {}
self.pjit_tgt = {}
lastif = self.__last_instr(code, 0, n-1, [POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE], n-4)

targets = {}
i = 0
Expand All @@ -851,7 +787,7 @@ def find_jump_targets(self, code):


if label is None:
if op in hasjrel:
if op in hasjrel and op != FOR_ITER:
label = i + 3 + oparg
elif op in hasjabs:
if op in [JUMP_IF_FALSE_OR_POP, JUMP_IF_TRUE_OR_POP]:
Expand Down

0 comments on commit 63c927b

Please sign in to comment.