Reputation: 384
This is related to my question on intercepting all accesses to a field in a given class, rather than just those done in a manner consistent with Groovy 'property' style accesses. You can view that here: intercepting LOCAL property access in groovy.
One way I've found that will definitely resolve my issue there is to use AST at compile time re-write any non-property accesses with property accesses. For example, a if a class looks like this:
class Foo {
def x = 1
def getter() {
x
}
def getProperty(String name) {
this."$name" ++
}
}
foo = new Foo()
assert foo.getter() == 1
assert foo.x == 2
These assert statements will work out because the getter method access x directly and the foo.x goes through getProperty("x") which increments x before returning.
After some trial and error I can use an AST transformation to change the behavior of the code such that the expression 'x' in the 'getter' method is actually accessed as a Property rather than as a local field. So far so good!
Now, how do I go about getting to ALL accesses of local fields in a given class? I've been combing the internet looking for an AST tree walker helper of some kind but haven't found one. Do I really need to implement an expression walker for all 38 expression types here http://groovy.codehaus.org/api/org/codehaus/groovy/ast/expr/package-summary.html and all 18 statement types here http://groovy.codehaus.org/api/org/codehaus/groovy/ast/stmt/package-summary.html? That seems like something that someone must have already written (since it would be integral to building an AST tree in the first place) but I can't seem to find it.
Upvotes: 1
Views: 2740
Reputation: 1033
Glenn
You are looking for some sort of visitor. Groovy has a few (weakly documented) visitors defined that you could use. I don't have the exact answer for your problem, but I can provide you a few directions.
The snippet below shows how to transverse the AST of a class and print all method names:
class TypeSystemUsageVisitor extends ClassCodeVisitorSupport {
@Override
public void visitExpression(MethodNode node) {
super.visitMethod(node)
println node.name
}
@Override
protected SourceUnit getSourceUnit() {
// I don't know ho I should implement this, but it makes no difference
return null;
}
}
And this is how I am using the visitor defined above
def visitor = new TypeSystemUsageVisitor()
def sourceFile = new File("path/to/Class.groovy")
def ast = new AstBuilder().buildFromString(CompilePhase.CONVERSION, false, sourceFile.text).find { it.class == ClassNode.class }
ast.visitContents(visitor)
Visitors take care of transversing the tree for you. They have visit* methods that you can override and do whatever you want with them. I believe the appropriate visitor for your problem is CodeVisitorSupport, which has a visitVariableExpression method.
I recommend you to read the code of the AST Browser that comes along with groovyConsole for more examples on how to use Groovy AST Visitors. Also, take a look at the api doc for CodeVisitorSupport.
Upvotes: 2