====== Parametric Polymorphism ======
FIXME
* Introduction
* StrongAspectJ in action
* How StrongAspectJ fails with ranges types and interfaces.
* How parametric return and arguments types can ensure generic advice definition.
* Examples
====== Introduction ======
We need to introduce Type Variables and Parameterized Type Declarations in order to implement parametric polymorphism in our jpi abc compiler extension. These characteristics will be implemented in the following constructs:
* jpi type declarations
* exhibits clause
* advice declarations
With parametric polymorphism we can write generic aspects without burdening programmers with the current verbosity of our approach.
Consider the implementation of the logger cross-cutting concern with the following base code:
class C{
void foo(){...}
Integer bar(int c, string l){...}
float zoo(C a){...}
Integer jar(){...}
}
The implementation of Logger is pretty straightforward using a plain AspectJ aspect:
aspect Logger{
Object around() : call(* *(...)){
/* do something before */
return proceed();
/* do something after */
}
}
Conversely, the implementation of Logger using a JPI aspect is pretty verbose which force programmers to declare for each different join point one jpi definition. Also, this situation lead programmers to copy/paste advices which is a error prone process. The following is the complete implementation of both Logger aspects as well base code:
jpi void MyJPI();
jpi Integer MyJPIInteger();
jpi float MyJPIFloat();
class C{
exhibits void MyJPI() : execution(void *(...));
exhibits Integer MyJPIInteger() : execution(Integer *(...));
exhibits float MyJPIFloat() : execution(float *(...));
void foo(){...}
Integer bar(int c, string l){...}
float zoo(C a){...}
Integer jar(){...}
}
aspect Logger{
void around MyJPI(){
/* do something before */
proceed();
/* do something after */
}
void around MyJPIInteger(){
/* do something before */
return proceed();
/* do something after */
}
void around MyJPIFloat(){
/* do something before */
return proceed();
/* do something after */
}
}
With parametric polymorphism, we can provide a safe and concise way to define those kind of cross-cutting concerns:
jpi T MyJPI();
class C{
exhibits A MyJPI() : execution(A *(...));
void foo(){...}
Integer bar(int c, string l){...}
float zoo(C a){...}
Integer jar(){...}
}
aspect Logger{
R around MyJPI(){
/* do something before */
return proceed();
/* do something after */
}
}
Due that current version of abc compiler does not support Generics, we need to define new typing rules for each expression on which a Type Variable or a Parameterized Typed Declaration is involved. Also, we need to implement a Translation algorithm which will allow us to do erasing all type parameters, mapping type variables to their bounds, and inserting casts as needed [1].
====== StrongAspectJ in action======
aspect Logger {
Integer around(Person p):
call(Integer *(..)) && args(p): //args(Clerk-Person p)
Integer proceed(Clerk){
System.out.println(p.getName());
return proceed(new Clerk());
//return proceed(p) <-- [aspects.Person] do not match [aspects.Clerk]
}
// AspectJ around advice definition
/*
Integer around(Person p):
call(Integer *(..)) && args(p){
System.out.println(p.getName());
return proceed(p);
}
*/
public static void main(String[] args){
Clerk c = new Clerk();
Employee e = new Employee();
Person p = new Person();
register(c);
register(e);
register(p);
}
public static Integer register(Person e){
System.out.println(e.getName()+"\n");
return new Integer(2);
}
}
бadvice ≤ Person -> Integer
Clerk --> Integer ≤ бproceed
pc = (Clerk - Person) --> Integer - Integer
pcupper = Clerk --> Integer
pclower = Person --> Integer
бadvice ≤ бpc_lower ≤ бjp ≤ бpc_upper ≤ бproceed
You mIght notice that proceed invocation does not be accomplished using the same value of the advice argument. Instead, you need to create a new value that be type compatible with the proceed signature.
===== Multiple path issue =====
Despite double around advice signature, it is possible get weaving time errors in StrongAspectJ. StrongAspectJ fails shortly when interfaces are used in type ranges. For example.
interface Item{
String foo();
}
class EcoFriendly implements Item{
public String foo() {
System.out.println("EF");
return "EF";
}
}
class BestSeller implements Item{
public String foo() {
System.out.println("BS");
return "BS";
}
}
public aspect example {
String around(Object ef) :
target(ef) && call(String foo(..)):
String proceed(Item){
return proceed(new BestSeller(){});
}
public static void main(String[] args){
EcoFriendly b = new EcoFriendly();
b.foo();
}
}
The output is the following:
Exception in thread "main" java.lang.ClassCastException: example$1 cannot be cast to EcoFriendly
at example.inline$0$around$0(example.java:23)
at example.main(example.java:27)
When a interface type is used in both sides of type ranges, StrongAspectJ raises a huge traceback exception indicating that there is a polyglot.util.InternalCompileError. That's why I use Object instead of Item in the advice definition.
====== References ======
[1] Gilad Bracha, Martin Odersky, David Stoutamire, and Philip Wadler. **GJ Specification** Manuscript, May 1998.
[2] B. De Fraine, M. Südholt, and V. Jonckers. StrongAspectJ: Flexible and Safe Pointcut/Advice Bindings. //Conf. on Aspect-Oriented Software Development (AOSD-2008)//.