Call Descriptor & Parameters

The importance of call descriptor and paramters is that they allow to specify the exact metaobject protocol (MOP) we want to use. Using these elements we can see the metaobject programming as a task of connecting objects (as we did in the tracing example).

In this section we will review the API of the ClassDescriptor| class and the Parameter interface.

CallDescriptor class

The CallDescriptor class allows us to specify the exact call we want to be made when the metaobejct is invoked.

The important part of the CallDescriptor API is its contructor, that takes all the necessary information to make the call:

public CallDescriptor(String aType, String aMethod, Parameter... aParameters){...}

This call descriptor will produce code like this:

((aType) metaobject).aMethod(aParameters)

The last argument is a list of Parameter intances (as it is a varargs constructor) that describes the parameters that will be used in the call.

For example, if we want to invoke println over System.out without arguments, the call descriptor should be like:

new CallDescriptor("java.io.PrintStream", "println");

Next we will review the interface Parameter in detail.

Parameter interface

The Parameter interface describes how to dinamically evaluate call parametres. Notice that “dinamically evaluate” means that contextual information (available at the hook) can be used to generate the parameter.

The parameter interface defines three methods that have to be implemented:

public String evaluate(Operation aOperation);

This method must return the code of the parameter, for the given operation. The code can include Javassist syntax.

public String getType(Operation aOperation);

This method must return the fully qualified type of the parameter's evaluation. If the parameter represents an int it should return int. If the paramter represents a String, it should return java.lang.String.

public boolean shouldBeEvaluatedAtHook(Operation aOperation);

This method is here for optimization only. It must return true if the parameter should be evaluated at the hook (because it uses contextual information).

Sometimes, certain parameters are evaluated but never used (because they are used to invoke a method in a metaobejct that is nested and the first metaobject in the chain does not call proceed, then, it would be perfect if their evaluation could be delayed until it is really necessary. This is the purpose of this method, to give a hint to the Reflex code generation engine.

An example parameter that records the time when the hook was reached is:

public class TimeParameter implements Parameter{
 
    public String evaluate(Operation aOperation){
        return "System.currentTimeMillis()";
    }
 
    public String getType(Operation aOperation){
        return "long";
    }
 
    public boolean shouldBeEvaluatedAtHook(Operation aOperation){
        return true;
    }
}

The TimeParameter class returns a String that represents a System.currentTimeMillis() call. As this code is inserted at the hook, the call will be made when the hook is reached. The type of the parameter is long and it should be evaluated at the hook.

Another parameter (a bit more complex) is one that makes use of the operation specified. In this case to determine if the read/write operation thas is being done is read:

public class IsRead implements Parameter{
 
    public String evaluate(Operation aOperation){
        return String.valueOf(((Operation.ReadWrite) aOperation).isRead());
    }
 
    public String getType(Operation aOperation){
        return "boolean";
    }
 
    public boolean shouldBeEvaluatedAtHook(Operation aOperation){
        return false;
    }
}

The evaluate method returns true if the operation is read and false if it is not. The type is boolean and its evaluation can be delayed, because it does not use contextual information.

Next we will see some of the ready-to-use parameters provided by Reflex.

Utility Parameters

  • Parameter.CONTEXT_OBJECT

This parameter evaluates to the context object: the current class if the operation is in static context and this if it is not.

  • Parameter.RESULT

This parameter evaluates to the result of the operation (the value about to be returned by a method invocation, the resulting value of the field access if the expression is read access, the resulting value of the object creation, etc). This parameter is the same as the $_ special variable of Javassist (Notice that this parameter is only available for AFTER control).

  • Parameter.CLOSURE

This parameter evaluates to the closure object used to invoke the next step in the chain of metoabjects.

  • StdParameters.ARGUMENTS_ARRAY

This method evaluates to an array of objects, objects that are the parameters of the operation:

new Object[]{ parameter1, parameter2, ..., parameterN}
  • StdParameters.NAME

This method evaluates to the name of the operation: the method name if it is a method receive or a method invocation, the field name if it is a field access, etc.

  • StdParameters.TARGET_TYPE

This parameter evaluates to the type of the object receiving the operation: the type of an object subject of a message send, the type of a field being accessed, etc.

  • StdParameters.DECLARING_CLASS

This parameter evaluates to the declaring class of the operation: the class declaring a method or constructor that is being invoked or a field that is being accessed.

  • StdParameters.LINE_NUMER

This parameter evaluates to the line number of the operation if available.