Source code for gtirb.symbol
import typing
from uuid import UUID
from .block import Block
from .node import Node, _NodeMessage
from .proto import Symbol_pb2
from .util import DeserializationError, _IndexedAttribute
if typing.TYPE_CHECKING: # pragma: no cover
# Ignore flake8 "imported but unused" errors.
from .ir import IR # noqa: F401
from .module import Module # noqa: F401
Payload = typing.Union[Block, int]
"""A type hint representing the possible Symbol payloads."""
[docs]class Symbol(Node):
"""Represents a symbol, which maps a name to an object in the IR.
:ivar ~.name: The name of this symbol.
:ivar ~.at_end: True if this symbol is at the end of its referent, rather
than at the beginning. Has no meaning for integral symbols.
"""
name = _IndexedAttribute[str]()(lambda self: self.module)
_payload = _IndexedAttribute[typing.Optional[Payload]]()(
lambda self: self.module
)
[docs] def __init__(
self,
name: str,
uuid: typing.Optional[UUID] = None,
payload: typing.Optional[Payload] = None,
at_end: bool = False,
module: typing.Optional["Module"] = None,
):
"""
:param name: The name of this symbol.
:param uuid: The UUID of this ``Symbol``,
or None if a new UUID needs generated via :func:`uuid.uuid4`.
Defaults to None.
:param payload: The value this symbol points to.
May be an address, a Node, or None.
:param at_end: True if this symbol is at the end of its referent,
rather than at the beginning.
:param module: The :class:`Module` this symbol belongs to.
"""
super().__init__(uuid)
self._module: typing.Optional["Module"] = None
self.name = name
self.at_end = at_end
self._payload = payload
# Use the property setter to ensure correct invariants.
self.module = module
@property
def value(self) -> typing.Optional[int]:
"""The value of a Symbol, which is an integer or None.
``value`` and ``referent`` are mutually exclusive.
"""
if not isinstance(self._payload, Block):
return self._payload
return None
@value.setter
def value(self, value: typing.Optional[int]) -> None:
self._payload = value
@property
def referent(self) -> typing.Optional[Block]:
"""The object referred to by a Symbol, which is :class:`Block`
or None. ``value`` and ``referent`` are mutually exclusive.
"""
if isinstance(self._payload, Block):
return self._payload
return None
@referent.setter
def referent(self, referent: typing.Optional[Block]) -> None:
self._payload = referent
@classmethod
def _decode_protobuf(
cls,
proto_symbol: _NodeMessage,
uuid: UUID,
ir: typing.Optional["IR"],
) -> "Symbol":
assert ir
assert isinstance(proto_symbol, Symbol_pb2.Symbol)
symbol = cls(
name=proto_symbol.name, at_end=proto_symbol.at_end, uuid=uuid
)
if proto_symbol.HasField("value"):
symbol.value = proto_symbol.value
if proto_symbol.HasField("referent_uuid"):
referent_uuid = UUID(bytes=proto_symbol.referent_uuid)
referent = ir.get_by_uuid(referent_uuid)
if not isinstance(referent, Block):
raise DeserializationError(
"Symbol: UUID %s is not a block" % referent_uuid
)
symbol.referent = referent
symbol._add_to_uuid_cache(ir._local_uuid_cache)
return symbol
def _to_protobuf(self) -> Symbol_pb2.Symbol:
proto_symbol = Symbol_pb2.Symbol()
proto_symbol.uuid = self.uuid.bytes
if self.value is not None:
proto_symbol.value = self.value
elif self.referent is not None:
proto_symbol.referent_uuid = self.referent.uuid.bytes
proto_symbol.name = self.name
proto_symbol.at_end = self.at_end
return proto_symbol
[docs] def deep_eq(self, other: object) -> bool:
# Do not move __eq__. See docstring for Node.deep_eq for more info.
if not isinstance(other, Symbol):
return False
if self.value != other.value:
return False
if self.referent is None:
if other.referent is not None:
return False
else:
if not self.referent.deep_eq(other.referent):
return False
return (
self.name == other.name
and self.at_end == other.at_end
and self.uuid == other.uuid
)
def __repr__(self) -> str:
return (
"Symbol("
"uuid={uuid!r}, "
"name={name!r}, "
"payload={payload!r}, "
"at_end={at_end!r}, "
")".format(name=self.name, payload=self._payload, **self.__dict__)
)
@property
def module(self) -> typing.Optional["Module"]:
return self._module
@module.setter
def module(self, value: typing.Optional["Module"]) -> None:
if self._module is not None:
self._module.symbols.discard(self)
if value is not None:
value.symbols.add(self)
def _add_to_uuid_cache(self, cache: typing.Dict[UUID, Node]) -> None:
"""Update the UUID cache when this node is added."""
cache[self.uuid] = self
def _remove_from_uuid_cache(self, cache: typing.Dict[UUID, Node]) -> None:
"""Update the UUID cache when this node is removed."""
del cache[self.uuid]
@property
def ir(self) -> typing.Optional["IR"]:
"""Get the IR this node ultimately belongs to."""
if self.module is None:
return None
return self.module.ir