def test_fake_node(self): ast = AST.build_from_javalang( build_ast( Path(__file__).absolute().parent / "MethodUseOtherMethodExample.java" ) ) fake_node = ast.create_fake_node() # fixed public interface self.assertTrue(fake_node.is_fake) self.assertEqual(list(fake_node.children), []) self.assertEqual(fake_node.line, -1) self.assertEqual(fake_node.parent, None) self.assertEqual(fake_node.node_index, -1) # proxy interface self.assertEqual(fake_node.node_type, None) # interface through standart python function self.assertEqual(str(fake_node), "node index: -1\n" "node_type: None") self.assertEqual(repr(fake_node), "<ASTNode node_type: None, node_index: -1>") self.assertEqual(dir(fake_node), ["children", "is_fake", "line", "node_index", "parent"]) try: hash(fake_node) except Exception as e: self.fail(f"Failed to hash fake node with following exception {e}.")
def test_fake_nodes_equality(self): ast1 = AST.build_from_javalang( build_ast( Path(__file__).absolute().parent / "MethodUseOtherMethodExample.java")) ast2 = AST.build_from_javalang( build_ast( Path(__file__).absolute().parent / "LottieImageAsset.java")) ast1_fake_node1 = ast1.create_fake_node() ast1_fake_node2 = ast1.create_fake_node() ast2_fake_node1 = ast2.create_fake_node() self.assertFalse(ast1_fake_node1 == ast1_fake_node2) self.assertFalse(ast1_fake_node1 == ast2_fake_node1)
def _get_lines(self, file, function_name): ast = AST.build_from_javalang(build_ast(file)) m_decl = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == function_name ][0] return method_body_lines(m_decl, file)
def test_simple_examples(self): filepath = self.current_directory / "BlockStatementGraphExamples.java" ast = AST.build_from_javalang(build_ast(filepath)) try: class_declaration = next( node for node in ast.get_root().types if node.node_type == ASTNodeType.CLASS_DECLARATION and node.name == "BlockStatementGraphExamples") except StopIteration: raise RuntimeError( f"Can't find class BlockStatementGraphExamples in file {filepath}" ) for method_declaration in class_declaration.methods: with self.subTest( f"Testing method {method_declaration.name} in class {class_declaration.name} in file {filepath}" ): block_statement_graph = build_block_statement_graph( ast.get_subtree(method_declaration)) self.assertEqual( BlockStatementTestCase.flatten_block_statement_graph( block_statement_graph), BlockStatementTestCase._expected_flattened_graphs[ method_declaration.name], )
def test_class_computed_fields(self): ast = AST.build_from_javalang( build_ast( Path(__file__).absolute().parent / "MethodUseOtherMethodExample.java" ) ) package = ast.get_root() assert len(package.types) == 1 and \ package.types[0].node_type == ASTNodeType.CLASS_DECLARATION java_class = package.types[0] self.assertEqual(java_class.name, "MethodUseOtherMethod") self.assertEqual(java_class.modifiers, set()) self.assertEqual(java_class.documentation, "/**\n* Some documentation\n*/") # consider each field declaration declares single field fields_names = {field.names[0] for field in java_class.fields} self.assertEqual(fields_names, {"connectingField", "redundantField"}) methods_names = {method.name for method in java_class.methods} self.assertEqual( methods_names, { "useOnlyMethods1", "useOnlyMethods2", "getField", "setField", "standAloneMethod", "shadowing", }, ) self.assertEqual(set(java_class.constructors), set())
def _get_method_ast(self, class_decl): with NamedTemporaryFile(delete=False) as f: _name = f.name f.write('\n'.join(class_decl).encode()) ast = AST.build_from_javalang(build_ast(_name)) os.unlink(_name) nodes = list(ast) _method_decl = ast.get_subtree(nodes[4]) return _method_decl
def get_ast_if_possible(file_path: Path) -> Optional[AST]: """ Processing file in order to check that its original version can be parsed """ ast = None try: ast = AST.build_from_javalang(build_ast(str(file_path))) except Exception: print(f"Processing {file_path} is aborted due to parsing") return ast
def test_is_valid_function_with_return_in_the_middle(self): filepath = self.current_directory / "Example.java" ast = AST.build_from_javalang(build_ast(filepath)) m_decl_original = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'severalReturns' ][0] m_inv = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_INVOCATION) if x.member == 'severalReturns' ][0] is_matched = is_match_to_the_conditions(ast, m_inv, m_decl_original) self.assertEqual(is_matched, False)
def _get_class_ast(self, filename: str, class_name: str) -> AST: package_ast = AST.build_from_javalang( build_ast(str(Path(__file__).parent.absolute() / filename))) package_declaration = package_ast.get_root() try: class_declaration = next( class_declaration for class_declaration in package_declaration.types if class_declaration.name == class_name) return package_ast.get_subtree(class_declaration) except StopIteration: raise ValueError( f"File '{filename}' does not have top level class '{class_name}'." )
def common_cli(main: MainFunction, description: str) -> None: parser = ArgumentParser(description=description) parser.add_argument( "-f", "--file", required=True, help="File path to JAVA source code for extracting semantic") parser.add_argument( "-c", "--class", default=None, dest="class_name", help= "Class name of method to parse, if omitted all classes are considered", ) parser.add_argument( "-m", "--method", default=None, dest="method_name", help="Method name to parse, if omitted all method are considered", ) args = parser.parse_args() ast = AST.build_from_javalang(build_ast(args.file)) classes_declarations = (node for node in ast.get_root().types if node.node_type == ASTNodeType.CLASS_DECLARATION) if args.class_name is not None: classes_declarations = (node for node in classes_declarations if node.name == args.class_name) methods_infos = (MethodInfo( ast=ast.get_subtree(method_declaration), method_name=method_declaration.name, class_name=class_declaration.name, ) for class_declaration in classes_declarations for method_declaration in class_declaration.methods) if args.method_name is not None: methods_infos = (method_info for method_info in methods_infos if method_info.method_name == args.method_name) for method_info in methods_infos: main(method_info.ast, args.file, method_info.class_name, method_info.method_name)
def test_invocation_inside_if_not_process(self): filepath = self.current_directory / "Example_nested.java" ast = AST.build_from_javalang(build_ast(filepath)) m_decl = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'doAction' ][0] m_decl_original = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'handleAction' ][0] m_inv = [ x for x in ast.get_subtree(m_decl).get_proxy_nodes( ASTNodeType.METHOD_INVOCATION) if x.member == 'handleAction' ][0] is_matched = is_match_to_the_conditions(ast, m_inv, m_decl_original) self.assertEqual(is_matched, False)
def test_is_return_type_not_assigning_value_valid(self): filepath = self.current_directory / "Example.java" ast = AST.build_from_javalang(build_ast(filepath)) m_decl = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'method_decl' ][0] m_inv = [ x for x in ast.get_subtree(m_decl).get_proxy_nodes( ASTNodeType.METHOD_INVOCATION) if x.member == 'invocation' ][0] m_decl_original = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'invocation' ][0] self.assertFalse( is_match_to_the_conditions(ast, m_inv, m_decl_original))
def test_is_valid_function_with_one_return(self): filepath = self.current_directory / "Example.java" ast = AST.build_from_javalang(build_ast(filepath)) m_decl = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'runDelete' ][0] m_decl_original = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'delete' ][0] m_inv = [ x for x in ast.get_subtree(m_decl).get_proxy_nodes( ASTNodeType.METHOD_INVOCATION) if x.member == 'delete' ][0] is_matched = is_match_to_the_conditions(ast, m_inv, m_decl_original) self.assertEqual(is_matched, True)
def test_inline_strange_body2(self): filepath = self.current_directory / 'InlineExamples' / 'cut.java' test_filepath = self.current_directory / 'InlineTestExamples' / 'cut.java' temp_filename = self.current_directory / 'temp1.java' algorithm_type = InlineTypesAlgorithms.WITHOUT_RETURN_WITHOUT_ARGUMENTS algorithm_for_inlining = AlgorithmFactory().create_obj(algorithm_type) ast = AST.build_from_javalang(build_ast(filepath)) m_decl = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'copy' ][0] body_start_line, body_end_line = method_body_lines(m_decl, filepath) algorithm_for_inlining().inline_function(filepath, 8, body_start_line, body_end_line, temp_filename) with open(temp_filename, encoding='utf-8') as actual_file, \ open(test_filepath, encoding='utf-8') as test_ex: self.assertEqual(actual_file.read(), test_ex.read()) temp_filename.unlink()
def get_method_ast(filename: str, class_name: str, method_name: str, method_decl_line: int) -> AST: ast = AST.build_from_javalang(build_ast(str(filename))) try: class_declaration = next( node for node in ast.get_root().types if node.node_type == ASTNodeType.CLASS_DECLARATION and node.name == class_name) method_declaration = next( node for node in class_declaration.methods if node.name == method_name and node.line == method_decl_line) except StopIteration: raise RuntimeError( f"Failed to find method {method_name} in class {class_name} in file {filepath}" ) return ast.get_subtree(method_declaration)
def test_inline_without_return_type(self): algorithm = InlineWithoutReturnWithoutArguments() file = self.current_directory / 'InlineExamples' / 'PlanetDialog.java' temp_filename = self.current_directory / 'temp.java' test_example = self.current_directory / 'InlineTestExamples' / 'PlanetDialog.java' ast = AST.build_from_javalang(build_ast(file)) m_decl = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'makeBloom' ][0] body_start_line, body_end_line = method_body_lines(m_decl, file) algorithm.inline_function(file, 70, body_start_line, body_end_line, temp_filename) with open(temp_filename, encoding='utf-8') as actual_file, \ open(test_example, encoding='utf-8') as test_ex: self.assertEqual(actual_file.read(), test_ex.read()) temp_filename.unlink()
def test_is_invocation_in_if_with_single_statement_valid(self): filepath = self.current_directory / "Example.java" ast = AST.build_from_javalang(build_ast(filepath)) m_decl = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'test_single_stat_in_if' ][0] m_decl_original = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'intersected_var' ][0] m_inv = [ x for x in ast.get_subtree(m_decl).get_proxy_nodes( ASTNodeType.METHOD_INVOCATION) if x.member == 'intersected_var' ][0] self.assertFalse( is_match_to_the_conditions(ast, m_inv, m_decl_original))
def test_determine_type_with_overridden_functions(self): filepath = self.current_directory / "Example.java" ast = AST.build_from_javalang(build_ast(filepath)) m_decl = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'invoke_overridden' ][0] m_decl_original = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'overridden_func' ] m_inv = [ x for x in ast.get_subtree(m_decl).get_proxy_nodes( ASTNodeType.METHOD_INVOCATION) if x.member == 'overridden_func' ][0] d = {'overridden_func': m_decl_original} type = determine_algorithm_insertion_type(ast, m_decl, m_inv, d) self.assertEqual(type, InlineTypesAlgorithms.DO_NOTHING)
def __decompose_with_setter_functionality(self, ignore_getters=False, ignore_setters=False): file = str(Path(self.cur_dir, 'LottieImageAsset.java')) ast = AST.build_from_javalang(build_ast(file)) classes_ast = [ ast.get_subtree(node) for node in ast.get_root().types if node.node_type == ASTNodeType.CLASS_DECLARATION ] components = list( decompose_java_class(classes_ast[0], "strong", ignore_setters=ignore_setters, ignore_getters=ignore_getters)) function_names = flatten([[ x.name for x in list(c.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION)) ] for c in components]) return function_names
def get_method_ast(filename: str, class_name: str, method_name: str) -> AST: current_directory = Path(__file__).absolute().parent filepath = current_directory / filename ast = AST.build_from_javalang(build_ast(str(filepath))) try: class_declaration = next( node for node in ast.get_root().types if node.node_type == ASTNodeType.CLASS_DECLARATION and node.name == class_name) method_declaration = next(node for node in class_declaration.methods if node.name == method_name) except StopIteration: raise RuntimeError( f"Failed to find method {method_name} in class {class_name} in file {filepath}" ) return ast.get_subtree(method_declaration)
def _get_method_subtree(class_decl: List[str]) -> AST: with NamedTemporaryFile(delete=False) as f: _name = f.name f.write('\n'.join(class_decl).encode()) try: javalang_ast = build_ast(_name) ast = AST.build_from_javalang(javalang_ast) os.unlink(_name) except JavaSyntaxError as e: os.unlink(_name) raise e class_node = list(ast.get_proxy_nodes(ASTNodeType.CLASS_DECLARATION))[0] objects_to_consider = list(class_node.methods) + \ list(class_node.constructors) method_node = objects_to_consider[0] ast_subtree = ast.get_subtree(method_node) return ast_subtree
def _get_method_ast(self, method_name) -> AST: path = str(Path(__file__).absolute().parent / "ScopeTest.java") ast = AST.build_from_javalang(build_ast(path)) package_declaration = ast.get_root() assert len(package_declaration.types) == 1 and \ package_declaration.types[0].node_type == ASTNodeType.CLASS_DECLARATION class_declaration = package_declaration.types[0] try: return next( ast.get_subtree(method_declaration) for method_declaration in class_declaration.methods if method_declaration.name == method_name) except StopIteration: raise ValueError( f"There is no method {method_name} in class {class_declaration.name}" )
def test_determine_type_without_variables_declaration(self): filepath = self.current_directory / "Example.java" ast = AST.build_from_javalang(build_ast(filepath)) m_decl = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'method' ][0] m_decl_original = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'method_without_params' ][0] m_inv = [ x for x in ast.get_subtree(m_decl).get_proxy_nodes( ASTNodeType.METHOD_INVOCATION) if x.member == 'method_without_params' ][0] d = {'method_without_params': [m_decl_original]} type = determine_algorithm_insertion_type(ast, m_decl, m_inv, d) self.assertEqual( type, InlineTypesAlgorithms.WITHOUT_RETURN_WITHOUT_ARGUMENTS) # We consider all cases (with or without return) # if there are no variables, declared in invoked function m_decl = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'method_with_return_not_var_decl' ][0] m_decl_original = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'closeServer_return' ][0] m_inv = [ x for x in ast.get_subtree(m_decl).get_proxy_nodes( ASTNodeType.METHOD_INVOCATION) if x.member == 'closeServer_return' ][0] d = {'closeServer_return': [m_decl_original]} type = determine_algorithm_insertion_type(ast, m_decl, m_inv, d) self.assertEqual(type, InlineTypesAlgorithms.WITH_RETURN_WITHOUT_ARGUMENTS)
def test_determine_type_with_return_without_parameters(self): filepath = self.current_directory / "Example.java" ast = AST.build_from_javalang(build_ast(filepath)) m_decl = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'reset_return_var_decl' ][0] m_decl_original = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'closeServer_return' ][0] m_inv = [ x for x in ast.get_subtree(m_decl).get_proxy_nodes( ASTNodeType.METHOD_INVOCATION) if x.member == 'closeServer_return' ][0] d = {'closeServer_return': [m_decl_original]} type = determine_algorithm_insertion_type(ast, m_decl, m_inv, d) self.assertEqual(type, InlineTypesAlgorithms.WITH_RETURN_WITHOUT_ARGUMENTS)
def test_determine_type_with_non_intersected_variables_declaration(self): filepath = self.current_directory / "Example.java" ast = AST.build_from_javalang(build_ast(filepath)) m_decl = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'test_not_intersected_var_decl' ][0] m_decl_original = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'intersected_var' ][0] m_inv = [ x for x in ast.get_subtree(m_decl).get_proxy_nodes( ASTNodeType.METHOD_INVOCATION) if x.member == 'intersected_var' ][0] d = {'intersected_var': [m_decl_original]} type = determine_algorithm_insertion_type(ast, m_decl, m_inv, d) self.assertTrue(type in [ InlineTypesAlgorithms.WITH_RETURN_WITHOUT_ARGUMENTS, InlineTypesAlgorithms.WITHOUT_RETURN_WITHOUT_ARGUMENTS ])
def test_determine_type_with_invalid_functions(self): """Tests if we have invocation, but we didn't find it in the list of method declarations in current class.""" filepath = self.current_directory / "Example.java" ast = AST.build_from_javalang(build_ast(filepath)) m_decl = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'invoke_overridden' ][0] m_decl_original = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'overridden_func' ] m_inv = [ x for x in ast.get_subtree(m_decl).get_proxy_nodes( ASTNodeType.METHOD_INVOCATION) if x.member == 'overridden_func' ][0] d = {'SOME_RANDOM_NAME': m_decl_original} type = determine_algorithm_insertion_type(ast, m_decl, m_inv, d) self.assertEqual(type, InlineTypesAlgorithms.DO_NOTHING)
def test_ncss(self): test_data_folder = self.cur_dir / "ncss" for filepath in test_data_folder.glob("*.java"): with self.subTest(f"Testing decomposition of {filepath}"): ast = AST.build_from_javalang(build_ast(filepath)) classes_ast = [ ast.get_subtree(node) for node in ast.get_root().types if node.node_type == ASTNodeType.CLASS_DECLARATION ] ncss_metric = NCSSMetric() # NCSS of a class may not be equal to sum of ncss of methods and fields # due to presence of nested classes. To bypass it we calculate NCSS only # of methods and fields. methods_ncss = 0 fields_ncss = 0 components_ncss = 0 components_qty = 0 for class_ast in classes_ast: class_declaration = class_ast.get_root() for method in class_declaration.methods: methods_ncss += ncss_metric.value( class_ast.get_subtree(method)) for field in class_declaration.fields: fields_ncss += ncss_metric.value( class_ast.get_subtree(field)) for component in decompose_java_class(class_ast, "strong"): components_qty += 1 components_ncss += ncss_metric.value(component) # Each component has a CLASS_DECLARATION node. It increase NCSS of each component by 1. # To achieve equality we add number of components to the sum of NCSS of just methods and fields. self.assertEqual(methods_ncss + fields_ncss + components_qty, components_ncss)
def test_inline_with_return_type_but_not_returning(self): """ Test check whether we can inline code function with return type, but actually this function is not saving return value, so we do not need to declare a variable """ algorithm = InlineWithReturnWithoutArguments() file = self.current_directory / 'InlineExamples' / 'ReturnTypeUseless.java' temp_filename = self.current_directory / 'temp.java' test_example = self.current_directory / 'InlineTestExamples' / 'ReturnTypeUseless.java' ast = AST.build_from_javalang(build_ast(file)) m_decl = [ x for x in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION) if x.name == 'invocation' ][0] body_start_line, body_end_line = method_body_lines(m_decl, file) algorithm.inline_function(file, 36, body_start_line, body_end_line, temp_filename) with open(temp_filename, encoding='utf-8') as actual_file, \ open(test_example, encoding='utf-8') as test_ex: self.assertEqual(actual_file.read(), test_ex.read()) temp_filename.unlink()
def _build_ast(self, filename: str): javalang_ast = build_ast( str(Path(__file__).parent.absolute() / filename)) return AST.build_from_javalang(javalang_ast)
def count_all_class_declarations(filename: str) -> int: ast = AST.build_from_javalang(build_ast(str(filename))) return len(list(ast.get_proxy_nodes(ASTNodeType.CLASS_DECLARATION)))