From 6c546fe6e1ffde97d809faa9bad8ed56c9466302 Mon Sep 17 00:00:00 2001 From: rocky Date: Tue, 27 Dec 2016 04:57:58 -0500 Subject: [PATCH] WIP : Add THEN to disambigute from "and" --- test/bytecode_2.6/04_if_and_bug.pyc | Bin 0 -> 573 bytes uncompyle6/parsers/parse26.py | 39 ++++++++---- uncompyle6/scanners/scanner2.py | 93 ++++++++++++++++++++++++---- uncompyle6/scanners/scanner26.py | 30 ++++----- uncompyle6/semantics/pysource.py | 6 +- 5 files changed, 130 insertions(+), 38 deletions(-) create mode 100644 test/bytecode_2.6/04_if_and_bug.pyc diff --git a/test/bytecode_2.6/04_if_and_bug.pyc b/test/bytecode_2.6/04_if_and_bug.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f2bb75c4d8a8273eda0a18484d1fc8a8e34d18c GIT binary patch literal 573 zcmZut!Ab)`41L*cYb{ox2o}8x_N1bs9z^^CFJ5FX!`ew@wC&dJ6zREN=+PhW1M~y* z&8$@q4kVM8yu5_Gyti9d$Nk$rzF&?16|JlZpnwG)5)>a0l?YN4Zv@E_yb`3)8Z7IA zMGBW-0c3U91X@QoYd{+T1fdqhreJC;l}at9cL-KsE3i%w#m+PN;iZw+V~N5Q_`m6F z*+hwo*WpY7SKf;208bF9R;uQ7H@C88*j}6Dke4COwRPO|8oY-4-i_46Aly6BLTTWpr8i6R1~ZeX z_EoJL-;+KseTTWf>ZbG@5r>Al@kMO!k%nJa?h=-6G#RQXi`lfV2Y0jkqtk=K6XOOZ nA6rAbKbiX`i5H{7&W3uKgr+Xto^+)pZQ8c1%O0&QoungQVAOFF literal 0 HcmV?d00001 diff --git a/uncompyle6/parsers/parse26.py b/uncompyle6/parsers/parse26.py index c65ab602..b0b01671 100644 --- a/uncompyle6/parsers/parse26.py +++ b/uncompyle6/parsers/parse26.py @@ -113,16 +113,10 @@ class Python26Parser(Python2Parser): break_stmt ::= BREAK_LOOP JUMP_BACK - # Semantic actions want the else to be at position 3 - ifelsestmt ::= testexpr c_stmts_opt jf_cf_pop else_suite come_froms - ifelsestmt ::= testexpr c_stmts_opt filler else_suitel come_froms POP_TOP - # Semantic actions want else_suitel to be at index 3 ifelsestmtl ::= testexpr c_stmts_opt jb_cf_pop else_suitel ifelsestmtc ::= testexpr c_stmts_opt ja_cf_pop else_suitec - iflaststmt ::= testexpr c_stmts_opt JUMP_ABSOLUTE come_froms POP_TOP - # Semantic actions want suite_stmts_opt to be at index 3 withstmt ::= expr setupwith SETUP_FINALLY suite_stmts_opt POP_BLOCK LOAD_CONST COME_FROM WITH_CLEANUP END_FINALLY @@ -159,6 +153,29 @@ class Python26Parser(Python2Parser): while1stmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK COME_FROM + ifstmt ::= testexpr_then _ifstmts_jump + + # Semantic actions want the else to be at position 3 + ifelsestmt ::= testexpr c_stmts_opt jf_cf_pop else_suite come_froms + ifelsestmt ::= testexpr_then c_stmts_opt jf_cf_pop else_suite come_froms + ifelsestmt ::= testexpr c_stmts_opt filler else_suitel come_froms POP_TOP + ifelsestmt ::= testexpr_then c_stmts_opt filler else_suitel come_froms POP_TOP + + # Semantic actions want else_suitel to be at index 3 + ifelsestmtl ::= testexpr_then c_stmts_opt jb_cf_pop else_suitel + ifelsestmtc ::= testexpr_then c_stmts_opt ja_cf_pop else_suitec + + iflaststmt ::= testexpr_then c_stmts_opt JUMP_ABSOLUTE come_froms POP_TOP + iflaststmt ::= testexpr c_stmts_opt JUMP_ABSOLUTE come_froms POP_TOP + + testexpr_then ::= testtrue_then + testexpr_then ::= testfalse_then + testtrue_then ::= expr jmp_true_then + testfalse_then ::= expr jmp_false_then + + jmp_false_then ::= JUMP_IF_FALSE THEN POP_TOP + jmp_true_then ::= JUMP_IF_TRUE THEN POP_TOP + # Common with 2.7 while1stmt ::= SETUP_LOOP return_stmts bp_come_from while1stmt ::= SETUP_LOOP return_stmts COME_FROM @@ -201,11 +218,11 @@ class Python26Parser(Python2Parser): def p_ret26(self, args): ''' - ret_and ::= expr jmp_false ret_expr_or_cond COME_FROM - ret_or ::= expr jmp_true ret_expr_or_cond COME_FROM - ret_cond ::= expr jmp_false expr RETURN_END_IF POP_TOP ret_expr_or_cond - ret_cond ::= expr jmp_false expr ret_expr_or_cond - ret_cond_not ::= expr jmp_true expr RETURN_END_IF POP_TOP ret_expr_or_cond + ret_and ::= expr jmp_false ret_expr_or_cond COME_FROM + ret_or ::= expr jmp_true ret_expr_or_cond COME_FROM + ret_cond ::= expr jmp_false_then expr RETURN_END_IF POP_TOP ret_expr_or_cond + ret_cond ::= expr jmp_false_then expr ret_expr_or_cond + ret_cond_not ::= expr jmp_true_then expr RETURN_END_IF POP_TOP ret_expr_or_cond return_if_stmt ::= ret_expr RETURN_END_IF POP_TOP return_stmt ::= ret_expr RETURN_VALUE POP_TOP diff --git a/uncompyle6/scanners/scanner2.py b/uncompyle6/scanners/scanner2.py index 052ab011..93508c0c 100644 --- a/uncompyle6/scanners/scanner2.py +++ b/uncompyle6/scanners/scanner2.py @@ -101,10 +101,10 @@ class Scanner2(scan.Scanner): Token = self.Token # shortcut - n = self.setup_code(co) + codelen = self.setup_code(co) - self.build_lines_data(co, n) - self.build_prev_op(n) + self.build_lines_data(co, codelen) + self.build_prev_op(codelen) free, names, varnames = self.unmangle_code_names(co, classname) self.names = names @@ -113,7 +113,7 @@ class Scanner2(scan.Scanner): # 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): + for i in self.op_range(0, codelen): # We need to detect the difference between: # raise AssertionError # and @@ -138,7 +138,7 @@ class Scanner2(scan.Scanner): last_stmt = self.next_stmt[0] i = self.next_stmt[last_stmt] replace = {} - while i < n-1: + while i < codelen - 1: if self.lines[last_stmt].next > i: # Distinguish "print ..." from "print ...," if self.code[last_stmt] == self.opc.PRINT_ITEM: @@ -150,7 +150,7 @@ class Scanner2(scan.Scanner): i = self.next_stmt[i] extended_arg = 0 - for offset in self.op_range(0, n): + for offset in self.op_range(0, codelen): if offset in jump_targets: jump_idx = 0 # We want to process COME_FROMs to the same offset to be in *descending* @@ -176,6 +176,7 @@ class Scanner2(scan.Scanner): offset="%s_%d" % (offset, jump_idx), has_arg = True)) jump_idx += 1 + pass op = self.code[offset] op_name = self.opc.opname[op] @@ -675,6 +676,8 @@ class Scanner2(scan.Scanner): self.fixed_jumps[offset] = rtarget return + jump_if_offset = offset + start = offset+3 pre = self.prev @@ -827,20 +830,86 @@ class Scanner2(scan.Scanner): jump_target = self.get_target(next_offset, next_op) if jump_target in self.setup_loops: self.structs.append({'type': 'while-loop', - 'start': start - 3, + 'start': jump_if_offset, 'end': jump_target}) - self.fixed_jumps[start-3] = jump_target + self.fixed_jumps[jump_if_offset] = jump_target return end = self.restrict_to_parent(if_end, parent) - self.structs.append({'type': 'if-then', - 'start': start-3, - 'end': pre_rtarget}) + if_then_maybe = None + + if 2.2 <= self.version <= 2.6: + # Take the JUMP_IF target. In an "if/then", it will be + # a POP_TOP instruction and the instruction before it + # will be a JUMP_FORWARD to just after the POP_TOP. + # For example: + # Good: + # 3 JUMP_IF_FALSE 33 'to 39' + # .. + # 36 JUMP_FORWARD 1 'to 40' + # 39 POP_TOP + # 40 ... + # example: + + # BAD (is an "and"): + # 28 JUMP_IF_FALSE 4 'to 35' + # ... + # 32 JUMP_ABSOLUTE 40 'to 40' # should be 36 or there should + # # be a COME_FROM at the pop top + # # before 40 to 35 + # 35 POP_TOP + # 36 ... + # 39 POP_TOP + # 39_0 COME_FROM 3 + # 40 ... + + if self.opc.opname[code[jump_if_offset]].startswith('JUMP_IF'): + jump_if_target = code[jump_if_offset+1] + if self.opc.opname[code[jump_if_target + jump_if_offset + 3]] == 'POP_TOP': + jump_inst = jump_if_target + jump_if_offset + jump_offset = code[jump_inst+1] + jump_op = self.opc.opname[code[jump_inst]] + if (jump_op == 'JUMP_FORWARD' and jump_offset == 1): + self.structs.append({'type': 'if-then', + 'start': start-3, + 'end': pre_rtarget}) + + self.thens[start] = end + elif jump_op == 'JUMP_ABSOLUTE': + if_then_maybe = {'type': 'if-then', + 'start': start-3, + 'end': pre_rtarget} + + elif self.version == 2.7: + self.structs.append({'type': 'if-then', + 'start': start-3, + 'end': pre_rtarget}) self.not_continue.add(pre_rtarget) if rtarget < end: + # We have an "else" block of some kind. + # Is it associated with "if_then_maybe" seen above? + # These will be linked in this funny way: + + # 198 JUMP_IF_FALSE 18 'to 219' + # 201 POP_TOP + # ... + # 216 JUMP_ABSOLUTE 256 'to 256' + # 219 POP_TOP + # ... + # 252 JUMP_FORWARD 1 'to 256' + # 255 POP_TOP + # 256 + if if_then_maybe and jump_op == 'JUMP_ABSOLUTE': + jump_target = self.get_target(jump_inst, code[jump_inst]) + if self.opc.opname[code[end]] == 'JUMP_FORWARD': + end_target = self.get_target(end, code[end]) + if jump_target == end_target: + self.structs.append(if_then_maybe) + self.thens[start] = end + self.structs.append({'type': 'else', 'start': rtarget, 'end': end}) @@ -849,6 +918,7 @@ class Scanner2(scan.Scanner): self.structs.append({'type': 'if-then', 'start': start, 'end': rtarget}) + self.thens[start] = rtarget if self.version == 2.7 or code[pre_rtarget+1] != self.opc.JUMP_FORWARD: self.return_end_ifs.add(pre_rtarget) @@ -887,6 +957,7 @@ class Scanner2(scan.Scanner): self.return_end_ifs = set() self.setup_loop_targets = {} # target given setup_loop offset self.setup_loops = {} # setup_loop offset given target + self.thens = {} # JUMP_IF's that separate the 'then' part of an 'if' targets = {} for offset in self.op_range(0, n): diff --git a/uncompyle6/scanners/scanner26.py b/uncompyle6/scanners/scanner26.py index fbd2764a..9562e056 100755 --- a/uncompyle6/scanners/scanner26.py +++ b/uncompyle6/scanners/scanner26.py @@ -94,35 +94,32 @@ class Scanner26(scan.Scanner2): for instr in bytecode.get_instructions(co): print(instr._disassemble()) - # from xdis.bytecode import Bytecode - # bytecode = Bytecode(co, self.opc) - # for instr in bytecode.get_instructions(co): - # print(instr._disassemble()) - # Container for tokens tokens = [] customize = {} + if self.is_pypy: + customize['PyPy'] = 1 + Token = self.Token # shortcut - n = self.setup_code(co) + codelen = self.setup_code(co) - self.build_lines_data(co, n) - self.build_prev_op(n) + self.build_lines_data(co, codelen) + self.build_prev_op(codelen) free, names, varnames = self.unmangle_code_names(co, classname) self.names = names - 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" + for i in self.op_range(0, codelen): + # 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 @@ -167,6 +164,11 @@ class Scanner26(scan.Scanner2): offset="%s_%d" % (offset, jump_idx), has_arg = True)) jump_idx += 1 + elif offset in self.thens: + tokens.append(Token( + 'THEN', None, self.thens[offset], + offset="%s_0" % offset, + has_arg = True)) has_arg = (op >= self.opc.HAVE_ARGUMENT) if has_arg: diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index cbc4840f..8b49eace 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -301,7 +301,7 @@ TABLE_DIRECT = { 'ifstmt': ( '%|if %c:\n%+%c%-', 0, 1 ), 'iflaststmt': ( '%|if %c:\n%+%c%-', 0, 1 ), 'iflaststmtl': ( '%|if %c:\n%+%c%-', 0, 1 ), - 'testtrue': ( 'not %p', (0, 22) ), + 'testtrue': ( 'not %p', (0, 22) ), 'ifelsestmt': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ), 'ifelsestmtc': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ), @@ -589,7 +589,9 @@ class SourceWalker(GenericASTTraversal, object): }) else: TABLE_DIRECT.update({ - 'except_cond3': ( '%|except %c, %c:\n', 1, 6 ), + 'except_cond3': ( '%|except %c, %c:\n', 1, 6 ), + 'testtrue_then': ( 'not %p', (0, 22) ), + }) if 2.4 <= version <= 2.6: TABLE_DIRECT.update({