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_var_replace(self): tree = AstBuilder().string_build("""\ x = 3 x = 2 y = x """) tree.body[0].targets[0].version = 0 tree.body[1].targets[0].version = 1 tree.body[2].value.version = 1 tree.locals["x_1"] = tree.body[1] link_stmts_to_def(tree.body[2]) assert tree.body[2].value.links == tree.body[1]
def test_function_def_global(self): tree = AstBuilder().string_build("""\ def foo(): global x, y, z global f """) assert tree.body[0].global_var.keys() == {"x", "y", "z", "f"}
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_bootstrap(self): tree = AstBuilder().string_build("""\ x = 1 + 3 """) s = tree.body[0].value.left.locals["__add__"] assert str( s) == 'Function __add__ in scope Class "int" in scope Module'
def test_repr_variable(self): tree = AstBuilder().string_build("""\ a.b.c = s """) tree.body[0].targets[0].version = 2 assert repr(tree.body[0].targets[0]) == "a.b.c_2" assert tree.body[0].targets[0].get_var_repr() == "c_2"
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_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_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_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_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_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_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_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_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_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_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_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_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_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 bootstrap_builtins(self): """Construct builtin class type (e.g. int, float) in ast form This will need to only parse builtins.pyi for the class definition, and construct the class with appropriate locals method with the return type. """ self.logger.info( "INITIALIZE", "constructing builtins ast classes for type inference purposes") def builtins_proxy(c): return self.builtins_ast_cls[type(c.value)] builtins_file = pathlib.PurePath(__file__).parent / self.BUILTINS_FILE self.builtins_tree = AstBuilder( tree_rewriter=StubTreeRewriter).file_build( builtins_file, py2_version_check=self.config.py_version == 2) for field in dir(builtins): loc = self.builtins_tree.locals.get(field) if loc: self.builtins_ast_cls[getattr(builtins, field)] = loc self.builtins_ast_cls[type(None)] = None nodes.Const.obj = property(builtins_proxy) nodes.List.obj = self.builtins_ast_cls[list] nodes.Dict.obj = self.builtins_ast_cls[dict] nodes.Tuple.obj = self.builtins_ast_cls[tuple] nodes.Set.obj = self.builtins_ast_cls[set]
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_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_lhs_multiple_assignment(self): tree = AstBuilder().string_build("""\ a, b = c, d = 3, 4 """) s = tree.body[0].get_lhs_value(tree.body[0].value.elts[0]) assert s == [ tree.body[0].targets[0].elts[0], tree.body[0].targets[1].elts[0] ]
def test_var_not_exist(self): tree = AstBuilder().string_build("""\ y = x """) tree.body[0].value.version = 0 tree.body[0].targets[0].version = 0 link_stmts_to_def(tree.body[0]) assert tree.body[0].value.links is None
def test_nested_tuple(self): tree = AstBuilder().string_build("""\ (a, b, *c, d), e, f = (1, 2, 3, 4, 5, 6), 7, 8 """) s = tree.body[0].get_rhs_value(tree.body[0].targets[0].elts[0].elts[2]) assert str(s) == "(3, 4, 5)" s = tree.body[0].get_rhs_value(tree.body[0].targets[0].elts[1]) assert str(s) == "7"
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_async(self): as_tree = AstBuilder().string_build("""\ async def f(): async with something(): async for i in range(3): await g() """) s = as_tree.body[0].body[0].body[0].body[0].value.scope() assert s == as_tree.body[0]
def test_nested_tuple_variable(self): tree = AstBuilder().string_build("""\ (a, b, *c, d), e, f = z (a, b, *c, d), e, f = z, 1, 3 """) s = tree.body[0].get_rhs_value(tree.body[0].targets[0].elts[0].elts[2]) assert str(s) == "z[0][2:-1:]" s = tree.body[1].get_rhs_value(tree.body[1].targets[0].elts[0].elts[2]) assert str(s) == "z[2:-1:]"
def test_starred_last(self): tree = AstBuilder().string_build("""\ a, *b = (1, 2, 3, 4) a, *b = a """) s = tree.body[0].get_rhs_value(tree.body[0].targets[0].elts[1]) assert str(s) == "(2, 3, 4)" s = tree.body[1].get_rhs_value(tree.body[1].targets[0].elts[1]) assert str(s) == "a[1::]"