def check(self, source, function=False): ref_code = get_code(source, function=function) code = ConcreteBytecode.from_code(ref_code).to_code() self.assertEqual(code, ref_code) code = Bytecode.from_code(ref_code).to_code() self.assertEqual(code, ref_code) bytecode = Bytecode.from_code(ref_code) blocks = ControlFlowGraph.from_bytecode(bytecode) code = blocks.to_bytecode().to_code() self.assertEqual(code, ref_code)
def optimize(self, code_obj): bytecode = Bytecode.from_code(code_obj) cfg = ControlFlowGraph.from_bytecode(bytecode) self.optimize_cfg(cfg) bytecode = cfg.to_bytecode() code = bytecode.to_code() return code
def test_from_code_load_fast(self): code = get_code(""" def func(): x = 33 y = x """, function=True) code = Bytecode.from_code(code) self.assertEqual(code, [Instr('LOAD_CONST', 33, lineno=2), Instr('STORE_FAST', 'x', lineno=2), Instr('LOAD_FAST', 'x', lineno=3), Instr('STORE_FAST', 'y', lineno=3), Instr('LOAD_CONST', None, lineno=3), Instr('RETURN_VALUE', lineno=3)])
def test_label3(self): """ When you delete ``extended_arg`` that have a value of 0, the following will fail when calling ``Bytecode.to_concrete_bytecode()`` because it cant find a label to correspond to the jump target """ code = get_code(""" def func(x): if x == 1: return x +1 elif x == 2: return x + 1 elif x == 3: return x + 1 elif x == 4: return x + 1 elif x == 5: return x + 1 elif x == 6: return x + 1 elif x == 7: return x + 1 elif x == 8: return x + 1 elif x == 9: return x + 1 elif x == 10: return x + 1 elif x == 11: return x + 1 elif x == 12: return x + 1 elif x == 13: return x + 1 elif x == 14: return x + 1 elif x == 15: return x + 1 elif x == 16: return x + 1 elif x == 17: return x + 1 return -1 """, function=True) code = Bytecode.from_code(code) concrete = code.to_concrete_bytecode() self.assertIsInstance(concrete, ConcreteBytecode)
def __init__(self, path: str, module_name='', to_import=['*']): self.path = path self.to_import = to_import self.module_name = module_name self._local_methods = [] source = open(path, 'rb') compiled_source = compile(source.read(), path, 'exec') self.bc = Bytecode.from_code(compiled_source) self.cfg = ControlFlowGraph.from_bytecode(self.bc) source.close() self.build()
def __init__(self, module, block, module_name, extra): self.module = module self.block = block self.module_name = module_name self._extra = extra self._id = uuid4() self.name = self.block[1].arg self.start_line_no = self.block[0].lineno self.code_object = self.block[0].arg # dis.dis(code_object) self.code, self.dictionary_defs = preprocess_method_body(self.code_object, self.name) self.bytecode = Bytecode.from_code(self.code) self.setup()
def test_from_code_load_fast(self): code = get_code(""" def func(): x = 33 y = x """, function=True) code = Bytecode.from_code(code) self.assertEqual(code, [ Instr('LOAD_CONST', 33, lineno=2), Instr('STORE_FAST', 'x', lineno=2), Instr('LOAD_FAST', 'x', lineno=3), Instr('STORE_FAST', 'y', lineno=3), Instr('LOAD_CONST', None, lineno=3), Instr('RETURN_VALUE', lineno=3) ])
def test_every_inner_setup_is_over_variable(bytecode_modifier, sample_inner): modified = bytecode_modifier.modify(sample_inner, inner=True) bc = Bytecode.from_code(modified) is_over_setup_instructions = bc[:4] assert is_over_setup_instructions[0].name == 'LOAD_NAME' assert is_over_setup_instructions[0].arg == bytecode_modifier._command assert is_over_setup_instructions[1].name == 'LOAD_CONST' assert is_over_setup_instructions[1].arg == DebugCommand.STEP_OVER assert is_over_setup_instructions[2].name == 'COMPARE_OP' assert is_over_setup_instructions[2].arg == Compare.EQ assert is_over_setup_instructions[3].name == 'STORE_NAME' assert is_over_setup_instructions[3].arg == 'is_over'
def test_from_code_freevars(self): ns = {} exec(textwrap.dedent(''' def create_func(): x = 1 def func(): return x return func func = create_func() '''), ns, ns) code = ns['func'].__code__ bytecode = Bytecode.from_code(code) self.assertEqual(bytecode, [Instr('LOAD_DEREF', FreeVar('x'), lineno=5), Instr('RETURN_VALUE', lineno=5)])
def support_recursion_bytecode(func): bytecodes = Bytecode.from_code(func.__code__) localvars = set() undefined = set() for inst in bytecodes: if inst.name == "STORE_FAST" and inst.arg.startswith("p"): localvars.add(inst.arg) elif inst.name == "LOAD_FAST" and inst.arg.startswith("p"): if inst.arg in localvars: undefined .add(inst.arg) ## define dummies def define_dummy_parser(v): code = [Instr("LOAD_GLOBAL", 'pFail'), Instr("LOAD_CONST", "null parser"), Instr("CALL_FUNCTION", 1), Instr("STORE_FAST", v)] return code result = [] for v in undefined: codes = define_dummy_parser(v) result.extend(codes) ## change assign to iadd def modify_store_bytecode(v): code = [Instr("LOAD_FAST", v), Instr("ROT_TWO"), Instr("INPLACE_ADD"), Instr("STORE_FAST", v)] return code for inst in bytecodes: if inst.name == "STORE_FAST" and \ inst.arg.startswith("p") and \ inst.arg in undefined: codes = modify_store_bytecode(inst.arg) result.extend(codes) else: result.append(inst) return Bytecode(result)
def test_from_code(self): code = get_code(""" if test: x = 1 else: x = 2 """) bytecode = Bytecode.from_code(code) label_else = Label() label_exit = Label() if sys.version_info < (3, 10): self.assertEqual( bytecode, [ Instr("LOAD_NAME", "test", lineno=1), Instr("POP_JUMP_IF_FALSE", label_else, lineno=1), Instr("LOAD_CONST", 1, lineno=2), Instr("STORE_NAME", "x", lineno=2), Instr("JUMP_FORWARD", label_exit, lineno=2), label_else, Instr("LOAD_CONST", 2, lineno=4), Instr("STORE_NAME", "x", lineno=4), label_exit, Instr("LOAD_CONST", None, lineno=4), Instr("RETURN_VALUE", lineno=4), ], ) # Control flow handling appears to have changed under Python 3.10 else: self.assertEqual( bytecode, [ Instr("LOAD_NAME", "test", lineno=1), Instr("POP_JUMP_IF_FALSE", label_else, lineno=1), Instr("LOAD_CONST", 1, lineno=2), Instr("STORE_NAME", "x", lineno=2), Instr("LOAD_CONST", None, lineno=2), Instr("RETURN_VALUE", lineno=2), label_else, Instr("LOAD_CONST", 2, lineno=4), Instr("STORE_NAME", "x", lineno=4), Instr("LOAD_CONST", None, lineno=4), Instr("RETURN_VALUE", lineno=4), ], )
def preprocess_method_body(source_code_obj, MethodName): src = inspect.getsource(source_code_obj) ast_tree = ast.parse(src) visitor = RewriteDicts() ast_tree = visitor.visit(ast_tree) ast.fix_missing_locations(ast_tree) updated_code = compile(ast_tree, filename='<ast>', mode='exec') bc = Bytecode.from_code(updated_code) dlist = visitor.updated_dicts RewriteDicts.updated_dicts = [] RewriteDicts.last_store_name = None return bc[0].arg, dlist
def exec_module(self, module): code = self.loader.get_code(module.__name__) if code is None: raise ImportError('cannot load module {!r} when get_code() ' 'returns None'.format(module.__name__)) bc = Bytecode.from_code(code) def update_bc(): for each in bc: yield Instr("LOAD_CONST", each) yield Instr("PRINT_EXPR") yield each lst = list(update_bc()) bc.clear() bc.extend(lst) code = bc.to_code() exec(code, module.__dict__)
def _ast_to_instr(self, astobj, lineno): src = astor.to_source(astobj).replace('"""', "'") cp = compile(src, filename='<ast>', mode='eval') bc = Bytecode.from_code(cp) instructions = bc[:-1] for instr in instructions: instr.lineno = lineno # if its calling a method, we don't want it to do `LOAD_NAME` if instructions[-1].opcode == pyop.CALL_FUNCTION and instructions[ 0].opcode == pyop.LOAD_NAME: instructions[0] = Instr("LOAD_GLOBAL", arg=instructions[0].arg, lineno=instructions[0].lineno) return instructions
def generate_function(source: str, namespace: DictType[str, Any], fn_name: str, optimize: bool = True) -> Callable[..., Any]: """Generate an optimized function Parameters ---------- source: str The function source code namespaced: dict Namespace available to the function fn_name: str The name of the generated function. Returns ------- fn: function The function generated. """ # print(source) try: assert source.startswith(f"def {fn_name}") or source.startswith( f"async def {fn_name}") code = compile(source, __name__, "exec", optimize=1) except Exception as e: raise RuntimeError(f"Could not generate code: {e}:\n{source}") result: DictType[str, Any] = {} exec(code, namespace, result) # Optimize global access fn = result[fn_name] fn.__source__ = source if optimize: bc = Bytecode.from_code(fn.__code__) for i, inst in enumerate(bc): if isinstance(inst, Label): continue if inst.name == "LOAD_GLOBAL" and inst.arg in namespace: bc[i] = Instr("LOAD_CONST", namespace[inst.arg]) fn.__code__ = bc.to_code() return fn
def func_info(cls, func: types.FunctionType) -> types.FunctionType: names = func.__code__.co_names code = Bytecode.from_code(func.__code__) codeinfo = cls.code_info(code) def r_compile(): jit_func = Aware.f(self) print("jit_func", type(jit_func)) bc = Bytecode() bc.append(PyInstr(InstrNames.LOAD_CONST, jit_func)) bc.extend([load_arg(each, cellvars, lineno) for each in argnames]) bc.extend([ PyInstr(InstrNames.CALL_FUNCTION, len(argnames)), PyInstr(InstrNames.RETURN_VALUE) ]) bc._copy_attr_from(code) start_func.__code__ = bc.to_code() start_func.__jit__ = jit_func return jit_func start_func = copy_func(func) start_func_code = Bytecode() lineno = code.first_lineno argnames = code.argnames start_func_code.argnames = argnames cellvars = code.cellvars start_func_code.extend([ PyInstr(InstrNames.LOAD_CONST, r_compile, lineno=lineno), PyInstr(InstrNames.CALL_FUNCTION, 0, lineno=lineno), *(load_arg(each, cellvars, lineno) for each in argnames), PyInstr(InstrNames.CALL_FUNCTION, len(argnames), lineno=lineno), PyInstr(InstrNames.RETURN_VALUE, lineno=lineno) ]) start_func_code._copy_attr_from(code) self = PyFuncInfo(func.__name__, func.__module__, func.__defaults__, func.__kwdefaults__, func.__closure__, func.__globals__, codeinfo, func, {}, names) start_func.__code__ = start_func_code.to_code() start_func.__func_info__ = self start_func.__compile__ = r_compile start_func.__jit__ = None return start_func
def instrument(target_code, jointpoint_payload): func_bytecode = Bytecode.from_code(target_code) beginpoint = 0 endpoint = -2 for jointpoint, payload_func in jointpoint_payload.items(): if isinstance(jointpoint, str): jointpoint = (jointpoint, ) if jointpoint[0] == 'func_begin': func_bytecode = FunctionInstrumenter.instrument_func_begin( func_bytecode, payload_func) elif jointpoint[0] == 'func_end': func_bytecode = FunctionInstrumenter.instrument_func_end( func_bytecode, payload_func) elif jointpoint[0] == 'line_before': lines = () if type(jointpoint) == tuple and len(jointpoint) > 1: lines = jointpoint[1] func_bytecode = LineInstrumenter.instrument_line_begin( func_bytecode, payload_func, lines) elif jointpoint[0] == 'line_after': lines = () if type(jointpoint) == tuple and len(jointpoint) > 1: lines = jointpoint[1] func_bytecode = LineInstrumenter.instrument_line_end( func_bytecode, payload_func, lines) elif jointpoint[0] == 'var_def': varnames = () if type(jointpoint) == tuple and len(jointpoint) > 1: varnames = jointpoint[1] func_bytecode = VariableInstrumenter.instrument_var_def( func_bytecode, payload_func, varnames) elif jointpoint[0] == 'var_use': varnames = () if type(jointpoint) == tuple and len(jointpoint) > 1: varnames = jointpoint[1] func_bytecode = VariableInstrumenter.instrument_var_use( func_bytecode, payload_func, varnames) else: #ToDo pass return func_bytecode.to_code()
def test_from_code_load_fast(self): code = get_code( """ def func(): x = 33 y = x """, function=True, ) code = Bytecode.from_code(code) self.assertEqual( code, [ Instr("LOAD_CONST", 33, lineno=2), Instr("STORE_FAST", "x", lineno=2), Instr("LOAD_FAST", "x", lineno=3), Instr("STORE_FAST", "y", lineno=3), Instr("LOAD_CONST", None, lineno=3), Instr("RETURN_VALUE", lineno=3), ], )
def test_optimize_code_obj(self): # Test optimize() method with a code object # # x = 3 + 5 => x = 8 noopt = Bytecode([Instr('LOAD_CONST', 3), Instr('LOAD_CONST', 5), Instr('BINARY_ADD'), Instr('STORE_NAME', 'x'), Instr('LOAD_CONST', None), Instr('RETURN_VALUE')]) noopt = noopt.to_code() optimizer = peephole_opt.PeepholeOptimizer() optim = optimizer.optimize(noopt) code = Bytecode.from_code(optim) self.assertEqual(code, [Instr('LOAD_CONST', 8, lineno=1), Instr('STORE_NAME', 'x', lineno=1), Instr('LOAD_CONST', None, lineno=1), Instr('RETURN_VALUE', lineno=1)])
def test_from_code(self): code = get_code(""" if test: x = 1 else: x = 2 """) bytecode = Bytecode.from_code(code) label_else = Label() label_exit = Label() self.assertEqual(bytecode, [ Instr('LOAD_NAME', 'test', lineno=1), Instr('POP_JUMP_IF_FALSE', label_else, lineno=1), Instr('LOAD_CONST', 1, lineno=2), Instr('STORE_NAME', 'x', lineno=2), Instr('JUMP_FORWARD', label_exit, lineno=2), label_else, Instr('LOAD_CONST', 2, lineno=4), Instr('STORE_NAME', 'x', lineno=4), label_exit, Instr('LOAD_CONST', None, lineno=4), Instr('RETURN_VALUE', lineno=4) ])
def exec_module(self, module): code = self.loader.get_code(module.__name__) if code is None: raise ImportError('cannot load module {!r} when get_code() ' 'returns None'.format(module.__name__)) __glob_refs__ = module.__glob_refs__ = { } # from a global symbol to jit functions it's referenced bc = Bytecode.from_code(code) def update_generations(name): functions = __glob_refs__.get(name, None) if functions is None: return for fn in functions: fn.__update_global_ref__(name) module.__dict__['__update_generations__'] = update_generations def update_bc(): for each in bc: yield each if isinstance(each, Instr) and each.name == InstrNames.STORE_NAME: yield Instr(InstrNames.LOAD_NAME, '__update_generations__', lineno=each.lineno) yield Instr(InstrNames.LOAD_CONST, each.arg, lineno=each.lineno) yield Instr(InstrNames.CALL_FUNCTION, 1, lineno=each.lineno) yield Instr(InstrNames.POP_TOP, lineno=each.lineno) lst = list(update_bc()) bc.clear() bc.extend(lst) code = bc.to_code() exec(code, module.__dict__)
def wrap_stores_bytecode(func): bytecodes = Bytecode.from_code(func.__code__) def wrappStore(x): code = [ Instr("LOAD_GLOBAL", 'goo'), Instr("LOAD_FAST", x), Instr("CALL_FUNCTION", 1), Instr("STORE_FAST", x) ] return code new_code = [] for inst in bytecodes: new_code.append(inst.copy()) if inst.name == "STORE_FAST": codes = wrappStore(inst.arg) new_code.extend(codes) return Bytecode(new_code)
def test_from_code(self): code = get_code(""" if test: x = 1 else: x = 2 """) bytecode = Bytecode.from_code(code) label_else = Label() label_exit = Label() self.assertEqual(bytecode, [Instr('LOAD_NAME', 'test', lineno=1), Instr('POP_JUMP_IF_FALSE', label_else, lineno=1), Instr('LOAD_CONST', 1, lineno=2), Instr('STORE_NAME', 'x', lineno=2), Instr('JUMP_FORWARD', label_exit, lineno=2), label_else, Instr('LOAD_CONST', 2, lineno=4), Instr('STORE_NAME', 'x', lineno=4), label_exit, Instr('LOAD_CONST', None, lineno=4), Instr('RETURN_VALUE', lineno=4)])
def __init__(self, path: str, module_name='', to_import=['*']): self.path = path self.to_import = to_import self.module_name = module_name self._local_methods = [] self.abi = None source = open(path, 'rb') source_src = source.read() compiled_source = compile(source_src, path, 'exec') ast_tree = ast.parse(source_src) if module_name == '': self.abi = ABI() self.abi.visit(ast_tree) self.bc = Bytecode.from_code(compiled_source) self.cfg = ControlFlowGraph.from_bytecode(self.bc) source.close() self.build()
def test_stack_size_computation_nested_try_except_else_finally(self): def test(*args, **kwargs): try: v = args[1] except IndexError: try: w = kwargs["value"] except KeyError: return -1 else: return w finally: print("second finally") else: return v finally: print("first finally") # A direct comparison of the stack depth fails because CPython # generate dead code that is used in stack computation. cpython_stacksize = test.__code__.co_stacksize test.__code__ = Bytecode.from_code(test.__code__).to_code() self.assertLessEqual(test.__code__.co_stacksize, cpython_stacksize) with redirect_stdout(io.BytesIO()) as stdout: self.assertEqual(test(1, 4), 4) self.assertEqual(stdout.getvalue(), "first finally\n") with redirect_stdout(io.BytesIO()) as stdout: self.assertEqual(test([], value=3), 3) self.assertEqual(stdout.getvalue(), "second finally\nfirst finally\n") with redirect_stdout(io.BytesIO()) as stdout: self.assertEqual(test([], name=None), -1) self.assertEqual(stdout.getvalue(), "second finally\nfirst finally\n")
def wrap_stores_bytecode(func): bytecodes = Bytecode.from_code(func.__code__) def wrappStore(x): label = x[1:] code = [Instr("LOAD_GLOBAL", 'pAst'), Instr("LOAD_CONST", label), Instr("ROT_THREE"), Instr("ROT_THREE"), Instr("CALL_FUNCTION", 2), Instr("STORE_FAST", x)] return code new_code = [] for inst in bytecodes: if inst.name == "STORE_FAST" and inst.arg.startswith("p"): codes = wrappStore(inst.arg) new_code.extend(codes) else: new_code.append(inst) return Bytecode(new_code)
def test_optimize_code_obj(self): # Test optimize() method with a code object # # x = 3 + 5 => x = 8 noopt = Bytecode([ Instr('LOAD_CONST', 3), Instr('LOAD_CONST', 5), Instr('BINARY_ADD'), Instr('STORE_NAME', 'x'), Instr('LOAD_CONST', None), Instr('RETURN_VALUE') ]) noopt = noopt.to_code() optimizer = peephole_opt.PeepholeOptimizer() optim = optimizer.optimize(noopt) code = Bytecode.from_code(optim) self.assertEqual(code, [ Instr('LOAD_CONST', 8, lineno=1), Instr('STORE_NAME', 'x', lineno=1), Instr('LOAD_CONST', None, lineno=1), Instr('RETURN_VALUE', lineno=1) ])
import dis from bytecode import Bytecode def f(x): return x dis.dis(f) for each in Bytecode.from_code(f.__code__): print(each)
def test_label3(self): """ CPython generates useless EXTENDED_ARG 0 in some cases. We need to properly track them as otherwise we can end up with broken offset for jumps. """ source = """ def func(x): if x == 1: return x + 0 elif x == 2: return x + 1 elif x == 3: return x + 2 elif x == 4: return x + 3 elif x == 5: return x + 4 elif x == 6: return x + 5 elif x == 7: return x + 6 elif x == 8: return x + 7 elif x == 9: return x + 8 elif x == 10: return x + 9 elif x == 11: return x + 10 elif x == 12: return x + 11 elif x == 13: return x + 12 elif x == 14: return x + 13 elif x == 15: return x + 14 elif x == 16: return x + 15 elif x == 17: return x + 16 return -1 """ code = get_code(source, function=True) bcode = Bytecode.from_code(code) concrete = bcode.to_concrete_bytecode() self.assertIsInstance(concrete, ConcreteBytecode) # Ensure that we do not generate broken code loc = {} exec(textwrap.dedent(source), loc) func = loc["func"] func.__code__ = bcode.to_code() for i, x in enumerate(range(1, 18)): self.assertEqual(func(x), x + i) self.assertEqual(func(18), -1) # Ensure that we properly round trip in such cases self.assertEqual( ConcreteBytecode.from_code(code).to_code().co_code, code.co_code )
def test_label3(self): """ CPython generates useless EXTENDED_ARG 0 in some cases. We need to properly track them as otherwise we can end up with broken offset for jumps. """ source = """ def func(x): if x == 1: return x + 0 elif x == 2: return x + 1 elif x == 3: return x + 2 elif x == 4: return x + 3 elif x == 5: return x + 4 elif x == 6: return x + 5 elif x == 7: return x + 6 elif x == 8: return x + 7 elif x == 9: return x + 8 elif x == 10: return x + 9 elif x == 11: return x + 10 elif x == 12: return x + 11 elif x == 13: return x + 12 elif x == 14: return x + 13 elif x == 15: return x + 14 elif x == 16: return x + 15 elif x == 17: return x + 16 return -1 """ code = get_code(source, function=True) bcode = Bytecode.from_code(code) concrete = bcode.to_concrete_bytecode() self.assertIsInstance(concrete, ConcreteBytecode) # Ensure that we do not generate broken code loc = {} exec(textwrap.dedent(source), loc) func = loc['func'] func.__code__ = bcode.to_code() for i, x in enumerate(range(1, 18)): self.assertEqual(func(x), x + i) self.assertEqual(func(18), -1) # Ensure that we properly round trip in such cases self.assertEqual(ConcreteBytecode.from_code(code).to_code().co_code, code.co_code)
def check_stack_size(self, func): code = func.__code__ bytecode = Bytecode.from_code(code) cfg = ControlFlowGraph.from_bytecode(bytecode) self.assertEqual(code.co_stacksize, cfg.compute_stacksize())
def modify(self, code, *, inner=False): initial_bytecode = Bytecode.from_code(code) modified_bytecode = Bytecode() modified_bytecode.first_lineno = initial_bytecode.first_lineno modified_bytecode.argcount = code.co_argcount modified_bytecode.argnames = initial_bytecode.argnames modified_bytecode.name = initial_bytecode.name modified_bytecode.freevars = code.co_freevars modified_bytecode.cellvars = code.co_cellvars first_line_no = initial_bytecode.first_lineno if inner: modified_bytecode.extend([ Instr('LOAD_NAME', arg=self._command, lineno=first_line_no), Instr('LOAD_CONST', arg=DebugCommand.STEP_OVER, lineno=first_line_no), Instr('COMPARE_OP', arg=Compare.EQ, lineno=first_line_no), Instr('STORE_NAME', arg='is_over', lineno=first_line_no), ]) # добавляем инструкции отладки перед первой строкой модуля if not inner: modified_bytecode.extend( self._get_trace_func_call_instructions(first_line_no)) previous_line_no = first_line_no for instr in initial_bytecode: if not isinstance(instr, Instr): modified_bytecode.append(instr) continue if isinstance(instr.arg, types.CodeType): old_instr_name = instr.name new_co = self.modify(instr.arg, inner=True) instr.set(old_instr_name, new_co) skip = Label() if instr.lineno != previous_line_no: if inner: modified_bytecode.extend([ Instr('LOAD_NAME', arg='is_over', lineno=instr.lineno), Instr('POP_JUMP_IF_TRUE', arg=skip, lineno=instr.lineno) ]) modified_bytecode.extend([ Instr('LOAD_NAME', arg=self._command, lineno=instr.lineno), Instr('LOAD_CONST', arg=DebugCommand.STEP_OUT, lineno=instr.lineno), Instr('COMPARE_OP', arg=Compare.EQ, lineno=instr.lineno), Instr('POP_JUMP_IF_TRUE', arg=skip, lineno=instr.lineno) ]) modified_bytecode.extend( self._get_trace_func_call_instructions(instr.lineno)) if inner: modified_bytecode.append(skip) previous_line_no = instr.lineno modified_bytecode.append(instr) code = modified_bytecode.to_code() return code
def test_integration_dominator_tree(): for_loop_cfg = cfg.CFG.from_bytecode(Bytecode.from_code(for_loop.__code__)) dom_tree = pdt.DominatorTree.compute(for_loop_cfg) # Every node of the cfg should be in the dominator tree assert for_loop_cfg.nodes == dom_tree.nodes
def test_integration_while_loop(): while_loop_cfg = CFG.from_bytecode(Bytecode.from_code(Foo.receive.__code__)) assert len(while_loop_cfg.nodes) == 3 assert while_loop_cfg.entry_node.index == -1 assert while_loop_cfg.exit_nodes.pop().index == sys.maxsize
def disassemble(source, *, filename="<string>", function=False): code = get_code(source, filename=filename, function=function) return Bytecode.from_code(code)
def extract_bytecode(func): return Bytecode.from_code(func.__code__)