Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

print the closure definition/source in Groovy

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() } 

I would like to have the String "it.twice()" or "{ it.twice() }"

Just a simple toString ofcourse won't work:

a.toString(); //results in: Script1$_run_closure1_closure4_closure6@12f1bf0
like image 492
Julian Avatar asked Mar 10 '11 16:03

Julian


People also ask

How are closures defined in Groovy?

A closure in Groovy is an open, anonymous, block of code that can take arguments, return a value and be assigned to a variable. A closure may reference variables declared in its surrounding scope.

How do I return a Groovy closure?

We can even return closures from methods or other closures. We can use the returned closure to execute the logic from the closure with the explicit call() method or the implicit syntax with just the closure object followed by opening and closing parentheses ( () ).

How do I print on Groovy?

You can use the print function to print a string to the screen. You can include \n to embed a newline character. There is no need for semi-colon ; at the end of the statement. Alternatively you can use the println function that will automatically append a newline to the end of the output.

How do I use lambda expression in Groovy?

The Groovy syntax doesn't support the lambda expressions, but we can rely on closure coersion to use Groovy closures as Java lambda expressions in our code. In the following sample we use the Java Streams API. Instead of lambda expressions for the filter and map methods we use Groovy closures.


2 Answers

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

like image 64
jpertino Avatar answered Sep 18 '22 15:09

jpertino


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.

like image 38
ataylor Avatar answered Sep 18 '22 15:09

ataylor