-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathbytecode.py
More file actions
118 lines (103 loc) · 3.58 KB
/
bytecode.py
File metadata and controls
118 lines (103 loc) · 3.58 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
"""
Bytecode handling classes and functions for use by the flow space.
"""
from rpython.tool.stdlib_opcode import host_bytecode_spec
from opcode import EXTENDED_ARG, HAVE_ARGUMENT
import opcode
from rpython.flowspace.argument import Signature
CO_GENERATOR = 0x0020
CO_VARARGS = 0x0004
CO_VARKEYWORDS = 0x0008
def cpython_code_signature(code):
"([list-of-arg-names], vararg-name-or-None, kwarg-name-or-None)."
argcount = code.co_argcount
argnames = list(code.co_varnames[:argcount])
if code.co_flags & CO_VARARGS:
varargname = code.co_varnames[argcount]
argcount += 1
else:
varargname = None
if code.co_flags & CO_VARKEYWORDS:
kwargname = code.co_varnames[argcount]
argcount += 1
else:
kwargname = None
return Signature(argnames, varargname, kwargname)
class BytecodeCorruption(Exception):
pass
class HostCode(object):
"""
A wrapper around a native code object of the host interpreter
"""
opnames = host_bytecode_spec.method_names
def __init__(self, argcount, nlocals, stacksize, flags,
code, consts, names, varnames, filename,
name, firstlineno, lnotab, freevars):
"""Initialize a new code object"""
assert nlocals >= 0
self.co_argcount = argcount
self.co_nlocals = nlocals
self.co_stacksize = stacksize
self.co_flags = flags
self.co_code = code
self.consts = consts
self.names = names
self.co_varnames = varnames
self.co_freevars = freevars
self.co_filename = filename
self.co_name = name
self.co_firstlineno = firstlineno
self.co_lnotab = lnotab
self.signature = cpython_code_signature(self)
@classmethod
def _from_code(cls, code):
"""Initialize the code object from a real (CPython) one.
"""
return cls(code.co_argcount,
code.co_nlocals,
code.co_stacksize,
code.co_flags,
code.co_code,
list(code.co_consts),
list(code.co_names),
list(code.co_varnames),
code.co_filename,
code.co_name,
code.co_firstlineno,
code.co_lnotab,
list(code.co_freevars))
@property
def formalargcount(self):
"""Total number of arguments passed into the frame, including *vararg
and **varkwarg, if they exist."""
return self.signature.scope_length()
def read(self, offset):
"""
Decode the instruction starting at position ``offset``.
Returns (next_offset, opname, oparg).
"""
co_code = self.co_code
opnum = ord(co_code[offset])
next_offset = offset + 1
if opnum >= HAVE_ARGUMENT:
lo = ord(co_code[next_offset])
hi = ord(co_code[next_offset + 1])
next_offset += 2
oparg = (hi * 256) | lo
else:
oparg = 0
while opnum == EXTENDED_ARG:
opnum = ord(co_code[next_offset])
if opnum < HAVE_ARGUMENT:
raise BytecodeCorruption
lo = ord(co_code[next_offset + 1])
hi = ord(co_code[next_offset + 2])
next_offset += 3
oparg = (oparg * 65536) | (hi * 256) | lo
if opnum in opcode.hasjrel:
oparg += next_offset
opname = self.opnames[opnum]
return next_offset, opname, oparg
@property
def is_generator(self):
return bool(self.co_flags & CO_GENERATOR)