Hooksets allow us to select _where_ an aspect will apply.
In Reflex, Hookset is an interface that defines two highly-related to the Reflex internals methods. This is the reason why two implementations are provided:
PrimitiveHookset
, that is able to match a set of points in the aplication according to a given operation.CompositeHookset
, that is able to compose other hooksets and match any of the application points matched by the hooksets it is composed of.Here, we have talked aboud “execution points”. Formalizing what they are, we can say that an execution point is defined as an operation occurring at the application. Operations can be any of the following:
MsgSend
class. This operation matches statements that are method invocations:object.hashCode() or string.substring(0, 5);
MsgReceive
class. This operation matches all the body statements of a method. It is the calle side operation for a message send:public int hashCode(){ <all statements> }
Instantiation
class. This operation matches statements using the keyword “new
”:new Object(); or new String("hello world!");
Creation
class. This operation matches all the body statements of a contructor. It is the callee side operation for an instantiation:public String(){ <all statements> }
FieldAccess
class. This operation matches all statements that represents field accesses (both read and write):System.out.println(this.x) or this.y = 228;
Cast
class. This operation matches statements where a cast is used:(Object) object; or (Appendable) string.replace(" ", "_");
With this stated, we can review the API of PrimitiveHookset
. In general terms, only the constructor is of interest:
public PrimitiveHookset(Class aOperation, ClassSelector aClassSelector, OperationSelector aOperationSelector)
As you can see, it takes the operation it must match and two additional parameters we have not seen yet: a ClassSelector
and an OperationSelector
. We will review them now.
The existence of class selectors is to avoid examining all the classes of the system: they are asked to know if the class can contain execution points that can be matched by any Hookset
. If none of the class selectors of all hooksets aswer that the class is of interest, Reflex simply does not analyze that class.
The ClassSelector
interface is defined as follows:
public interface ClassSelector{ public boolean accept(RClass aClass); }
And its unique method accept
returns true if the given class should be selected.
If a class selector selects a class, Reflex will analyze that class and will reify all operations on it. Then for each of these operations, the operation selectori will be asked if the operation is of interest. If it returns true, the assosiated hookset is said to match to that operation in particular.
The OperationSelector
interface is defined as follows:
public interface OperationSelector{ public boolean accept(Operation aOp, RClass aClass); }
Where its unique method accept
should return true if the given operation occurrence, found in the given class, should be selected.
As you can see, the hookset matching mechanism in Reflex is pretty powerfull, and, at the same time, performant.
One of the things we intentionally skipped is the presence of all those R* classes (that are reifications of the different structures of the application). We will review them later in this {section].