How to get the Groovy generated java source code

6.4k views Asked by At

We have some legacy code with Groovy, and we want to remove Groovy from the application, so, we need to get the java source code generated after using the gmaven plug-in.

Basically, in other words I am dynamically generating new classes (using gmaven Groovy maven plug in) and I would like to be able to obtain the java source code of such generated classes.

I researched a little bit and can see that the only goals for this plug in are

<goal>generateStubs</goal>
<goal>compile</goal>
<goal>generateTestStubs</goal>
<goal>testCompile</goal>

I can't see any goal that allows you to obtain the fully implemented java source code, the stub code is not enough for us as we need the final implementation source code in order to get rid of Groovy.

4

There are 4 answers

1
Andre Steingress On

This question has been on the mailing-list some time ago [0]. To summarize: Groovy to Java is hard to achieve since there are language constructs and APIs (if you do want to totally remove the Groovy dependency) that are not available in Java.

Especially with the introduction of call-site caching and other performance optimizing techniques the generated Java code would look a lot like this (for the matter of simplicity I just threw some script into JD-GUI [1]):

public class script1351632333660 extends Script
{
public script1351632333660()
{
script1351632333660 this;
CallSite[] arrayOfCallSite = $getCallSiteArray();
}

public script1351632333660(Binding arg1)
{
Binding context;
CallSite[] arrayOfCallSite = $getCallSiteArray();
ScriptBytecodeAdapter.invokeMethodOnSuperN($get$$class$groovy$lang$Script(), this, "setBinding", new Object[] { context });
}

public Object run()
{
CallSite[] arrayOfCallSite = $getCallSiteArray(); Object items = ScriptBytecodeAdapter.createList(new Object[0]);
Object[] item = (Object[])ScriptBytecodeAdapter.castToType(ScriptBytecodeAdapter.createList(new Object[] { "Fluff", arrayOfCallSite[1].callConstructor($get$$class$java$util$Date()), (Integer)DefaultTypeTransformation.box(11235813) }), $get$array$$class$java$lang$Object());

arrayOfCallSite[2].call(items, item);

arrayOfCallSite[3].callCurrent(this, items);

ValueRecorder localValueRecorder = new ValueRecorder();
try
{
Object tmp102_101 = items; localValueRecorder.record(tmp102_101, 8);
Object tmp126_121 = arrayOfCallSite[4].call(tmp102_101, new script1351632333660._run_closure1(this)); localValueRecorder.record(tmp126_121, 14); if (DefaultTypeTransformation.booleanUnbox(tmp126_121)) localValueRecorder.clear(); else ScriptBytecodeAdapter.assertFailed(AssertionRenderer.render("assert items.findAll { it }", localValueRecorder), null);  } finally {
localValueRecorder.clear(); throw finally; } return null; return null; } 
static { __$swapInit();
Long localLong1 = (Long)DefaultTypeTransformation.box(0L);
__timeStamp__239_neverHappen1351632333665 = localLong1.longValue();
Long localLong2 = (Long)DefaultTypeTransformation.box(1351632333665L);
__timeStamp = localLong2.longValue(); } 
class _run_closure1 extends Closure implements GeneratedClosure { public _run_closure1(Object _thisObject) { super(_thisObject); } 
public Object doCall(Object it) { CallSite[] arrayOfCallSite = $getCallSiteArray(); return it; return null;
}

// ...

[0] http://groovy.329449.n5.nabble.com/Java-lt-gt-Groovy-converters-td337442.html

[1] http://java.decompiler.free.fr

1
David Rabinowitz On

I'm not very familiar with the gmaven plugin, but I assume it compiles the groovy code into byte code. In this case, you can use a byte code decompiler, there is a nice list here. In the past I've used JAD and it was quite nice. The best ones will also try to create meaningful variable names based on class names.

One warning though - Groovy objects are derived from GObject, not java.lang.Object, so you would probably need to keep the groovy jar until the groovy->java porting is done. Also, be prepared that it won't be a very easy to read java...

1
maba On

The generated stubs will be useless for you. They are just what their names suggests: stubs.

The stubs are only useful when doing joint java/groovy compilation. That's because there are two compilers involved in a java/groovy mixed project.

  1. Parse groovy
  2. Create stubs
  3. Compile java and stubs (using javac)
  4. Continue groovy compilation (using groovyc)

The groovy code will be compiled using groovyc compiler and the result is byte code.

This is an example of a generated stub:

package maba.groovy;

import java.lang.*;
import java.io.*;
import java.net.*;
import java.util.*;
import groovy.lang.*;
import groovy.util.*;

@groovy.util.logging.Log4j() public class Order
    extends java.lang.Object  implements
    groovy.lang.GroovyObject {
    public  groovy.lang.MetaClass getMetaClass() { return (groovy.lang.MetaClass)null;}
    public  void setMetaClass(groovy.lang.MetaClass mc) { }
    public  java.lang.Object invokeMethod(java.lang.String method, java.lang.Object arguments) { return null;}
    public  java.lang.Object getProperty(java.lang.String property) { return null;}
    public  void setProperty(java.lang.String property, java.lang.Object value) { }
    public  int getPrice() { return (int)0;}
    public  void setPrice(int value) { }
    public  int getQuantity() { return (int)0;}
    public  void setQuantity(int value) { }
    @java.lang.Override() public  java.lang.String toString() { return (java.lang.String)null;}
}

As you can see there is nothing useful. And you will still depend on some groovy libraries.

1
Michael Laffargue On

It may be out of your scope (1 year old) but I fought against the same problem and found a method to retrieve the algorithm (not the java source code) from the decompiled groovy classes.

You may want to take a look : http://michael.laffargue.fr/blog/2013/11/02/decompiling-groovy-made-classes/