Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Restructuring] Resolve unresolved reaching conditions #407

Merged
Merged
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
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
Loading