|
aspectjml is | aspectjml enables | ||
|
XCS in AspectJML is based on a subset
of AspectJ's constructs. However, since JML is a design by contract language tailored for plain Java,
we need
special support to use the traditional AspectJ syntax.
To simplify the adoption of AspectJML, the included AspectJ constructs
are based on the alternative
@AspectJ syntax (which is part of the standard distribution of AspectJ).
The @AspectJ (often pronounced as "at AspectJ") syntax was conceived as a part of the
merge of standard AspectJ with AspectWerkz. This merge enables
crosscutting concern implementation by using constructs based on the metadata annotation
facility of Java 5. The main advantage of this syntactic style is
that one can
compile a program using a plain Java compiler, allowing the modularized
code using AspectJ to work better with conventional Java IDEs and
other tools that
do not understand the traditional AspectJ syntax.
In particular, this restriction applies to the JML common tools, including the JML
compiler
on which ajmlc, the AspectJML compiler,
is based.
In the following, we present how AspectJML supports crosscutting contract specification. The presentation is based on a running example.
The major difference, in relation to plain AspectJ,
is that a specified pointcut is always processed when using our AspectJML compiler (ajmlc).
In standard AspectJ,
a single pointcut declaration does not contribute to the execution
flow of a program unless we define some AspectJ advice that uses such a pointcut.
In AspectJML,
we do not need to define an advice to check a specification in a crosscutting fashion.
Although it is possible to use advice declarations in AspectJML, we do not require them.
This makes AspectJML simpler and a programmer only needs to know AspectJ's
pointcut language in addition to the main JML features.
The code for this example is available here.
The reader is also encouraged to consult our page on installing and running AspectJML compiler (ajmlc).
Also, the reader may refer to the main JML page for an overview of the main JML features.
For an overview of AspectJ features, please refer to the AspectJ page.
Note that for AspectJML use, the reader may refer only
to AspectJ's pointcut designators.
As an example, consider the canonical set of graphical shape classes, such as Point, Line, etc.
Each element is part of the overall figure and implements the
Shape interface (for figure elements).
The interface Shape is shown in the listing below.
interface Shape {}The class Point is an example of a Shape. Points have an x and a y location and setter and getter methods setX, setY, getX, and getY. Points also have a method
All five methods are specified with JML and illustrated below.
class Point implements Shape { int x, y; //@ requires x >= 0; //@ ensures this.x == x; //@ signals_only \nothing; void setX(int x) { this.x = x; } //@ requires y >= 0; //@ ensures this.y == y; //@ signals_only \nothing; void setY(int y) { this.y = y; } //@ ensures \result >= 0; //@ signals_only \nothing; int getX() { return x; } //@ ensures \result >= 0; //@ signals_only \nothing; int getY() { return y; } //@ requires dx >= 0 && dy >= 0; //@ ensures this.x == \old(this.x + dx) //@ && this.y == \old(this.y + dy); //@ signals_only \nothing; void moveBy(int dx, int dy) { x += dx; y += dy; } }
For this scenario, consider the JML annotated pointcut with the following precondition:
//@ requires xy >= 0; @Pointcut("execution(* Point.set*(int)) && args(xy)") void sets(int xy) {}The pointcut sets matches all the executions of methods starting with "set" of class Point like setX and setY. As observed, this pointcut is exposing the intercepted method arguments of type
The main difference between this pointcut declaration and
standard pointcut declarations in AspectJ is that
we are adding one JML specification
(using the requires clause). In this example the
JML
says to check the declared
precondition before the executions of intercepted methods.
For the first crosscutting scenario, we rely on JML normal postconditions. As such,
the two normal postconditions of the methods getX and getY are the same. The JML ensures clause states
that both
methods should return integer values greater than or equal to zero. In JML, we refer to the return values of a method
using the special construct \result. The problem is that we cannot write a simple
and local quantified form of these postconditions and
apply them to the constrained methods.
To handle this crosscutting scenario, we use the following specified pointcut in AspectJML:
//@ ensures ((int)\result) >= 0; @Pointcut("execution(int Point.get*())") void gets() {}This pointcut constrains the executions of the getX and getY methods in Point to ensure that, after their executions, the returning integer values should be greater or equal to zero.
It is important to note that the JML \result expression, used within a pointcut declaration, should do a cast to the desired return type.
In relation to our second crosscutting scenarion (involving JML exceptional postconditions), we can observe that
the specification //@ signals_only \nothing are written for all methods in Point to forbid
exceptions. There is no
way to modularize such a JML contract in one place and apply it to all constrained methods.
As a consequence, a programmer that adds 20 methods more in Point should explicitly
write the specification
for all the added methods.
To modularize this second crosscutting postcondition scenario in AspectJML, we use the following JML annotated pointcut declaration:
//@ signals_only \nothing; @Pointcut("execution(* Point.*(..))") void pointMeths() {}The above specification forbids the execution of any method in Point to throw an exception. If any intercepted method throws an exception (even a runtime exception), a JML exceptional postcondition error is thrown to signal the contract violation. In this pointcut, we do not expose any intercepted method's context. Hence, instead of writing //@ signals_only \nothing for all methods in Point, the pointcut pointMeths selects all the method executions in Point and adds the constraint "//@ signals_only \nothing" in a modular fashion.
pointcut sets(): execution(* set*(int));selects any method starting with "set" and has one argument of type int. In AspectJ, this pointcut matches any type in a system. Since we omit the type signature pattern, any type is candidate to expose the join points of interest. In AspectJ, although not required, we can also use a wildcard * to represent a type signature pattern that intercepts any type in the system (i.e., execution(* *.set*(int)). However AspectJML has a different semantics compared with AspectJ. For example, recall the previous pointcut method sets in AspectJML:
//@ requires xy >= 0; @Pointcut("execution(* Point.set*(int)) && args(xy)") void sets(int xy) {}this pointcut method still selects the same methods starting with "set" and that has one argument of type int. The main difference is that even with the absence of the target type, AspectJML restricts the join points to the type (Point in this case) enclosing the pointcut declaration. AspectJML works in this manner to avoid the obliviousness problem to propagate to the JML specifications.
Instead of ruling this completely out, the design of AspectJML allows the specifier to use specifications that constrain unrelated inter-types, but in an explicit and limited manner.
As an example, recall our running example. We know that all the methods declared in Point are forbidden to throw exceptions (see the signals_only specification). Suppose now that the methods in Line class (which is a valid subtype of Shape) also have this constraint. Consider the following type declaration:
interface ExceptionSignalling { @InterfaceXCS class ExceptionSignallingXCS { //@ signals_only \nothing; @Pointcut("execution(* ExceptionSignalling+.*(..))") void allMeth() {} } }This type declaration illustrates how we specify crosscutting contracts for interfaces. In @AspectJ, pointcuts are not allowed to be declared within interfaces. We overcome this problem by adding an inner class that represents the crosscutting contracts of the outer interface declaration. As a part of our strategy, the pointcut declared in the inner class refers only to the outer interface (see the reference in the pointcut predicate expression). Now any type that wants to forbid its method declarations to throw exceptions need only to implement the interface ExceptionSignallingConstraint (as shown below). Such an interface acts like a marker interface. This is important to avoid obliviousness and maintain modular reasoning.
class Point implements Shape, ExceptionSignallingXCS {...} class Line implements Shape, ExceptionSignallingXCS {...}
Note that the inner class is marked with the @InterfaceXCS annotation. This is to distinguish from any other inner class that could be also declared within our crosscutting contract interface. Without this mechanism, the AspectJML compiler will not be able to find the crosscutting contracts for the interface ExceptionSignalling.
The resulting class Point with such modularized crosscutting contracts are shown below:
class Point implements Shape { int x, y; // starting crosscutting contract specifications in AspectJML with pointcuts + specifications //@ requires xy >= 0; @Pointcut("execution(* Point.set*(int)) && args(xy)") void sets(int xy) {} //@ signals_only \nothing; @Pointcut("execution(* Point.*(..))") void pointMeths() {} //@ ensures ((int)\result) >= 0; @Pointcut("execution(int Point.get*())") void gets() {} // ending of the crosscutting contract specifications in AspectJML //@ ensures this.x == x; void setX(int x) { this.x = x; } //@ ensures this.y == y; void setY(int y) { this.y = y; } int getX() { return x; } int getY() { return y; } //@ requires dx >= 0 && dy >= 0; //@ ensures this.x == \old(this.x + dx) //@ && this.y == \old(this.y + dy); void moveBy(int dx, int dy) { x += dx; y += dy; } }
For more details, please refer to our Modularity'14 paper at ACM DL.