| # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 |
| # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt |
| |
| """Bytecode manipulation for coverage.py""" |
| |
| import opcode |
| import types |
| |
| from coverage.backward import byte_to_int |
| |
| |
| class ByteCode(object): |
| """A single bytecode.""" |
| def __init__(self): |
| # The offset of this bytecode in the code object. |
| self.offset = -1 |
| |
| # The opcode, defined in the `opcode` module. |
| self.op = -1 |
| |
| # The argument, a small integer, whose meaning depends on the opcode. |
| self.arg = -1 |
| |
| # The offset in the code object of the next bytecode. |
| self.next_offset = -1 |
| |
| # The offset to jump to. |
| self.jump_to = -1 |
| |
| |
| class ByteCodes(object): |
| """Iterator over byte codes in `code`. |
| |
| This handles the logic of EXTENDED_ARG byte codes internally. Those byte |
| codes are not returned by this iterator. |
| |
| Returns `ByteCode` objects. |
| |
| """ |
| def __init__(self, code): |
| self.code = code |
| |
| def __getitem__(self, i): |
| return byte_to_int(self.code[i]) |
| |
| def __iter__(self): |
| offset = 0 |
| ext_arg = 0 |
| while offset < len(self.code): |
| bc = ByteCode() |
| bc.op = self[offset] |
| bc.offset = offset |
| |
| next_offset = offset+1 |
| if bc.op >= opcode.HAVE_ARGUMENT: |
| bc.arg = ext_arg + self[offset+1] + 256*self[offset+2] |
| next_offset += 2 |
| |
| label = -1 |
| if bc.op in opcode.hasjrel: |
| label = next_offset + bc.arg |
| elif bc.op in opcode.hasjabs: |
| label = bc.arg |
| bc.jump_to = label |
| |
| bc.next_offset = offset = next_offset |
| if bc.op == opcode.EXTENDED_ARG: |
| ext_arg = bc.arg * 256*256 |
| else: |
| ext_arg = 0 |
| yield bc |
| |
| |
| class CodeObjects(object): |
| """Iterate over all the code objects in `code`.""" |
| def __init__(self, code): |
| self.stack = [code] |
| |
| def __iter__(self): |
| while self.stack: |
| # We're going to return the code object on the stack, but first |
| # push its children for later returning. |
| code = self.stack.pop() |
| for c in code.co_consts: |
| if isinstance(c, types.CodeType): |
| self.stack.append(c) |
| yield code |