【问题标题】:print the closure definition/source in Groovy在 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 绑定),如果闭包只是一个字符串呢?

标签: groovy closures


【解决方案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 字节码。闭包没有任何不同的处理方式,它们像常规方法一样编译。到代码运行时,源代码不再可用。

【讨论】:

    猜你喜欢
    • 2010-11-21
    • 2011-03-15
    • 1970-01-01
    • 1970-01-01
    • 2018-03-16
    • 2014-11-11
    • 2015-12-26
    • 2013-11-16
    • 1970-01-01
    相关资源
    最近更新 更多