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> T MyJPI();
 
class C{
 
   exhibits <A> A MyJPI() : execution(A *(...));
 
   void foo(){...}
   Integer bar(int c, string l){...}
   float zoo(C a){...}
   Integer jar(){...}
}
 
aspect Logger{
   <R> 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).