or_check.py 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. # Copyright (c) 2020 Rocky Bernstein
  2. ASSERT_OPS = frozenset(["LOAD_ASSERT", "RAISE_VARARGS_1"])
  3. def or_check(self, lhs, n, rule, ast, tokens, first, last):
  4. rhs = rule[1]
  5. # print("XXX", first, last, rule)
  6. # for t in range(first, last): print(tokens[t])
  7. # print("="*40)
  8. if rhs[0:2] in (("expr_jt", "expr"),
  9. ("expr_jitop", "expr"),
  10. ("expr_jit", "expr")):
  11. if tokens[last] in ASSERT_OPS or tokens[last-1] in ASSERT_OPS:
  12. return True
  13. # The following test is be the most accurate. It prevents "or" from being
  14. # mistake for part of an "assert".
  15. # There one might conceivably be "expr or AssertionError" code, but the
  16. # likelihood of that is vanishingly small.
  17. # The below then is useful until we get better control-flow analysis.
  18. # Note it is too hard in the scanner right nowto turn the LOAD_GLOBAL into
  19. # int LOAD_ASSERT, however in 3.9ish code generation does this by default.
  20. load_global = tokens[last - 1]
  21. if load_global == "LOAD_GLOBAL" and load_global.attr == "AssertionError":
  22. return True
  23. first_offset = tokens[first].off2int()
  24. expr_jt = ast[0]
  25. if expr_jt == "expr_jitop":
  26. jump_true = expr_jt[1]
  27. else:
  28. jump_true = expr_jt[1][0]
  29. jmp_true_target = jump_true.attr
  30. last_token = tokens[last]
  31. last_token_offset = last_token.off2int()
  32. # FIXME: use instructions for all of this
  33. if jmp_true_target < first_offset:
  34. return False
  35. elif jmp_true_target < last_token_offset:
  36. return True
  37. # If the jmp is backwards
  38. if last_token == "POP_JUMP_IF_FALSE" and not self.version[:2] in ((2, 7), (3, 5), (3, 6)):
  39. if last_token.attr < last_token_offset:
  40. # For a backwards loop, well compare to the instruction *after*
  41. # then POP_JUMP...
  42. last_token = tokens[last + 1]
  43. # HACK alert 3 is the Python < 3.6ish thing.
  44. # Convert to using instructions
  45. return not (
  46. (last_token_offset <= jmp_true_target <= last_token_offset + 3)
  47. or jmp_true_target < tokens[first].off2int()
  48. )
  49. elif last_token == "JUMP_FORWARD" and expr_jt.kind != "expr_jitop":
  50. # "or" has to fall through to the next statement
  51. # FIXME: use instructions for all of this
  52. return True
  53. return False