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