ifelsestmt2.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. # Copyright (c) 2020-2022 Rocky Bernstein
  2. """
  3. If/else statement reduction check for Python 2.6 (and older?)
  4. """
  5. IFELSE_STMT_RULES = frozenset(
  6. [
  7. (
  8. "ifelsestmt",
  9. (
  10. "testexpr_then",
  11. "pass",
  12. "filler",
  13. "else_suitel",
  14. "COME_FROM",
  15. "POP_TOP",
  16. ),
  17. ),
  18. (
  19. "ifelsestmt",
  20. (
  21. "testexpr_then",
  22. "c_stmts_opt",
  23. "\\e_filler",
  24. "else_suitel",
  25. "come_froms",
  26. "POP_TOP",
  27. ),
  28. ),
  29. (
  30. "ifelsestmt",
  31. (
  32. "testexpr_then",
  33. "\\e_c_stmts_opt",
  34. "\\e_filler",
  35. "else_suitel",
  36. "come_froms",
  37. "POP_TOP",
  38. ),
  39. ),
  40. # We may do something like add these in the future:
  41. ]
  42. )
  43. def ifelsestmt2(self, lhs, n, rule, tree, tokens, first, last):
  44. if (last + 1) < n and tokens[last + 1] == "COME_FROM_LOOP" and lhs != "ifelsestmtc":
  45. # ifelsestmt jumped outside of loop. No good.
  46. return True
  47. # print("XXX", first, last)
  48. # for t in range(first, last):
  49. # print(tokens[t])
  50. # print("=" * 30)
  51. if rule not in IFELSE_STMT_RULES:
  52. # print("XXX", rule)
  53. return False
  54. # Avoid if/else where the "then" is a "raise_stmt1" for an
  55. # assert statement. Parse this as an "assert" instead.
  56. stmts = tree[1]
  57. if stmts in ("c_stmts",) and len(stmts) == 1:
  58. raise_stmt1 = stmts[0]
  59. if raise_stmt1 == "raise_stmt1" and raise_stmt1[0] in ("LOAD_ASSERT",):
  60. return True
  61. # Make sure all of the "come_froms" offset at the
  62. # end of the "if" come from somewhere inside the "if".
  63. # Since the come_froms are ordered so that lowest
  64. # offset COME_FROM is last, it is sufficient to test
  65. # just the last one.
  66. if len(tree) == 6 and tree[-1] == "POP_TOP":
  67. # FIXME: There is weirdness in the grammar we need to work around.
  68. # we need to clean up the grammar.
  69. last_token = tree[-2]
  70. if last_token == "COME_FROM" and tokens[first].offset > last_token.attr:
  71. if (
  72. self.insts[self.offset2inst_index[last_token.attr]].opname
  73. != "SETUP_LOOP"
  74. ):
  75. return True
  76. testexpr = tree[0]
  77. # Check that the condition portion of the "if"
  78. # jumps to the "else" part.
  79. if testexpr[0] in ("testtrue", "testfalse", "testfalse_then"):
  80. if_condition = testexpr[0]
  81. else_suite = tree[3]
  82. assert else_suite.kind.startswith("else_suite")
  83. if len(if_condition) > 1 and if_condition[1].kind.startswith("jmp_"):
  84. if last == n:
  85. last -= 1
  86. jmp = if_condition[1]
  87. jmp_target = int(jmp[0].pattr)
  88. # Below we check that jmp_target is jumping to a feasible
  89. # location. It should be to the transition after the "then"
  90. # block and to the beginning of the "else" block.
  91. # However the "if/else" is inside a loop the false test can be
  92. # back to the loop.
  93. # FIXME: the below logic for jf_cfs could probably be
  94. # simplified.
  95. if tree[2] == "filler":
  96. jump_else_end = tree[3]
  97. else:
  98. jump_else_end = tree[2]
  99. if jump_else_end == "jf_cfs":
  100. jump_else_end = jump_else_end[0]
  101. if jump_else_end == "JUMP_FORWARD":
  102. endif_target = int(jump_else_end.pattr)
  103. last_offset = tokens[last].off2int()
  104. if endif_target != last_offset:
  105. return True
  106. last_offset = tokens[last].off2int(prefer_last=False)
  107. if jmp_target <= last_offset:
  108. # jmp_target should be jumping to the end of the if/then/else
  109. # but is it jumping to the beginning of the "else" or before
  110. return True
  111. if (
  112. jump_else_end in ("jf_cfs", "jump_forward_else")
  113. and jump_else_end[0] == "JUMP_FORWARD"
  114. ):
  115. # If the "else" jump jumps before the end of the the "if .. else end", then this
  116. # is not this kind of "ifelsestmt".
  117. jump_else_forward = jump_else_end[0]
  118. jump_else_forward_target = jump_else_forward.attr
  119. if jump_else_forward_target < last_offset:
  120. return True
  121. pass
  122. if (
  123. jump_else_end in ("jb_elsec", "jb_elsel", "jf_cfs", "jb_cfs")
  124. and jump_else_end[-1] == "COME_FROM"
  125. ):
  126. if jump_else_end[-1].off2int() != jmp_target:
  127. return True
  128. if tokens[first].off2int() > jmp_target:
  129. return True
  130. return (jmp_target > last_offset) and tokens[last] != "JUMP_FORWARD"
  131. return False