226 lines
7.4 KiB
Python
226 lines
7.4 KiB
Python
#! /usr/bin/env python
|
|
# ______________________________________________________________________
|
|
|
|
import dis
|
|
import opcode
|
|
|
|
# ______________________________________________________________________
|
|
# Module data
|
|
|
|
hasjump = opcode.hasjrel + opcode.hasjabs
|
|
hascbranch = [op for op in hasjump
|
|
if 'IF' in opcode.opname[op]
|
|
or opcode.opname[op] in ('FOR_ITER', 'SETUP_LOOP')]
|
|
|
|
# Since the actual opcode value may change, manage opcode abstraction
|
|
# data by opcode name.
|
|
|
|
OPCODE_MAP = {
|
|
'BINARY_ADD': (2, 1, None),
|
|
'BINARY_AND': (2, 1, None),
|
|
'BINARY_DIVIDE': (2, 1, None),
|
|
'BINARY_FLOOR_DIVIDE': (2, 1, None),
|
|
'BINARY_LSHIFT': (2, 1, None),
|
|
'BINARY_MODULO': (2, 1, None),
|
|
'BINARY_MULTIPLY': (2, 1, None),
|
|
'BINARY_OR': (2, 1, None),
|
|
'BINARY_POWER': (2, 1, None),
|
|
'BINARY_RSHIFT': (2, 1, None),
|
|
'BINARY_SUBSCR': (2, 1, None),
|
|
'BINARY_SUBTRACT': (2, 1, None),
|
|
'BINARY_TRUE_DIVIDE': (2, 1, None),
|
|
'BINARY_XOR': (2, 1, None),
|
|
'BREAK_LOOP': (0, None, 1),
|
|
'BUILD_CLASS': (None, None, None),
|
|
'BUILD_LIST': (-1, 1, None),
|
|
'BUILD_MAP': (None, None, None),
|
|
'BUILD_SET': (None, None, None),
|
|
'BUILD_SLICE': (None, None, None),
|
|
'BUILD_TUPLE': (-1, 1, None),
|
|
'CALL_FUNCTION': (-2, 1, None),
|
|
'CALL_FUNCTION_KW': (-3, 1, None),
|
|
'CALL_FUNCTION_VAR': (-3, 1, None),
|
|
'CALL_FUNCTION_VAR_KW': (-4, 1, None),
|
|
'COMPARE_OP': (2, 1, None),
|
|
'CONTINUE_LOOP': (None, None, None),
|
|
'DELETE_ATTR': (1, None, 1),
|
|
'DELETE_DEREF': (None, None, None),
|
|
'DELETE_FAST': (0, None, 1),
|
|
'DELETE_GLOBAL': (0, None, 1),
|
|
'DELETE_NAME': (0, None, 1),
|
|
'DELETE_SLICE+0': (1, None, 1),
|
|
'DELETE_SLICE+1': (2, None, 1),
|
|
'DELETE_SLICE+2': (2, None, 1),
|
|
'DELETE_SLICE+3': (3, None, 1),
|
|
'DELETE_SUBSCR': (2, None, 1),
|
|
'DUP_TOP': (None, None, None),
|
|
'DUP_TOPX': (None, None, None),
|
|
'DUP_TOP_TWO': (None, None, None),
|
|
'END_FINALLY': (None, None, None),
|
|
'EXEC_STMT': (None, None, None),
|
|
'EXTENDED_ARG': (None, None, None),
|
|
'FOR_ITER': (1, 1, 1),
|
|
'GET_ITER': (1, 1, None),
|
|
'IMPORT_FROM': (None, None, None),
|
|
'IMPORT_NAME': (None, None, None),
|
|
'IMPORT_STAR': (1, None, 1),
|
|
'INPLACE_ADD': (2, 1, None),
|
|
'INPLACE_AND': (2, 1, None),
|
|
'INPLACE_DIVIDE': (2, 1, None),
|
|
'INPLACE_FLOOR_DIVIDE': (2, 1, None),
|
|
'INPLACE_LSHIFT': (2, 1, None),
|
|
'INPLACE_MODULO': (2, 1, None),
|
|
'INPLACE_MULTIPLY': (2, 1, None),
|
|
'INPLACE_OR': (2, 1, None),
|
|
'INPLACE_POWER': (2, 1, None),
|
|
'INPLACE_RSHIFT': (2, 1, None),
|
|
'INPLACE_SUBTRACT': (2, 1, None),
|
|
'INPLACE_TRUE_DIVIDE': (2, 1, None),
|
|
'INPLACE_XOR': (2, 1, None),
|
|
'JUMP_ABSOLUTE': (0, None, 1),
|
|
'JUMP_FORWARD': (0, None, 1),
|
|
'JUMP_IF_FALSE': (1, 1, 1),
|
|
'JUMP_IF_FALSE_OR_POP': (None, None, None),
|
|
'JUMP_IF_TRUE': (1, 1, 1),
|
|
'JUMP_IF_TRUE_OR_POP': (None, None, None),
|
|
'LIST_APPEND': (2, 0, 1),
|
|
'LOAD_ATTR': (1, 1, None),
|
|
'LOAD_BUILD_CLASS': (None, None, None),
|
|
'LOAD_CLOSURE': (None, None, None),
|
|
'LOAD_CONST': (0, 1, None),
|
|
'LOAD_DEREF': (0, 1, None),
|
|
'LOAD_FAST': (0, 1, None),
|
|
'LOAD_GLOBAL': (0, 1, None),
|
|
'LOAD_LOCALS': (None, None, None),
|
|
'LOAD_NAME': (0, 1, None),
|
|
'MAKE_CLOSURE': (None, None, None),
|
|
'MAKE_FUNCTION': (-2, 1, None),
|
|
'MAP_ADD': (None, None, None),
|
|
'NOP': (0, None, None),
|
|
'POP_BLOCK': (0, None, 1),
|
|
'POP_EXCEPT': (None, None, None),
|
|
'POP_JUMP_IF_FALSE': (1, None, 1),
|
|
'POP_JUMP_IF_TRUE': (1, None, 1),
|
|
'POP_TOP': (1, None, 1),
|
|
'PRINT_EXPR': (1, None, 1),
|
|
'PRINT_ITEM': (1, None, 1),
|
|
'PRINT_ITEM_TO': (2, None, 1),
|
|
'PRINT_NEWLINE': (0, None, 1),
|
|
'PRINT_NEWLINE_TO': (1, None, 1),
|
|
'RAISE_VARARGS': (None, None, None),
|
|
'RETURN_VALUE': (1, None, 1),
|
|
'ROT_FOUR': (None, None, None),
|
|
'ROT_THREE': (None, None, None),
|
|
'ROT_TWO': (None, None, None),
|
|
'SETUP_EXCEPT': (None, None, None),
|
|
'SETUP_FINALLY': (None, None, None),
|
|
'SETUP_LOOP': (None, None, None),
|
|
'SETUP_WITH': (None, None, None),
|
|
'SET_ADD': (None, None, None),
|
|
'SLICE+0': (1, 1, None),
|
|
'SLICE+1': (2, 1, None),
|
|
'SLICE+2': (2, 1, None),
|
|
'SLICE+3': (3, 1, None),
|
|
'STOP_CODE': (None, None, None),
|
|
'STORE_ATTR': (2, None, 1),
|
|
'STORE_DEREF': (1, 0, 1),
|
|
'STORE_FAST': (1, None, 1),
|
|
'STORE_GLOBAL': (1, None, 1),
|
|
'STORE_LOCALS': (None, None, None),
|
|
'STORE_MAP': (1, None, 1),
|
|
'STORE_NAME': (1, None, 1),
|
|
'STORE_SLICE+0': (1, None, 1),
|
|
'STORE_SLICE+1': (2, None, 1),
|
|
'STORE_SLICE+2': (2, None, 1),
|
|
'STORE_SLICE+3': (3, None, 1),
|
|
'STORE_SUBSCR': (3, None, 1),
|
|
'UNARY_CONVERT': (1, 1, None),
|
|
'UNARY_INVERT': (1, 1, None),
|
|
'UNARY_NEGATIVE': (1, 1, None),
|
|
'UNARY_NOT': (1, 1, None),
|
|
'UNARY_POSITIVE': (1, 1, None),
|
|
'UNPACK_EX': (None, None, None),
|
|
'UNPACK_SEQUENCE': (None, None, None),
|
|
'WITH_CLEANUP': (None, None, None),
|
|
'YIELD_VALUE': (1, None, 1),
|
|
}
|
|
|
|
# ______________________________________________________________________
|
|
# Module functions
|
|
|
|
def itercode(code, start = 0):
|
|
"""Return a generator of byte-offset, opcode, and argument
|
|
from a byte-code-string
|
|
"""
|
|
i = 0
|
|
extended_arg = 0
|
|
if isinstance(code[0], str):
|
|
code = [ord(c) for c in code]
|
|
n = len(code)
|
|
while i < n:
|
|
op = code[i]
|
|
num = i + start
|
|
i = i + 1
|
|
oparg = None
|
|
if op >= opcode.HAVE_ARGUMENT:
|
|
oparg = code[i] + (code[i + 1] * 256) + extended_arg
|
|
extended_arg = 0
|
|
i = i + 2
|
|
if op == opcode.EXTENDED_ARG:
|
|
extended_arg = oparg * 65536
|
|
|
|
delta = yield num, op, oparg
|
|
if delta is not None:
|
|
abs_rel, dst = delta
|
|
assert abs_rel == 'abs' or abs_rel == 'rel'
|
|
i = dst if abs_rel == 'abs' else i + dst
|
|
|
|
# ______________________________________________________________________
|
|
|
|
def extendlabels(code, labels = None):
|
|
"""Extend the set of jump target labels to account for the
|
|
passthrough targets of conditional branches.
|
|
|
|
This allows us to create a control flow graph where there is at
|
|
most one branch per basic block.
|
|
"""
|
|
if labels is None:
|
|
labels = []
|
|
if isinstance(code[0], str):
|
|
code = [ord(c) for c in code]
|
|
n = len(code)
|
|
i = 0
|
|
while i < n:
|
|
op = code[i]
|
|
i += 1
|
|
if op >= dis.HAVE_ARGUMENT:
|
|
i += 2
|
|
label = -1
|
|
if op in hasjump:
|
|
label = i
|
|
if label >= 0:
|
|
if label not in labels:
|
|
labels.append(label)
|
|
elif op == opcode.opmap['BREAK_LOOP']:
|
|
if i not in labels:
|
|
labels.append(i)
|
|
return labels
|
|
|
|
# ______________________________________________________________________
|
|
|
|
def get_code_object (func):
|
|
return getattr(func, '__code__', getattr(func, 'func_code', None))
|
|
|
|
# ______________________________________________________________________
|
|
|
|
def build_basic_blocks (co_obj):
|
|
co_code = co_obj.co_code
|
|
labels = extendlabels(co_code, dis.findlabels(co_code))
|
|
labels.sort()
|
|
blocks = dict((index, list(itercode(co_code[index:next_index], index)))
|
|
for index, next_index in zip([0] + labels,
|
|
labels + [len(co_code)]))
|
|
return blocks
|
|
|
|
# ______________________________________________________________________
|
|
# End of opcode_util.py
|