# pylint:disable=arguments-differ,no-member
from typing import Dict, Optional, TYPE_CHECKING
from ...code_location import CodeLocation
from ...serializable import Serializable
from ...protos import variables_pb2
if TYPE_CHECKING:
from angr.sim_variable import SimVariable
[docs]class VariableAccessSort:
"""
Provides enums for variable access types.
"""
WRITE = 0
READ = 1
REFERENCE = 2
[docs]class VariableAccess(Serializable):
"""
Describes a variable access.
"""
__slots__ = (
"variable",
"access_type",
"location",
"offset",
"atom_hash",
)
def __init__(self, variable, access_type, location, offset, atom_hash=None):
self.variable: "SimVariable" = variable
self.access_type: int = access_type
self.location: CodeLocation = location
self.offset: Optional[int] = offset
self.atom_hash: Optional[int] = atom_hash
def __repr__(self):
access_type = {
VariableAccessSort.WRITE: "write",
VariableAccessSort.READ: "read",
VariableAccessSort.REFERENCE: "reference",
}[self.access_type]
return f"{access_type} {self.variable} @ {self.location} (offset {self.offset})"
def __eq__(self, other):
return (
type(other) is VariableAccess
and self.variable == other.variable
and self.access_type == other.access_type
and self.location == other.location
and self.offset == other.offset
)
def __hash__(self):
return hash((VariableAccess, self.variable, self.access_type, self.location, self.offset))
@classmethod
def _get_cmsg(cls):
return variables_pb2.VariableAccess()
[docs] def serialize_to_cmessage(self):
# pylint:disable=no-member
cmsg = self._get_cmsg()
cmsg.ident = self.variable.ident
cmsg.block_addr = self.location.block_addr
cmsg.stmt_idx = self.location.stmt_idx
cmsg.ins_addr = self.location.ins_addr
if self.offset is not None:
if isinstance(self.offset, int):
cmsg.offset = self.offset
if self.atom_hash is not None:
cmsg.atom_hash = self.atom_hash
if self.access_type == VariableAccessSort.READ:
cmsg.access_type = variables_pb2.VariableAccess.READ
elif self.access_type == VariableAccessSort.WRITE:
cmsg.access_type = variables_pb2.VariableAccess.WRITE
elif self.access_type == VariableAccessSort.REFERENCE:
cmsg.access_type = variables_pb2.VariableAccess.REFERENCE
else:
raise NotImplementedError()
return cmsg
[docs] @classmethod
def parse_from_cmessage(
cls, cmsg, variable_by_ident: Optional[Dict[str, "SimVariable"]] = None, **kwargs
) -> "VariableAccess":
assert variable_by_ident is not None
variable = variable_by_ident[cmsg.ident]
location = CodeLocation(cmsg.block_addr, cmsg.stmt_idx, ins_addr=cmsg.ins_addr)
if cmsg.access_type == variables_pb2.VariableAccess.READ:
access_type = VariableAccessSort.READ
elif cmsg.access_type == variables_pb2.VariableAccess.WRITE:
access_type = VariableAccessSort.WRITE
elif cmsg.access_type == variables_pb2.VariableAccess.REFERENCE:
access_type = VariableAccessSort.REFERENCE
else:
raise NotImplementedError()
model = VariableAccess(
variable,
access_type,
location,
cmsg.offset if cmsg.HasField("offset") else None,
atom_hash=cmsg.atom_hash if cmsg.HasField("atom_hash") else None,
)
return model