|
1 """Bytecode manipulation for coverage.py""" |
|
2 |
|
3 import opcode, sys, types |
|
4 |
|
5 class ByteCode(object): |
|
6 """A single bytecode.""" |
|
7 def __init__(self): |
|
8 self.offset = -1 |
|
9 self.op = -1 |
|
10 self.arg = -1 |
|
11 self.next_offset = -1 |
|
12 self.jump_to = -1 |
|
13 |
|
14 |
|
15 class ByteCodes(object): |
|
16 """Iterator over byte codes in `code`. |
|
17 |
|
18 Returns `ByteCode` objects. |
|
19 |
|
20 """ |
|
21 def __init__(self, code): |
|
22 self.code = code |
|
23 self.offset = 0 |
|
24 |
|
25 if sys.hexversion > 0x03000000: |
|
26 def __getitem__(self, i): |
|
27 return self.code[i] |
|
28 else: |
|
29 def __getitem__(self, i): |
|
30 return ord(self.code[i]) |
|
31 |
|
32 def __iter__(self): |
|
33 return self |
|
34 |
|
35 def __next__(self): |
|
36 if self.offset >= len(self.code): |
|
37 raise StopIteration |
|
38 |
|
39 bc = ByteCode() |
|
40 bc.op = self[self.offset] |
|
41 bc.offset = self.offset |
|
42 |
|
43 next_offset = self.offset+1 |
|
44 if bc.op >= opcode.HAVE_ARGUMENT: |
|
45 bc.arg = self[self.offset+1] + 256*self[self.offset+2] |
|
46 next_offset += 2 |
|
47 |
|
48 label = -1 |
|
49 if bc.op in opcode.hasjrel: |
|
50 label = next_offset + bc.arg |
|
51 elif bc.op in opcode.hasjabs: |
|
52 label = bc.arg |
|
53 bc.jump_to = label |
|
54 |
|
55 bc.next_offset = self.offset = next_offset |
|
56 return bc |
|
57 |
|
58 next = __next__ # Py2k uses an old-style non-dunder name. |
|
59 |
|
60 |
|
61 class CodeObjects(object): |
|
62 """Iterate over all the code objects in `code`.""" |
|
63 def __init__(self, code): |
|
64 self.stack = [code] |
|
65 |
|
66 def __iter__(self): |
|
67 return self |
|
68 |
|
69 def __next__(self): |
|
70 if self.stack: |
|
71 # We're going to return the code object on the stack, but first |
|
72 # push its children for later returning. |
|
73 code = self.stack.pop() |
|
74 for c in code.co_consts: |
|
75 if isinstance(c, types.CodeType): |
|
76 self.stack.append(c) |
|
77 return code |
|
78 |
|
79 raise StopIteration |
|
80 |
|
81 next = __next__ |