Skip to content

Commit

Permalink
Explicit hash/eq for dataflow objects (#417)
Browse files Browse the repository at this point in the history
* Explicit hash/eqs

* black

---------

Co-authored-by: Niklas Bergmann <[email protected]>
  • Loading branch information
rihi and 0x6e62 authored Jun 20, 2024
1 parent 8c7938e commit df6e516
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 14 deletions.
93 changes: 80 additions & 13 deletions decompiler/structures/pseudo/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

from ...util.insertion_ordered_set import InsertionOrderedSet
from .complextypes import Enum
from .typing import ArrayType, CustomType, Type, UnknownType
from .typing import CustomType, Type, UnknownType

T = TypeVar("T")
DecompiledType = TypeVar("DecompiledType", bound=Type)
Expand All @@ -58,18 +58,6 @@ class DataflowObject(ABC):
def __init__(self, tags: Optional[Tuple[Tag, ...]] = None):
self.tags = tags

def __eq__(self, other) -> bool:
"""Check for equality."""
return type(other) == type(self) and hash(self) == hash(other)

def __hash__(self) -> int:
"""Return a hash value for the expression."""
return hash(repr(self))

def __repr__(self):
"""Return a debug representation."""
return str(self)

@abstractmethod
def __iter__(self) -> Iterator[DataflowObject]:
"""Iterate all nested DataflowObjects."""
Expand Down Expand Up @@ -149,6 +137,12 @@ def __init__(self, msg: str, tags: Optional[Tuple[Tag, ...]] = None):
self.msg = msg
super().__init__(tags)

def __eq__(self, __value):
return isinstance(__value, UnknownExpression) and self.msg == __value.msg

def __hash__(self):
return hash(self.msg)

def __str__(self) -> str:
"""Return the error message as string representation."""
return self.msg
Expand Down Expand Up @@ -183,6 +177,17 @@ def __init__(
self._pointee = pointee
super().__init__(tags)

def __eq__(self, __value):
return (
isinstance(__value, Constant)
and self.value == __value.value
and self._type == __value._type
and self._pointee == __value.pointee
)

def __hash__(self):
return hash((tuple(self.value) if isinstance(self.value, list) else self.value, self._type, self._pointee))

def __repr__(self) -> str:
value = str(self) if isinstance(self.value, str) else self.value
if self.pointee:
Expand Down Expand Up @@ -235,6 +240,12 @@ class NotUseableConstant(Constant):
def __init__(self, value: str, tags: Optional[Tuple[Tag, ...]] = None):
super().__init__(value, CustomType("double", 0), tags=tags)

def __eq__(self, __value):
return isinstance(__value, NotUseableConstant) and self.value == __value.value

def __hash__(self):
return hash(self.value)

def __str__(self) -> str:
"""Return a string because NotUseableConstant are string only"""
return self.value
Expand All @@ -255,6 +266,12 @@ def __init__(self, name: str, value: Union[int, float], vartype: Type = UnknownT
super().__init__(value, vartype, tags=tags)
self._name = name

def __eq__(self, __value):
return isinstance(__value, Symbol) and self._name == __value._name and self.value == __value.value

def __hash__(self):
return hash((self._name, self.value))

@property
def name(self) -> str:
return self._name
Expand All @@ -278,13 +295,25 @@ def copy(self) -> Symbol:
class FunctionSymbol(Symbol):
"""Represents a function name"""

def __eq__(self, __value):
return isinstance(__value, FunctionSymbol) and super().__eq__(__value)

def __hash__(self):
return super().__hash__()

def copy(self) -> FunctionSymbol:
return FunctionSymbol(self.name, self.value, self._type.copy(), self.tags)


class ImportedFunctionSymbol(FunctionSymbol):
"""Represents an imported function name"""

def __eq__(self, __value):
return isinstance(__value, ImportedFunctionSymbol) and super().__eq__(__value)

def __hash__(self):
return super().__hash__()

def copy(self) -> ImportedFunctionSymbol:
return ImportedFunctionSymbol(self._name, self.value, self._type.copy(), self.tags)

Expand All @@ -297,6 +326,12 @@ class IntrinsicSymbol(FunctionSymbol):
def __init__(self, name: str):
super().__init__(name, self.INTRINSIC_ADDRESS)

def __eq__(self, __value):
return isinstance(__value, IntrinsicSymbol) and self.name == __value.name

def __hash__(self):
return hash(self.name)

def __repr__(self):
return f"intrinsic '{self.name}'"

Expand Down Expand Up @@ -324,6 +359,18 @@ def __init__(
self.ssa_name = ssa_name
super().__init__(tags)

def __eq__(self, __value):
return (
isinstance(__value, Variable)
and self._name == __value._name
and self.ssa_label == __value.ssa_label
and self._type == __value._type
and self.is_aliased == __value.is_aliased
)

def __hash__(self):
return hash((self._name, self.ssa_label, self._type, self.is_aliased))

def __repr__(self) -> str:
"""Return a debug representation of the variable, which includes all the attributes"""
return f"{self.name}#{self.ssa_label} (type: {self.type} aliased: {self.is_aliased})"
Expand Down Expand Up @@ -399,6 +446,12 @@ def __init__(
self.initial_value = initial_value
self.is_constant = is_constant

def __eq__(self, __value):
return isinstance(__value, GlobalVariable) and super().__eq__(__value)

def __hash__(self):
return super().__hash__()

def copy(
self,
name: str = None,
Expand Down Expand Up @@ -445,6 +498,14 @@ def __init__(self, high: Variable, low: Variable, vartype: Type = UnknownType(),
self._low = low
self._type = vartype

def __eq__(self, __value):
return (
isinstance(__value, RegisterPair) and self._high == __value._high and self._low == __value._low and self._type == __value._type
)

def __hash__(self):
return hash((self._high, self._low, self._type))

def __repr__(self) -> str:
"""Return debug representation of register pair"""
return f"{repr(self._high)}:{repr(self._low)} type: {self.type}"
Expand Down Expand Up @@ -507,6 +568,12 @@ def __init__(self, value: list[Constant], vartype: DecompiledType = UnknownType(
tags,
)

def __eq__(self, __value):
return isinstance(__value, ConstantComposition) and super().__eq__(__value)

def __hash__(self):
return super().__hash__()

def __str__(self) -> str:
"""Return a string representation of the ConstantComposition"""
return "{" + ",".join([str(x) for x in self.value]) + "}"
Expand Down
60 changes: 60 additions & 0 deletions decompiler/structures/pseudo/instructions.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ def __init__(self, comment: str, comment_style: str = "C", tags: Optional[Tuple[
self._comment_style = comment_style
self._open_comment, self._close_comment = self.STYLES.get(comment_style, self.STYLES[self.DEFAULT_STYLE])

def __eq__(self, __value):
return isinstance(__value, Comment) and self._comment == __value._comment and self._comment_style == __value._comment_style

def __hash__(self):
return hash((self._comment, self._comment_style))

def __repr__(self) -> str:
"""Return representation of comment."""
return f"{self._open_comment} {self._comment} {self._close_comment}"
Expand Down Expand Up @@ -161,6 +167,12 @@ def __init__(self, destination: Expression, value: Expression, tags: Optional[Tu
"""Init a new Assignment."""
super(Assignment, self).__init__(destination, value, tags=tags)

def __eq__(self, __value):
return isinstance(__value, Assignment) and self._destination == __value._destination and self._value == __value._value

def __hash__(self):
return hash((self._destination, self._value))

def __str__(self) -> str:
"""Return a string representation starting with the lhs."""
if isinstance(self._destination, ListOperation) and not self._destination.operands:
Expand Down Expand Up @@ -211,6 +223,12 @@ def __init__(self, destination: Variable, value: Variable, tags: Optional[Tuple[
"""Init a new Relation."""
super(Relation, self).__init__(destination, value, tags=tags)

def __eq__(self, __value):
return isinstance(__value, Relation) and self._destination == __value._destination and self._value == __value._value

def __hash__(self):
return hash((self._destination, self._value))

def __str__(self) -> str:
"""Return a string representation starting with the lhs."""
return f"{self.destination} -> {self.value}"
Expand Down Expand Up @@ -314,6 +332,12 @@ def __init__(self, condition: Condition, tags: Optional[Tuple[Tag, ...]] = None)
"""Init a new branch instruction."""
super(Branch, self).__init__(condition, tags=tags)

def __eq__(self, __value):
return isinstance(__value, Branch) and self._condition == __value._condition

def __hash__(self):
return hash(self._condition)

def __repr__(self) -> str:
"""Return a debug representation of a branch"""
return f"if {repr(self.condition)}"
Expand All @@ -333,6 +357,12 @@ def __init__(self, condition: Expression, tags: Optional[Tuple[Tag, ...]] = None
"""Init a new branch instruction."""
super(IndirectBranch, self).__init__(condition, tags=tags)

def __eq__(self, __value):
return isinstance(__value, IndirectBranch) and self._condition

def __hash__(self):
return hash(self._condition)

def __repr__(self) -> str:
"""Return a debug representation of a branch"""
return f"jmp {repr(self.condition)}"
Expand All @@ -355,6 +385,12 @@ def __init__(self, values, tags: Optional[Tuple[Tag, ...]] = None):
super().__init__(tags)
self._values = ListOperation(values)

def __eq__(self, __value):
return isinstance(__value, Return) and self._values == __value._values

def __hash__(self):
return hash(self._values)

def __repr__(self) -> str:
return f"return {repr(self._values)}"

Expand Down Expand Up @@ -395,6 +431,12 @@ def accept(self, visitor: DataflowObjectVisitorInterface[T]) -> T:


class Break(Instruction):
def __eq__(self, __value):
return isinstance(__value, Break)

def __hash__(self):
return hash(Break)

def __iter__(self) -> Iterator[Expression]:
yield from ()

Expand All @@ -417,6 +459,12 @@ def accept(self, visitor: DataflowObjectVisitorInterface[T]) -> T:


class Continue(Instruction):
def __eq__(self, __value):
return isinstance(__value, Continue)

def __hash__(self):
return hash(Continue)

def __iter__(self) -> Iterator[Expression]:
yield from ()

Expand Down Expand Up @@ -457,6 +505,12 @@ def __init__(
self._origin_block = origin_block if origin_block else {}
super().__init__(destination, ListOperation(value), tags=tags)

def __eq__(self, __value):
return isinstance(__value, Phi) and self._destination == __value._destination and self._value == __value._value

def __hash__(self):
return hash((self._destination, self._value))

def __repr__(self):
return f"{repr(self.destination)} = ϕ({repr(self.value)})"

Expand Down Expand Up @@ -516,6 +570,12 @@ class MemPhi(Phi):
def __init__(self, destination_var: Variable, source_vars: Sequence[Variable], tags: Optional[Tuple[Tag, ...]] = None):
super().__init__(destination_var, source_vars, tags=tags)

def __eq__(self, __value):
return isinstance(__value, MemPhi) and super().__eq__(__value)

def __hash__(self):
return super().__hash__()

def __str__(self) -> str:
return f"{self.destination} = ϕ({self.value})"

Expand Down
Loading

0 comments on commit df6e516

Please sign in to comment.