ifelsestmt.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. # Copyright (c) 2020-2022 Rocky Bernstein
  2. from uncompyle6.scanners.tok import Token
  3. IFELSE_STMT_RULES = frozenset(
  4. [
  5. (
  6. "ifelsestmt",
  7. (
  8. "testexpr",
  9. "c_stmts_opt",
  10. "jump_forward_else",
  11. "else_suite",
  12. "_come_froms",
  13. ),
  14. ),
  15. (
  16. "ifelsestmt",
  17. (
  18. "testexpr",
  19. "c_stmts_opt",
  20. "jump_forward_else",
  21. "else_suite",
  22. "\\e__come_froms",
  23. ),
  24. ),
  25. (
  26. "ifelsestmtl",
  27. (
  28. "testexpr",
  29. "c_stmts_opt",
  30. "jump_forward_else",
  31. "else_suitec",
  32. ),
  33. ),
  34. (
  35. "ifelsestmtc",
  36. (
  37. "testexpr",
  38. "c_stmts_opt",
  39. "jump_forward_else",
  40. "else_suitec",
  41. "\\e__come_froms",
  42. ),
  43. ),
  44. (
  45. "ifelsestmtc",
  46. (
  47. "testexpr",
  48. "c_stmts_opt",
  49. "jump_absolute_else",
  50. "else_suitec",
  51. ),
  52. ),
  53. (
  54. "ifelsestmtc",
  55. (
  56. "testexpr",
  57. "c_stmts_opt",
  58. "JUMP_ABSOLUTE",
  59. "else_suitec",
  60. ),
  61. ),
  62. (
  63. "ifelsestmt",
  64. (
  65. "testexpr",
  66. "c_stmts_opt",
  67. "jf_cfs",
  68. "else_suite",
  69. "\\e_opt_come_from_except",
  70. ),
  71. ),
  72. (
  73. "ifelsestmt",
  74. (
  75. "testexpr",
  76. "c_stmts_opt",
  77. "JUMP_FORWARD",
  78. "else_suite",
  79. "come_froms",
  80. ),
  81. ),
  82. (
  83. "ifelsestmtc",
  84. ("testexpr", "c_stmts_opt", "JUMP_FORWARD", "else_suite", "come_froms"),
  85. ),
  86. (
  87. "ifelsestmt",
  88. (
  89. "testexpr",
  90. "c_stmts",
  91. "come_froms",
  92. "else_suite",
  93. "come_froms",
  94. ),
  95. ),
  96. (
  97. "ifelsestmt",
  98. (
  99. "testexpr",
  100. "c_stmts_opt",
  101. "jf_cfs",
  102. "else_suite",
  103. "opt_come_from_except",
  104. ),
  105. ),
  106. (
  107. "ifelsestmt",
  108. (
  109. "testexpr",
  110. "c_stmts_opt",
  111. "jf_cf_pop",
  112. "else_suite",
  113. ),
  114. ),
  115. (
  116. "ifelsestmt",
  117. (
  118. "testexpr",
  119. "stmts",
  120. "jf_cfs",
  121. "else_suite_opt",
  122. "opt_come_from_except",
  123. ),
  124. ),
  125. (
  126. "ifelsestmt",
  127. (
  128. "testexpr",
  129. "stmts",
  130. "jf_cfs",
  131. "\\e_else_suite_opt",
  132. "\\e_opt_come_from_except",
  133. ),
  134. ),
  135. (
  136. "ifelsestmt",
  137. (
  138. "testexpr",
  139. "stmts",
  140. "jf_cfs",
  141. "\\e_else_suite_opt",
  142. "opt_come_from_except",
  143. ),
  144. ),
  145. ]
  146. )
  147. def ifelsestmt(self, lhs, n, rule, tree, tokens, first, last):
  148. if (last + 1) < n and tokens[last + 1] == "COME_FROM_LOOP" and lhs != "ifelsestmtc":
  149. # ifelsestmt jumped outside of loop. No good.
  150. return True
  151. # print("XXX", first, last)
  152. # for t in range(first, last):
  153. # print(tokens[t])
  154. # print("=" * 30)
  155. first_offset = tokens[first].off2int()
  156. if rule not in IFELSE_STMT_RULES:
  157. # print("XXX", rule)
  158. return False
  159. # Avoid if/else where the "then" is a "raise_stmt1" for an
  160. # assert statement. Parse this as an "assert" instead.
  161. stmts = tree[1]
  162. if stmts in ("c_stmts",) and len(stmts) == 1:
  163. raise_stmt1 = stmts[0]
  164. if raise_stmt1 == "raise_stmt1" and raise_stmt1[0] in ("LOAD_ASSERT",):
  165. return True
  166. # Make sure all the offsets from the "COME_FROMs" at the
  167. # end of the "if" come from somewhere inside the "if".
  168. # Since the come_froms are ordered so that lowest
  169. # offset COME_FROM is last, it is sufficient to test
  170. # just the last one.
  171. if len(tree) == 5:
  172. end_come_froms = tree[-1]
  173. if end_come_froms.kind != "else_suite" and self.version >= (3, 0):
  174. if end_come_froms == "opt_come_from_except" and len(end_come_froms) > 0:
  175. end_come_froms = end_come_froms[0]
  176. if not isinstance(end_come_froms, Token):
  177. if len(end_come_froms):
  178. return first_offset > end_come_froms[-1].attr
  179. elif first_offset > end_come_froms.attr:
  180. return True
  181. # FIXME: There is weirdness in the grammar we need to work around.
  182. # we need to clean up the grammar.
  183. if self.version < (3, 0):
  184. last_token = tree[-1]
  185. else:
  186. last_token = tokens[last]
  187. if last_token == "COME_FROM" and first_offset > last_token.attr:
  188. if (
  189. self.version < (3, 0)
  190. and self.insts[self.offset2inst_index[last_token.attr]].opname
  191. != "SETUP_LOOP"
  192. ):
  193. return True
  194. testexpr = tree[0]
  195. # Check that the condition portion of the "if"
  196. # jumps to the "else" part.
  197. if testexpr[0] in ("testtrue", "testfalse"):
  198. if_condition = testexpr[0]
  199. else_suite = tree[3]
  200. assert else_suite.kind.startswith("else_suite")
  201. if len(if_condition) > 1 and if_condition[1].kind.startswith("jmp_"):
  202. if last == n:
  203. last -= 1
  204. jmp = if_condition[1]
  205. if self.version >= (2, 7):
  206. jmp_target = jmp[0].attr
  207. else:
  208. jmp_target = int(jmp[0].pattr)
  209. # Below we check that jmp_target is jumping to a feasible
  210. # location. It should be to the transition after the "then"
  211. # block and to the beginning of the "else" block.
  212. # However the "if/else" is inside a loop the false test can be
  213. # back to the loop.
  214. # FIXME: the below logic for jf_cfs could probably be
  215. # simplified.
  216. jump_else_end = tree[2]
  217. if jump_else_end == "jf_cf_pop":
  218. jump_else_end = jump_else_end[0]
  219. if jump_else_end == "JUMP_FORWARD":
  220. endif_target = int(jump_else_end.pattr)
  221. last_offset = tokens[last].off2int()
  222. if endif_target != last_offset:
  223. return True
  224. last_offset = tokens[last].off2int(prefer_last=False)
  225. if jmp_target == last_offset:
  226. # jmp_target should be jumping to the end of the if/then/else
  227. # but is it jumping to the beginning of the "else"
  228. return True
  229. if (
  230. jump_else_end in ("jf_cfs", "jump_forward_else")
  231. and jump_else_end[0] == "JUMP_FORWARD"
  232. ):
  233. # If the "else" jump jumps before the end of the the "if .. else end", then this
  234. # is not this kind of "ifelsestmt".
  235. jump_else_forward = jump_else_end[0]
  236. jump_else_forward_target = jump_else_forward.attr
  237. if jump_else_forward_target < last_offset:
  238. return True
  239. pass
  240. if (
  241. jump_else_end in ("jb_elsec", "jb_elsel", "jf_cfs", "jb_cfs")
  242. and jump_else_end[-1] == "COME_FROM"
  243. ):
  244. if jump_else_end[-1].off2int() != jmp_target:
  245. return True
  246. if first_offset > jmp_target:
  247. # A backward or loop jump from the end of an "else"
  248. # clause before the beginning of the "if" test is okay
  249. # only if we are trying to match or reduce an "if"
  250. # statement of the kind that can occur only inside a
  251. # loop construct.
  252. if lhs in ("ifelsestmtl", "ifelsestmtc"):
  253. jump_false = jmp
  254. if (
  255. tree[2].kind in ("JUMP_FORWARD", "JUMP_ABSOLUTE")
  256. and jump_false == "jmp_false"
  257. and len(else_suite) == 1
  258. ):
  259. suite_stmts = else_suite[0]
  260. continue_stmt = suite_stmts[0]
  261. if (
  262. suite_stmts in ("suite_stmts", "c_stmts")
  263. and len(suite_stmts) == 1
  264. and continue_stmt == "continue"
  265. and jump_false[0].attr == continue_stmt[0].attr
  266. ):
  267. # for ...:
  268. # if ...:
  269. # ...
  270. # else:
  271. # continue
  272. return False
  273. return True
  274. return (jmp_target > last_offset) and tokens[last] != "JUMP_FORWARD"
  275. return False