Proposal: Allow annotations on module directives

Alessandro Autiero alautiero at gmail.com
Mon Nov 11 16:30:19 UTC 2024


*Summary*
This proposal suggests allowing annotations on module directives in a
module-info, similar to how module declarations can currently be annotated.
This enhancement, while narrow in scope, would give developers more
flexibility when designing libraries deeply integrated with Java's built-in
module system. I have built a proof of concept on Github
<https://github.com/Auties00/jdk/tree/module_directives_annotations> which
is already fully working and completely backwards compatible. I still
haven't:

   1. Build automated integration tests
   2. Added support for the new ClassFile API <https://openjdk.org/jeps/457>


Before doing that though I wanted to collect some feedback on my
implementation, especially on:

   1. Whether this change makes sense to the maintainers
   2. The new bytecode instructions I introduced and whether better
   alternatives exist
   3. The uses() accessor in ModuleDescriptor

All changes I made are documented in the respective commits I made, but if
you have any questions please let me know.

*Bytecode*
My implementation introduces two new bytecode attributes:

   - ModuleDirectivesRuntimeInvisibleAnnotations
   - ModuleDirectivesRuntimeVisibleAnnotations

which both contain, for each type of module directive, the number of
directives that were annotated and for each directive that was annotated:

   1. The index of the directive relative to all the directives of the same
   type that are present in the module
   2. The number of annotations that were attached to the directive
   3. The metadata of the annotation in the standard format used in the
   bytecode

Before working on this implementation, I had never designed bytecode
instructions so my approach might be suboptimal, so if any better
alternatives exist, I'll implement the changes as soon as possible. Other
approaches I considered:

   - Using RuntimeInvisibleAnnotations and RuntimeVisibleAnnotations
   Module directives are stored in the Module attribute alongside the
   module name, flags and version, instead of in a specialized table like
   class fields or local variables. For this reason, they cannot declare their
   own attributes(nested attributes are not supported by the JVM).
   - Modifying the Module attribute to contain the annotations directly in
   the existing directives metadata
   While this approach doesn't require additional bytecode attributes, it
   breaks completely existing tooling that parses a module-info because of the
   same constraints explained for the previous approach.

In conclusion, while my proposed implementation is fully backwards
compatible, it introduces two new bytecode attributes.


*Public API changes*
All changes are backwards-compatible, but I've included some reasoning on
some design choices I made.
*javax.lang.model.element.ModuleElement$Directive*

   - Implement AnnotatedConstruct to support annotations during
   compile-time processing
   - Directive does not currently implement Element and cannot do so in the
   future as it already defines a method named getKind which clashes with
   Element's getKind because of their different return types(DirectiveKind and
   ElementKind respectively). Implementing Element would also require
   ElementVisitor to implement DirectiveVisitor. I don't think that it's
   necessary for Directive to implement Element as it wasn't intended to do so
   when Java 9 was introduced, so I see no reason or backwards-compatible way
   to do so.

*java.lang.module.ModuleDescriptor*

   - Added a new sealed common class named Directive that Exports,
   Requires, Opens, Uses and Provides extend. This class contains an
   annotations() accessor to allow callers to access annotations
   - Added a new Uses class that represents uses directives. This
   introduces a slight backwards compatibility issue as an accessor named
   uses() already exists in ModuleDescriptor and it returns a Set<String>
   instead of a Set<Uses>. For now I added an accessor named used() that
   returns a Set<Uses>, but usedServices could be a better name. I don't think
   that changing uses() to return Set<Uses> is an option as that would break
   backwards compatibility and having an odd-named accessor shouldn't be that
   much of an issue if we find a fitting name.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/compiler-dev/attachments/20241111/7feb53fb/attachment-0001.htm>


More information about the compiler-dev mailing list