print the closure definition/source in Groovy

前端 未结 2 409
孤独总比滥情好
孤独总比滥情好 2020-12-03 08:07

Anyone who knows how the print the source of a closure in Groovy?

For example, I have this closure (binded to a)

def a = { it.twice() }          


        
相关标签:
2条回答
  • 2020-12-03 08:20

    short answer is you can't. long answer is:
    depending on what you need the code for, you could perhaps get away with

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

    BUT
    you will need the source code of the script available in the classpath AT RUNTIME as explained in

    groovy.lang.MetaClass#getClassNode()
    "Obtains a reference to the original AST for the MetaClass if it is available at runtime
    @return The original AST or null if it cannot be returned"

    AND
    the text trick does not really return the same code, just a code like representation of the AST, as can be seen in this script

    // file: example2.groovy
    def b = {p-> p.twice() * "p"}
    println b.metaClass.classNode.getDeclaredMethods("doCall")[0].code.text
    // prints: { return (p.twice() * p) }
    

    still, it might be useful as it is if you just want to take a quick look

    AND, if you have too much time on your hands and don't know what to do you could write your own org.codehaus.groovy.ast.GroovyCodeVisitor to pretty print it

    OR, just steal an existing one like 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")
    //     })
    // })
    

    now.
    if you want the original, exact, runnable code ... you are out of luck
    i mean, you could use the source line information, but last time i checked, it wasn't really getting them right

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

    line numbers shuld be at least near the original code though

    0 讨论(0)
  • 2020-12-03 08:44

    That isn't possible in groovy. Even when a groovy script is run directly, without compiling it first, the script is converted into JVM bytecode. Closures aren't treated any differently, they are compiled like regular methods. By the time the code is run, the source code isn't available any more.

    0 讨论(0)
提交回复
热议问题