diff --git a/test/bytecode_3.6_run/02_if_not_or.pyc b/test/bytecode_3.6_run/02_if_not_or.pyc new file mode 100644 index 00000000..5f19f054 Binary files /dev/null and b/test/bytecode_3.6_run/02_if_not_or.pyc differ diff --git a/test/bytecode_3.7_run/02_if_not_or.pyc b/test/bytecode_3.7_run/02_if_not_or.pyc new file mode 100644 index 00000000..a158b359 Binary files /dev/null and b/test/bytecode_3.7_run/02_if_not_or.pyc differ diff --git a/test/simple_source/bug37/02_if_not_or.py b/test/simple_source/bug37/02_if_not_or.py new file mode 100644 index 00000000..c68d93a4 --- /dev/null +++ b/test/simple_source/bug37/02_if_not_or.py @@ -0,0 +1,43 @@ +# from 3.7 inspect.py +# Bug was "if not predicate or" inside "for". +# Jump optimization turns a POP_JUMP_IF_TRUE into +# a POP_JUMP_IF_FALSE and this has to be +# dealt with at the "if" (or actually "testfalse") level. + +# RUNNABLE! +def getmembers(names, object, predicate): + for key in names: + if not predicate or object: + object = 2 + object += 1 + return object + +assert getmembers([1], 0, False) == 3 +assert getmembers([1], 1, True) == 3 +assert getmembers([1], 0, True) == 1 +assert getmembers([1], 1, False) == 3 +assert getmembers([], 1, False) == 1 +assert getmembers([], 2, True) == 2 + +def _shadowed_dict(klass, a, b, c): + for entry in klass: + if not (a and b): + c = 1 + return c + +assert _shadowed_dict([1], True, True, 3) == 3 +assert _shadowed_dict([1], True, False, 3) == 1 +assert _shadowed_dict([1], False, True, 3) == 1 +assert _shadowed_dict([1], False, False, 3) == 1 +assert _shadowed_dict([], False, False, 3) == 3 + +# Bug: the double "and" comes out as if .. if not and +def _shadowed_dict2(klass, a, b, c, d): + for entry in klass: + if not (a and b and c): + d = 1 + return d + +# Not yet -- +# assert _shadowed_dict2([1], False, False, False, 3) == 1 +# assert _shadowed_dict2([1], True, True, True, 3) == 3 diff --git a/uncompyle6/parsers/parse37.py b/uncompyle6/parsers/parse37.py index c0eb64b9..007dcccd 100644 --- a/uncompyle6/parsers/parse37.py +++ b/uncompyle6/parsers/parse37.py @@ -954,13 +954,21 @@ class Python37Parser(Python37BaseParser): ret_cond ::= expr POP_JUMP_IF_FALSE expr RETURN_END_IF COME_FROM ret_expr_or_cond jitop_come_from ::= JUMP_IF_TRUE_OR_POP COME_FROM - or ::= and jitop_come_from expr COME_FROM - or ::= expr JUMP_IF_TRUE_OR_POP expr COME_FROM - or ::= expr JUMP_IF_TRUE expr COME_FROM + or ::= and jitop_come_from expr COME_FROM + or ::= expr JUMP_IF_TRUE_OR_POP expr COME_FROM + or ::= expr JUMP_IF_TRUE expr COME_FROM + testfalse_not_or ::= expr jmp_false expr jmp_false COME_FROM + testfalse_not_and ::= and jmp_true come_froms + + testfalse_not_and ::= expr jmp_false expr jmp_true COME_FROM + testfalse ::= testfalse_not_or + testfalse ::= testfalse_not_and testfalse ::= or jmp_false COME_FROM or ::= expr jmp_true expr + + and ::= expr JUMP_IF_FALSE_OR_POP expr COME_FROM and ::= expr JUMP_IF_FALSE expr COME_FROM and ::= expr jmp_false expr diff --git a/uncompyle6/parsers/parse37base.py b/uncompyle6/parsers/parse37base.py index c6f047d8..f9a2b362 100644 --- a/uncompyle6/parsers/parse37base.py +++ b/uncompyle6/parsers/parse37base.py @@ -1046,7 +1046,7 @@ class Python37BaseParser(PythonParser): if tokens[last] == "POP_JUMP_IF_FALSE": # Ok if jump_target doesn't jump to last instruction return jmp_target != tokens[last].attr - elif tokens[last] == "JUMP_IF_TRUE_OR_POP": + elif tokens[last] in ("POP_JUMP_IF_TRUE", "JUMP_IF_TRUE_OR_POP"): # Ok if jump_target jumps to a COME_FROM after # the last instruction or jumps right after last instruction if last + 1 < n and tokens[last + 1] == "COME_FROM": diff --git a/uncompyle6/scanners/scanner37base.py b/uncompyle6/scanners/scanner37base.py index 8f7fd7e0..4c4fd911 100644 --- a/uncompyle6/scanners/scanner37base.py +++ b/uncompyle6/scanners/scanner37base.py @@ -888,32 +888,6 @@ class Scanner37Base(Scanner): elif op in self.setup_opts_no_loop: count_SETUP_ += 1 - def rem_or(self, start, end, instr, target=None, include_beyond_target=False): - """ - Find offsets of all requested between and , - optionally ing specified offset, and return list found - offsets which are not within any POP_JUMP_IF_TRUE jumps. - """ - assert start >= 0 and end <= len(self.code) and start <= end - - # Find all offsets of requested instructions - instr_offsets = self.inst_matches( - start, end, instr, target, include_beyond_target - ) - # Get all POP_JUMP_IF_TRUE (or) offsets - jump_true_op = self.opc.POP_JUMP_IF_TRUE - pjit_offsets = self.inst_matches(start, end, jump_true_op) - filtered = [] - for pjit_offset in pjit_offsets: - pjit_tgt = self.get_target(pjit_offset) - 3 - for instr_offset in instr_offsets: - if instr_offset <= pjit_offset or instr_offset >= pjit_tgt: - filtered.append(instr_offset) - instr_offsets = filtered - filtered = [] - return instr_offsets - - if __name__ == "__main__": from uncompyle6 import PYTHON_VERSION diff --git a/uncompyle6/semantics/customize37.py b/uncompyle6/semantics/customize37.py index 7d97d4a1..c559bfde 100644 --- a/uncompyle6/semantics/customize37.py +++ b/uncompyle6/semantics/customize37.py @@ -67,5 +67,7 @@ def customize_for_version37(self, version): '%[3]{pattr.replace("-", " ")} %p %p', (0, 19), (6, 19) ), 'if_exp_37a': ( '%p if %p else %p', (1, 'expr', 27), (0, 27), (4, 'expr', 27) ), 'if_exp_37b': ( '%p if %p else %p', (2, 'expr', 27), (0, 'expr', 27), (5, 'expr', 27) ), - + 'testfalse_not_or': ( "not %c or %c", + (0, "expr"), + (2, "expr") ), })