どう書くorg課題「ローカル変数の一覧を取得」を書いてみました。
対象を今回クロージャに限定しています。ふつー、JVM上で動く言語としてのJavaでもGroovyでも、ローカル変数名はコンパイル後には消え去っているので、こういうことはできません。なので、今回ASTを引っ張ってくる裏口の1つを使っています。GSQLでwhere句やorder句で行っているのと同じ方法です。具体的に何をしているかというと裏でgroovyソースを再度コンパイルしてASTを取ってくるということをしているのです。
こういう変なことをしなくても、いまやAST変換のAPIが充実しているので、もそっときれいに書くことはできると思います。ただ、今回に関しては、ローカル変数にアノテーションをつけてもAST変換のマーカーとして起動してくれないという問題がありまして、関数一般に効くものは作りませんでした。
import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.expr.*; import org.codehaus.groovy.ast.stmt.*; csv = new CodeVisitorSupport() { Map vars = [:]; void visitDeclarationExpression(DeclarationExpression expr) { vars[expr.leftExpression.name] = expr.rightExpression.value } }; def localVars(Closure closure) { if (closure != null) { ClassNode classNode = closure.getMetaClass().getClassNode(); if (classNode == null) { throw new GroovyRuntimeException("Could not find the ClassNode for MetaClass: " + closure.getMetaClass()); } List methods = classNode.getDeclaredMethods("doCall"); if (!methods.isEmpty()) { MethodNode method = (MethodNode) methods.get(0); if (method != null) { Statement statement = method.getCode(); if (statement != null) { statement.visit(csv); } } } } return csv.vars } foo = { def x = 1 def y = "hello" result = localVars(clone()) return result } foo().each{key,value-> println "$key => $value" }