[TOC]
----------------------------------------------------------------------------------------------------
# Intermediate Java code introspection {#intermediate_java_code_introspection}
Groovy docs look unfriendly in that they show us the generated intermediate Java class examples,
like [, [exact point](http://groovy-lang.org/structure.html#_script_class)],
but give us no easy way to see the same about our own Groovy scripts. Here are some possible
reasons:
- Scripts are meant to be "transparent" --- Groovy's design assumes that users care more about
results than about how scripts get turned into classes --- especially for scripting use cases
- The transformation is internal: The actual script-to-class conversion is done by the Groovy
compiler behind the scenes. It creates a class that extends `groovy.lang.Script` and puts
our code inside the `run()` method, but doesn't keep source artifacts unless asked to ---
and only partially (e.g., in joint compilation)
- No built-in option for "show me the generated class" --- Unlike Scala (`scalac -Xprint:typer`)
or Kotlin (`-Xdump-declarations`), Groovy lacks an official `--emit-java` or
`--emit-script-class` flag. That makes it harder to introspect
Here's the workaround we are going to use:
- Compile our Groovy script: `groovyc myscript.groovy`
- Run CFR (other decompilers --- like [Fernflower](https://github.com/fesh0r/fernflower) and
[JD](https://java-decompiler.github.io/) --- may be also tried) to decompile the
`myscript.class` file
- Delete the class file to clean up: `del myscript.class`
CFR Java decompiler:
- Home page:
- GitHub:
- Download the `cfr-0.152.jar` file that is ready to use
Here's a short example. Groovy generated class file `methods.class` is going to be decompiled:
````shell
>set CFR_HOME=directory\where\cfr-0.152.jar\is_located\
>java -jar %CFR_HOME%cfr-0.152.jar methods.class > methods.java
> del methods.class
````
Here are the most interesting fragments of the result:
project1/methods.java
````wrapped-java
/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* groovy.lang.Binding
* groovy.lang.MetaClass
* groovy.lang.Script
...
*/
import groovy.lang.Binding;
import groovy.lang.MetaClass;
import groovy.lang.Script;
...
public class methods
extends Script {
...
public methods() {
}
public methods(Binding context) {
super(context);
}
public static void main(String ... args) {
IndyInterface.bootstrap("invoke", "runScript", 0, InvokerHelper.class, methods.class, args);
}
public Object run() {
IndyInterface.bootstrap("invoke", "println", 2, this, IndyInterface.bootstrap("invoke", "sum", 2, this, 7, 4));
return IndyInterface.bootstrap("invoke", "println", 2, this, IndyInterface.bootstrap("invoke", "sum", 2, this, 7));
}
public Object sum(int a, int b) {
return IndyInterface.bootstrap("invoke", "plus", 0, a, b);
}
...
}
````
## Additional considerations
Our script may create multiple classes, like with closures:
- `methods.class`
- `methods$_run_closure1.class`
- etc.
In such cases we may want to decompile all of them in one go:
````shell
java -jar cfr-0.152.jar *.class
````
See the output of:
- `java -jar %CFR_HOME%cfr-0.152.jar --help`
- `groovyc --help`
to know how to specify the input and output directories for better code organization.
Also see the following options to control the decompilation process:
````
--hidebridgemethods true --innerclasses false --decodelambdas false --comments false
````
Here are explanations given by Chat GPT []:
- `--hidebridgemethods` --- suppresses synthetic bridge methods (Java/Groovy creates these for
type compatibility)
- `--innerclasses` --- prevents CFR from showing nested/inner classes inline in the parent class
- `--decodelambdas` --- avoids reconstructing lambdas and closures as if they were written inline
(useful if Groovy uses `invokedynamic`)
- `--comments` --- suppresses extra helpful comments added by CFR, so it looks cleaner
----------------------------------------------------------------------------------------------------
# Running by Java runtime {#running_by_java_runtime}
As it's shown [above](#groovyc_usage) Groovy compiler can compile a normal Java class file.
Starting from Groovy 3.0+, compiled script classes include a generated
`public static void main(String... args)` method, so it may be executed by a Java runtime.
Still Groovy runtime JARs (`groovy-*.jar`) need to be added to the class path:
````shell
>type hello_world.groovy
println "Hello world!"
>groovyc hello_world.groovy
>dir /b | find "hello_world.class"
hello_world.class
>java -cp ".;%GROOVY_HOME%\lib\*" hello_world
Hello world!
>del hello_world.class
````
The `GROOVY_HOME` environment variable should point to the Groovy installation directory (or the
certain path may be used).