Events¶
The ObjectContainer¶
angr management uses a class called ObjectContainer to implement a pub-sub model
and synchronize changing object references. Let’s use instance.project as an
example. This is an ObjectContainer that contains the current project. You can
use it in every way that you would normally use a project - you can access
project.factory, project.kb, etc. However, it also has two very
important features that are helpful for building UIs.
First, the pub-sub model. You can subscribe to changes to this object by calling
instance.project.am_subscribe(callback). Then, you can notify listeners of
changes by calling instance.project.am_event(). Note that events are NEVER
automatically triggered - you must call am_event in order to trigger the
callbacks. One useful feature of this model is that you can provide arbitrary
keyword arguments to am_event, and they will be passed on to each callback.
This means that you should always have your callbacks take **kwargs in order
to account for unknown parameters. This feature is particularly useful to
prevent feedback loops - if you ever find yourself in a situation where you need
to broadcast an event from your callback, you can add an argument that you can
use as a flag not to recurse any further.
Next, object reference mutability. Let’s say you have a widget that displays
information about the project. Following the principle of least access, you
should only provide as much information as is necessary to do the job - in this
case, just the project object. If you provide the basic project object, this
will cause issues when a new project is loaded. Notably, there will be a
dangling reference held to the original project, preventing it from being
garbage collected, and the widget will not update, continuing to show the old
project’s information. Now, if you provide the project’s ObjectContainer, a new
project can be created and inserted into the container and the reference will
instantly be available to your widget. If you ever wanted to load a new project
yourself, all you have to do is assign to instance.project.am_obj and then
send off an event. Combined with the event publication model, this provides an
efficient way to build responsive UIs that follow the principle of least access.
One important way that you can’t use the object container the same way that you
would a normal object is that is None will obviously not work. To resolve
this, you can use instance.project.am_none - this will be True when no
project is loaded.
One interesting feature of the ObjectContainer is that they can nest. If you have a container which contains a container which contains an object, any events sent to the inner container will also be sent to subscribers to the outer container. This allows patterns such as the list of SimStates actually containing a list of ObjectContainers which contain states, and the “current state” container actually contains one of these containers. The result of this is that UI elements can either subscribe to the current state, no matter
A full list of standard ObjectContainers that can be found in the instance
__init__ method.
There are more containers floating around for synchronizing on non-global
elements - for example, the current state of the disassembly view is
synchronized through its InfoDock object. Given a disassembly view instance, you
can subscribe to, for example, its current selected instructions through
view.infodock.selected_insns.