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.
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With