Add "if_not_and" rule similar to "if_not_or" rule.

This commit is contained in:
rocky
2019-12-13 05:13:51 -05:00
parent 668141662e
commit 805ec7dbfc
7 changed files with 58 additions and 31 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -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

View File

@@ -954,13 +954,21 @@ class Python37Parser(Python37BaseParser):
ret_cond ::= expr POP_JUMP_IF_FALSE expr RETURN_END_IF COME_FROM ret_expr_or_cond 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 jitop_come_from ::= JUMP_IF_TRUE_OR_POP COME_FROM
or ::= and jitop_come_from 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_OR_POP expr COME_FROM
or ::= expr JUMP_IF_TRUE 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 testfalse ::= or jmp_false COME_FROM
or ::= expr jmp_true expr or ::= expr jmp_true expr
and ::= expr JUMP_IF_FALSE_OR_POP expr COME_FROM and ::= expr JUMP_IF_FALSE_OR_POP expr COME_FROM
and ::= expr JUMP_IF_FALSE expr COME_FROM and ::= expr JUMP_IF_FALSE expr COME_FROM
and ::= expr jmp_false expr and ::= expr jmp_false expr

View File

@@ -1046,7 +1046,7 @@ class Python37BaseParser(PythonParser):
if tokens[last] == "POP_JUMP_IF_FALSE": if tokens[last] == "POP_JUMP_IF_FALSE":
# Ok if jump_target doesn't jump to last instruction # Ok if jump_target doesn't jump to last instruction
return jmp_target != tokens[last].attr 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 # Ok if jump_target jumps to a COME_FROM after
# the last instruction or jumps right after last instruction # the last instruction or jumps right after last instruction
if last + 1 < n and tokens[last + 1] == "COME_FROM": if last + 1 < n and tokens[last + 1] == "COME_FROM":

View File

@@ -888,32 +888,6 @@ class Scanner37Base(Scanner):
elif op in self.setup_opts_no_loop: elif op in self.setup_opts_no_loop:
count_SETUP_ += 1 count_SETUP_ += 1
def rem_or(self, start, end, instr, target=None, include_beyond_target=False):
"""
Find offsets of all requested <instr> between <start> and <end>,
optionally <target>ing specified offset, and return list found
<instr> 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__": if __name__ == "__main__":
from uncompyle6 import PYTHON_VERSION from uncompyle6 import PYTHON_VERSION

View File

@@ -67,5 +67,7 @@ def customize_for_version37(self, version):
'%[3]{pattr.replace("-", " ")} %p %p', (0, 19), (6, 19) ), '%[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_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) ), '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") ),
}) })