(self, lhs, n, rule, ast, tokens, first, last)
| 2 | |
| 3 | |
| 4 | def while1stmt(self, lhs, n, rule, ast, tokens, first, last): |
| 5 | |
| 6 | # If there is a fall through to the COME_FROM_LOOP, then this is |
| 7 | # not a while 1. So the instruction before should either be a |
| 8 | # JUMP_BACK or the instruction before should not be the target of a |
| 9 | # jump. (Well that last clause i not quite right; that target could be |
| 10 | # from dead code. Ugh. We need a more uniform control flow analysis.) |
| 11 | if last == n or tokens[last - 1] == "COME_FROM_LOOP": |
| 12 | cfl = last - 1 |
| 13 | else: |
| 14 | cfl = last |
| 15 | assert tokens[cfl] == "COME_FROM_LOOP" |
| 16 | |
| 17 | for loop_end in range(cfl - 1, first, -1): |
| 18 | if tokens[loop_end] != "POP_BLOCK": |
| 19 | break |
| 20 | if tokens[loop_end].kind not in ("JUMP_BACK", "RETURN_VALUE", "RAISE_VARARGS_1"): |
| 21 | if not tokens[loop_end].kind.startswith("COME_FROM"): |
| 22 | return True |
| 23 | # Check that the SETUP_LOOP jumps to the offset after the |
| 24 | # COME_FROM_LOOP |
| 25 | if 0 <= last and tokens[last] in ("COME_FROM_LOOP", "JUMP_BACK"): |
| 26 | # jump_back should be right before COME_FROM_LOOP? |
| 27 | last += 1 |
| 28 | if last == n: |
| 29 | last -= 1 |
| 30 | offset = tokens[last].off2int() |
| 31 | assert tokens[first] == "SETUP_LOOP" |
| 32 | |
| 33 | # Scan for jumps out of the loop. Skip the initial "SETUP_LOOP" instruction. |
| 34 | # If there is a JUMP_BACK at the end, jumping to that is not breaking out |
| 35 | # of the loop. However after that, any "POP_BLOCK"s or "COME_FROM_LOOP"s |
| 36 | # are considered to break out of the loop. |
| 37 | if tokens[loop_end] == "JUMP_BACK": |
| 38 | loop_end += 1 |
| 39 | loop_end_offset = tokens[loop_end].off2int(prefer_last=False) |
| 40 | for t in range(first + 1, loop_end): |
| 41 | token = tokens[t] |
| 42 | # token could be a pseudo-op like "LOAD_STR", which is not in |
| 43 | # token.opc. We will replace that with LOAD_CONST as an |
| 44 | # example of an instruction that is not in token.opc.JUMP_OPS |
| 45 | if token.opc.opmap.get(token.kind, "LOAD_CONST") in token.opc.JUMP_OPS: |
| 46 | if token.attr >= loop_end_offset: |
| 47 | return True |
| 48 | |
| 49 | # SETUP_LOOP location must jump either to the last token or the token after the last one |
| 50 | return tokens[first].attr not in (offset, offset + 2) |
no test coverage detected