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 _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 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 _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 insert_code_with_new_file_creation( class_name: str, ast: AST, method_node: ASTNode, invocation_node: ASTNode, file_path: Path, output_path: Path, dict_original_invocations: Dict[str, List[ASTNode]]) -> Dict[str, Any]: """ If invocations of class methods were found, we process through all of them and for each substitution opportunity by method's body, we create new file. """ file_name = file_path.stem if not os.path.exists(output_path): output_path.mkdir(parents=True) new_full_filename = Path( output_path, f'{file_name}_{method_node.name}_{invocation_node.line}.java') original_func = dict_original_invocations.get( invocation_node.member)[0] # type: ignore ncss = NCSSMetric().value(ast.get_subtree(original_func)) line_to_csv = {} # @acheshkov asked to consider only methods with ncss > 3, that's all. if ncss > 3: body_start_line, body_end_line = method_body_lines( original_func, file_path) text_lines = read_text_with_autodetected_encoding( str(file_path)).split('\n') # we do not inline one-line methods like # public String getRemainingString() {return str.substring(index);} if body_start_line != body_end_line: algorithm_type = determine_algorithm_insertion_type( ast, method_node, invocation_node, dict_original_invocations) algorithm_for_inlining = AlgorithmFactory().create_obj( algorithm_type) if algorithm_type != InlineTypesAlgorithms.DO_NOTHING: line_to_csv = { 'input_filename': file_path, 'class_name': class_name, 'invocation_text_string': text_lines[invocation_node.line - 1].lstrip(), 'method_where_invocation_occurred': method_node.name, 'invocation_method_name': original_func.name, 'output_filename': new_full_filename } inline_method_bounds = algorithm_for_inlining( ).inline_function( file_path, invocation_node.line, body_start_line, body_end_line, new_full_filename, ) if inline_method_bounds is not None: line_to_csv[ 'inline_insertion_line_start'] = inline_method_bounds[ 0] line_to_csv[ 'inline_insertion_line_end'] = inline_method_bounds[1] if get_ast_if_possible(new_full_filename): rest_of_csv_row_for_changed_file = find_lines_in_changed_file( class_name=class_name, method_node=method_node, new_full_filename=new_full_filename, original_func=original_func) can_be_parsed = True line_to_csv.update(rest_of_csv_row_for_changed_file) else: can_be_parsed = False line_to_csv['can_be_parsed'] = can_be_parsed return line_to_csv
def is_match_to_the_conditions(ast: AST, method_invoked: ASTNode, found_method_decl=None) -> bool: if method_invoked.parent.node_type == ASTNodeType.THIS: parent = method_invoked.parent.parent class_names = [ x for x in method_invoked.parent.children if hasattr(x, 'string') ] member_references = [ x for x in method_invoked.parent.children if hasattr(x, 'member') ] lst = [ x for x in member_references if x.member != method_invoked.member ] + class_names no_children = not lst else: parent = method_invoked.parent no_children = True maybe_if = parent.parent is_not_method_inv_single_statement_in_if = True if maybe_if.node_type == ASTNodeType.IF_STATEMENT: if hasattr(maybe_if.then_statement, 'expression'): if maybe_if.then_statement.expression.node_type == ASTNodeType.METHOD_INVOCATION: is_not_method_inv_single_statement_in_if = False is_not_assign_value_with_return_type = True is_not_several_returns = True if found_method_decl.return_type: if parent.node_type == ASTNodeType.VARIABLE_DECLARATOR: is_not_assign_value_with_return_type = False ast_subtree = ast.get_subtree(found_method_decl) stats = [ x for x in ast_subtree.get_proxy_nodes(ASTNodeType.RETURN_STATEMENT) ] if len(stats) > 1: is_not_several_returns = False is_not_parent_member_ref = not (method_invoked.parent.node_type == ASTNodeType.MEMBER_REFERENCE) is_not_chain_before = not ( parent.node_type == ASTNodeType.METHOD_INVOCATION) and no_children chains_after = [ x for x in method_invoked.children if x.node_type == ASTNodeType.METHOD_INVOCATION ] is_not_chain_after = not chains_after is_not_inside_if = not (parent.node_type == ASTNodeType.IF_STATEMENT) is_not_inside_while = not (parent.node_type == ASTNodeType.WHILE_STATEMENT) is_not_inside_for = not (parent.node_type == ASTNodeType.FOR_STATEMENT) is_not_enhanced_for_control = not (parent.node_type == ASTNodeType.ENHANCED_FOR_CONTROL) # ignore case else if (getServiceInterface() != null) { is_not_binary_operation = not (parent.node_type == ASTNodeType.BINARY_OPERATION) is_not_ternary = not (parent.node_type == ASTNodeType.TERNARY_EXPRESSION) # if a parameter is any expression, we ignore it, # since it is difficult to extract with AST is_actual_parameter_simple = all( [hasattr(x, 'member') for x in method_invoked.arguments]) is_not_class_creator = not (parent.node_type == ASTNodeType.CLASS_CREATOR) is_not_cast = not (parent.node_type == ASTNodeType.CAST) is_not_array_creator = not (parent.node_type == ASTNodeType.ARRAY_CREATOR) is_not_lambda = not (parent.node_type == ASTNodeType.LAMBDA_EXPRESSION) is_not_at_the_same_line_as_prohibited_stats = check_nesting_statements( method_invoked) other_requirements = all([ is_not_chain_before, is_actual_parameter_simple, is_not_chain_after, is_not_inside_if, is_not_inside_while, is_not_binary_operation, is_not_ternary, is_not_class_creator, is_not_cast, is_not_array_creator, is_not_parent_member_ref, is_not_inside_for, is_not_enhanced_for_control, is_not_lambda, is_not_method_inv_single_statement_in_if, is_not_assign_value_with_return_type, is_not_several_returns, is_not_at_the_same_line_as_prohibited_stats, not method_invoked.arguments ]) if (not method_invoked.qualifier and other_requirements) or \ (method_invoked.qualifier == 'this' and other_requirements): return True else: return False