在Groovy中打印闭包定义/源

在Groovy中打印闭包定义/源,groovy,closures,Groovy,Closures,谁知道Groovy中如何打印闭包的源代码 例如,我有这个闭包(绑定到a) 我想要字符串“it.tweep()”或“{it.tweep()}” 当然,仅仅是一个简单的toString是行不通的: a.toString(); //results in: Script1$_run_closure1_closure4_closure6@12f1bf0 这在groovy中是不可能的。即使直接运行groovy脚本,而不首先编译它,脚本也会转换为JVM字节码。闭包的处理没有任何区别,它们像常规方法一样编译。

谁知道Groovy中如何打印闭包的源代码

例如,我有这个闭包(绑定到
a

我想要
字符串
“it.tweep()”或“{it.tweep()}”

当然,仅仅是一个简单的
toString
是行不通的:

a.toString(); //results in: Script1$_run_closure1_closure4_closure6@12f1bf0

这在groovy中是不可能的。即使直接运行groovy脚本,而不首先编译它,脚本也会转换为JVM字节码。闭包的处理没有任何区别,它们像常规方法一样编译。当代码运行时,源代码不再可用。

简而言之,您不能。答案很长:
根据您需要代码做什么,您可能会侥幸逃脱

// file: example1.groovy
def a = { it.twice() }
println a.metaClass.classNode.getDeclaredMethods("doCall")[0].code.text
// prints: { return it.twice() }
但是
您将需要运行时类路径中可用的脚本源代码,如中所述

groovy.lang.MetaClass#getClassNode()
“获得对原件的引用 元类的AST,如果是 在运行时可用
@归还 原始AST或null(如果无法输入) 返回“


文本技巧实际上并没有返回相同的代码,只是AST的类似代码的表示,正如在这个脚本中可以看到的那样

// file: example2.groovy
def b = {p-> p.twice() * "p"}
println b.metaClass.classNode.getDeclaredMethods("doCall")[0].code.text
// prints: { return (p.twice() * p) }
不过,如果您只想快速浏览一下,它可能会很有用

而且,如果你手头有太多的时间,不知道该做什么,你可以编写自己的
org.codehaus.groovy.ast.GroovyCodeVisitor
来打印它

或者,只偷一个现有的,比如
groovy.inspect.swingui.AstNodeToScriptVisitor

// file: example3.groovy
def c = {w->
  [1,2,3].each {
    println "$it"
    (1..it).each {x->
      println 'this seems' << ' somewhat closer' << ''' to the 
      original''' << " $x"
    }
  }
}
def node = c.metaClass.classNode.getDeclaredMethods("doCall")[0].code
def writer = new StringWriter()
node.visit new groovy.inspect.swingui.AstNodeToScriptVisitor(writer)
println writer
// prints: return [1, 2, 3].each({
//     this.println("$it")
//     return (1.. it ).each({ java.lang.Object x ->
//         return this.println('this seems' << ' somewhat closer' << ' to the \n      original' << " $x")
//     })
// })

行号至少应该接近原始代码,尽管这可能与您的情况不符,但您可以选择另一种方式。既然GroovyShell.evaluate()执行一个字符串(带有适当的var绑定),那么如果闭包只是一个字符串呢?我不明白“运行时类路径中可用的脚本”到底是什么意思?它还不能工作(ClassNode是
null
),但我有所有可用的源代码。我必须在Groovy项目的类路径中添加脚本的位置吗?@Julian意味着.Groovy文件应该在类路径中。getClassNode()基本上会加载文件,并使用一个特殊的钩子再次编译它以保存相关的ast节点,然后返回it@jpertino您对在Grails环境中如何做到这一点有何想法?我有一个Config.groovy文件,它在集成测试期间位于类路径上,但在run-app期间不存在。这与Jenkins无关——只是说,就我所做的努力而言,它无法在那里工作——可能这些类在Jenkins slave运行时中不可用。
// file: example3.groovy
def c = {w->
  [1,2,3].each {
    println "$it"
    (1..it).each {x->
      println 'this seems' << ' somewhat closer' << ''' to the 
      original''' << " $x"
    }
  }
}
def node = c.metaClass.classNode.getDeclaredMethods("doCall")[0].code
def writer = new StringWriter()
node.visit new groovy.inspect.swingui.AstNodeToScriptVisitor(writer)
println writer
// prints: return [1, 2, 3].each({
//     this.println("$it")
//     return (1.. it ).each({ java.lang.Object x ->
//         return this.println('this seems' << ' somewhat closer' << ' to the \n      original' << " $x")
//     })
// })
// file: example1.groovy
....
def code = a.metaClass.classNode.getDeclaredMethods("doCall")[0].code
println "$code.lineNumber $code.columnNumber $code.lastLineNumber $code.lastColumnNumber"
new File('example1.groovy').readLines()
... etc etc you get the idea.