Saturday, January 23, 2010

Helpful tips - Working with AST Transformation

I have been playing with Groovy AST transformation for some time now. You can walk through some of the earlier posts here. With this entry I want to capture some of the lessons learnt, tips, etc.
 
Using AstViewer Tool (Inspect Ast)
With Groovy 1.6 AST transformation, one of the difficulty was to come up with tree structure for the code you want to inject or change. It involves calling complex set of Statement and Expression APIs. It is ridden with intricate details of language grammar. With groovy 1.7 AstBuilder, you can use buildFromString and buildFromCode which is lot more easier. But still if you have to use buildFromSpec you will need to have some understanding of Statement/Expression API.
 
One of the tool I have found useful is Groovy Console's AST Browser or Eclipse's ASTViewer. Write code you want to generate language structure for and try to replicate using API or using AstBuilder's buildFromSpec. The tool has gotten better from 1.6 to 1.7.
Open Groovy Console and type in following code

            def advice = new String("Hello AST")
            println advice

Go to Script --> Inspect Ast (or press Ctrl+T) and you will see the AST structure needed.

 
Educated Guess in Script
With AstBuilder now you can just start building AST in main method or just as script. This has proven to be useful if you just want to compare what you are generating with what is displayed in AstBuilder.

def nodes = new AstBuilder().buildFromXXX{
}
 
Working with Variables
If you are using variables that gets replaced during runtime you may face problem generating AST. One of the method that I found useful was to replace the variable with static value and see if it works. If it does than the problem is possibly due to the type of the variable.
For example, I wanted to generate

          def advice = new <<variableClassName>>()

where variableClassName will be passed in from annotation value. One option is to try generating it with static value like java.lang.String (or just String), if it works than there is some problem with the type of the variable you are passing.
 
Compile file individually (if using IDE)
Sometimes the tools you are working with just does not seem to give different output even when you are changing things around in your code. Sometimes stale classes just gets called and you never see the output from the updated classes. You may want to compile both transformation class and the class where the transformation gets used. 
 
ByteCode Viewer
Also using Byte Code Viewer like this helps in looking at the classes that are getting generated. You can also write your class how it will look after AST transformation. Use byte code viewer to see the class and compare it with the class that is being generated through AST Transformation. See which statements are properly generated and which are not.
 
Testcases
Another extremely resourceful source for AstBuilder is test cases found here. It contains rich example set of how to use each node in the builder specifically for buildFromSpec option. AstAssert is another excellent utility to compare and test expected and actually generated structure. Looks like AstAssert class is not packaged with groovy-1.7, so you may need to download and drop it in your project.

Divide and Conquer
Treat each section of code for which AST is being generated as separate section. Comment out blocks of code and gradually uncomment them to see which section is actually failing and/or not giving desired output.
 
Hope this helps! Drop in a line to share your tips or problems you face with Groovy's Ast transformation. 

Blogged with the Flock Browser

2 comments:

Hamlet D'Arcy said...

great wrap up!

AstAssert is not really a general purpose assertion method, so it is not packaged with Groovy. It is quite specific in what it does _not_ assert :) Dropping it into your project is a good idea if you want to use it because you will probably need to modify it a little for whatever it is you are doing.

Also, the TranformTestHelper class is a way to avoid the stale copies of classes. There is an example here: http://is.gd/6SSob

Kartik Shah said...

Thanks for the pointer.