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].