====== 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)//.