def test_initial_info_given_3_simple_stmt_given_if(self): as_tree = AstBuilder().string_build( dedent( """\ a = 3 if c < 3: y = a + b x = a y = b a.b = c """ ) ) cfg_real = Cfg(as_tree) cfg_real.gather_initial_info() expected_ue_var = (set(), {"c"}, {"a", "b", "c"}, set()) expected_var_kill = (set(), {"a"}, {"y", "x", "a.b"}, set()) self.assert_uevar_varkill(cfg_real.block_list, expected_ue_var, expected_var_kill) expected_globals_var = {"b", "a", "c"} assert cfg_real.globals_var == expected_globals_var real_block_set = {str(k): v for k, v in cfg_real.block_set.items()} expected_block_set = { "x": cfg_real.block_list[2], "a": cfg_real.block_list[1], "y": cfg_real.block_list[2], "a.b": cfg_real.block_list[2], } assert real_block_set == expected_block_set
def test_initial_info_given_3_simple_stmt_expect_ue_a_vk_a_y_x(self): as_tree = AstBuilder().string_build( dedent( """\ a = 3 y = a + b x = a y = b """ ) ) cfg_real = Cfg(as_tree) cfg_real.gather_initial_info() expected_ue_var = {"b"} expected_var_kill = {"a", "y", "x"} assert cfg_real.block_list[1].ue_var == expected_ue_var assert cfg_real.block_list[1].var_kill == expected_var_kill expected_globals_var = {"b"} assert cfg_real.globals_var == expected_globals_var real_block_set = {str(k): v for k, v in cfg_real.block_set.items()} expected_block_set = {"x": cfg_real.block_list[1], "a": cfg_real.block_list[1], "y": cfg_real.block_list[1]} assert real_block_set == expected_block_set
def test_renaming_given_3_scopes(self): as_tree = AstBuilder().string_build( dedent("""\ class Foo(): x = 1 def __init__(self): x = 2 x = 3 f = Foo(2) """)) cfg_real = Cfg(as_tree) cfg_real.convert_to_ssa() self.assertBlockSsaList( cfg_real.block_list, { "L2": dedent("""\ Assign: (x_0,) = 1 Assign: (__init___0,) = Proxy to the object: Function __init__ in scope Class "Foo" in scope Module """), "L4": "Assign: (x_0,) = 2", "L5": dedent("""\ Assign: (x_0,) = 3 Assign: (f_0,) = Call: Foo_0((2,)) """), }, )
def test_renaming_multiple_scope(self): as_tree = AstBuilder().string_build( dedent("""\ def foo(x): a = 3 return x a = 2 foo(a) """)) cfg_real = Cfg(as_tree) cfg_real.convert_to_ssa() self.assertBlockSsaList( cfg_real.block_list, { "L2": dedent("""\ Assign: (ret_val_0,) = x_0 Assign: (a_0,) = 3 """), "L5": dedent("""\ Assign: (a_0,) = 2 Call: foo_0((a_0,)) """), }, )
def test_get_dunder_binop(self): as_tree = AstBuilder().string_build("""\ a = 1 a - 2 class F: def __add__(self, other): pass f = F() """) cfg_real = Cfg(as_tree) cfg_real.convert_to_ssa() with self.assertRaises(exceptions.OperationIncompatible) as e: list( protocols.get_custom_dunder_method(as_tree.body[1].value.left, "-")) assert e.exception.msg == "the node: 1 is not of type ClassInstance" meth = list( protocols.get_custom_dunder_method(as_tree.body[-1].targets[0], "+"))[0] assert str( meth ) == 'Proxy to the object: Function __add__ in scope Class "F" in scope Module' with self.assertRaises(exceptions.DunderUnimplemented) as e: list( protocols.get_custom_dunder_method(as_tree.body[-1].targets[0], "<")) assert e.exception.method_name == "__lt__"
def test_renaming_args_attribute(self): as_tree = AstBuilder().string_build( dedent("""\ class Temp: def __init__(self): pass def foo(x): return x + 1 y = Temp() y.z = 3 foo(y.z) """)) cfg_real = Cfg(as_tree) cfg_real.convert_to_ssa() self.assertBlockSsaList( cfg_real.block_list, { "L9": dedent("""\ Assign: (y_0.z_0,) = 3 Call: foo_0((y_0.z_0,)) """) }, )
def test_renaming_nameconstant(self): as_tree = AstBuilder().string_build( dedent("""\ if True: x = True else: x = None if False: pass """)) cfg_real = Cfg(as_tree) cfg_real.convert_to_ssa() self.assertBlockSsaList( cfg_real.block_list, { "L1": dedent("""\ True """), "L4": "Assign: (x_1,) = None", "L5": dedent("""\ Assign: (x_2,) = Phi(x_0, x_1) False """), }, )
def test_1_function_def(self): as_tree = AstBuilder().string_build( dedent("""\ def foo(x): if x < 2: x = 2 else: x = 1 z = 1 f = 5 z = 5 """)) cfg_real = Cfg(as_tree) cfg_real.fill_df() expected_idom = { "foo": ["L2"], "L2": ["L3", "L5", "L6"], "L3": [], "L5": [], "L6": ["PhiStub"], "PhiStub": [] } self.assert_idom_equal(cfg_real, expected_idom) assert cfg_real.block_list[4].df == [cfg_real.block_list[6]]
def test_renaming_str_format(self): as_tree = AstBuilder().string_build( dedent("""\ v = 1 s = "some{}".format(v) """)) cfg_real = Cfg(as_tree) cfg_real.convert_to_ssa()
def test_compute_liveout_with_attr(self): blocks, as_tree = self.build_arbitrary_blocks( block_links={"A": ["B"], "B": []}, code={ "A": dedent( """\ a = 4 a.b = 2 """ ), "B": dedent( """\ c = a.b """ ), }, ) cfg_real = Cfg() cfg_real.block_list = blocks cfg_real.as_tree = as_tree cfg_real.root = cfg_real.block_list[0] cfg_real.gather_initial_info() cfg_real.compute_live_out() expected_live_out = {"A": {"a", "a.b"}, "B": set()} self.assertLiveOutEqual(cfg_real.block_list, expected_live_out)
def build_arbitrary_blocks(block_links, code=None, block_type=None): # build all block and assume it's in module scope block_list = BlockList() as_tree_string = "" current_line_number = 1 for i in range(len(block_links)): block_name = chr(65 + i) if block_type: type_instance = block_type.get(block_name) if type_instance is not None: if type_instance.__name__ == "ParentScopeBlock": basic_block = type_instance(current_line_number, current_line_number, None, scope_name=block_name) else: basic_block = type_instance(current_line_number, current_line_number, None) else: basic_block = RawBasicBlock(current_line_number, current_line_number, None) else: basic_block = RawBasicBlock(current_line_number, current_line_number, None) current_line_number += 1 basic_block.name = block_name block_list.append(basic_block) if code is not None: ast_stmts = code.get(basic_block.name) # type: str as_tree_string += ast_stmts as_tree = AstBuilder().string_build(as_tree_string) for i in range(len(block_links)): basic_block = block_list[i] if code is not None: ast_stmts = code.get(basic_block.name) # type: str ast = AstBuilder().string_build(ast_stmts) for b in ast.body: b.parent = as_tree basic_block.ssa_code.code_list = ast.body basic_block.end_line += ast_stmts.count("\n") - 1 for key, value in block_links.items(): key_block = block_list.get_block_by_name(key) for value_block_str in value: Cfg.connect_2_blocks(key_block, block_list.get_block_by_name(value_block_str)) if code is None: return block_list else: return block_list, as_tree
def test_renaming_for_var(self): as_tree = AstBuilder().string_build( dedent("""\ for line in something: if x: line = "s".join('y') else: line = "z".join('y') line.strip() """)) cfg_real = Cfg(as_tree) cfg_real.convert_to_ssa()
def remove_block(self, blk): if len(blk.nxt_block_list) == 0: for prev in blk.prev_block_list: prev.nxt_block_list.remove(blk) elif len(blk.prev_block_list) == 0: for nxt in blk.nxt_block_list: nxt.prev_block_list.remove(blk) for prev, nxt in itertools.product(blk.prev_block_list, blk.nxt_block_list): prev.nxt_block_list.remove(blk) nxt.prev_block_list.remove(blk) Cfg.connect_2_blocks(prev, nxt)
def test_delete_node(self): as_tree = AstBuilder().string_build( dedent("""\ z = 2 # 0th block while a < 3: # 1st block if a < 2: # 2nd block z = 2 # 3rd block b = 2 # 4th block c = 3 # 5th block """)) cfg_real = Cfg(as_tree) cfg_real.root = cfg_common.delete_node(cfg_real.root, RawBasicBlock(1, 1))
def test_locals_dict_if(self): as_tree = AstBuilder().string_build( dedent("""\ x = 1 y = 2 if x < 2: pass """)) cfg_real = Cfg(as_tree) cfg_real.convert_to_ssa() assert as_tree.locals == { "x_0": as_tree.body[0].value, "y_0": as_tree.body[1].value }
def test_rename_given_repeated_definition(self): as_tree = AstBuilder().string_build( dedent("""\ a = 3 # 1st if a > 3: # | a = 3 # 2nd a = 98 else: # 3rd z = 4 # | # expected phi func for 'a' here y = a # 4th a = 4 """)) cfg_real = Cfg(as_tree) cfg_real.fill_df() cfg_real.gather_initial_info() cfg_real.ins_phi_function_all() cfg_real.rename_to_ssa() self.assertBlockSsaList( cfg_real.block_list, { "Module": dedent("""\ Module """), "L1": dedent("""\ Assign: (a_0,) = 3 a_0 > 3 """), "L3": dedent("""\ Assign: (a_1,) = 3 Assign: (a_2,) = 98 """), "L6": dedent("""\ Assign: (z_0,) = 4 """), "L8": """\ Assign: (z_1,) = Phi(z, z_0) Assign: (a_3,) = Phi(a_2, a_0) Assign: (y_0,) = a_3 Assign: (a_4,) = 4 """, }, )
def test_locals_dict_functiondef(self): as_tree = AstBuilder().string_build( dedent("""\ def foo(x): y = 2 return y """)) cfg_real = Cfg(as_tree) cfg_real.convert_to_ssa() self.assert_locals_str( { "x_0": "Arg: 'x'", "y_0": "2", "ret_val_0": "y_0" }, as_tree.body[0].locals)
def test_with_simple_attr(self): as_tree = AstBuilder().string_build( dedent( """\ a.b = c.d """ ) ) cfg_real = Cfg(as_tree) cfg_real.gather_initial_info() expected_ue_var = {"a", "c", "c.d"} expected_var_kill = {"a.b"} assert cfg_real.block_list[1].ue_var == expected_ue_var assert cfg_real.block_list[1].var_kill == expected_var_kill
def test_cfg_given_while_body_if(self): as_tree = AstBuilder().string_build( dedent("""\ z = 2 # 0th block while a < 3: # 1st block if a < 2: # 2nd block z = 2 # 3rd block b = 2 # 4th block c = 3 # 5th block """)) cfg_real = Cfg(as_tree) self.assertCfgWithBasicBlocks( cfg_real, [1, 1, "", "L1"], [2, 2, "While", "L2"], [3, 3, "If", "L3"], [4, 4, "", "L4"], [5, 5, "", "L5"], [6, 6, "", "L6"], block_links={ "0": [1], "1": [2, 5], "2": [3, 4], "3": [4], "4": [1], "5": [] }, )
def test_locals_dict_multiple_assignment(self): as_tree = AstBuilder().string_build( dedent("""\ y = 3 x = 1 z = y x = 1 """)) cfg_real = Cfg(as_tree) cfg_real.convert_to_ssa() assert as_tree.locals == { "y_0": as_tree.body[0].value, "x_0": as_tree.body[1].value, "z_0": as_tree.body[2].value, "x_1": as_tree.body[3].value, }
def test_cfg_given_if_else_with_link_tail(self): as_tree = AstBuilder().string_build( dedent("""\ a = 3 # 1st if a > 3: # | a = 4 # 2nd else: # 3rd z = 5 # | y = 5 # 4th """)) cfg_real = Cfg(as_tree) self.assertCfgWithBasicBlocks( cfg_real, [1, 2, "If", "L1"], [3, 3, "", "L3"], [5, 5, "", "L5"], [6, 6, "", "L6"], [None, None, "Module", "Module"], [-1, -1, "", "PhiStub"], block_links={ "0": [1, 2], "1": [3], "2": [3], "3": [5], "4": [0], "5": [] }, )
def test_cfg_given_if_elif_no_else(self): as_tree = AstBuilder().string_build( dedent("""\ a = 3 #--- 1st if a > 3: #--- | a = 4 #--- 2nd elif a < 1: #--- 3rd a = 5 #--- 4th c = 5 #--- 5th """)) cfg_real = Cfg(as_tree) self.assertCfgWithBasicBlocks( cfg_real, [1, 2, "If", "L1"], [3, 3, "", "L3"], [4, 4, "If", "L4"], [5, 5, "", "L5"], [6, 6, "", "L6"], [None, None, "Module", "Module"], [-1, -1, "", "PhiStub"], block_links={ "0": [1, 2], "1": [4], "2": [3, 4], "3": [4], "4": [6], "5": [0], "6": [] }, )
def test_cfg_given_for(self): as_tree = AstBuilder().string_build( dedent("""\ for a in z: # 0st block if a < 2: # 1nd block z = 2 # 2rd block b = 2 # 3th block z = 4 """)) cfg_real = Cfg(as_tree) self.assertCfgWithBasicBlocks( cfg_real, [1, 1, "For", "L1"], [2, 2, "If", "L2"], [3, 3, "", "L3"], [4, 4, "", "L4"], [5, 5, "", "L5"], block_links={ "0": [1, 4], "1": [2, 3], "2": [3], "3": [0], "4": [] }, )
def test_cfg_given_while(self): as_tree = AstBuilder().string_build( dedent("""\ while 3 < 4: x = 3 if x < 2: y = 5 """)) cfg_real = Cfg(as_tree) self.assertCfgWithBasicBlocks( cfg_real, [1, 1, "While", "L1"], [2, 3, "If", "L2"], [4, 4, "", "L4"], [None, None, "Module", "Module"], [-1, -1, "", "PhiStub"], block_links={ "0": [1, 4], "1": [2, 0], "2": [0], "3": [0], "4": [] }, )
def test_cfg_given_simple_class(self): as_tree = AstBuilder().string_build( dedent("""\ class SomeClass: def __init__(self): pass """)) cfg_real = Cfg(as_tree) self.assertCfgWithBasicBlocks( cfg_real, [1, 1, "ClassDef", "SomeClass"], [2, 2, "FunctionDef", "__init__"], [3, 3, "", "L3"], [None, None, "Module", "Module"], [1, 1, TEMP_ASSIGN, TEMP_ASSIGN], [2, 2, TEMP_ASSIGN, TEMP_ASSIGN], [-1, -1, "", "PhiStub"], [-1, -1, "", "PhiStub"], [-1, -1, "", "PhiStub"], block_links={ "0": [5], "1": [2], "2": [6], "3": [4], "4": [7], "5": [6], "6": [], "7": [], "8": [] }, )
def test_cfg_given_simple_class_stmt_between(self): as_tree = AstBuilder().string_build( dedent("""\ class SomeClass: if True: x = 1 else: x = 3 def __init__(self): pass """)) cfg_real = Cfg(as_tree) self.assertCfgWithBasicBlocks( cfg_real, [1, 1, "ClassDef", "SomeClass"], [2, 2, "If", "L2"], [3, 3, "", "L3"], [5, 5, "", "L5"], [6, 6, TEMP_ASSIGN, TEMP_ASSIGN], block_links={ "0": [1], "1": [2, 3], "2": [4], "3": [4], "4": [] }, )
def test_ssa_generation_2_stmt(self): as_tree = AstBuilder().string_build( dedent( """\ a = 1 y = 2 b = 3 c = 4 z = a + y x = b + c""" ) ) cfg = Cfg(as_tree) cfg.root.nxt_block_list[0].enumerate() expected_ssa_dict = { "z": deque([0]), "a": deque([0]), "y": deque([0]), "x": deque([0]), "b": deque([0]), "c": deque([0]), } real_ssa_dict = as_tree.ssa_record.var_version_list assert real_ssa_dict == expected_ssa_dict
def test_cfg_given_for_with_else(self): as_tree = AstBuilder().string_build( dedent("""\ z = 2 # 0th block for a in z: # 1st block if a < 2: # 2nd block z = 2 # 3rd block b = 2 # 4th block else: c = 3 # 5th block z = 4 """)) cfg_real = Cfg(as_tree) self.assertCfgWithBasicBlocks( cfg_real, [1, 1, "", "L1"], [2, 2, "For", "L2"], [3, 3, "If", "L3"], [4, 4, "", "L4"], [5, 5, "", "L5"], [7, 7, "", "L7"], [8, 8, "", "L8"], block_links={ "0": [1], "1": [2, 5, 6], "2": [3, 4], "3": [4], "4": [1], "5": [6], "6": [] }, )
def test_renaming_globals_var_1_var(self): as_tree = AstBuilder().string_build( dedent("""\ def foo(x): x = y """)) cfg_real = Cfg(as_tree) cfg_real.convert_to_ssa() self.assertBlockSsaList( cfg_real.block_list, { "L2": dedent("""\ Assign: (x_1,) = y """) }, )
def test_locals_functiondef_default_args(self): as_tree = AstBuilder().string_build( dedent("""\ def foo(x, y=1.5): f = 4 """), name="test_mod", ) cfg_real = Cfg(as_tree) cfg_real.convert_to_ssa() # y_0 = Arg: y because handling of default arg will be `infer_arg` job self.assert_locals_str( { "x_0": "Arg: 'x'", "y_0": "Arg: 'y'", "f_0": "4" }, as_tree.body[0].locals)