ifstmt2.py 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. # Copyright (c) 2022 Rocky Bernstein
  2. """
  3. If statement reduction check for Python 2.6 (and older?)
  4. """
  5. def ifstmt2(self, lhs, n, rule, ast, tokens, first, last):
  6. # for t in range(first, last):
  7. # print(tokens[t])
  8. # print("=" * 30)
  9. if lhs == "ifstmtl":
  10. if last == n:
  11. last -= 1
  12. pass
  13. if tokens[last].attr and isinstance(tokens[last].attr, int):
  14. if tokens[first].offset >= tokens[last].attr:
  15. return True
  16. pass
  17. pass
  18. # Make sure jumps don't extend beyond the end of the if statement.
  19. l = last
  20. if l == n:
  21. l -= 1
  22. if isinstance(tokens[l].offset, str):
  23. last_offset = int(tokens[l].offset.split("_")[0], 10)
  24. else:
  25. last_offset = tokens[l].offset
  26. for i in range(first, l):
  27. t = tokens[i]
  28. # instead of POP_JUMP_IF, should we use op attributes?
  29. if t.kind in ("JUMP_IF_FALSE", "JUMP_IF_TRUE"):
  30. jif_target = int(t.pattr)
  31. target_instr = self.insts[self.offset2inst_index[jif_target]]
  32. if lhs == "iflaststmtl" and target_instr.opname == "JUMP_ABSOLUTE":
  33. jif_target = target_instr.arg
  34. if jif_target > last_offset:
  35. # In come cases, where we have long bytecode, a
  36. # "POP_JUMP_IF_TRUE/FALSE" offset might be too
  37. # large for the instruction; so instead it
  38. # jumps to a JUMP_FORWARD. Allow that here.
  39. if tokens[l] == "JUMP_FORWARD":
  40. return tokens[l].attr != jif_target
  41. return True
  42. elif lhs == "ifstmtl" and tokens[first].off2int() > jif_target:
  43. # A conditional JUMP to the loop is expected for "ifstmtl"
  44. return False
  45. pass
  46. pass
  47. pass
  48. if ast:
  49. testexpr = ast[0]
  50. if (last + 1) < n and tokens[last + 1] == "COME_FROM_LOOP":
  51. # iflastsmtl jumped outside of loop. No good.
  52. return True
  53. if testexpr[0] in ("testtrue", "testfalse"):
  54. test = testexpr[0]
  55. jmp = test[1]
  56. if len(test) > 1 and jmp.kind.startswith("jmp_"):
  57. jmp_target = int(jmp[0].pattr)
  58. if last == len(tokens):
  59. last -= 1
  60. if_end_offset = tokens[last].off2int(prefer_last=False)
  61. if (
  62. tokens[first].off2int(prefer_last=True)
  63. <= jmp_target
  64. < if_end_offset
  65. ):
  66. # In 2.6 (and before?) we need to check if the previous instruction
  67. # is a jump to the last token. If so, testexpr is negated? and so
  68. # jmp_target < if_end_offset.
  69. previous_inst_index = self.offset2inst_index[jmp_target] - 1
  70. previous_inst = self.insts[previous_inst_index]
  71. if previous_inst.opname != "JUMP_ABSOLUTE" and previous_inst.argval != if_end_offset:
  72. return True
  73. # jmp_target less than tokens[first] is okay - is to a loop
  74. # jmp_target equal tokens[last] is also okay: normal non-optimized non-loop jump
  75. if jmp_target > tokens[last].off2int():
  76. # One more weird case to look out for
  77. # if c1:
  78. # if c2: # Jumps around the *outer* "else"
  79. # ...
  80. # else:
  81. if jmp_target == tokens[last - 1].attr:
  82. return False
  83. if last < n and tokens[last].kind.startswith("JUMP"):
  84. return False
  85. return True
  86. pass
  87. pass
  88. return False