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

ClassFormatError when using sealed classes in Kotlin #638

Closed
lhess opened this issue May 5, 2022 · 2 comments
Closed

ClassFormatError when using sealed classes in Kotlin #638

lhess opened this issue May 5, 2022 · 2 comments

Comments

@lhess
Copy link

lhess commented May 5, 2022

Describe the bug
I am trying to use Kotlin's sealed classes along with equalsverifier which results into a ClassFormatError, see stacktrace below.

To Reproduce
I created a repository that contains the minimum amount of code to reproduce the described issue. It contains an EqualsverifierTest.kt class that can be run with JUnit.

Code that triggers the behavior
Any class that has members that uses kotlin's sealed classes.

Error message

Stacktrace
EqualsVerifier found a problem in class Foo.
-> java.lang.IllegalStateException: Failed to invoke proxy for public abstract java.lang.Class net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup$MethodHandles$Lookup.defineClass(java.lang.Object,byte[]) throws java.lang.IllegalAccessException

For more information, go to: https://www.jqno.nl/equalsverifier/errormessages
java.lang.AssertionError: EqualsVerifier found a problem in class Foo.
-> java.lang.IllegalStateException: Failed to invoke proxy for public abstract java.lang.Class net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup$MethodHandles$Lookup.defineClass(java.lang.Object,byte[]) throws java.lang.IllegalAccessException

For more information, go to: https://www.jqno.nl/equalsverifier/errormessages
	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.verify(SingleTypeEqualsVerifierApi.java:316)
	at SealedClassFailure.sealedClassDemo(SealedClassFailure.kt:8)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	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 jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
	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 worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: java.lang.IllegalStateException: java.lang.IllegalStateException: Failed to invoke proxy for public abstract java.lang.Class net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup$MethodHandles$Lookup.defineClass(java.lang.Object,byte[]) throws java.lang.IllegalAccessException
	at net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup.injectRaw(ClassInjector.java:1639)
	at net.bytebuddy.dynamic.loading.ClassInjector$AbstractBase.inject(ClassInjector.java:117)
	at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$UsingLookup.load(ClassLoadingStrategy.java:505)
	at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:101)
	at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6161)
	at nl.jqno.equalsverifier.internal.reflection.Instantiator.giveDynamicSubclass(Instantiator.java:98)
	at nl.jqno.equalsverifier.internal.reflection.Instantiator.of(Instantiator.java:47)
	at nl.jqno.equalsverifier.internal.reflection.ClassAccessor.buildObjectAccessor(ClassAccessor.java:261)
	at nl.jqno.equalsverifier.internal.reflection.ClassAccessor.getRedAccessor(ClassAccessor.java:179)
	at nl.jqno.equalsverifier.internal.reflection.ClassAccessor.getRedObject(ClassAccessor.java:168)
	at nl.jqno.equalsverifier.internal.prefabvalues.factories.FallbackFactory.giveInstances(FallbackFactory.java:95)
	at nl.jqno.equalsverifier.internal.prefabvalues.factories.FallbackFactory.createValues(FallbackFactory.java:40)
	at nl.jqno.equalsverifier.internal.prefabvalues.PrefabValues.createTuple(PrefabValues.java:157)
	at nl.jqno.equalsverifier.internal.prefabvalues.PrefabValues.realizeCacheFor(PrefabValues.java:140)
	at nl.jqno.equalsverifier.internal.prefabvalues.PrefabValues.giveTuple(PrefabValues.java:81)
	at nl.jqno.equalsverifier.internal.prefabvalues.PrefabValues.giveOther(PrefabValues.java:104)
	at nl.jqno.equalsverifier.internal.reflection.FieldModifier.lambda$changeField$2(FieldModifier.java:97)
	at nl.jqno.equalsverifier.internal.reflection.FieldModifier.wrappedChange(FieldModifier.java:118)
	at nl.jqno.equalsverifier.internal.reflection.FieldModifier.lambda$change$3(FieldModifier.java:113)
	at nl.jqno.equalsverifier.internal.util.Rethrow.lambda$rethrow$0(Rethrow.java:47)
	at nl.jqno.equalsverifier.internal.util.Rethrow.rethrow(Rethrow.java:30)
	at nl.jqno.equalsverifier.internal.util.Rethrow.rethrow(Rethrow.java:45)
	at nl.jqno.equalsverifier.internal.util.Rethrow.rethrow(Rethrow.java:55)
	at nl.jqno.equalsverifier.internal.reflection.FieldModifier.change(FieldModifier.java:113)
	at nl.jqno.equalsverifier.internal.reflection.FieldModifier.changeField(FieldModifier.java:100)
	at nl.jqno.equalsverifier.internal.reflection.InPlaceObjectAccessor.scramble(InPlaceObjectAccessor.java:52)
	at nl.jqno.equalsverifier.internal.reflection.ClassAccessor.getRedAccessor(ClassAccessor.java:179)
	at nl.jqno.equalsverifier.internal.reflection.ClassAccessor.getRedObject(ClassAccessor.java:168)
	at nl.jqno.equalsverifier.internal.util.Configuration.ensureUnequalExamples(Configuration.java:199)
	at nl.jqno.equalsverifier.internal.util.Configuration.build(Configuration.java:99)
	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.buildConfig(SingleTypeEqualsVerifierApi.java:381)
	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.performVerification(SingleTypeEqualsVerifierApi.java:367)
	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.verify(SingleTypeEqualsVerifierApi.java:312)
	... 84 more
Caused by: java.lang.IllegalStateException: Failed to invoke proxy for public abstract java.lang.Class net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup$MethodHandles$Lookup.defineClass(java.lang.Object,byte[]) throws java.lang.IllegalAccessException
	at net.bytebuddy.utility.dispatcher.JavaDispatcher$ProxiedInvocationHandler.invoke(JavaDispatcher.java:1164)
	at jdk.proxy3/jdk.proxy3.$Proxy25.defineClass(Unknown Source)
	at net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup.injectRaw(ClassInjector.java:1637)
	... 116 more
Caused by: java.lang.ClassFormatError: Illegal class name "/Baz$$DynamicSubclass$1336777650" in class file /Baz$$DynamicSubclass$1336777650
	at java.base/java.lang.ClassLoader.defineClass0(Native Method)
	at java.base/java.lang.System$2.defineClass(System.java:2307)
	at java.base/java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClass(MethodHandles.java:2439)
	at java.base/java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClass(MethodHandles.java:2416)
	at java.base/java.lang.invoke.MethodHandles$Lookup.defineClass(MethodHandles.java:1843)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at net.bytebuddy.utility.Invoker$Dispatcher.invoke(Unknown Source)
	at net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher$ForNonStaticMethod.invoke(JavaDispatcher.java:1019)
	at net.bytebuddy.utility.dispatcher.JavaDispatcher$ProxiedInvocationHandler.invoke(JavaDispatcher.java:1149)
	... 118 more

Expected behavior
It should just work as it does with any other type.

Version
v3.10

Additional context
Seems like the root cause is with the dynamic subclassing in particular their names.

@jqno
Copy link
Owner

jqno commented May 5, 2022

Hi! Thanks for taking the time to create the repository, it helps a lot, especially because I have almost no experience with Kotlin. I was able to reproduce the issue, but I'm not sure how to solve it either.

You could try to add prefab values for Baz: add .withPrefabValues(Baz::class.java, ...) to the call to EqualsVerifier. If that helps, I'll see if I can improve the error message to make it more understandable.

If not, we'd have to dig deeper. I agree with you that the name of the dynamic subclass (/Baz$$DynamicSubclass$1336777650) is suspicious. This is how the name is created:

String namePrefix = isSystemClass ? FALLBACK_PACKAGE_NAME : getPackageName(superclass);
String name =
namePrefix +
"." +
superclass.getSimpleName() +
"$$DynamicSubclass$" +
superclass.hashCode();

I don't understand where the slash comes from (and why there's no . in it), but since the first part of the name is the package, and your sealed class doesn't live in a package, maybe you can try putting it in one and see what happens?

If that also doesn't help, we should see if we can run this bit of code from Kotlin:

Class<?> context = isSystemClass ? Instantiator.class : superclass;
ClassLoadingStrategy<? super ClassLoader> cs = getClassLoadingStrategy(context);
return (Class<S>) new ByteBuddy()
.with(TypeValidation.DISABLED)
.subclass(superclass)
.name(name)
.make()
.load(context.getClassLoader(), cs)
.getLoaded();

I can investigate when I have some free time, but since I have little free time these days, and no experience with Kotlin, it might take a little more time than usual. If you're willing to try one or more of these things yourself, it would speed things up :).

@jqno
Copy link
Owner

jqno commented Dec 2, 2022

This is fixed in EqualsVerifier 3.12.1. Turns out it has nothing to do with Kotlin sealed types, but everything with the fact that the sealed class wasn't defined in a package. The same problem occurs when testing a Java class that isn't in a package. You can fix it by moving the class into a package, or just by updating to version 3.12.1 :).

@jqno jqno closed this as completed Dec 2, 2022
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

2 participants