This repository was archived by the owner on Jan 4, 2026. It is now read-only.
forked from MatthieuDartiailh/bytecode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbytecode.py
More file actions
128 lines (105 loc) · 4.07 KB
/
bytecode.py
File metadata and controls
128 lines (105 loc) · 4.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# alias to keep the 'bytecode' variable free
import bytecode as _bytecode
from bytecode.instr import UNSET, Label, SetLineno, Instr
class BaseBytecode:
def __init__(self):
self.argcount = 0
self.kwonlyargcount = 0
# FIXME: insane and safe value until _ConvertBytecodeToConcrete is able
# to compute the value itself
self._stacksize = 256
# FIXME: use something higher level? make it private?
self.flags = 0
self.first_lineno = 1
self.name = '<module>'
self.filename = '<string>'
self.docstring = UNSET
self.cellvars = []
# we cannot recreate freevars from instructions because of super()
# special-case
self.freevars = []
def _copy_attr_from(self, bytecode):
self.argcount = bytecode.argcount
self.kwonlyargcount = bytecode.kwonlyargcount
self._stacksize = bytecode._stacksize
self.flags = bytecode.flags
self.first_lineno = bytecode.first_lineno
self.name = bytecode.name
self.filename = bytecode.filename
self.docstring = bytecode.docstring
self.cellvars = list(bytecode.cellvars)
self.freevars = list(bytecode.freevars)
def __eq__(self, other):
if type(self) != type(other):
return False
if self.argcount != other.argcount:
return False
if self.kwonlyargcount != other.kwonlyargcount:
return False
if self._stacksize != other._stacksize:
return False
if self.flags != other.flags:
return False
if self.first_lineno != other.first_lineno:
return False
if self.filename != other.filename:
return False
if self.name != other.name:
return False
if self.docstring != other.docstring:
return False
if self.cellvars != other.cellvars:
return False
if self.freevars != other.freevars:
return False
return True
class _InstrList(list):
def _flat(self):
instructions = []
labels = {}
jumps = []
offset = 0
for index, instr in enumerate(self):
if isinstance(instr, Label):
instructions.append('label_instr%s' % index)
labels[instr] = offset
else:
if isinstance(instr, Instr) and isinstance(instr.arg, Label):
target_label = instr.arg
instr = _bytecode.ConcreteInstr(instr.name, 0,
lineno=instr.lineno)
jumps.append((target_label, instr))
instructions.append(instr)
offset += 1
for target_label, instr in jumps:
instr.arg = labels[target_label]
return instructions
def __eq__(self, other):
if not isinstance(other, _InstrList):
other = _InstrList(other)
return (self._flat() == other._flat())
class Bytecode(_InstrList, BaseBytecode):
def __init__(self, instructions=None):
BaseBytecode.__init__(self)
if instructions is not None:
self.extend(instructions)
self.argnames = []
def __iter__(self):
instructions = super().__iter__()
for instr in instructions:
if not isinstance(instr, (Label, SetLineno, Instr,
_bytecode.ConcreteInstr)):
raise ValueError("Bytecode must only contain Label, "
"SetLineno, Instr and ConcreteInstr objects, "
"but %s was found"
% instr.__class__.__name__)
yield instr
@staticmethod
def from_code(code):
concrete = _bytecode.ConcreteBytecode.from_code(code)
return concrete.to_bytecode()
def to_code(self):
return self.to_concrete_bytecode().to_code()
def to_concrete_bytecode(self):
converter = _bytecode._ConvertBytecodeToConcrete(self)
return converter.to_concrete_bytecode()