Hi Saravanan,

Do I assume right in that you are trying to use a Kotlin defined
Annotation in Groovy? Isn't in Kotlin a @Repeatable required? At least I
read that from
https://kotlinlang.org/docs/annotations.html#repeatable-annotations Or
in this case I would have expected
@JvmRepeatable(MyGroupAnnotation::class) on MyAnnotation.

I would suggest, that you first try to make what you try to get done
with normal Java or Groovy based annotations. After that we can then
compare the Kotlin and the Java version on the bytecode level to
actually see what (if anything) is different.

For example when you say you load "someMethod from a jar or class path"
then I would have before assumed you do this using Java or Groovy. But
now this could be also Kotlin. If Kotlin is loading those classes it may
do something to them I do not know. This is complete speculation on my
side of course, but something I cannot exclude as option.

That is why it is important to make such a scenario as bare-bone as
possible.

bye Jochen

On 10.03.22 16:11, Saravanan Palanichamy wrote:
Thank you Jochen

I further narrowed this down to this behaviour

  * I have an annotation MyAnnotation. This is marked as SOURCE
    retention and is used as a repeatable annoation
  * I have another annotation MyGroupAnnotation. This is marked as
    RUNTIME retention
  * I have an AST transformation that removes all MyAnnotation on the
    type it is defined on and collates them into MyGroupAnnotation

@MustBeDocumented
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.FUNCTION)
annotation class MyAnnotation(...)

@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class MyGroupAnnotation(
     val all:Array<RemoteActivity> = []
)

This is how I use it

@MyAnnotation(...)
@MyAnnotation(...)
List<MyStuff> someMethod(List<MyStuff> blah)

And this is how it ends up after the AST

@MyGroupAnnotation(all = [MyAnnotation(...), MyAnnotation(...)])
List<MyStuff> someMethod(List<MyStuff> blah)


When I use someMethod, if it has already been loaded separately by the
same class loader, the MethodNode representing someMethod contains 1
annotation (MyGroupAnnotation) with 1 member which is a list but that
list has one NULL value in it

This seems to be because it goes through
Java8::annotationValueToExpression(...) through the array section and
then back into annotationValueToExpression where it sees an instance of
a proxy object instead of a Class definition. So it returns NULL

Not sure if this rings any bells, I am lost though. This seems to work
if I load someMethod from a jar or class path. This only fails if I use
the same class loader that compiled someMethod

regards
Saravanan


On Fri, Mar 4, 2022 at 5:44 PM Jochen Theodorou <blackd...@gmx.org
<mailto:blackd...@gmx.org>> wrote:

    On 15.02.22 05:20, Saravanan Palanichamy wrote:
     > Hello Groovy users
     >
     > I am using Groovy 3.0.5
     >
     >   * I have a file A.groovy and B.groovy. B depends on A
     >   * I have ast transformations that add annotations to methods in A
     >     (specifically an annotation with a list expression that contains
     >     other annotations)
     >   * I compile A.groovy using the loadClass and then compile B.groovy
     >     again with load class. When I inspect a method call to
    A(using the
     >     method target field inside method call expression), I see
    only null
     >     values for the annotations I added
     >   * If I compiled B.groovy and have it automatically detect
    A.groovy, I
     >     see the right values for my annotations (no nulls)
     >
     > Any idea why this is happening?

    If the annotations are directly in the file, then the one way this makes
    sense to me is that you load a different A. That would mean your class
    loader you use for B finds a different A, then the one you loaded (given
    you loaded the right A of course). Without further details about the
    setup this is a bit difficult. I suggest you do the following style
    of test:

    * loadClass for A, and keep a reference to A: x=loadClass("A")
    * have B return the class A (something like "return A.class")
    * loadClass for B and execute the code that returns A:
    y=loadClass("B").getA()
    * now the first A and the second A must be fulfilling referential
    identity: assert x===y

    If the assert fails it is clear that you are doing something wrong with
    your class loaders. If the assert does not fail I suggest to stop after
    loading A and checking that the annotations are on it. If they are not,
    you load the wrong A - wherever A is coming from. If they are there, but
    later not... well then you created some very very strange case that
    violates classloader constraints, because that means A has been modified
    after the class has been loaded, without loading a new A. Not sure you
    can do that even with instrumentation to that extend.

    bye Jochen


Reply via email to