Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Must call initializers using invokespecial with @NotNull instrumentation and @NotNull on clone() #115

Closed
tagliola opened this issue Jun 17, 2015 · 13 comments

Comments

@tagliola
Copy link

Not sure if I should report it here, but we are running our builds with @NotNull instrumentation (See https://github.com/atorstling/intellij-annotations-instrumenter-maven-plugin). When we have a class under test with EqualsVerifier, and that class has the clone() method annotated with @NotNull, we get the following stack trace:

Running com.foo.bar.MyObjectTest
Tests run: 6, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.008 sec <<< FAILURE!
shouldVerifyHashCodeEquals(com.foo.bar.MyObjectTest)  Time elapsed: 0.007 sec  <<< FAILURE!
java.lang.AssertionError: java.lang.VerifyError: (class: com/foo/bar/MyObject$$EnhancerByCGLIB$$5e3ec132, method: clone signature: ()Lcom/foo/bar/MyBaseObject;) Must call initializers using invokespecial
For more information, go to: http://www.jqno.nl/equalsverifier/errormessages
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at nl.jqno.equalsverifier.internal.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:386)
    at nl.jqno.equalsverifier.internal.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219)
    at nl.jqno.equalsverifier.internal.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
    at nl.jqno.equalsverifier.internal.cglib.proxy.Enhancer.createClass(Enhancer.java:317)
    at nl.jqno.equalsverifier.util.Instantiator.createDynamicSubclass(Instantiator.java:103)
    at nl.jqno.equalsverifier.util.Instantiator.instantiateAnonymousSubclass(Instantiator.java:81)
    at nl.jqno.equalsverifier.util.ObjectAccessor.copyIntoAnonymousSubclass(ObjectAccessor.java:123)
    at nl.jqno.equalsverifier.HierarchyChecker.checkSubclass(HierarchyChecker.java:106)
    at nl.jqno.equalsverifier.HierarchyChecker.check(HierarchyChecker.java:63)
    at nl.jqno.equalsverifier.EqualsVerifier.verifyWithExamples(EqualsVerifier.java:459)
    at nl.jqno.equalsverifier.EqualsVerifier.performVerification(EqualsVerifier.java:427)
    at nl.jqno.equalsverifier.EqualsVerifier.verify(EqualsVerifier.java:394)
    at com.foo.bar.MyObjectTest.shouldVerifyHashCodeEquals(MyObjectTest.java:29)

I can prepare a minimal use case, if needed.

@jqno
Copy link
Owner

jqno commented Jun 17, 2015

Hi,

Wow, that's quite...interesting :).

If you can make a minimal test case, that would definitely help. But before you do that, can you see what happens if you try to mock such a class using Mockito? The way EqualsVerifier instantiates objects is inspired by how Mockito does it. Given that the stacktrace is deep into cglib, I suspect the same issue might be at play there. If so, then we should probably report this at cglib as well.

Jan

@tagliola
Copy link
Author

I will try. What I forgot to mention is, this test is working when using equalsverifier 1.4.1, any version after that (1.5.1, 1.6 and 1.7.2) are failing, hence I opened the issue here.

@jqno
Copy link
Owner

jqno commented Jun 17, 2015

Ah, that's quite interesting! You're right, that suggests that there must be something that I can do in EV to fix this. If you can upload a small sample project, I'll take a closer look.

@tagliola
Copy link
Author

@jqno
Copy link
Owner

jqno commented Jun 17, 2015

Thanks!
I've reproduced the issue, I'll look into it later.

@jqno
Copy link
Owner

jqno commented Jun 21, 2015

OK, I've looked into it a bit, and I've found out:

  • the problem doesn't occur with Mockito.
  • it only occurs in a JVM 1.8, not in a 1.7.
    So far, though, I'm no step closer to a solution. Can't figure out why this is happening. I will keep looking into it, but it might be a while. In the mean time, if you can think of anything, please let me know!

@jqno
Copy link
Owner

jqno commented Jul 3, 2015

I've managed to reduce the problem further. See this Gist: https://gist.github.com/jqno/1739d120887c785f3832

I conclude that the issue must either be in the notnull-instrumenter-maven-plugin, or in CGLib. Perhaps it could be due to the fact that CGLib doesn't fully support ASM 5.0 yet, which the notnull-instrumenter-maven-plugin and EqualsVerifier both use.

Unfortunately, my knowledge of both notnull-instrumenter-maven-plugin and CGLib is insufficient to help you further with this. I would ask you to take it up with either of them. In the mean time, I'm closing this issue, since there's nothing I can do about it anyway.

If you get a response from them about a possible workaround that I could implement in EqualsVerifier (for example, a different way to invoke CGLib), please let me know by re-opening this issue or creating a new one, and I'll check it out.

@jqno jqno closed this as completed Jul 3, 2015
@tagliola
Copy link
Author

tagliola commented Jul 3, 2015

I will have a look if using the latest master of cglib is making a difference. See also cglib/cglib#20. Thanks for the investigation and elimination of EV as a bug candidate!

@jqno
Copy link
Owner

jqno commented Jul 3, 2015

Cool! I'd be interested to see if that works.

@wjbakker
Copy link

wjbakker commented Aug 5, 2015

Hi,

This problem is superseded if cglib is replaced by bytebuddy. Bytebuddy is actively maintained, latest release was July 2015 (compare to cglib, December 2013). I did a quick test with bytebuddy 0.6.14 and confirmed this issue no longer occurs.

pom.xml

        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>0.6.14</version>
            <scope>provided</scope>
        </dependency>

Instantiator.java

    @SuppressWarnings({"rawtypes", "unchecked"})
    private static <S> Class<S> createDynamicSubclass(Class<S> superclass) {
        DynamicType.Builder<S> builder = createBuilder(superclass);
        return (Class<S>) builder
            .name(new NamingStrategy.Fixed(superclass.getName() + "$DynamicSubclass"))
            .make()
            .load(superclass.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
            .getLoaded();
    }

    @SuppressWarnings("unchecked")
    private static <S> DynamicType.Builder<S> createBuilder(Class<S> superclass)
    {
        ByteBuddy byteBuddy = new ByteBuddy();
        if (superclass.isInterface()) {
            return (DynamicType.Builder<S>) byteBuddy.subclass(Object.class).implement(superclass);
        }
        else {
            return byteBuddy.subclass(superclass);
        }
    }

@jqno
Copy link
Owner

jqno commented Aug 5, 2015

That's very interesting, thanks! I was aware of Bytebuddy's existence but I've never really looked at it.
I'm going to look into this, I'll let you know what I think.

@jqno jqno reopened this Aug 5, 2015
@jqno
Copy link
Owner

jqno commented Mar 6, 2016

I've just released EqualsVerifier 2.0, which uses ByteBuddy instead of CGLib. That should solve this issue.

@jqno jqno closed this as completed Mar 6, 2016
@atorstling
Copy link

atorstling commented Apr 5, 2017

Hi everybody, I didn't see this issue until now unfortunately. But I figured that it would be good to comment here anyway in case anyone Googles this problem. Turns out that cglib cannot handle the NotNull-checks which the instrumenter inserted into synthetic methods: cglib/cglib#48 . The error will be triggered if you have synthetic methods affected by NotNull instrumentation and create a cglib proxy for the containing class.

This happens with pure cglib and also Springs bundled cglib for AOP. One can argue whether it's cglibs or the NotNull-instrumenters fault. But there is a fix on its way in the NotNull instrumenter: osundblad/intellij-annotations-instrumenter-maven-plugin#17

Cheers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants