On 07.07.25 02:10, Saravanan Palanichamy wrote:
Hello Groovy users
I am looking for help in setting up my class loaders with the Groovy
compiler. This is my understanding so far
1. The compiler is an external tool to compile (Groovy is a java based
compiler) and therefore it must have its own class paths and a class
loader to load libraries it needs (the COMPILER class loader). This
includes the transform loaders needed for static compilation etc
2. The compiled code has its own set of class paths and there must have
a separate class loader (the CLASSPATH class loader)
3. The two should be completely separate (I think)
the Groovy compiler is not based on the java compiler. And both
compilers can be used internally and externally. Unless I misunderstand
what you mean with "external tool".
It does need a class loader, because that is used to find classes during
compilation. If you for example compile "class X extends Foo", then Foo
needs to be found. For example the compiler has to check if the class is
final, if it has abstract methods and many more things.
The transform loader is needed for compilation regardless of static or
not. those transforms consists of a front class in form of an annotation
and a backing class, with the actual implementation. The front class
must be visible in the normal class loader to find classes, the
implementing class must be visible by the transform loader. Both loaders
must have a parent knowing the same groovy library, or be that loader
itself. The compiler will then use the transform loader to load and
execute the transform.
However, the compiler sometimes will add class nodes as part of AST
transformations (Class nodes created with ClassHelper.create) ... In
that case, which class loader should be used for creating these class
nodes? Or does it not matter (I think it matters because I may use a
different version of jar in the compiler vs the compiled code)
Let me try some ascii art
G
/ \
D T
So D and T have as parent G
These loaders could be all one loader long as it is a GroovyClassLoader,
because that is what the compiler requires.
D is the defining class loader for classes produced by the compiler. D
is really only used if there is no target directory given I think. the
FileSystemCompiler for example compiles to the file system, not to in
memory classloaders. GroovyShell on the other hand typically does not
compile to the file system.
G is the class loader used by the compiler to find classes. The compiler
thus sees all classes visible to G. In the FileSystemCompiler we tend to
make one classloader for G load all those classes from the classpath,
but of course G can have parents as well. G is the loader containing the
Groovy library itself, including the compiler and all helper classes of
the normal compiler.
T is the transform loader, that can be used extra to load transforms. T
needs to use the same Groovy lib, the facade of the transform must be in
C, otherwise the compiler cannot find it and the implementing code in T.
A usual scenario is to do
GT
|
D
meaning G and T are the same loader. GroovyClassLoader, if used for
compilation as well uses for example an inner loader for D, allowing
recompilation of classes to some extend.
Anyway... as I said you can merge the loader with the parent in my
structure and still get a valid setup for class loading if you want to
do it like that.
If I have to use the CLASSPATH class loader for creating class nodes,
are there standard patterns to help do this (given that
ClassHelper.create is a static method)?
You mean classes nodes for preexisting classes, which we do not compile.
You mean ClassHelper.make, which does not safe the created ClassNode. It
is safe for compiling with it multiple times, just do not the
"withCaching" methods.
there is actually one more variant of class loaders you can use. Have
the Groovy library as parent and then a class loader that contains your
classes you want to compile against as child. D would be then be a child
of this. T can be a child of G, if you need not to load any of those
classes you compile against in T, or if you do a child of that loader.
But maybe you describe a bit the structure you want to have Groovy
itself and for the classes you compile against and if you really want T
to be separate.
bye Jochen