def determine_algorithm_insertion_type( ast: AST, method_node: ASTNode, invocation_node: ASTNode, dict_original_nodes: Dict[str, List[ASTNode]]) -> InlineTypesAlgorithms: """ :param ast: ast tree :param dict_original_nodes: dict with names of function as key and list of ASTNode as values :param method_node: Method declaration. In this method invocation occurred :param invocation_node: invocation node :return: InlineTypesAlgorithms enum """ original_invoked_method = dict_original_nodes.get(invocation_node.member, []) # ignore overridden functions if (len(original_invoked_method) == 0) or (len(original_invoked_method) > 1): return InlineTypesAlgorithms.DO_NOTHING else: original_method = original_invoked_method[0] if not original_method.parameters: if not original_method.return_type: # Find the original method declaration by the name of method invocation var_decls = set( get_variables_decl_in_node( ast.get_subtree(original_method))) return check_whether_method_has_return_type( ast.get_subtree(method_node), var_decls) else: return InlineTypesAlgorithms.WITH_RETURN_WITHOUT_ARGUMENTS else: return InlineTypesAlgorithms.DO_NOTHING
def find_patterns(tree: AST, patterns: List[Any]) -> Set[str]: """ Searches all setters in a component :param patterns: list of patterns to check :param tree: ast tree :return: list of method name which are setters """ patterns_method_names: Set[str] = set() for method_declaration in tree.get_root().methods: method_ast = tree.get_subtree(method_declaration) for pattern in patterns: if is_ast_pattern(method_ast, pattern): patterns_method_names.add(method_declaration.name) return patterns_method_names
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 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_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_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 _find_local_method_invocations(method_ast: AST) -> Set[str]: invoked_methods: Set[str] = set() for method_invocation in method_ast.get_proxy_nodes( ASTNodeType.METHOD_INVOCATION): if method_invocation.qualifier is None: invoked_methods.add(method_invocation.member) return invoked_methods
def value(self, ast: AST) -> List[int]: lines: List[int] = [] for node in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION): method_name = node.name if method_name.startswith('get') and self._check_body_nodes( node.body): lines.append(node.line) return sorted(lines)
def _filter_class_methods_and_fields(class_ast: AST, allowed_fields_names: Set[str], allowed_methods_names: Set[str]) -> AST: class_declaration = class_ast.get_root() allowed_nodes = {class_declaration.node_index} for field_declaration in class_declaration.fields: if len(allowed_fields_names & set(field_declaration.names)) != 0: field_ast = class_ast.get_subtree(field_declaration) allowed_nodes.update(node.node_index for node in field_ast) for method_declaration in class_declaration.methods: if method_declaration.name in allowed_methods_names: method_ast = class_ast.get_subtree(method_declaration) allowed_nodes.update(node.node_index for node in method_ast) return AST(class_ast.tree.subgraph(allowed_nodes), class_declaration.node_index)
def get_variables_decl_in_node(method_decl: AST) -> List[str]: names = [] for x in method_decl.get_proxy_nodes(ASTNodeType.VARIABLE_DECLARATOR): if hasattr(x, 'name'): names.append(x.name) elif hasattr(x, 'names'): names.extend(x.names) for x in method_decl.get_proxy_nodes(ASTNodeType.VARIABLE_DECLARATION): if hasattr(x, 'name'): names.append(x.name) elif hasattr(x, 'names'): names.extend(x.names) for x in method_decl.get_proxy_nodes(ASTNodeType.TRY_RESOURCE): names.append(x.name) return names
def _find_fields_usage(method_ast: AST) -> Set[str]: local_variables: Set[str] = set() for variable_declaration in method_ast.get_proxy_nodes( ASTNodeType.LOCAL_VARIABLE_DECLARATION): local_variables.update(variable_declaration.names) method_declaration = method_ast.get_root() for parameter in method_declaration.parameters: local_variables.add(parameter.name) used_fields: Set[str] = set() for member_reference in method_ast.get_proxy_nodes( ASTNodeType.MEMBER_REFERENCE): if member_reference.qualifier is None and \ member_reference.member not in local_variables: used_fields.add(member_reference.member) return used_fields
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 _create_usage_graph(class_ast: AST) -> DiGraph: usage_graph = DiGraph() fields_ids: Dict[str, int] = {} methods_ids: Dict[str, int] = {} class_declaration = class_ast.get_root() for field_declaration in class_declaration.fields: # several fields can be declared at one line for field_name in field_declaration.names: fields_ids[field_name] = len(fields_ids) usage_graph.add_node(fields_ids[field_name], type="field", name=field_name) for method_declaration in class_declaration.methods: method_name = method_declaration.name # overloaded methods considered as single node in usage_graph if method_name not in methods_ids: methods_ids[method_name] = len(fields_ids) + 1 + len(methods_ids) usage_graph.add_node(methods_ids[method_name], type="method", name=method_name) for method_declaration in class_declaration.methods: method_ast = class_ast.get_subtree(method_declaration) for invoked_method_name in _find_local_method_invocations(method_ast): if invoked_method_name in methods_ids: usage_graph.add_edge( methods_ids[method_declaration.name], methods_ids[invoked_method_name], ) for used_field_name in _find_fields_usage(method_ast): if used_field_name in fields_ids: usage_graph.add_edge(methods_ids[method_declaration.name], fields_ids[used_field_name]) return usage_graph
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 parameters(self) -> Dict[str, AST]: parameters: Dict[str, AST] = {} for parameter_node in self.children_with_type( self.root, ASTNodeType.FORMAL_PARAMETER): parameter_name_node = next( iter( self.children_with_type(parameter_node, ASTNodeType.STRING))) parameter_name = self.get_attr(parameter_name_node, 'string') parameters[parameter_name] = AST( dfs_tree(self.tree, parameter_node), parameter_node) return parameters
def value(self, ast: AST) -> int: metric = 0 for node in ast.get_proxy_nodes(*NCSSMetric._keyword_node_types, *NCSSMetric._declarations_node_types, *NCSSMetric._misc_node_types): metric += 1 if node.node_type == ASTNodeType.IF_STATEMENT and self._has_pure_else_statements( node): metric += 1 elif node.node_type == ASTNodeType.TRY_STATEMENT and self._has_finally_block( node): metric += 1 return metric
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_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_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_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 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_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 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 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_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 _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(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)