raphw / byte-buddy

Runtime code generation for the Java virtual machine.
https://bytebuddy.net
Apache License 2.0
6.25k stars 805 forks source link

java.lang.ArrayIndexOutOfBoundsException if the mockito-core is used to mock enum class #1203

Closed Yukooo closed 1 year ago

Yukooo commented 2 years ago

If there an enum class that has constructor with some parameters with annotation @org.springframework.lang.NonNull then it fails with the next stack trace

Mockito cannot mock this class: class com.my.type.MessageType.
Can not mock final classes with the following settings :
 - explicit serialization (e.g. withSettings().serializable())
 - extra interfaces (e.g. withSettings().extraInterfaces(...))

You are seeing this disclaimer because Mockito is configured to create inlined mocks.
You can learn about inline mocks and their limitations under item #39 of the Mockito class javadoc.

Underlying exception : org.mockito.exceptions.base.MockitoException: Could not modify all classes [interface java.lang.Comparable, class java.lang.Enum, class com.my.type.MessageType, interface com.my.util.Sortable, interface com.helper.common.helper.EnumIntConverter]
org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: class com.my.type.MessageType.
Can not mock final classes with the following settings :
 - explicit serialization (e.g. withSettings().serializable())
 - extra interfaces (e.g. withSettings().extraInterfaces(...))

You are seeing this disclaimer because Mockito is configured to create inlined mocks.
You can learn about inline mocks and their limitations under item #39 of the Mockito class javadoc.

Underlying exception : org.mockito.exceptions.base.MockitoException: Could not modify all classes [interface java.lang.Comparable, class java.lang.Enum, class com.my.type.MessageType, interface com.my.util.Sortable, interface com.helper.common.helper.EnumIntConverter]
    at app//com.app.to.MessageTreeTO.getType(MessageTreeTO.java:548)
    at app//com.app.common.connector.helper.MapperHelper.mapToNewTopicMessage(MapperHelper.java:91)
    at app//com.app.common.connector.helper.MapperHelperTest.should_map_new_topic(MapperHelperTest.java:53)
    at java.base@11.0.14.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base@11.0.14.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base@11.0.14.1/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base@11.0.14.1/java.lang.reflect.Method.invoke(Method.java:566)
    at app//org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    at app//org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at app//org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at app//org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at app//org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
    at app//org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    at app//org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at app//org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at app//org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at app//org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at app//org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at app//org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
    at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
    at java.base@11.0.14.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base@11.0.14.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base@11.0.14.1/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base@11.0.14.1/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    at com.sun.proxy.$Proxy5.processTestClass(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
    at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
    at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
    at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: org.mockito.exceptions.base.MockitoException: Could not modify all classes [interface java.lang.Comparable, class java.lang.Enum, class com.my.type.MessageType, interface com.my.util.Sortable, interface com.helper.common.helper.EnumIntConverter]
    at app//net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:157)
    at app//net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:371)
    at app//net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:179)
    at app//net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:382)
    ... 46 more
Caused by: java.lang.IllegalStateException: 
Byte Buddy could not instrument all classes within the mock's type hierarchy

This problem should never occur for javac-compiled classes. This problem has been observed for classes that are:
 - Compiled by older versions of scalac
 - Classes that are part of the Android distribution
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:280)
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.mockClass(InlineBytecodeGenerator.java:213)
    at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.lambda$mockClass$0(TypeCachingBytecodeGenerator.java:47)
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:157)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:371)
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:179)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:382)
    at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClass(TypeCachingBytecodeGenerator.java:40)
    at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.createMockType(InlineDelegateByteBuddyMockMaker.java:389)
    at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.doCreateMock(InlineDelegateByteBuddyMockMaker.java:349)
    at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.createMock(InlineDelegateByteBuddyMockMaker.java:328)
    at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createMock(InlineByteBuddyMockMaker.java:56)
    at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:53)
    at org.mockito.internal.MockitoCore.mock(MockitoCore.java:96)
    at org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs.newDeepStubMock(ReturnsDeepStubs.java:115)
    at org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs.deepStub(ReturnsDeepStubs.java:90)
    at org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs.answer(ReturnsDeepStubs.java:74)
    at org.mockito.Answers.answer(Answers.java:90)
    at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:110)
    at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
    at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:34)
    at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:82)
    at org.mockito.internal.creation.bytebuddy.MockMethodAdvice.handle(MockMethodAdvice.java:133)
    ... 46 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
    at net.bytebuddy.description.method.ParameterDescription$ForLoadedParameter$OfConstructor.getDeclaredAnnotations(ParameterDescription.java:505)
    at net.bytebuddy.description.method.ParameterDescription$AbstractBase.asToken(ParameterDescription.java:186)
    at net.bytebuddy.description.method.ParameterDescription$AbstractBase.asToken(ParameterDescription.java:135)
    at net.bytebuddy.description.method.ParameterList$AbstractBase.asTokenList(ParameterList.java:96)
    at net.bytebuddy.description.method.MethodDescription$AbstractBase.asToken(MethodDescription.java:890)
    at net.bytebuddy.description.method.MethodDescription$AbstractBase.asToken(MethodDescription.java:445)
    at net.bytebuddy.description.method.MethodList$AbstractBase.asTokenList(MethodList.java:87)
    at net.bytebuddy.dynamic.scaffold.InstrumentedType$Factory$Default$1.represent(InstrumentedType.java:438)
    at net.bytebuddy.ByteBuddy.redefine(ByteBuddy.java:866)
    at net.bytebuddy.ByteBuddy.redefine(ByteBuddy.java:841)
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.transform(InlineBytecodeGenerator.java:382)
    at java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:246)
    at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
    at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:563)
    at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
    at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:167)
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:276)

Patient zero is this

Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
    at net.bytebuddy.description.method.ParameterDescription$ForLoadedParameter$OfConstructor.getDeclaredAnnotations(ParameterDescription.java:505)

After some small investigation it was found that if @org.springframework.lang.NonNull is present it fails of it is not present everything is ok. In addition I tried to use @NotNull from JetBrains and to my surprise, it is OK no exception, so something wrong only with @org.springframework.lang.NonNull

raphw commented 2 years ago

Could you set a breakpoint in the getDeclaredAnnotations method where the exception is thrown? I assume that the JVM has some sort of mismatch between the annotation array and the amount of parameters.

Have you tried using a more recent JVM? I have fixed a row of issues in the JVM myself to get these annotation issues resolved, but not all of it is backported.