Gradle throws exception when test classes use type annotations

When tests contain type annotations, gradle will fail with an exception (Stack Trace). This is reproducible both with Gradle 2.2.1 and 2.3-rc-1, e.g. using a class like this:

public abstract class Dog implements Animal, @Trait Talkative {
}

I was under the impression that the ‘@Trait’ annotation could only be applied to a class. I’m not sure the syntax you have there is even valid, and I’m surprised Groovy didn’t fail to compile it.

This is Java and it the bug affects any Type annotation at this point (I didn’t try other cases of type annotations).

It’s from a project of mine that will enable Traits pure Java. The qualified name of this annotation is @ch.raffael.sangria.traits.Trait. Sorry for any confusion it may have caused. In the original code, it was:

public abstract class Dog implements Animal, @Trait @Word("Wau") Talkative {
}

(this implementation of traits will support parametrised traits), commenting out “@Trait” and just leaving “@Word” doesn’t change anything. Just tested it to be sure it’s not some confusion in the code. Note that these annotations don’t do anything yet, it really just from a draft that demonstrates how to write stuff when this thing is implemented. These are just “empty” annotations with no practical meaning whatsoever, currently.

The exact location of where the exception is thrown:

// org.objectweb.asm.ClassVisitor.java
public AnnotationVisitor visitTypeAnnotation(int typeRef,
        TypePath typePath, String desc, boolean visible) {
    if (api < Opcodes.ASM5) {
        throw new RuntimeException(); // HERE! LINE 197
    }
    if (cv != null) {
        return cv.visitTypeAnnotation(typeRef, typePath, desc, visible);
    }
    return null;
}

It looks like Gradle is using ASM 5, but passing ASM4 as apiVersion to the ClassVisitor and ASM is choking on this, when it encounters a TypeAnnotation. I guess, ASM shouldn’t call visitTypeAnnotation() when the apiVersion ASM4 is passed to the constructor.

Does this fail for all usages of type annotations? For example:

Dog dog = new @Trait Dog()

Also, does the use or type annotations cause issues in your production code at runtime, or is this only affecting test executions?

Sorry, I thought my bug report was a dream of a bug report. :wink: I’ll try to explain even more exactly, what’s going on. After that, the only thing I can do to describe the problem better is to submit a patch.

  • Gradle scans the classes for @Test annotations and other constructs that indicate test classes. This is when the exception is thrown (see the stack trace I provided).

  • It uses ASM5 to do this. This is needed because ASM4 won’t understand Java8 bytecode.

  • While using ASM5, it still passes Opcodes.ASM4 as API key.

  • Because of telling ASM that Gradle’s using the ASM4 API, ASM throws an exception when it encounters elements that weren’t present in ASM4. The type annotation, introduced with Java8, is a typical example of such an element.

  • ASM would simply ignore the type annotation, if the API version was set to ASM5 – which is actually the right thing to do. Type annotations can be safely ignored when scanning for test classes, but ASM want’s to know that you are aware of the fact that you might encounter a type annotation (which you do with the ASM5 API key).

  • Resolution: Init your ASM visitors with API version ASM5. Intentionally using ASM5 and passing ASM4 to the visitor’s constructor as API version is a weird thing to do, anyway. The effect will be that the ClassVisitor will simply ignore type annotations instead of throwing an exception.

For the question above:

Dog dog = new @Trait Dog();

No, this doesn’t trigger any exception.

Thanks for the analysis, Raffael. I’ve raised this as GRADLE-3238.