# Copyright (c) 2015-2018 by Rocky Bernstein """ Python 2.7 bytecode ingester. This massages tokenized 2.7 bytecode to make it more amenable for grammar parsing. """ from uncompyle6.scanners.scanner2 import Scanner2 from uncompyle6 import PYTHON3 if PYTHON3: import sys intern = sys.intern # bytecode verification, verify(), uses JUMP_OPs from here from xdis.opcodes import opcode_27 JUMP_OPS = opcode_27.JUMP_OPs class Scanner27(Scanner2): def __init__(self, show_asm=False, is_pypy=False): super(Scanner27, self).__init__(2.7, show_asm, is_pypy) # opcodes that start statements self.stmt_opcodes = frozenset([ self.opc.SETUP_LOOP, self.opc.BREAK_LOOP, self.opc.SETUP_FINALLY, self.opc.END_FINALLY, self.opc.SETUP_EXCEPT, self.opc.POP_BLOCK, self.opc.STORE_FAST, self.opc.DELETE_FAST, self.opc.STORE_DEREF, self.opc.STORE_GLOBAL, self.opc.DELETE_GLOBAL, self.opc.STORE_NAME, self.opc.DELETE_NAME, self.opc.STORE_ATTR, self.opc.DELETE_ATTR, self.opc.STORE_SUBSCR, self.opc.DELETE_SUBSCR, self.opc.RETURN_VALUE, self.opc.RAISE_VARARGS, self.opc.POP_TOP, self.opc.PRINT_EXPR, self.opc.PRINT_ITEM, self.opc.PRINT_NEWLINE, self.opc.PRINT_ITEM_TO, self.opc.PRINT_NEWLINE_TO, self.opc.CONTINUE_LOOP, self.opc.JUMP_ABSOLUTE, self.opc.EXEC_STMT, # New in 2.7 self.opc.SETUP_WITH, self.opc.STORE_SLICE_0, self.opc.STORE_SLICE_1, self.opc.STORE_SLICE_2, self.opc.STORE_SLICE_3, self.opc.DELETE_SLICE_0, self.opc.DELETE_SLICE_1, self.opc.DELETE_SLICE_2, self.opc.DELETE_SLICE_3, ]) # opcodes which expect a variable number pushed values and whose # count is in the opcode. For parsing we generally change the # opcode name to include that number. varargs_ops = set([ self.opc.BUILD_LIST, self.opc.BUILD_TUPLE, self.opc.BUILD_SLICE, self.opc.UNPACK_SEQUENCE, self.opc.MAKE_FUNCTION, self.opc.CALL_FUNCTION, self.opc.MAKE_CLOSURE, self.opc.CALL_FUNCTION_VAR, self.opc.CALL_FUNCTION_KW, self.opc.CALL_FUNCTION_VAR_KW, self.opc.DUP_TOPX, self.opc.RAISE_VARARGS, # New in Python 2.7 self.opc.BUILD_SET, self.opc.BUILD_MAP]) if is_pypy: varargs_ops.add(self.opc.CALL_METHOD) self.varargs_ops = frozenset(varargs_ops) # "setup" opcodes self.setup_ops = frozenset([ self.opc.SETUP_EXCEPT, self.opc.SETUP_FINALLY, # New in 2.7 self.opc.SETUP_WITH]) # opcodes that store values into a variable self.designator_ops = frozenset([ self.opc.STORE_FAST, self.opc.STORE_NAME, self.opc.STORE_GLOBAL, self.opc.STORE_DEREF, self.opc.STORE_ATTR, self.opc.STORE_SLICE_0, self.opc.STORE_SLICE_1, self.opc.STORE_SLICE_2, self.opc.STORE_SLICE_3, self.opc.STORE_SUBSCR, self.opc.UNPACK_SEQUENCE, self.opc.JUMP_ABSOLUTE ]) self.pop_jump_if_or_pop = frozenset([self.opc.JUMP_IF_FALSE_OR_POP, self.opc.JUMP_IF_TRUE_OR_POP]) return def patch_continue(self, tokens, offset, op): if op in (self.opc.JUMP_FORWARD, self.opc.JUMP_ABSOLUTE): # FIXME: this is a hack to catch stuff like: # for ... # try: ... # except: continue # the "continue" is not on a new line. n = len(tokens) if (n > 2 and tokens[-1].kind == 'JUMP_BACK' and self.code[offset+3] == self.opc.END_FINALLY): tokens[-1].kind = intern('CONTINUE') pass if __name__ == "__main__": from uncompyle6 import PYTHON_VERSION if PYTHON_VERSION == 2.7: import inspect co = inspect.currentframe().f_code tokens, customize = Scanner27().ingest(co) for t in tokens: print(t) pass else: print("Need to be Python 2.7 to demo; I am %s." % PYTHON_VERSION)