我们从Python开源项目中,提取了以下39个代码示例,用于说明如何使用dis.get_instructions()。
def accessed_attributes_of_local(f, local_name): """ Get a list of attributes of ``local_name`` accessed by ``f``. The analysis performed by this function is conservative, meaning that it's not guaranteed to find **all** attributes used. """ used = set() # Find sequences of the form: LOAD_FAST(local_name), LOAD_ATTR(<name>). # This will find all usages of the form ``local_name.<name>``. # # It will **NOT** find usages in which ``local_name`` is aliased to # another name. for first, second in sliding_window(dis.get_instructions(f), 2): if first.opname == 'LOAD_FAST' and first.argval == local_name: if second.opname in ('LOAD_ATTR', 'STORE_ATTR'): used.add(second.argval) return used
def test_elim_jump_after_return1(self): # Eliminate dead code: jumps immediately after returns can't be reached def f(cond1, cond2): if cond1: return 1 if cond2: return 2 while 1: return 3 while 1: if cond1: return 4 return 5 return 6 self.assertNotInBytecode(f, 'JUMP_FORWARD') self.assertNotInBytecode(f, 'JUMP_ABSOLUTE') returns = [instr for instr in dis.get_instructions(f) if instr.opname == 'RETURN_VALUE'] self.assertEqual(len(returns), 6)
def test_constant_folding(self): # Issue #11244: aggressive constant folding. exprs = [ '3 * -5', '-3 * 5', '2 * (3 * 4)', '(2 * 3) * 4', '(-1, 2, 3)', '(1, -2, 3)', '(1, 2, -3)', '(1, 2, -3) * 6', 'lambda x: x in {(3 * -5) + (-1 - 6), (1, -2, 3) * 2, None}', ] for e in exprs: code = compile(e, '', 'single') for instr in dis.get_instructions(code): self.assertFalse(instr.opname.startswith('UNARY_')) self.assertFalse(instr.opname.startswith('BINARY_')) self.assertFalse(instr.opname.startswith('BUILD_'))
def _get_instructions(code_obj): if hasattr(dis, 'get_instructions'): return list(dis.get_instructions(code_obj)) instructions = [] instruction = None for byte in code_obj.co_code: byte = _six_ord(byte) if instruction is None: instruction = [byte] else: instruction.append(byte) if instruction[0] < dis.HAVE_ARGUMENT or len(instruction) == 3: op_code = instruction[0] op_name = dis.opname[op_code] if instruction[0] < dis.HAVE_ARGUMENT: instructions.append(_Instruction(op_code, op_name, None, None)) else: arg = instruction[1] instructions.append(_Instruction(op_code, op_name, arg, arg)) instruction = None return instructions
def assertInBytecode(self, x, opname, argval=_UNSPECIFIED): """Returns instr if op is found, otherwise throws AssertionError""" for instr in dis.get_instructions(x): if instr.opname == opname: if argval is _UNSPECIFIED or instr.argval == argval: return instr disassembly = self.get_disassembly_as_string(x) if argval is _UNSPECIFIED: msg = '%s not found in bytecode:\n%s' % (opname, disassembly) else: msg = '(%s,%r) not found in bytecode:\n%s' msg = msg % (opname, argval, disassembly) self.fail(msg)
def assertNotInBytecode(self, x, opname, argval=_UNSPECIFIED): """Throws AssertionError if op is found""" for instr in dis.get_instructions(x): if instr.opname == opname: disassembly = self.get_disassembly_as_string(co) if opargval is _UNSPECIFIED: msg = '%s occurs in bytecode:\n%s' % (opname, disassembly) elif instr.argval == argval: msg = '(%s,%r) occurs in bytecode:\n%s' msg = msg % (opname, argval, disassembly) self.fail(msg)
def test_folding_of_tuples_of_constants(self): for line, elem in ( ('a = 1,2,3', (1, 2, 3)), ('("a","b","c")', ('a', 'b', 'c')), ('a,b,c = 1,2,3', (1, 2, 3)), ('(None, 1, None)', (None, 1, None)), ('((1, 2), 3, 4)', ((1, 2), 3, 4)), ): code = compile(line,'','single') self.assertInBytecode(code, 'LOAD_CONST', elem) self.assertNotInBytecode(code, 'BUILD_TUPLE') # Long tuples should be folded too. code = compile(repr(tuple(range(10000))),'','single') self.assertNotInBytecode(code, 'BUILD_TUPLE') # One LOAD_CONST for the tuple, one for the None return value load_consts = [instr for instr in dis.get_instructions(code) if instr.opname == 'LOAD_CONST'] self.assertEqual(len(load_consts), 2) # Bug 1053819: Tuple of constants misidentified when presented with: # . . . opcode_with_arg 100 unary_opcode BUILD_TUPLE 1 . . . # The following would segfault upon compilation def crater(): (~[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ],)
def test_folding_of_binops_on_constants(self): for line, elem in ( ('a = 2+3+4', 9), # chained fold ('"@"*4', '@@@@'), # check string ops ('a="abc" + "def"', 'abcdef'), # check string ops ('a = 3**4', 81), # binary power ('a = 3*4', 12), # binary multiply ('a = 13//4', 3), # binary floor divide ('a = 14%4', 2), # binary modulo ('a = 2+3', 5), # binary add ('a = 13-4', 9), # binary subtract ('a = (12,13)[1]', 13), # binary subscr ('a = 13 << 2', 52), # binary lshift ('a = 13 >> 2', 3), # binary rshift ('a = 13 & 7', 5), # binary and ('a = 13 ^ 7', 10), # binary xor ('a = 13 | 7', 15), # binary or ): code = compile(line, '', 'single') self.assertInBytecode(code, 'LOAD_CONST', elem) for instr in dis.get_instructions(code): self.assertFalse(instr.opname.startswith('BINARY_')) # Verify that unfoldables are skipped code = compile('a=2+"b"', '', 'single') self.assertInBytecode(code, 'LOAD_CONST', 2) self.assertInBytecode(code, 'LOAD_CONST', 'b') # Verify that large sequences do not result from folding code = compile('a="x"*1000', '', 'single') self.assertInBytecode(code, 'LOAD_CONST', 1000)
def test_folding_of_unaryops_on_constants(self): for line, elem in ( ('-0.5', -0.5), # unary negative ('-0.0', -0.0), # -0.0 ('-(1.0-1.0)', -0.0), # -0.0 after folding ('-0', 0), # -0 ('~-2', 1), # unary invert ('+1', 1), # unary positive ): code = compile(line, '', 'single') self.assertInBytecode(code, 'LOAD_CONST', elem) for instr in dis.get_instructions(code): self.assertFalse(instr.opname.startswith('UNARY_')) # Check that -0.0 works after marshaling def negzero(): return -(1.0-1.0) for instr in dis.get_instructions(code): self.assertFalse(instr.opname.startswith('UNARY_')) # Verify that unfoldables are skipped for line, elem, opname in ( ('-"abc"', 'abc', 'UNARY_NEGATIVE'), ('~"abc"', 'abc', 'UNARY_INVERT'), ): code = compile(line, '', 'single') self.assertInBytecode(code, 'LOAD_CONST', elem) self.assertInBytecode(code, opname)
def test_elim_jump_to_return(self): # JUMP_FORWARD to RETURN --> RETURN def f(cond, true_value, false_value): return true_value if cond else false_value self.assertNotInBytecode(f, 'JUMP_FORWARD') self.assertNotInBytecode(f, 'JUMP_ABSOLUTE') returns = [instr for instr in dis.get_instructions(f) if instr.opname == 'RETURN_VALUE'] self.assertEqual(len(returns), 2)
def test_elim_jump_after_return2(self): # Eliminate dead code: jumps immediately after returns can't be reached def f(cond1, cond2): while 1: if cond1: return 4 self.assertNotInBytecode(f, 'JUMP_FORWARD') # There should be one jump for the while loop. returns = [instr for instr in dis.get_instructions(f) if instr.opname == 'JUMP_ABSOLUTE'] self.assertEqual(len(returns), 1) returns = [instr for instr in dis.get_instructions(f) if instr.opname == 'RETURN_VALUE'] self.assertEqual(len(returns), 2)
def test_default_first_line(self): actual = dis.get_instructions(simple) self.assertEqual(list(actual), expected_opinfo_simple)
def test_outer(self): actual = dis.get_instructions(outer, first_line=expected_outer_line) self.assertEqual(list(actual), expected_opinfo_outer)
def test_nested(self): with captured_stdout(): f = outer() actual = dis.get_instructions(f, first_line=expected_f_line) self.assertEqual(list(actual), expected_opinfo_f)
def test_doubly_nested(self): with captured_stdout(): inner = outer()() actual = dis.get_instructions(inner, first_line=expected_inner_line) self.assertEqual(list(actual), expected_opinfo_inner)
def test_jumpy(self): actual = dis.get_instructions(jumpy, first_line=expected_jumpy_line) self.assertEqual(list(actual), expected_opinfo_jumpy) # get_instructions has its own tests above, so can rely on it to validate # the object oriented API
def test_iteration(self): for obj in [_f, _C(1).__init__, "a=1", _f.__code__]: with self.subTest(obj=obj): via_object = list(dis.Bytecode(obj)) via_generator = list(dis.get_instructions(obj)) self.assertEqual(via_object, via_generator)
def instructions(self): if not self._instructions: self._instructions = list(dis.get_instructions(self)) return self._instructions
def print_codeobj(self, codeobj): dis.dis(codeobj) self.print_codeobj_attr(codeobj) print('!'*80) for st in dis.get_instructions(codeobj): print(st.offset, st, sep=' -> ') print('!' * 80) for code in codeobj.co_code: print(opcode.opname[code])
def get_instructions(code): """ Iterator parsing the bytecode into easy-usable minimal emulation of Python 3.4 `dis.Instruction` instances. """ # shortcuts HAVE_ARGUMENT = dis.HAVE_ARGUMENT EXTENDED_ARG = dis.EXTENDED_ARG class Instruction: # Minimal emulation of Python 3.4 dis.Instruction def __init__(self, opcode, oparg): self.opname = dis.opname[opcode] self.arg = oparg # opcode, argval, argrepr, offset, is_jump_target and # starts_line are not used by our code, so we leave them away # here. code = code.co_code extended_arg = 0 i = 0 n = len(code) while i < n: c = code[i] i = i + 1 op = _cOrd(c) if op >= HAVE_ARGUMENT: oparg = _cOrd(code[i]) + _cOrd(code[i + 1]) * 256 + extended_arg extended_arg = 0 i += 2 if op == EXTENDED_ARG: extended_arg = oparg*65536 else: oparg = None yield Instruction(op, oparg) #FIXME: Leverage this rather than magic numbers below.
def _walk_global_ops(code): """ Yield (opcode, argument number) tuples for all global-referencing instructions in *code*. """ for instr in dis.get_instructions(code): op = instr.opcode if op in GLOBAL_OPS: yield op, instr.arg
def get_instructions(f): return update_break_instruction(dis.get_instructions(f))
def _is_safe_generator(code): ''' Examine the code of an async generator to see if it appears unsafe with respect to async finalization. A generator is unsafe if it utilizes any of the following constructs: 1. Use of async-code in a finally block try: yield v finally: await coro() 2. Use of yield inside an async context manager async with m: ... yield v ... 3. Use of async-code in try-except try: yield v except Exception: await coro() ''' def _is_unsafe_block(instr, end_offset=-1): is_generator = False in_final = False is_unsafe = False for op in instr: if op.offset == end_offset: in_final = True if op.opname == 'YIELD_VALUE': is_generator = True if op.opname == 'END_FINALLY': return (is_generator, is_unsafe) if op.opname in {'SETUP_FINALLY', 'SETUP_EXCEPT', 'SETUP_ASYNC_WITH'}: is_g, is_u = _is_unsafe_block(instr, op.argval) is_generator |= is_g is_unsafe |= is_u if op.opname == 'YIELD_FROM' and is_generator and in_final: is_unsafe = True return (is_generator, is_unsafe) return not _is_unsafe_block(dis.get_instructions(code))[1]
def wrap_code(self, codeobj, codeobj_id=0): codes = [] constants = [ self.wrap_code(item, self.get_codeobj_id()) if isinstance(item, CodeType) else item for item in codeobj.co_consts ] update_offset = partial(self.calculate_offset, code=codeobj.co_code) for st in dis.get_instructions(codeobj): self.mark(codeobj_id, st) constants.append( lambda co_id=codeobj_id, opcode=st: self.visit(co_id, opcode) ) codes.extend(self.make_trace(len(constants) - 1)) codes.append(st.opcode) if st.opcode in opcode.hasjrel: current_position = update_offset(st.offset) taget_position = update_offset(st.argval) - self.TRACE_CODE_LEN - self.TRACE_CODE_LEN new_delta = taget_position - current_position codes.extend(self.make_args(new_delta - self.AGR_OP_LEN)) elif st.opcode in opcode.hasjabs: codes.extend(self.make_args( update_offset(st.arg) - self.TRACE_CODE_LEN - self.TRACE_CODE_LEN )) elif st.opcode >= opcode.HAVE_ARGUMENT: codes.extend(self.make_args(st.arg)) new_code = CodeType( codeobj.co_argcount, codeobj.co_kwonlyargcount, codeobj.co_nlocals, codeobj.co_stacksize + self.AGR_OP_LEN, codeobj.co_flags, bytes(codes), # codestring tuple(constants), # constants codeobj.co_names, codeobj.co_varnames, codeobj.co_filename, codeobj.co_name, codeobj.co_firstlineno, codeobj.co_lnotab, codeobj.co_freevars, codeobj.co_cellvars, ) return new_code