From 7b7a9fa4cfa2d63b2f7f8955427adb9f88262a67 Mon Sep 17 00:00:00 2001 From: rocky Date: Thu, 14 Jul 2016 05:19:16 -0400 Subject: [PATCH] Python 3.4 assertion handling. Improve verify 3.4 has jump optimization like 3.5. verify.py: show mismatch on verification mismatch --- test/bytecode_3.4/04_raise.pyc | Bin 0 -> 286 bytes test/bytecode_3.4/15_assert.pyc-notyet | Bin 232 -> 0 bytes test/bytecode_3.5/04_raise.pyc | Bin 0 -> 283 bytes uncompyle6/main.py | 1 + uncompyle6/parsers/parse3.py | 5 ++--- uncompyle6/parsers/parse34.py | 4 ++++ uncompyle6/scanners/scanner2.py | 6 ++++++ uncompyle6/scanners/scanner26.py | 6 ++++++ uncompyle6/scanners/scanner3.py | 11 +++++++---- uncompyle6/verify.py | 12 +++++++++--- 10 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 test/bytecode_3.4/04_raise.pyc delete mode 100644 test/bytecode_3.4/15_assert.pyc-notyet create mode 100644 test/bytecode_3.5/04_raise.pyc diff --git a/test/bytecode_3.4/04_raise.pyc b/test/bytecode_3.4/04_raise.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e8f3023be23265a752a250597b5012ff7845f900 GIT binary patch literal 286 zcmaFI!^^eZy&*h|k%8ec0}@~YvK@f9SO7?*01+cY3j;$GkioInGL=BJ@WhepbXl7(+Vr0kz8xX9?<%;HqNg34Qh$@#ejiOD7La2wb_E&>q@jBJejj76+KmY*hs1{;b*umI(5aoFVM OrlJ$-Vh$f$iVQJ0SPbx*$zNlEC3`@fQXTyg@GXo$Y5d!)?`iwss{rQ4N~h2 z#KmGjq6SC{F_eIHG&3?ZF@kglYcly&Nt6^NCZ{GPCTA<8r<^=} literal 0 HcmV?d00001 diff --git a/uncompyle6/main.py b/uncompyle6/main.py index 345c3c6d..4fe41a4b 100644 --- a/uncompyle6/main.py +++ b/uncompyle6/main.py @@ -148,6 +148,7 @@ def main(in_base, out_base, files, codes, outfile=None, else: print('\n# %s\n\t%s', infile, msg) except verify.VerifyCmpError as e: + print(e) verify_failed_files += 1 os.rename(outfile, outfile + '_unverified') if not outfile: diff --git a/uncompyle6/parsers/parse3.py b/uncompyle6/parsers/parse3.py index 4279f909..ffd1935c 100644 --- a/uncompyle6/parsers/parse3.py +++ b/uncompyle6/parsers/parse3.py @@ -247,7 +247,7 @@ class Python3Parser(PythonParser): ''' - def p_misc(self, args): + def p_misc3(self, args): """ try_middle ::= JUMP_FORWARD COME_FROM except_stmts END_FINALLY NOP COME_FROM """ @@ -288,7 +288,6 @@ class Python3Parser(PythonParser): def p_expr3(self, args): ''' expr ::= LOAD_CLASSNAME - expr ::= LOAD_ASSERT # Python 3.4+ expr ::= LOAD_CLASSDEREF @@ -615,7 +614,7 @@ class Python35onParser(Python3Parser): expr ::= yield_from yield_from ::= expr GET_YIELD_FROM_ITER LOAD_CONST YIELD_FROM - # Python 3.5 has more loop optimization that removes + # Python 3.4+ has more loop optimization that removes # JUMP_FORWARD in some cases, and hence we also don't # see COME_FROM _ifstmts_jump ::= c_stmts_opt diff --git a/uncompyle6/parsers/parse34.py b/uncompyle6/parsers/parse34.py index 6be0a665..280d2bd4 100644 --- a/uncompyle6/parsers/parse34.py +++ b/uncompyle6/parsers/parse34.py @@ -37,6 +37,10 @@ class Python34Parser(Python3Parser): # Is this 3.4 only? yield_from ::= expr GET_ITER LOAD_CONST YIELD_FROM + # Python 3.4+ has more loop optimization that removes + # JUMP_FORWARD in some cases, and hence we also don't + # see COME_FROM + _ifstmts_jump ::= c_stmts_opt """ class Python34ParserSingle(Python34Parser, PythonParserSingle): pass diff --git a/uncompyle6/scanners/scanner2.py b/uncompyle6/scanners/scanner2.py index 74bca070..4abf4983 100755 --- a/uncompyle6/scanners/scanner2.py +++ b/uncompyle6/scanners/scanner2.py @@ -89,8 +89,14 @@ class Scanner2(scan.Scanner): varnames = co.co_varnames self.names = names + # Scan for assertions. Later we will + # turn 'LOAD_GLOBAL' to 'LOAD_ASSERT'. + # 'LOAD_ASSERT' is used in assert statements. self.load_asserts = set() for i in self.op_range(0, n): + # We need to detect the difference between + # "raise AssertionError" and + # "assert" if self.code[i] == self.opc.PJIT and self.code[i+3] == self.opc.LOAD_GLOBAL: if names[self.get_argument(i+3)] == 'AssertionError': self.load_asserts.add(i+3) diff --git a/uncompyle6/scanners/scanner26.py b/uncompyle6/scanners/scanner26.py index 8ad6854c..5366efaf 100755 --- a/uncompyle6/scanners/scanner26.py +++ b/uncompyle6/scanners/scanner26.py @@ -125,8 +125,14 @@ class Scanner26(scan.Scanner2): codelen = len(self.code) + # Scan for assertions. Later we will + # turn 'LOAD_GLOBAL' to 'LOAD_ASSERT'. + # 'LOAD_ASSERT' is used in assert statements. self.load_asserts = set() for i in self.op_range(0, n): + # We need to detect the difference between + # "raise AssertionError" and + # "assert" if (self.code[i] == self.opc.JUMP_IF_TRUE and i + 4 < codelen and self.code[i+3] == self.opc.POP_TOP and diff --git a/uncompyle6/scanners/scanner3.py b/uncompyle6/scanners/scanner3.py index be49626d..1f81d313 100644 --- a/uncompyle6/scanners/scanner3.py +++ b/uncompyle6/scanners/scanner3.py @@ -134,16 +134,19 @@ class Scanner3(scan.Scanner): bytecode = Bytecode(co, self.opc) # Scan for assertions. Later we will - # turn 'LOAD_GLOBAL' to 'LOAD_ASSERT' for those - # assertions + # turn 'LOAD_GLOBAL' to 'LOAD_ASSERT'. + # 'LOAD_ASSERT' is used in assert statements. self.load_asserts = set() bs = list(bytecode) n = len(bs) for i in range(n): inst = bs[i] - if inst.opname == 'POP_JUMP_IF_TRUE' and i+1 < n: - next_inst = bs[i+1] + # We need to detect the difference between + # "raise AssertionError" and + # "assert" + if inst.opname == 'POP_JUMP_IF_TRUE' and i+3 < n: + next_inst = bs[i+3] if (next_inst.opname == 'LOAD_GLOBAL' and next_inst.argval == 'AssertionError'): self.load_asserts.add(next_inst.offset) diff --git a/uncompyle6/verify.py b/uncompyle6/verify.py index fc8754cb..21e90f01 100755 --- a/uncompyle6/verify.py +++ b/uncompyle6/verify.py @@ -180,7 +180,13 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''): if member in __IGNORE_CODE_MEMBERS__: pass elif member == 'co_code': - if version == 2.5: + if version == 2.3: + import uncompyle6.scanners.scanner23 as scan + scanner = scan.Scanner26() + elif version == 2.4: + import uncompyle6.scanners.scanner24 as scan + scanner = scan.Scanner25() + elif version == 2.5: import uncompyle6.scanners.scanner25 as scan scanner = scan.Scanner25() elif version == 2.6: @@ -206,8 +212,8 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''): JUMP_OPs = list(scan.JUMP_OPs) + ['JUMP_BACK'] # use changed Token class - # We (re)set this here to save exception handling, - # which would get confusing. + # We (re)set this here to save exception handling, + # which would get confusing. scanner.setTokenClass(Token) try: # disassemble both code-objects