在 Groovy 中打印闭包定义/源代码

Posted

技术标签:

【中文标题】在 Groovy 中打印闭包定义/源代码【英文标题】:print the closure definition/source in Groovy 【发布时间】:2011-07-12 21:22:29 【问题描述】:

谁知道在 Groovy 中如何打印闭包的来源?

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

def a =  it.twice()  

我想要String "it.twice()" 或 " it.twice() "

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

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

【问题讨论】:

这可能与您的情况不符,但您或许可以另辟蹊径。由于 GroovyShell.evaluate() 执行一个字符串(带有适当的 var 绑定),如果闭包只是一个字符串呢? 【参考方案1】:

简短的回答是你不能。长答案是: 根据您需要代码的目的,您也许可以逃脱

// file: example1.groovy
def a =  it.twice() 
println a.metaClass.classNode.getDeclaredMethods("doCall")[0].code.text
// prints:  return it.twice() 

但是 您将需要在类路径 AT RUNTIME 中可用的脚本源代码,如

中所述

groovy.lang.MetaClass#getClassNode() "获取对原始的引用 MetaClass 的 AST(如果是) 在运行时可用 @return 原始 AST 或 null 如果不能 归还”

text 技巧并没有真正返回相同的代码,只是一个类似于 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")
//     )
// )

现在。 如果您想要原始、准确、可运行的代码......那你就走运了 我的意思是,您可以使用源代码行信息,但上次我检查时,并没有真正让它们正确

// 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.  

行号应该至少接近原始代码

【讨论】:

我不明白“运行时类路径中可用的脚本”到底是什么意思?它还不能工作(ClassNode 是null),但我有所有可用的资源。是否必须在 Groovy 项目的类路径中添加脚本的位置? @Julian 表示 .groovy 文件应该在类路径中。 getClassNode() 基本上会加载文件,然后用一个特殊的钩子再次编译它以保存相关的 ast 节点,并返回它 @jpertino 关于如何在 Grails 环境中执行此操作有什么想法吗?我有一个 Config.groovy 文件,它在集成测试期间位于类路径上,但在运行应用程序期间却没有。 与 Jenkins 无关 - 只是说就我而言,我无法让它在那里工作 - 可能这些类在 jenkins slave 运行时中不可用。【参考方案2】:

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

【讨论】:

以上是关于在 Groovy 中打印闭包定义/源代码的主要内容,如果未能解决你的问题,请参考以下文章

Groovy闭包 Closure ( 闭包调用 | 闭包默认参数 it | 代码示例 )

Groovy闭包 Closure ( 闭包调用 与 call 方法关联 | 接口中定义 call() 方法 | 类中定义 call() 方法 | 代码示例 )

Groovy闭包 Closure ( 闭包调用 与 call 方法关联 | 接口中定义 call() 方法 | 类中定义 call() 方法 | 代码示例 )

Groovy闭包 Closure ( 闭包定义 | 闭包类型 | 查看编译后的字节码文件中的闭包类型变量 )

Groovy闭包 Closure ( 闭包作为函数参数 | 代码示例 )

Groovy闭包 Closure ( 闭包作为函数参数 | 代码示例 )