Source code for angr.knowledge_plugins.key_definitions.uses

# pylint:disable=unsubscriptable-object
from typing import Set, Optional, Tuple, Any, Union, TYPE_CHECKING

from ...utils.cowdict import DefaultChainMapCOW
from ...code_location import CodeLocation

if TYPE_CHECKING:
    from .definition import Definition


[docs]class Uses: """ Describes uses (including the use location and the use expression) for definitions. """ __slots__ = ("_uses_by_definition", "_uses_by_location")
[docs] def __init__( self, uses_by_definition: Optional[DefaultChainMapCOW] = None, uses_by_location: Optional[DefaultChainMapCOW] = None, ): self._uses_by_definition: DefaultChainMapCOW["Definition", Set[Tuple[CodeLocation, Optional[Any]]]] = ( DefaultChainMapCOW(set, collapse_threshold=25) if uses_by_definition is None else uses_by_definition ) self._uses_by_location: DefaultChainMapCOW[CodeLocation, Set[Tuple["Definition", Optional[Any]]]] = ( DefaultChainMapCOW(set, collapse_threshold=25) if uses_by_location is None else uses_by_location )
[docs] def add_use(self, definition: "Definition", codeloc: CodeLocation, expr: Optional[Any] = None): """ Add a use for a given definition. :param definition: The definition that is used. :param codeloc: The code location where the use occurs. :param expr: The expression that uses the specified definition at this location. """ self._uses_by_definition[definition].add((codeloc, expr)) self._uses_by_location[codeloc].add((definition, expr))
[docs] def get_uses(self, definition: "Definition") -> Set[CodeLocation]: """ Retrieve the uses of a given definition. :param definition: The definition for which we get the uses. """ return {codeloc for codeloc, _ in self._uses_by_definition.get(definition, set())}
[docs] def get_uses_with_expr(self, definition: "Definition") -> Set[Tuple[CodeLocation, Optional[Any]]]: """ Retrieve the uses and the corresponding expressions of a given definition. :param definition: The definition for which we get the uses and the corresponding expressions. """ return self._uses_by_definition.get(definition, set())
[docs] def remove_use(self, definition: "Definition", codeloc: "CodeLocation", expr: Optional[Any] = None) -> None: """ Remove one use of a given definition. :param definition: The definition of which to remove the uses. :param codeloc: The code location where the use is. :param expr: The expression that uses the definition at the given location. :return: None """ if definition in self._uses_by_definition: if codeloc in self._uses_by_definition[definition]: if expr is None: for codeloc_, expr_ in list(self._uses_by_definition[definition]): if codeloc_ == codeloc: self._uses_by_definition[definition].remove((codeloc_, expr_)) else: self._uses_by_definition[definition].remove((codeloc, expr)) if codeloc in self._uses_by_location: for item in list(self._uses_by_location[codeloc]): if item[0] == definition: self._uses_by_location[codeloc].remove(item)
[docs] def remove_uses(self, definition: "Definition"): """ Remove all uses of a given definition. :param definition: The definition of which to remove the uses. :return: None """ if definition in self._uses_by_definition: codeloc_and_ids = self._uses_by_definition[definition] del self._uses_by_definition[definition] for codeloc, _ in codeloc_and_ids: for item in list(self._uses_by_location[codeloc]): if item[0] == definition: self._uses_by_location[codeloc].remove(item)
[docs] def get_uses_by_location( self, codeloc: CodeLocation, exprs: bool = False ) -> Union[Set["Definition"], Set[Tuple["Definition", Optional[Any]]]]: """ Retrieve all definitions that are used at a given location. :param codeloc: The code location. :return: A set of definitions that are used at the given location. """ if exprs: return self._uses_by_location.get(codeloc, set()) return {item[0] for item in self._uses_by_location.get(codeloc, set())}
[docs] def get_uses_by_insaddr( self, ins_addr: int, exprs: bool = False ) -> Union[Set["Definition"], Set[Tuple["Definition", Optional[Any]]]]: """ Retrieve all definitions that are used at a given location specified by the instruction address. :param ins_addr: The instruction address. :return: A set of definitions that are used at the given location. """ all_uses = set() for codeloc, uses in self._uses_by_location.items(): if codeloc.ins_addr == ins_addr: all_uses |= uses if exprs: return all_uses return {item[0] for item in all_uses}
[docs] def copy(self) -> "Uses": """ Copy the instance. :return: Return a new <Uses> instance containing the same data. """ u = Uses( uses_by_definition=self._uses_by_definition.copy(), uses_by_location=self._uses_by_location.copy(), ) return u
[docs] def merge(self, other: "Uses") -> bool: """ Merge an instance of <Uses> into the current instance. :param other: The other <Uses> from which the data will be added to the current instance. :return: True if any merge occurred, False otherwise """ merge_occurred = False for k, v in other._uses_by_definition.items(): if k not in self._uses_by_definition: self._uses_by_definition[k] = v merge_occurred = True elif not v.issubset(self._uses_by_definition[k]): merge_occurred = True self._uses_by_definition[k] |= v for k, v in other._uses_by_location.items(): if k not in self._uses_by_location: self._uses_by_location[k] = v merge_occurred = True elif not v.issubset(self._uses_by_location[k]): merge_occurred = True self._uses_by_location[k] |= v return merge_occurred