Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Groovy Compile time AST transformation: Assignment to a field

I'm currently trying to implement some Groovy compile time AST transformations, but I ran into trouble:

How do I specify an AST transformation for an assignment statement to a field? i.e. the AST transformation should transform the following code:

class MyClass {
    @MyTransformation
    String myField

    public void init() {
    }
}

into something like

class MyClass {
    String myField

    public void init() {
        this.myField = "initialized!"
    }
}

I tried it with this AST builder invocation:

def ast = new AstBuilder().buildFromSpec {
        expression{
            declaration {
                variable "myField"
                token "="
                constant "initialized!"
            }
        }
}

But after inserting the resulting statement in the "init" method of the declaring class, it instead inserted a variable assignment, as in

java.lang.Object myField = "initialized!"

I looked through the examples incorporated in the Ast Builder TestCase, but they only cover field declaration in the class body, not assignments to fields. My own tries using fieldNode all resulted in compiler errors. I set the compile phase to INSTRUCTION_SELECTION; I think this should be fine.

How do I achieve this? A solution based on the AstBuilder#buildFromSpec method is preferred, but any help would be highly appreciated.

like image 234
SputNick Avatar asked Jul 31 '14 07:07

SputNick


1 Answers

I usually recommand not to use the AST builder. It's good for prototyping, but you don't really control what it generates. In particular, here, it's not capable of handling the fact that the variable expression you create should reference the field node. AST Builder is very nice to learn about the AST, but shouldn't be used in production code IMHO.

Here is a self contained example that demonstrates how you can acheive what you want. The code inside @ASTTest would correspond to your transform code:

import groovy.transform.ASTTest                                                         
import org.codehaus.groovy.ast.expr.BinaryExpression                            
import org.codehaus.groovy.ast.expr.VariableExpression                          
import org.codehaus.groovy.ast.expr.ConstantExpression                          
import org.codehaus.groovy.ast.stmt.ExpressionStatement                         
import org.codehaus.groovy.syntax.Token                                         
import org.codehaus.groovy.syntax.Types      

class MyClass {                                                                         
    String myField                                                                      

    @ASTTest(phase=SEMANTIC_ANALYSIS,value={                                            
            def classNode = node.declaringClass                                         
            def field = classNode.getDeclaredField('myField')                           
            def assignment = new BinaryExpression(                                      
              new VariableExpression(field),                                              
              Token.newSymbol(Types.EQUAL, 0, 0),                                         
              new ConstantExpression('initialized!')                                      
            )                                                                                   
            node.code.addStatement(new ExpressionStatement(assignment))                 
    })                                                                          
    public void init() {                                                                
    }                                                                                   
}                                                                                       
def c = new MyClass()                                                                   
c.init()                                                                                
println c.myField

Hope this helps!

like image 197
melix Avatar answered Oct 23 '22 15:10

melix