Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ from program_slicing.graph.statement import StatementType
```

- **FUNCTION** - function declaration _Statement_.
- **VARIABLE** - variable declaration _Statement_.
- **VARIABLE** - standard type variable declaration _Statement_.
- **OBJECT** - object type variable declaration _Statement_.
- **ASSIGNMENT** - variable assignment _Statement_ (such as `i = 0`, `i += 1`, `i++`, etc).
- **CALL** - function call _Statement_.
- **SCOPE** - scope _Statement_ (such as braces `{}` or empty body in `if (...) a = 0`).
Expand Down Expand Up @@ -225,10 +226,10 @@ manager_by_cfg = ProgramGraphsManager.from_control_flow_graph(control_flow_graph
but not one of them.
- **get_affecting_statements** - return _Statements_ from the given set of _Statements_ that affect
some _Statement_ not form the given set.
- **get_changed_variables_statements** - return `VARIABLE` _Statements_ that represent variables changed
in the given set of _Statements_.
- **get_involved_variables_statements** - return `VARIABLE` _Statements_ that represent variables involved
(including usage) in the given set of _Statements_.
- **get_changed_variables_statements** - return `VARIABLE` and `OBJECT` _Statements_ that represent
variables changed in the given set of _Statements_.
- **get_involved_variables_statements** - return `VARIABLE` and `OBJECT` _Statements_ that represent
variables involved (including usage) in the given set of _Statements_.
- **contain_redundant_statements** - check if the given set of _Statements_ contain part of some construction
not fully included in the given set.

Expand Down
4 changes: 0 additions & 4 deletions integration_tests/files/expected_EMOs.json
Original file line number Diff line number Diff line change
Expand Up @@ -1333,10 +1333,6 @@
45,
56
],
[
49,
55
],
[
50,
55
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/files/method_12.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
protected IApiProblem createExternalDependenciesProblem(HashMap problems, IReferenceDescriptor dependency, String referenceTypeName, IMemberDescriptor referencedMember, int elementType, int flag) {
String resource = referenceTypeName;
String primaryTypeName = referenceTypeName.replace('$', '.');
int charStart = -1, charEnd = -1, lineNumber = -1;
int charStart = -1, charEnd = -1, lineNumber = -1;
if (fJavaProject != null) {
try {

Expand Down
4 changes: 3 additions & 1 deletion program_slicing/decomposition/block/extension/slicing.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ def __get_incoming_variables(
# can be multimap, but it's ok for our purposes
if data_dom not in block_statements and data_dom.name not in incoming_variables:
if __flow_dep_given_data_dep(statement, data_dom):
# FIXME: what if one variable has been passed to several different statements?
incoming_variables[data_dom.name] = statement
return incoming_variables

Expand Down Expand Up @@ -181,6 +182,7 @@ def __flow_dep_given_data_dep(
if statement_2.statement_type not in {
StatementType.ASSIGNMENT,
StatementType.VARIABLE,
StatementType.OBJECT,
StatementType.FUNCTION
}:
return False
Expand Down Expand Up @@ -286,7 +288,7 @@ def __compute_forward_slice(
variable_def,
forward_slice,
manager,
recursion=(variable_def.statement_type == StatementType.VARIABLE))
recursion=(variable_def.statement_type in {StatementType.VARIABLE, StatementType.OBJECT}))
return forward_slice


Expand Down
10 changes: 6 additions & 4 deletions program_slicing/decomposition/block/slicing.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def get_block_slices_from_manager(
general_statements = sorted((
statement
for statement in statements_in_scope
if statement in manager.general_statements),
if statement in manager.general_statements and statement.ast_node_type != "formal_parameters"),
key=lambda x: (x.start_point, -x.end_point))
general_groups = __build_general_groups(general_statements) if unite_statements_into_groups else [
[statement] for statement in general_statements
Expand All @@ -78,8 +78,6 @@ def get_block_slices_from_manager(
extended_statements = manager.get_statements_in_range(
current_groups[0][0].start_point,
current_groups[-1][-1].end_point)
if "formal_parameters" in {statement.ast_node_type for statement in extended_statements}:
continue
if slice_predicate is not None:
if not slice_predicate.check_statements(
{statement for statement in extended_statements if statement in manager.general_statements},
Expand Down Expand Up @@ -130,7 +128,11 @@ def __build_general_groups(general_statements: List[Statement]) -> List[List[Sta
general_groups = []
for statement in general_statements:
if statement.statement_type in {
StatementType.UNKNOWN, StatementType.ASSIGNMENT, StatementType.VARIABLE, StatementType.CALL
StatementType.UNKNOWN,
StatementType.ASSIGNMENT,
StatementType.VARIABLE,
StatementType.OBJECT,
StatementType.CALL
}:
last_general_group.append(statement)
else:
Expand Down
2 changes: 1 addition & 1 deletion program_slicing/decomposition/slice_predicate.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ def __check_has_returnable_variable(self, context: ProgramGraphsManager = None,
return not self.__has_returnable_variable
start_point, end_point = bounds
for statement in self.__statements:
if statement.statement_type == StatementType.VARIABLE:
if statement.statement_type in {StatementType.VARIABLE, StatementType.OBJECT}:
variable = self.__program_slice.variable if self.__program_slice else kwargs.get("variable", None)
if variable and variable.name != statement.name:
continue
Expand Down
13 changes: 9 additions & 4 deletions program_slicing/decomposition/variable/slicing.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def get_complete_computation_slices(
def __obtain_variable_statements(cdg: ControlDependenceGraph, root: Statement) -> Set[Statement]:
return {
statement for statement in networkx.algorithms.traversal.dfs_tree(cdg, root)
if statement.statement_type == StatementType.VARIABLE
if statement.statement_type in {StatementType.VARIABLE, StatementType.OBJECT}
}


Expand Down Expand Up @@ -231,9 +231,14 @@ def __obtain_content(root: Statement, basic_block: BasicBlock) -> Iterator[State

def __is_slicing_criterion(assignment_statement: Statement, variable_statement: Statement) -> bool:
return \
(assignment_statement.statement_type == StatementType.VARIABLE or
assignment_statement.statement_type == StatementType.ASSIGNMENT) and \
variable_statement.statement_type == StatementType.VARIABLE and \
assignment_statement.statement_type in {
StatementType.VARIABLE,
StatementType.OBJECT,
StatementType.ASSIGNMENT
} and \
variable_statement.statement_type in {
StatementType.VARIABLE,
StatementType.OBJECT} and \
variable_statement.name == assignment_statement.name


Expand Down
99 changes: 75 additions & 24 deletions program_slicing/graph/convert/cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
__maintainer__ = 'kuyaki'
__date__ = '2021/04/01'

from typing import Dict, Set
from typing import Dict, Set, Tuple, List

import networkx

Expand Down Expand Up @@ -36,8 +36,8 @@ def to_ddg(cfg: ControlFlowGraph) -> DataDependenceGraph:
:return: Data Dependence Graph which nodes where contained in the Control Flow Graph on which it was based on.
"""
ddg = DataDependenceGraph()
visited: Dict[BasicBlock, Dict[str, Set[Statement]]] = {}
variables: Dict[str, Set[Statement]] = {}
visited: Dict[BasicBlock, Dict[str, Set[Tuple[Statement, StatementType]]]] = {}
variables: Dict[str, Set[Tuple[Statement, StatementType]]] = {}
for root in cfg.entry_points:
__to_ddg(root, cfg=cfg, ddg=ddg, visited=visited, variables=variables)
ddg.add_entry_point(root.root)
Expand All @@ -61,32 +61,34 @@ def __to_ddg(
root: BasicBlock,
cfg: ControlFlowGraph,
ddg: DataDependenceGraph,
visited: Dict[BasicBlock, Dict[str, Set[Statement]]],
variables: Dict[str, Set[Statement]]) -> None:
visited: Dict[BasicBlock, Dict[str, Set[Tuple[Statement, StatementType]]]],
variables: Dict[str, Set[Tuple[Statement, StatementType]]]) -> None:
if root in visited:
if not __update_variables(visited[root], variables):
return
else:
visited[root] = {variable: variable_set.copy() for variable, variable_set in variables.items()}
variables_entered: Dict[str, Set[Statement]] = visited[root]
variables_passed: Dict[str, Set[Statement]] = {
variables_entered: Dict[str, Set[Tuple[Statement, StatementType]]] = visited[root]
variables_passed: Dict[str, Set[Tuple[Statement, StatementType]]] = {
variable: variable_set for variable, variable_set in variables_entered.items()
}
for statement in root:
ddg.add_node(statement)
for affecting_variable_name in statement.affected_by:
if statement.statement_type == StatementType.VARIABLE and affecting_variable_name == statement.name:
continue
if affecting_variable_name in variables_passed:
for variable_statement in variables_passed[affecting_variable_name]:
ddg.add_edge(variable_statement, statement)
if statement.statement_type == StatementType.VARIABLE or statement.statement_type == StatementType.ASSIGNMENT:
variables_passed[statement.name] = {statement}
should_be_thrown = __add_edges_and_get_variables_should_be_thrown(ddg, variables_passed, statement)
if __statement_is_object_or_variable(statement):
variables_passed[statement.name] = {(statement, statement.statement_type)}
elif statement.statement_type == StatementType.ASSIGNMENT:
variables_passed[statement.name] = {
(statement, StatementType.OBJECT if statement.name in should_be_thrown else StatementType.VARIABLE)
}
if statement.statement_type in {StatementType.CALL, StatementType.VARIABLE, StatementType.OBJECT}:
__pass_variables(variables_passed, should_be_thrown, statement)
for child in cfg.successors(root):
__to_ddg(child, cfg=cfg, ddg=ddg, visited=visited, variables=variables_passed)


def __update_variables(old_variables: Dict[str, Set[Statement]], new_variables: Dict[str, Set[Statement]]) -> bool:
def __update_variables(
old_variables: Dict[str, Set[Tuple[Statement, StatementType]]],
new_variables: Dict[str, Set[Tuple[Statement, StatementType]]]) -> bool:
updated = False
for variable, variable_set in new_variables.items():
if variable not in old_variables:
Expand All @@ -102,7 +104,10 @@ def __update_variables(old_variables: Dict[str, Set[Statement]], new_variables:


def __correct_scope_relations(ddg: DataDependenceGraph) -> None:
variable_statements = [statement for statement in ddg if statement.statement_type == StatementType.VARIABLE]
variable_statements = [
statement for statement in ddg
if statement.statement_type in {StatementType.VARIABLE, StatementType.OBJECT}
]
for variable_statement in variable_statements:
if variable_statement not in ddg.scope_dependency:
continue
Expand All @@ -112,10 +117,56 @@ def __correct_scope_relations(ddg: DataDependenceGraph) -> None:
if statement.start_point < variable_scope.start_point or statement.end_point > variable_scope.end_point:
remove_statements.append(statement)
for statement in remove_statements:
remove_edges = []
for predecessor in ddg.predecessors(statement):
if predecessor.name == variable_statement.name and \
variable_scope.start_point <= predecessor.start_point and \
variable_scope.end_point >= predecessor.end_point:
remove_edges.append((predecessor, statement))
remove_edges = __get_removed_edges(ddg, variable_scope, variable_statement, statement)
ddg.remove_edges_from(remove_edges)


def __get_removed_edges(
ddg: DataDependenceGraph,
variable_scope: Statement,
variable_statement: Statement,
corrected_statement: Statement) -> List[Tuple[Statement, Statement]]:
remove_edges = []
for predecessor in ddg.predecessors(corrected_statement):
if variable_scope.start_point <= predecessor.start_point and \
variable_scope.end_point >= predecessor.end_point:
if predecessor.statement_type in {
StatementType.VARIABLE,
StatementType.OBJECT,
StatementType.ASSIGNMENT
}:
if predecessor.name == variable_statement.name:
remove_edges.append((predecessor, corrected_statement))
elif predecessor.statement_type == StatementType.CALL:
if variable_statement.name in predecessor.affected_by:
remove_edges.append((predecessor, corrected_statement))
return remove_edges


def __statement_is_object_or_variable(statement: Statement) -> bool:
return statement.statement_type in {StatementType.VARIABLE, StatementType.OBJECT}


def __pass_variables(
variables_passed: Dict[str, Set[Tuple[Statement, StatementType]]],
should_be_thrown: Set[str],
ddg_predecessor_statement: Statement) -> None:
for variable_name in should_be_thrown:
variables_passed[variable_name] = {(ddg_predecessor_statement, StatementType.OBJECT)}


def __add_edges_and_get_variables_should_be_thrown(
ddg: DataDependenceGraph,
variables_passed: Dict[str, Set[Tuple[Statement, StatementType]]],
statement: Statement) -> Set[str]:
should_be_thrown = set()
ddg.add_node(statement)
for affecting_variable_name in statement.affected_by:
if __statement_is_object_or_variable(statement) and affecting_variable_name == statement.name:
continue
if affecting_variable_name in variables_passed:
for variable_statement, variable_type in variables_passed[affecting_variable_name]:
ddg.add_edge(variable_statement, statement)
if variable_type == StatementType.OBJECT:
should_be_thrown.add(affecting_variable_name)
return should_be_thrown
Loading