diff options
| author | Peter LaFosse <peter@vector35.com> | 2021-06-23 09:31:22 -0400 |
|---|---|---|
| committer | Peter LaFosse <peter@vector35.com> | 2021-09-05 10:08:09 -0400 |
| commit | 6de4900db74a341fddfda46f7cb4342fdc31f8e0 (patch) | |
| tree | 790a990b2b9ccd8229a31754d28a74a2db839851 /python/variable.py | |
| parent | ea052d32732f62000164f243a1aad0bb7425199e (diff) | |
Refactor Variable class
The Variable class is now broken into 3 separate classes with helper
methods to convert between them.
CoreVariable is implemented as a frozen dataclass and is analogous
to BNVariable
VariableNameAndType is analogous to BNVariableNameAndType
Bothe the above are passive objects and can not be changed directly
The new and improved Variable object is now an active object which
can be operated on directly and changes will take effect immediately
Diffstat (limited to 'python/variable.py')
| -rw-r--r-- | python/variable.py | 178 |
1 files changed, 104 insertions, 74 deletions
diff --git a/python/variable.py b/python/variable.py index 187d5e3d..011402d8 100644 --- a/python/variable.py +++ b/python/variable.py @@ -683,21 +683,76 @@ class StackVariableReference(object): @decorators.passive -class Variable(object): - def __init__(self, func, source_type, index, storage, name = None, var_type = None, identifier = None): +@dataclass(frozen=True, order=True) +class CoreVariable: + _source_type:int + index:int + storage:int + + @property + def identifier(self) -> int: + return core.BNToVariableIdentifier(self.to_BNVariable()) + + @property + def source_type(self) -> VariableSourceType: + return VariableSourceType(self._source_type) + + def to_BNVariable(self): + v = core.BNVariable() + v.type = self._source_type + v.index = self.index + v.storage = self.storage + return v + + @classmethod + def from_BNVariable(cls, var:core.BNVariable): + return cls(var.type, var.index, var.storage) + + @classmethod + def from_identifier(cls, identifier): + var = core.BNFromVariableIdentifier(identifier) + return cls(var.type, var.index, var.storage) + +@decorators.passive +@dataclass(frozen=True, order=True) +class VariableNameAndType(CoreVariable): + name:str + type:'binaryninja.types.Type' + + @classmethod + def from_identifier(cls, identifier, name, type): + var = core.BNFromVariableIdentifier(identifier) + return cls(name, type, var.type, var.index, var.storage) + + @classmethod + def from_core_variable(cls, var, name, type): + return cls(name, type, var.type, var.index, var.storage) + + +class Variable: + def __init__(self, func:'binaryninja.function.Function', source_type:VariableSourceType, index:int, storage:int): self._function = func - self._source_type = source_type - self._index = index - self._storage = storage - self._identifier = identifier - self._name = name - self._type = var_type + self._var = CoreVariable(source_type, index, storage) + + @classmethod + def from_variable_name_and_type(cls, func:'binaryninja.function.Function', var:VariableNameAndType): + return cls(func, VariableSourceType(var.type), var.index, var.storage) + + @classmethod + def from_core_variable(cls, func:'binaryninja.function.Function', var:CoreVariable): + return cls(func, var.source_type, var.index, var.storage) + + @classmethod + def from_BNVariable(cls, func:'binaryninja.function.Function', var:core.BNVariable): + return cls(func, var.type, var.index, var.storage) + + @classmethod + def from_identifier(cls, func:'binaryninja.function.Function', identifier:int): + var = core.BNFromVariableIdentifier(identifier) + return cls(func, VariableSourceType(var.type), var.index, var.storage) def __repr__(self): - if self.type is not None: - return f"<var {self.type.get_string_before_name()} {self.name}{self.type.get_string_after_name()}>" - else: - return f"<var {self.name}>" + return f"<var {self.type.get_string_before_name()} {self.name}{self.type.get_string_after_name()}>" def __str__(self): return self.name @@ -705,7 +760,7 @@ class Variable(object): def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented - return (self.identifier, self.function) == (other.identifier, other.function) + return (self.identifier, self._function) == (other.identifier, other._function) def __ne__(self, other): if not isinstance(other, self.__class__): @@ -715,108 +770,83 @@ class Variable(object): def __lt__(self, other): if not isinstance(other, self.__class__): return NotImplemented - return (self.identifier, self.function) < (other.identifier, other.function) + return (self.identifier, self._function) < (other.identifier, other._function) def __gt__(self, other): if not isinstance(other, self.__class__): return NotImplemented - return (self.identifier, self.function) > (other.identifier, other.function) + return (self.identifier, self._function) > (other.identifier, other._function) def __le__(self, other): if not isinstance(other, self.__class__): return NotImplemented - return (self.identifier, self.function) <= (other.identifier, other.function) + return (self.identifier, self._function) <= (other.identifier, other._function) def __ge__(self, other): if not isinstance(other, self.__class__): return NotImplemented - return (self.identifier, self.function) >= (other.identifier, other.function) + return (self.identifier, self._function) >= (other.identifier, other._function) def __hash__(self): - return hash((self.identifier, self.function)) - - @property - def function(self) -> 'binaryninja.function.Function': - """Function where the variable is defined""" - return self._function - - @function.setter - def function(self, value:'binaryninja.function.Function'): - self._function = value + return hash((self.identifier)) @property def source_type(self) -> VariableSourceType: - """:class:`~enums.VariableSourceType`""" - if not isinstance(self._source_type, VariableSourceType): - self._source_type = VariableSourceType(self._source_type) - - return self._source_type - - @source_type.setter - def source_type(self, value:VariableSourceType) -> None: - self._source_type = value + return self._var.source_type @property def index(self) -> int: - return self._index - - @index.setter - def index(self, value:int) -> None: - self._index = value + return self._var.index @property def storage(self) -> int: """Stack offset for StackVariableSourceType, register index for RegisterVariableSourceType""" - return self._storage - - @storage.setter - def storage(self, value:int) -> None: - self._storage = value + return self._var.storage @property def identifier(self) -> int: - if self._identifier is None: - self._identifier = core.BNToVariableIdentifier(self.to_BNVariable()) - return self._identifier + return self._var.identifier + + @property + def core_var(self) -> CoreVariable: + return self._var + + @property + def var_name_and_type(self) -> VariableNameAndType: + return VariableNameAndType.from_core_variable(self._var, self.name, self.type) @property def name(self): """Name of the variable""" - if self._name is None: - if self._function is not None: - self._name = core.BNGetVariableName(self._function.handle, self.to_BNVariable()) - return self._name + return core.BNGetVariableName(self._function.handle, self._var.to_BNVariable()) + + @name.setter + def name(self, name:Optional[str]) -> None: + if name is None: + name = "" + self._function.create_user_var(self, self.type, name) @property - def type(self) -> Optional['binaryninja.types.Type']: - if self._type is None: - if self._function is not None: - var_type_conf = core.BNGetVariableType(self._function.handle, self.to_BNVariable()) - if var_type_conf.type: - self._type = binaryninja.types.Type(var_type_conf.type, platform = self._function.platform, confidence = var_type_conf.confidence) - return self._type + def type(self) -> 'binaryninja.types.Type': + var_type_conf = core.BNGetVariableType(self._function.handle, self._var.to_BNVariable()) + assert var_type_conf.type + _type = binaryninja.types.Type(var_type_conf.type, self._function.platform, var_type_conf.confidence) + return _type - def to_BNVariable(self): - v = core.BNVariable() - v.type = self.source_type - v.index = self._index - v.storage = self._storage - return v + @type.setter + def type(self, new_type:'binaryninja.types.Type') -> None: + self._function.create_user_var(self, new_type, self.name) @property def dead_store_elimination(self): - if self._function is not None and self._identifier is not None: - return DeadStoreElimination(core.BNGetFunctionVariableDeadStoreElimination(self._function.handle, self.to_BNVariable())) - return None + return DeadStoreElimination(core.BNGetFunctionVariableDeadStoreElimination(self._function.handle, self._var.to_BNVariable())) @dead_store_elimination.setter def dead_store_elimination(self, value): - core.BNSetFunctionVariableDeadStoreElimination(self._function.handle, self.to_BNVariable(), value) + core.BNSetFunctionVariableDeadStoreElimination(self._function.handle, self._var.to_BNVariable(), value) - @staticmethod - def from_identifier(func, identifier, name=None, var_type=None): - var = core.BNFromVariableIdentifier(identifier) - return Variable(func, VariableSourceType(var.type), var.index, var.storage, name, var_type, identifier) + def to_BNVariable(self): + return self._var.to_BNVariable() @decorators.passive class ConstantReference(object): |
