You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
Bug in 3.x detecting "if" structure and ...
scanner3.py: bug in 3.x detecting "if" structure Make scanner2.py look more like scanner3.py verify.py: add weak-verify which tests Pytyon syntax, but not code
This commit is contained in:
BIN
test/bytecode_3.4/04_aug_assign.pyc
Normal file
BIN
test/bytecode_3.4/04_aug_assign.pyc
Normal file
Binary file not shown.
11
test/simple_source/bug33/04_aug_assign.py
Normal file
11
test/simple_source/bug33/04_aug_assign.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# From python 3.4 difflib
|
||||||
|
# Bug in 3.x is combining bestsize +=1 with while condition
|
||||||
|
bestsize = 5
|
||||||
|
b = [2]
|
||||||
|
def isbjunk(x):
|
||||||
|
return False
|
||||||
|
|
||||||
|
while 1 < bestsize and \
|
||||||
|
not isbjunk(b[0]) and \
|
||||||
|
b[0]:
|
||||||
|
bestsize += 1
|
@@ -111,11 +111,13 @@ if __name__ == '__main__':
|
|||||||
test_options_keys = list(test_options.keys())
|
test_options_keys = list(test_options.keys())
|
||||||
test_options_keys.sort()
|
test_options_keys.sort()
|
||||||
opts, args = getopt.getopt(sys.argv[1:], '',
|
opts, args = getopt.getopt(sys.argv[1:], '',
|
||||||
['start-with=', 'verify', 'all', ] \
|
['start-with=', 'verify', 'weak-verify', 'all', ] \
|
||||||
+ test_options_keys )
|
+ test_options_keys )
|
||||||
for opt, val in opts:
|
for opt, val in opts:
|
||||||
if opt == '--verify':
|
if opt == '--verify':
|
||||||
do_verify = True
|
do_verify = True
|
||||||
|
if opt == '--weak-verify':
|
||||||
|
do_verify = 'weak'
|
||||||
elif opt == '--start-with':
|
elif opt == '--start-with':
|
||||||
start_with = val
|
start_with = val
|
||||||
elif opt[2:] in test_options_keys:
|
elif opt[2:] in test_options_keys:
|
||||||
|
@@ -147,8 +147,9 @@ def main(in_base, out_base, files, codes, outfile=None,
|
|||||||
if outfile:
|
if outfile:
|
||||||
outstream.close()
|
outstream.close()
|
||||||
if do_verify:
|
if do_verify:
|
||||||
|
weak_verify = do_verify == 'weak'
|
||||||
try:
|
try:
|
||||||
msg = verify.compare_code_with_srcfile(infile, outfile)
|
msg = verify.compare_code_with_srcfile(infile, outfile, weak_verify=weak_verify)
|
||||||
if not outfile:
|
if not outfile:
|
||||||
if not msg:
|
if not msg:
|
||||||
print('\n# okay decompiling %s' % infile)
|
print('\n# okay decompiling %s' % infile)
|
||||||
|
38
uncompyle6/scanners/scanner2.py
Executable file → Normal file
38
uncompyle6/scanners/scanner2.py
Executable file → Normal file
@@ -438,7 +438,7 @@ class Scanner2(scan.Scanner):
|
|||||||
elif op in self.setup_ops:
|
elif op in self.setup_ops:
|
||||||
count_SETUP_ += 1
|
count_SETUP_ += 1
|
||||||
|
|
||||||
def detect_structure(self, pos, op=None):
|
def detect_structure(self, pos, op):
|
||||||
'''
|
'''
|
||||||
Detect type of block structures and their boundaries to fix optimized jumps
|
Detect type of block structures and their boundaries to fix optimized jumps
|
||||||
in python2.3+
|
in python2.3+
|
||||||
@@ -447,26 +447,23 @@ class Scanner2(scan.Scanner):
|
|||||||
# TODO: check the struct boundaries more precisely -Dan
|
# TODO: check the struct boundaries more precisely -Dan
|
||||||
|
|
||||||
code = self.code
|
code = self.code
|
||||||
# Ev remove this test and make op a mandatory argument -Dan
|
|
||||||
if op is None:
|
|
||||||
op = code[pos]
|
|
||||||
|
|
||||||
# Detect parent structure
|
# Detect parent structure
|
||||||
parent = self.structs[0]
|
parent = self.structs[0]
|
||||||
start = parent['start']
|
start = parent['start']
|
||||||
end = parent['end']
|
end = parent['end']
|
||||||
for s in self.structs:
|
for struct in self.structs:
|
||||||
_start = s['start']
|
_start = struct['start']
|
||||||
_end = s['end']
|
_end = struct['end']
|
||||||
if (_start <= pos < _end) and (_start >= start and _end <= end):
|
if (_start <= pos < _end) and (_start >= start and _end <= end):
|
||||||
start = _start
|
start = _start
|
||||||
end = _end
|
end = _end
|
||||||
parent = s
|
parent = struct
|
||||||
|
|
||||||
if op == self.opc.SETUP_LOOP:
|
if op == self.opc.SETUP_LOOP:
|
||||||
|
|
||||||
# We categorize loop types: 'for', 'while', 'while 1' with
|
# We categorize loop types: 'for', 'while', 'while 1' with
|
||||||
# possibly suffices '-loop' and '-else'
|
# possibly suffixes '-loop' and '-else'
|
||||||
# Try to find the jump_back instruction of the loop.
|
# Try to find the jump_back instruction of the loop.
|
||||||
# It could be a return instruction.
|
# It could be a return instruction.
|
||||||
|
|
||||||
@@ -803,30 +800,31 @@ class Scanner2(scan.Scanner):
|
|||||||
self.return_end_ifs = set()
|
self.return_end_ifs = set()
|
||||||
|
|
||||||
targets = {}
|
targets = {}
|
||||||
for i in self.op_range(0, n):
|
for offset in self.op_range(0, n):
|
||||||
op = self.code[i]
|
op = self.code[offset]
|
||||||
|
|
||||||
# Determine structures and fix jumps in Python versions
|
# Determine structures and fix jumps in Python versions
|
||||||
# since 2.3
|
# since 2.3
|
||||||
self.detect_structure(i, op)
|
self.detect_structure(offset, op)
|
||||||
|
|
||||||
if op >= self.opc.HAVE_ARGUMENT:
|
if op >= self.opc.HAVE_ARGUMENT:
|
||||||
label = self.fixed_jumps.get(i)
|
label = self.fixed_jumps.get(offset)
|
||||||
oparg = self.get_argument(i)
|
oparg = self.get_argument(offset)
|
||||||
|
|
||||||
if label is None:
|
if label is None:
|
||||||
if op in self.opc.hasjrel and op != self.opc.FOR_ITER:
|
if op in self.opc.hasjrel and op != self.opc.FOR_ITER:
|
||||||
label = i + 3 + oparg
|
label = offset + 3 + oparg
|
||||||
elif self.version == 2.7 and op in self.opc.hasjabs:
|
elif self.version == 2.7 and op in self.opc.hasjabs:
|
||||||
if op in (self.opc.JUMP_IF_FALSE_OR_POP,
|
if op in (self.opc.JUMP_IF_FALSE_OR_POP,
|
||||||
self.opc.JUMP_IF_TRUE_OR_POP):
|
self.opc.JUMP_IF_TRUE_OR_POP):
|
||||||
if (oparg > i):
|
if (oparg > offset):
|
||||||
label = oparg
|
label = oparg
|
||||||
|
|
||||||
if label is not None and label != -1:
|
if label is not None and label != -1:
|
||||||
targets[label] = targets.get(label, []) + [i]
|
targets[label] = targets.get(label, []) + [offset]
|
||||||
elif op == self.opc.END_FINALLY and i in self.fixed_jumps:
|
elif op == self.opc.END_FINALLY and offset in self.fixed_jumps:
|
||||||
label = self.fixed_jumps[i]
|
label = self.fixed_jumps[offset]
|
||||||
targets[label] = targets.get(label, []) + [i]
|
targets[label] = targets.get(label, []) + [offset]
|
||||||
return targets
|
return targets
|
||||||
|
|
||||||
# FIXME: combine with scanner3.py code and put into scanner.py
|
# FIXME: combine with scanner3.py code and put into scanner.py
|
||||||
|
@@ -415,7 +415,7 @@ class Scanner3(Scanner):
|
|||||||
|
|
||||||
if label is None:
|
if label is None:
|
||||||
if op in op3.hasjrel and op != self.opc.FOR_ITER:
|
if op in op3.hasjrel and op != self.opc.FOR_ITER:
|
||||||
label = offset + self.op_size(op) + oparg
|
label = offset + 3 + oparg
|
||||||
elif op in op3.hasjabs:
|
elif op in op3.hasjabs:
|
||||||
if op in self.jump_if_pop:
|
if op in self.jump_if_pop:
|
||||||
if oparg > offset:
|
if oparg > offset:
|
||||||
@@ -547,6 +547,12 @@ class Scanner3(Scanner):
|
|||||||
parent = struct
|
parent = struct
|
||||||
|
|
||||||
if op == self.opc.SETUP_LOOP:
|
if op == self.opc.SETUP_LOOP:
|
||||||
|
|
||||||
|
# We categorize loop types: 'for', 'while', 'while 1' with
|
||||||
|
# possibly suffixes '-loop' and '-else'
|
||||||
|
# Try to find the jump_back instruction of the loop.
|
||||||
|
# It could be a return instruction.
|
||||||
|
|
||||||
start = offset+3
|
start = offset+3
|
||||||
target = self.get_target(offset)
|
target = self.get_target(offset)
|
||||||
end = self.restrict_to_parent(target, parent)
|
end = self.restrict_to_parent(target, parent)
|
||||||
@@ -679,8 +685,7 @@ class Scanner3(Scanner):
|
|||||||
self.fixed_jumps[offset] = fix or match[-1]
|
self.fixed_jumps[offset] = fix or match[-1]
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if is_jump_forward:
|
self.fixed_jumps[offset] = match[-1]
|
||||||
self.fixed_jumps[offset] = match[-1]
|
|
||||||
return
|
return
|
||||||
# op == POP_JUMP_IF_TRUE
|
# op == POP_JUMP_IF_TRUE
|
||||||
else:
|
else:
|
||||||
|
@@ -133,7 +133,8 @@ class CmpErrorMember(VerifyCmpError):
|
|||||||
# these members are ignored
|
# these members are ignored
|
||||||
__IGNORE_CODE_MEMBERS__ = ['co_filename', 'co_firstlineno', 'co_lnotab', 'co_stacksize', 'co_names']
|
__IGNORE_CODE_MEMBERS__ = ['co_filename', 'co_firstlineno', 'co_lnotab', 'co_stacksize', 'co_names']
|
||||||
|
|
||||||
def cmp_code_objects(version, is_pypy, code_obj1, code_obj2, name=''):
|
def cmp_code_objects(version, is_pypy, code_obj1, code_obj2,
|
||||||
|
name='', ignore_code=False):
|
||||||
"""
|
"""
|
||||||
Compare two code-objects.
|
Compare two code-objects.
|
||||||
|
|
||||||
@@ -178,9 +179,9 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2, name=''):
|
|||||||
|
|
||||||
tokens1 = None
|
tokens1 = None
|
||||||
for member in members:
|
for member in members:
|
||||||
if member in __IGNORE_CODE_MEMBERS__:
|
if member in __IGNORE_CODE_MEMBERS__ or ignore_code:
|
||||||
pass
|
pass
|
||||||
elif member == 'co_code':
|
elif member == 'co_code' and not ignore_code:
|
||||||
if version == 2.3:
|
if version == 2.3:
|
||||||
import uncompyle6.scanners.scanner23 as scan
|
import uncompyle6.scanners.scanner23 as scan
|
||||||
scanner = scan.Scanner26()
|
scanner = scan.Scanner26()
|
||||||
@@ -379,7 +380,7 @@ class Token(scanner.Token):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s\t%-17s %r' % (self.offset, self.type, self.pattr)
|
return '%s\t%-17s %r' % (self.offset, self.type, self.pattr)
|
||||||
|
|
||||||
def compare_code_with_srcfile(pyc_filename, src_filename):
|
def compare_code_with_srcfile(pyc_filename, src_filename, weak_verify=False):
|
||||||
"""Compare a .pyc with a source code file."""
|
"""Compare a .pyc with a source code file."""
|
||||||
version, timestamp, magic_int, code_obj1, is_pypy = load_module(pyc_filename)
|
version, timestamp, magic_int, code_obj1, is_pypy = load_module(pyc_filename)
|
||||||
if magic_int != PYTHON_MAGIC_INT:
|
if magic_int != PYTHON_MAGIC_INT:
|
||||||
@@ -387,14 +388,14 @@ def compare_code_with_srcfile(pyc_filename, src_filename):
|
|||||||
% (PYTHON_MAGIC_INT, magic_int))
|
% (PYTHON_MAGIC_INT, magic_int))
|
||||||
return msg
|
return msg
|
||||||
code_obj2 = load_file(src_filename)
|
code_obj2 = load_file(src_filename)
|
||||||
cmp_code_objects(version, is_pypy, code_obj1, code_obj2)
|
cmp_code_objects(version, is_pypy, code_obj1, code_obj2, ignore_code=weak_verify)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def compare_files(pyc_filename1, pyc_filename2):
|
def compare_files(pyc_filename1, pyc_filename2, weak_verify=False):
|
||||||
"""Compare two .pyc files."""
|
"""Compare two .pyc files."""
|
||||||
version, timestamp, magic_int1, code_obj1, is_pypy = uncompyle6.load_module(pyc_filename1)
|
version, timestamp, magic_int1, code_obj1, is_pypy = uncompyle6.load_module(pyc_filename1)
|
||||||
version, timestamp, magic_int2, code_obj2, is_pypy = uncompyle6.load_module(pyc_filename2)
|
version, timestamp, magic_int2, code_obj2, is_pypy = uncompyle6.load_module(pyc_filename2)
|
||||||
cmp_code_objects(version, is_pypy, code_obj1, code_obj2)
|
cmp_code_objects(version, is_pypy, code_obj1, code_obj2, ignore_code=weak_verify)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
t1 = Token('LOAD_CONST', None, 'code_object _expandLang', 52)
|
t1 = Token('LOAD_CONST', None, 'code_object _expandLang', 52)
|
||||||
|
Reference in New Issue
Block a user