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)
|