smallrye / jandex

Java Annotation Indexer

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add an annotation overlay

Ladicek opened this issue · comments

Jandex is immutable, but it is often required to have a mutable layer for annotations. This is because annotations are very often used to represent framework-specific metadata, and frameworks that use Jandex as a language model require the ability to mutate the metadata.

ArC and RESTEasy Reactive, both parts of Quarkus, have their own annotation overlay mechanism (called AnnotationStore). Hibernate also has interest in this (#117). Over time, I grew convinced that Jandex should just carry a similar mechanism, which I'd like to call AnnotationOverlay.

I'm actually thinking of 2 kinds of an annotation overlay:

  1. An immutable overlay that is created from an IndexView and a collection of lazily-applied annotation transformations. This is what ArC and RESTEasy Reactive currently have.
  2. A mutable overlay created from an IndexView that exposes operations to directly mutate annotations. This kind of overlay would expose a freeze() operation, returning an annotation transformation that is suitable for creating the 1st kind of overlay. After freezing, this kind of annotation overlay should be thrown away. It is useful for implementing things like CDI Build Compatible Extensions.

Internally, an annotation overlay would contain a Map from an EquivalenceKey to a set of annotations present on the declaration. When the annotation overlay is first asked for information about given declaration, the annotation transformations are run and the result is stored to the map. In case no annotation transformation affected given declaration, we should be able to store a sentinel value that means "just look it up from the passed annotation target". That would conserve memory.

Extra care needs to be taken when it comes to annotations on nested annotation targets. I'm inclined to say that annotation overlays should only store/return annotations present directly on given declaration and ignore annotations present on nested annotation targets. However, ArC currently (being based on Jandex 2.x) sometimes uses method annotation transformations to transform annotations on the method parameters. So this might require some extra wiggle room.

Are these "lazily-applied annotation transformations" already defined as a contract? Trying to visualize what this would look like and how it would work; maybe seeing the actually interface would help.

I don't have anything concrete yet, but I expect the immutable overlay (item 1) to be fairly similar to what ArC and RESTEasy Reactive have today.

EDIT: I mean, the annotation overlay itself would likely look like this: https://github.com/quarkusio/quarkus/blob/main/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AnnotationStore.java and the "lazily applied transformation function" would look like this: https://github.com/quarkusio/quarkus/blob/main/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AnnotationsTransformation.java

Will both of these ultimately result in a Index? Or will the result be a IndexView?

Trying to account for this possibility in the proof-of-concept work I am doing and this would be good to know; specifically the Collection/List return difference.

Thanks!

The annotation overlay won't be an Index nor an IndexView. The object graph coming from an index is immutable, so this would require expensive copying. Instead, the idea is that you'll obtain the objects from an index as usual, and if you want to obtain annotations for that object, you'll call into the overlay.

(The overlay will be built on top of an IndexView though.)

Sorry, I wasn't asking is the overlay will be an Index or IndexView. I was asking whether it would produce either.

Or are you saying it is a completely separate beast and we would just consume the overlay instead of a Index or IndexView?

Sorry for confusion, I considered "be" or "produce" the same in this context. Neither is possible without massively breaking changes to Jandex I'm afraid.

So this will be a totally separate beast sitting on top of an IndexView. You'd have to first have an index, then you'd create the overlay that represents annotation alterations, and then you'd consume both: obtain classes/methods/fields from the index, and their annotations from the overlay.

So for what it is worth, I'd not worry about this for Hibernate. I think anything you could do here would not really fit our needs. There's this aspect (XML overlay) plus stuff like dynamic models (no Class), auto-discovery, etc that we are just simply going to have to handle on our own. And if we need to handle these aspects ourselves, I'm not sure we are getting a lot of benefit from this feature in Jandex. Since these other projects already have working support for overlay I just wanted to make sure you aren't adding this just for Hibernate.

No worries, it certainly isn't only for Hibernate. I think the idea is to centralize language modelling in Quarkus to Jandex, and this metadata (annotation) overlaying is a big missing piece there. So it's for Quarkus overall. (Whether there will be a single overlay for all Quarkus extensions, or each will maintain their own instance, I have no idea. The first option is very powerful, but also somewhat dangerous. We'll see.)