Skip to content

Commit

Permalink
[Restructuring] Resolve unresolved reaching conditions (#407)
Browse files Browse the repository at this point in the history
* Create draft PR for #28

* start with issue

* some minor changes

* no endless recursion

* fix tests

* add test

* modify to always pair two

* fix format

* some refactoring and docstrings

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Eva-Maria Behner <[email protected]>
  • Loading branch information
github-actions[bot] and ebehner authored Jun 26, 2024
1 parent 9647abc commit 749e702
Show file tree
Hide file tree
Showing 17 changed files with 448 additions and 183 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ def _construct_refined_ast(self, seq_node_root: SeqNode) -> AbstractSyntaxTreeNo
ConditionBasedRefinement.refine(self.asforest)
acyclic_processor.preprocess_condition_aware_refinement()
if self.options.reconstruct_switch:
ConditionAwareRefinement.refine(self.asforest, self.options)
updated_switch_nodes = ConditionAwareRefinement.refine(self.asforest, self.options)
for switch_node in updated_switch_nodes:
for sequence_case in (c for c in switch_node.cases if isinstance(c.child, SeqNode)):
ConditionBasedRefinement.refine(self.asforest, sequence_case.child)
acyclic_processor.postprocess_condition_refinement()
root = self.asforest.current_root
self.asforest.remove_current_root()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ def _group_by_reaching_conditions(self, nodes: Tuple[AbstractSyntaxTreeNode]) ->
:param nodes: The AST nodes that we want to group.
:return: A dictionary that assigns to a reaching condition the list of AST code nodes with this reaching condition,
if it are at least two with the same.
if there are at least two with the same.
"""
initial_groups: Dict[LogicCondition, List[AbstractSyntaxTreeNode]] = dict()
for node in nodes:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Module for Condition Aware Refinement
"""

from typing import Set

from decompiler.pipeline.controlflowanalysis.restructuring_commons.condition_aware_refinement_commons.base_class_car import (
BaseClassConditionAwareRefinement,
)
Expand All @@ -21,6 +23,7 @@
SwitchExtractor,
)
from decompiler.pipeline.controlflowanalysis.restructuring_options import RestructuringOptions
from decompiler.structures.ast.ast_nodes import SwitchNode
from decompiler.structures.ast.syntaxforest import AbstractSyntaxForest


Expand All @@ -35,13 +38,14 @@ class ConditionAwareRefinement(BaseClassConditionAwareRefinement):
]

@classmethod
def refine(cls, asforest: AbstractSyntaxForest, options: RestructuringOptions):
def refine(cls, asforest: AbstractSyntaxForest, options: RestructuringOptions) -> Set[SwitchNode]:
condition_aware_refinement = cls(asforest, options)
for stage in condition_aware_refinement.REFINEMENT_PIPELINE:
asforest.clean_up(asforest.current_root)
stage(asforest, options)
condition_aware_refinement.updated_switch_nodes.update(stage(asforest, options))
condition_aware_refinement._remove_redundant_reaching_condition_from_switch_nodes()
asforest.clean_up(asforest.current_root)
return set(switch for switch in condition_aware_refinement.updated_switch_nodes if switch in asforest)

def _remove_redundant_reaching_condition_from_switch_nodes(self):
"""Remove the reaching condition from all switch nodes if it is redundant."""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dataclasses import dataclass
from typing import Iterator, Optional, Tuple
from typing import Iterator, Optional, Set, Tuple

from decompiler.pipeline.controlflowanalysis.restructuring_options import LoopBreakOptions, RestructuringOptions
from decompiler.structures.ast.ast_nodes import AbstractSyntaxTreeNode, CaseNode, FalseNode, SwitchNode, TrueNode
Expand Down Expand Up @@ -63,6 +63,7 @@ def __init__(self, asforest: AbstractSyntaxForest, options: RestructuringOptions
self.asforest: AbstractSyntaxForest = asforest
self.condition_handler: ConditionHandler = asforest.condition_handler
self.options: RestructuringOptions = options
self.updated_switch_nodes: Set[SwitchNode] = set()

def _get_constant_equality_check_expressions_and_conditions(
self, condition: LogicCondition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,14 @@ class InitialSwitchNodeConstructor(BaseClassConditionAwareRefinement):
"""Class that constructs switch nodes."""

@classmethod
def construct(cls, asforest: AbstractSyntaxForest, options: RestructuringOptions):
def construct(cls, asforest: AbstractSyntaxForest, options: RestructuringOptions) -> Set[SwitchNode]:
"""Constructs initial switch nodes if possible."""
initial_switch_constructor = cls(asforest, options)
for cond_node in asforest.get_condition_nodes_post_order(asforest.current_root):
initial_switch_constructor._extract_case_nodes_from_nested_condition(cond_node)
for seq_node in asforest.get_sequence_nodes_post_order(asforest.current_root):
initial_switch_constructor._try_to_construct_initial_switch_node_for(seq_node)
return initial_switch_constructor.updated_switch_nodes

def _extract_case_nodes_from_nested_condition(self, cond_node: ConditionNode) -> None:
"""
Expand Down Expand Up @@ -336,6 +337,7 @@ def _try_to_construct_initial_switch_node_for(self, seq_node: SeqNode) -> None:
sibling_reachability = self.asforest.get_sibling_reachability_of_children_of(seq_node)
switch_cases = list(possible_switch_node.construct_switch_cases())
switch_node = self.asforest.create_switch_node_with(possible_switch_node.expression, switch_cases)
self.updated_switch_nodes.add(switch_node)
case_dependency = CaseDependencyGraph.construct_case_dependency_for(self.asforest.children(switch_node), sibling_reachability)
self._update_reaching_condition_for_case_node_children(switch_node)
self._add_constants_to_cases(switch_node, case_dependency)
Expand Down Expand Up @@ -393,7 +395,7 @@ def _update_reaching_condition_for_case_node_children(self, switch_node: SwitchN
case_node.reaching_condition.is_disjunction_of_literals
), f"The condition of a case node should be a disjunction, but it is {case_node.reaching_condition}!"

if isinstance(cond_node := case_node.child, ConditionNode) and cond_node.false_branch is None:
if (cond_node := case_node.child).is_single_branch:
self._update_condition_for(cond_node, case_node)

case_node.child.reaching_condition = case_node.child.reaching_condition.substitute_by_true(case_node.reaching_condition)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def _insert_case_node(self, new_case_node: AbstractSyntaxTreeNode, case_constant
if default_case := switch_node.default:
new_children.append(default_case)
switch_node._sorted_cases = tuple(new_children)
self.updated_switch_nodes.add(switch_node)

def _new_case_nodes_for(
self, new_case_node: AbstractSyntaxTreeNode, switch_node: SwitchNode, sorted_case_constants: List[Constant]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class MissingCaseFinderCondition(MissingCaseFinder):
"""

@classmethod
def find(cls, asforest: AbstractSyntaxForest, options: RestructuringOptions):
def find(cls, asforest: AbstractSyntaxForest, options: RestructuringOptions) -> Set[SwitchNode]:
"""Try to find missing cases that are branches of condition nodes."""
missing_case_finder = cls(asforest, options)
for condition_node in asforest.get_condition_nodes_post_order(asforest.current_root):
Expand All @@ -40,6 +40,7 @@ def find(cls, asforest: AbstractSyntaxForest, options: RestructuringOptions):
asforest.extract_switch_from_sequence(case_candidate_information.switch_node)
else:
asforest.replace_condition_node_by_single_branch(condition_node)
return missing_case_finder.updated_switch_nodes

def _can_insert_missing_case_node(self, condition_node: ConditionNode) -> Optional[CaseCandidateInformation]:
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def insert(self, possible_case: CaseNodeCandidate):
first fallthrough-cases.
- If the possible-case node is reached by the switch-node, then the content must be after any other code.
Thus, it must contain all constants from a block of fallthrough-cases. But here, it can contain more.
- If neither one reaches the other, then it can be insert anywhere, at long as it can be archived by only
- If neither one reaches the other, then it can be inserted anywhere, as long as it can be archived by only
resorting fallthrough-cases all leading to the same code-execution.
"""
cases_of_switch_node = {case.constant for case in self._switch_node.children}
Expand All @@ -70,6 +70,7 @@ def insert(self, possible_case: CaseNodeCandidate):
return

self._sibling_reachability_graph.update_when_inserting_new_case_node(compare_node, self._switch_node)
self.updated_switch_nodes.add(self._switch_node)
compare_node.clean()

def _add_case_before(self, intersecting_linear_case: Tuple[CaseNode], possible_case_properties: IntersectingCaseNodeProperties) -> bool:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def __init__(self, asforest: AbstractSyntaxForest, options: RestructuringOptions
self._switch_node_of_expression: Dict[ExpressionUsages, SwitchNode] = dict()

@classmethod
def find(cls, asforest: AbstractSyntaxForest, options: RestructuringOptions):
def find(cls, asforest: AbstractSyntaxForest, options: RestructuringOptions) -> Set[SwitchNode]:
"""
Try to find missing cases that are children of sequence nodes.
Expand All @@ -58,6 +58,7 @@ def find(cls, asforest: AbstractSyntaxForest, options: RestructuringOptions):

if seq_node in asforest:
seq_node.clean()
return missing_case_finder.updated_switch_nodes

def _initialize_switch_node_of_expression_dictionary(self) -> None:
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional, Union
from typing import Optional, Set, Union

from decompiler.pipeline.controlflowanalysis.restructuring_commons.condition_aware_refinement_commons.base_class_car import (
BaseClassConditionAwareRefinement,
Expand All @@ -19,6 +19,7 @@ def extract(cls, asforest: AbstractSyntaxForest, options: RestructuringOptions):
for switch_node in list(asforest.get_switch_nodes_post_order(asforest.current_root)):
while switch_extractor._successfully_extracts_switch_nodes(switch_node):
pass
return switch_extractor.updated_switch_nodes

def _successfully_extracts_switch_nodes(self, switch_node: SwitchNode) -> bool:
"""
Expand Down
Loading

0 comments on commit 749e702

Please sign in to comment.