Annotation Processing issues on modules

Jeremy Kuhn jeremy.kuhn1 at gmail.com
Fri Dec 6 00:01:38 UTC 2019


Hi,

I've been working lately on an IoC/DI framework for Java 9+ which relies on
annotation processing and code generation. I'm actually defining
annotations on modules and I discovered couple of bugs during the
processing of these annotations in the Java compiler.

The first one is when we want to print a compilation message targeting a
module's annotation, it exists on all versions since JDK 9. Let's say we
have the following module descriptor:

    @SampleAnnotation
    module sampleModule {

    }

and a properly configured annotation processor to process the
@SampleAnnotation, if we do:

    this.processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING,
"Module warning", element, annotationMirror);

where element corresponds to the sampleModule and annotationMirror to the
@SampleAnnotation, the compiler crash with a java.lang.AssertionError:

An annotation processor threw an uncaught exception.
Consult the following stack trace for details.
java.lang.AssertionError
     at jdk.compiler/com.sun.tools.javac.util.Assert.error(Assert.java:155)
     at
jdk.compiler/com.sun.tools.javac.tree.JCTree$Visitor.visitTree(JCTree.java:3235)
     at
jdk.compiler/com.sun.tools.javac.tree.JCTree$Visitor.visitModuleDef(JCTree.java:3227)
     at
jdk.compiler/com.sun.tools.javac.tree.JCTree$JCModuleDecl.accept(JCTree.java:2780)
     at
jdk.compiler/com.sun.tools.javac.model.JavacElements.matchAnnoToTree(JavacElements.java:298)
     at
jdk.compiler/com.sun.tools.javac.model.JavacElements.getTreeAndTopLevel(JavacElements.java:743)
     at
jdk.compiler/com.sun.tools.javac.processing.JavacMessager.printMessage(JavacMessager.java:108)
     at
jdk.compiler/com.sun.tools.javac.processing.JavacMessager.printMessage(JavacMessager.java:87)
     at
processor/com.example.processor.ModuleWarnProcessor.lambda$process$1(ModuleWarnProcessor.java:21)
     at
java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
     at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
     at
java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
     at
java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
     at
java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:274)
     at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
     at
java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
     at
java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
     at
java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
     at
java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
     at
java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
     at
java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
     at
java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
     at
processor/com.example.processor.ModuleWarnProcessor.process(ModuleWarnProcessor.java:19)
     at
jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:1023)
     at
jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:939)
     at
jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1267)
     at
jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1381)
     at
jdk.compiler/com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1263)
     at
jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:935)
     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:318)
     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:176)
     at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:57)
     at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:43)

I've been able to track down the issue in
com.sun.tools.javac.model.JavacElements#matchAnnoToTree(), the Vis class
does not implement the visitModuleDef() method and as a result it is not
possible to find module's annotations hence the AssertionError.

The second issue occurs when you want to use imports in a module-info.java
file, if you do so the file is simply ignored during annotation processing.
This issue also exists on all versions since JDK 9. Let's say we have the
following module descriptor:

    import com.example.SampleAnnotation;

    @SampleAnnotation
    module sampleModule {

    }

and a properly configured annotation processor to process the
@com.example.SampleAnnotation, the compiler simply doesn't take the module
into account when processing annotation.

I've been able to track down the issue in
com.sun.tools.javac.processing.JavacProcessingEnvironment#getModuleInfoFiles()

    private List<ModuleSymbol> getModuleInfoFiles(List<? extends
JCCompilationUnit> units) {
        List<ModuleSymbol> modules = List.nil();
        for (JCCompilationUnit unit : units) {
            if (isModuleInfo(unit.sourcefile, JavaFileObject.Kind.SOURCE) &&
                unit.defs.nonEmpty() &&
                unit.defs.head.hasTag(Tag.MODULEDEF)) {
                modules = modules.prepend(unit.modle);
            }
        }
        return modules.reverse();
    }

Here it is assumed that the first unit in a module descriptor has to be a
module statement however according to the java language specification, a
module descriptor can have import statements before (and this actually
compiles just fine) as a result the module is not added to the list of
modules candidates for annotation processing.

I've also found another more tricky one which relates to the usage of
deprecated module in the module providing the annotation processor: if we
create a module which depends on a jdk deprecated module and which provides
an annotation processor, having that module on the processor module path
will make the compiler to return with nothing compiled, no error and no
output. I didn't dig too much into this as this only impacts jdk9 which
comes with deprecated modules (eg. java.se.ee), later versions don't have
any issues for now. I've stopped my investigation in the
java.lang.module.Resolver class line 181, deprecated modules are actually
not resolved.

I have prepared patches with jtreg tests for the first two issues on the
repository head and I'd be happy to submit them. Please let me know if the
description of the issues is clear enough and how we can proceed to fix
them, I've already signed the OCA.

Jeremy


More information about the jdk-dev mailing list