Skip to content

Commit

Permalink
Merge branch 'main' into refactoring_switch_node_handler
Browse files Browse the repository at this point in the history
  • Loading branch information
steffenenders authored Jun 26, 2024
2 parents 85905e5 + c7098e4 commit 62949aa
Show file tree
Hide file tree
Showing 20 changed files with 529 additions and 191 deletions.
8 changes: 4 additions & 4 deletions decompiler/pipeline/commons/expressionpropagationcommons.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,9 @@ def _is_address_into_dereference(self, definition: Assignment, target: Instructi
if self._is_address(definition.value):
for subexpr in target:
for sub in self._find_subexpressions(subexpr):
if self._is_dereference(sub) and sub.operand == definition.destination:
if self._is_dereference(sub) and sub.operand in definition.definitions:
return True
return False

def _contains_aliased_variables(self, definition: Assignment) -> bool:
"""
Expand Down Expand Up @@ -326,14 +327,13 @@ def _has_any_of_dangerous_uses_between_definition_and_target(
def _get_dangerous_uses_of_variable_address(self, var: Variable) -> Set[Instruction]:
"""
Dangerous use of & of x is func(&x) cause it can potentially modify x.
*(&x) could also do the job but I consider it to be too exotic so that we could get such instruction from Binary Ninja
If it happens we can handle it later.
Another case is an Assignment where the left side is *(&).
:param var: aliased variable
:return: set of function call assignments that take &var as parameter
"""
dangerous_uses = set()
for use in self._use_map.get(var):
if not self._is_call_assignment(use):
if not self._is_call_assignment(use) and not (isinstance(use, Assignment) and self._is_dereference(use.destination)):
continue
for subexpr in self._find_subexpressions(use):
if self._is_address(subexpr):
Expand Down
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 @@ -62,6 +62,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 62949aa

Please sign in to comment.