and_check.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. # Copyright (c) 2020, 2022, 2024 Rocky Bernstein
  2. # This program is free software: you can redistribute it and/or modify
  3. # it under the terms of the GNU General Public License as published by
  4. # the Free Software Foundation, either version 3 of the License, or
  5. # (at your option) any later version.
  6. #
  7. # This program is distributed in the hope that it will be useful,
  8. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. # GNU General Public License for more details.
  11. #
  12. # You should have received a copy of the GNU General Public License
  13. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. NOT_POP_FOLLOW_OPS = frozenset(
  15. """
  16. LOAD_ASSERT RAISE_VARARGS_1 STORE_FAST STORE_DEREF STORE_GLOBAL STORE_ATTR STORE_NAME
  17. """.split()
  18. )
  19. def and_invalid(
  20. self, lhs: str, n: int, rule, tree, tokens: list, first: int, last: int
  21. ) -> bool:
  22. # print("XXX", first, last, rule)
  23. # for t in range(first, last): print(tokens[t])
  24. # print("="*40)
  25. # a LOAD_ASSERT is not an expression and not part of an "and"
  26. # FIXME: the below really should have been done in the ingest
  27. # phase.
  28. ltm1 = tokens[last - 1]
  29. rhs = rule[1]
  30. if ltm1 == "LOAD_ASSERT" or (
  31. ltm1 == "LOAD_GLOBAL" and ltm1.attr == "AssertionError"
  32. ):
  33. return True
  34. expr_pjif = tree[0]
  35. if expr_pjif == "expr_pjif":
  36. jump = expr_pjif[1]
  37. elif expr_pjif == "expr_jifop_cfs":
  38. expr_jifop_cfs = expr_pjif
  39. jump = expr_jifop_cfs[1]
  40. if expr_jifop_cfs[0][0] == "or":
  41. # FIXME check if the "or" jumps to the same place as jump.attr
  42. return True
  43. if first > 0:
  44. ftm1 = tokens[first - 1]
  45. if ftm1 == "JUMP_IF_TRUE_OR_POP" and ftm1.attr == jump.attr:
  46. return True
  47. else:
  48. jump = tree[1]
  49. elif rhs == ("and_parts", "expr") and expr_pjif[0] == "expr_pjif":
  50. expr_pjif = expr_pjif[0]
  51. jump = expr_pjif[1]
  52. else:
  53. # Probably not needed: was expr POP_JUMP_IF_FALSE
  54. jump = tree[1]
  55. if jump.kind.startswith("POP_JUMP_IF_"):
  56. if last == n:
  57. return True
  58. jump_target = jump.attr
  59. jump_offset = jump.offset
  60. if tokens[first].off2int() <= jump_target < tokens[last].off2int():
  61. return True
  62. if rule == ("and", ("expr_pjif", "expr_pjif")):
  63. jump2_target = tree[1][1].attr
  64. return jump_target != jump2_target
  65. elif rule == ("and", ("expr_pjif", "expr", "POP_JUMP_IF_TRUE")):
  66. jump2_target = tree[2].attr
  67. return jump_target == jump2_target
  68. elif rule == ("and", ("expr_pjif", "expr")):
  69. if tokens[last] == "POP_JUMP_IF_FALSE":
  70. # Ok if jump_target doesn't jump to last instruction
  71. return jump_target != tokens[last].attr
  72. elif tokens[last] in ("POP_JUMP_IF_TRUE", "JUMP_IF_TRUE_OR_POP"):
  73. # Ok if jump_target jumps to a COME_FROM after
  74. # the last instruction or jumps right after the last instruction
  75. if last + 1 < n and tokens[last + 1] == "COME_FROM":
  76. return jump_target != tokens[last + 1].off2int()
  77. return jump_target + 2 != tokens[last].attr
  78. elif rule == ("and", ("expr_pjif", "expr", "COME_FROM")):
  79. return tree[-1].attr != jump_offset
  80. elif (
  81. rule == ("and", ("and_parts", "expr"))
  82. and jump_target > tokens[last].off2int()
  83. and tokens[last].kind.startswith("JUMP_IF_")
  84. and jump_target < tokens[last].attr
  85. ):
  86. # This could be an "(i and j) or k"
  87. # or:
  88. # - and: expr, POP_JUMP_IF_FALSE jump_target, expr
  89. # - JUMP_IF_TRUE_OR_POP end_or
  90. # - jump_target: expr
  91. # end_or:
  92. return False
  93. return jump_target != tokens[last].off2int()
  94. return False