Glenn Filson
Glenn Filson

Reputation: 384

Exhaustively walking the AST tree in Groovy

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

Answers (1)

Carlos
Carlos

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

Related Questions