Thursday, February 7, 2013

Switching away from AspectJ

Up until now I used AspectJ to insert functions into the AST. This allowed me to conveniently call something like .constantEvaluate() on any node and get back the value if applicable. Also all functionality for creating a constant from the AST was encapsulated into one file. For example for Literals this would look like this:
 
public BigInteger HDLLiteral.constantEvaluate(HDLEvaluationContext context) {
 switch (getPresentation()){
  case STR:
  case BOOL:
   return null;
  default:
   return getValueAsBigInt();
  }
}

This would insert the function constantEvaluate into the type HDLLiteral. On a byte code level this function would really been copied into it. But the downside of using AspectJ is that this really only happens at the byte code level. And so quite a few tools, including GWT, could not use the result. I really like GWT however and want to be able to do fancy stuff with it on pshdl.org. So I needed to find a replacement. Just inserting the functions into the respective classes was not an option to me, as I really want to have a good seperation of concerns. So I decided to use XTend.

The nice thing about XTend is that is has a polymorphic dispatch. You essentially just declare a bunch of overloaded functions and during runtime XTend will dispatch the call to the most specific function. One would assume that Java does this automatically, but the called method is determined at compile time. Take this example:
protected CharSequence _print(final Integer i) {
 StringConcatenation _builder = new StringConcatenation();
 _builder.append("Integer:");
 _builder.append(i, "");
 return _builder;
}

protected CharSequence _print(final String s) {
 StringConcatenation _builder = new StringConcatenation();
 _builder.append("String:");
 _builder.append(s, "");
 return _builder;
}

protected CharSequence _print(final Object o) {
 StringConcatenation _builder = new StringConcatenation();
 _builder.append("Object:");
 _builder.append(o, "");
 return _builder;
}

public CharSequence print(final Object i) {
 if (i instanceof Integer) {
  return _print((Integer) i);
 } else if (i instanceof String) {
  return _print((String) i);
 } else if (i != null) {
  return _print(i);
 } else {
  throw new IllegalArgumentException("Unhandled parameter types");
 }
}

public static void main(String[] args) {
 Bla b = new Bla();
 Integer i = 5;
 String s = "Hallo";
 Object o = "Moin";
 System.out.println(b._print(i));
 System.out.println(b._print(s));
 System.out.println(b._print(o));
 System.out.println(b.print(i));
 System.out.println(b.print(s));
 System.out.println(b.print(o));
}

The output will be:

Integer:5
String:Hallo
Object:Moin
Integer:5
String:Hallo
String:Moin

What you can see is that the called method depends on the declared type, not the acutal type. This is fixed with the call of print vs _print. To generated the print method, all you have to do is write the following in XTend:
class Bla {

 def dispatch print(Integer i)'''Integer:«i»'''
 def dispatch print(String s)'''String:«s»'''
 def dispatch print(Object o)'''Object:«o»'''
 
}
With that in place, you can create a very nice collection of constantEvaluate functions that automatically dispatch to the type determined at runtime. This is very neat. This way you can create a single XTend class which has all constantEvaluates with a function for each type. While playing around with XTend I however discovered a few downsides that I will explain in the next post..

No comments:

Post a Comment