119 lines
3.2 KiB
Python
119 lines
3.2 KiB
Python
import compile
|
|
|
|
|
|
def disassemble(bytecode, indent='', pc=-1):
|
|
""" disassemble a bytecode object and print a readabable version of it"""
|
|
assert isinstance(bytecode, compile.Bytecode)
|
|
findlabeltargets = FindLabelTargets()
|
|
findlabeltargets.disassemble(bytecode)
|
|
disass = Disassembler(indent, findlabeltargets.targets)
|
|
disass.disassemble(bytecode, pc)
|
|
|
|
|
|
opcode2name = {}
|
|
for name, value in list(compile.__dict__.items()):
|
|
if name == name.upper() and isinstance(value, int):
|
|
opcode2name[value] = name
|
|
|
|
|
|
class AbstractDisassembler(object):
|
|
|
|
def read4(self, code, pc):
|
|
""" Converts 4 unicode characters to single 4 byte value """
|
|
highval = ord(code[pc + 3])
|
|
if highval >= 128:
|
|
highval -= 256
|
|
return (ord(code[pc]) |
|
|
(ord(code[pc + 1]) << 8) |
|
|
(ord(code[pc + 2]) << 16) |
|
|
(highval << 24))
|
|
|
|
def disassemble(self, bytecode, currpc=-1):
|
|
self.currpc = currpc
|
|
self.bytecode = bytecode
|
|
code = bytecode.code
|
|
pc = 0
|
|
while pc < len(code):
|
|
self.start(pc)
|
|
opcode = ord(code[pc])
|
|
pc += 1
|
|
if compile.isjump(opcode):
|
|
oparg = self.read4(code, pc)
|
|
pc += 4
|
|
elif compile.hasarg(opcode):
|
|
oparg = ord(code[pc])
|
|
pc += 1
|
|
if oparg >= 128:
|
|
if oparg > 128:
|
|
oparg -= 256
|
|
else:
|
|
oparg = self.read4(code, pc)
|
|
pc += 4
|
|
else:
|
|
oparg = None
|
|
self.pc = pc
|
|
self.end(opcode, oparg)
|
|
name = opcode2name[opcode]
|
|
method = getattr(self, name, self.dummy)
|
|
method(opcode, oparg)
|
|
|
|
def start(self, pc):
|
|
pass
|
|
|
|
def end(self, opcode, oparg):
|
|
pass
|
|
|
|
def dummy(self, opcode, oparg):
|
|
pass
|
|
|
|
|
|
class FindLabelTargets(AbstractDisassembler):
|
|
|
|
def __init__(self):
|
|
self.targets = {}
|
|
|
|
def JUMP_IF_FALSE(self, opcode, oparg):
|
|
self.targets[self.pc + oparg] = True
|
|
|
|
JUMP = JUMP_IF_FALSE
|
|
|
|
|
|
class Disassembler(AbstractDisassembler):
|
|
|
|
def __init__(self, indent, targets):
|
|
self.indent = indent
|
|
self.targets = targets
|
|
|
|
def start(self, pc):
|
|
if pc in self.targets:
|
|
print(self.indent, '>>', pc)
|
|
if pc == self.currpc:
|
|
print(self.indent, '->', pc)
|
|
|
|
def end(self, opcode, oparg):
|
|
print(self.indent, '\t', opcode2name[opcode], end=' ')
|
|
|
|
def JUMP_IF_FALSE(self, opcode, oparg):
|
|
print('\t', '-->', self.pc + oparg)
|
|
|
|
JUMP = JUMP_IF_FALSE
|
|
|
|
def ASSIGNMENT(self, opcode, oparg):
|
|
print('\t', repr(self.bytecode.symbols[oparg]))
|
|
|
|
METHOD_LOOKUP = ASSIGNMENT
|
|
ASSIGNMENT_APPEND_PARENT = ASSIGNMENT
|
|
GET_LOCAL = ASSIGNMENT
|
|
SET_LOCAL = ASSIGNMENT
|
|
|
|
def PRIMITIVE_METHOD_CALL(self, opcode, oparg):
|
|
import primitives
|
|
func = primitives.all_primitives[oparg]
|
|
print('\t', repr('$' + func.__qualname__))
|
|
|
|
def dummy(self, opcode, oparg):
|
|
if oparg is None:
|
|
print()
|
|
else:
|
|
print('\t', oparg)
|