ifstmt.py 3.0 KB

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