google / ksp

Kotlin Symbol Processing API
https://github.com/google/ksp
Apache License 2.0
2.83k stars 265 forks source link

`IllegalAccessError` in Dagger with KSP2.0.0-1.0.22 and KSP2 #1956

Closed lwasyl closed 2 months ago

lwasyl commented 3 months ago

Stacktrace:

Caused by: java.lang.IllegalAccessError: failed to access class com.google.common.base.NullnessCasts from class com.google.common.base.Suppliers$NonSerializableMemoizingSupplier (com.google.common.base.NullnessCasts is in unnamed module of loader java.net.URLClassLoader @28a2ead8; com.google.common.base.Suppliers$NonSerializableMemoizingSupplier is in unnamed module of loader java.net.URLClassLoader @6cdeb8d1)
        at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers.java:189)
        at dagger.internal.codegen.binding.Binding.dependencies(Binding.java:98)
        at dagger.internal.codegen.binding.SourceFiles.generateBindingFieldsForDependencies(SourceFiles.java:97)
        at dagger.internal.codegen.writing.FactoryGenerator.frameworkFields(FactoryGenerator.java:178)
        at dagger.internal.codegen.writing.FactoryGenerator.constructorParams(FactoryGenerator.java:158)
        at dagger.internal.codegen.writing.FactoryGenerator.addConstructorAndFields(FactoryGenerator.java:146)
        at dagger.internal.codegen.writing.FactoryGenerator.factoryBuilder(FactoryGenerator.java:130)
        at dagger.internal.codegen.writing.FactoryGenerator.topLevelTypes(FactoryGenerator.java:113)
        at dagger.internal.codegen.writing.FactoryGenerator.topLevelTypes(FactoryGenerator.java:82)
        at dagger.internal.codegen.base.SourceFileGenerator.generate(SourceFileGenerator.java:70)
        at dagger.internal.codegen.validation.InjectBindingRegistryImpl$BindingsCollection.generateBindings(InjectBindingRegistryImpl.java:106)
        at dagger.internal.codegen.validation.InjectBindingRegistryImpl.generateSourcesForRequiredBindings(InjectBindingRegistryImpl.java:234)
        at dagger.internal.codegen.DelegateComponentProcessor.postRound(DelegateComponentProcessor.java:127)
        at dagger.internal.codegen.KspComponentProcessor.postRound(KspComponentProcessor.java:68)
        at dagger.spi.internal.shaded.androidx.room.compiler.processing.ksp.KspBasicAnnotationProcessor.process(KspBasicAnnotationProcessor.kt:63)
        at com.google.devtools.ksp.impl.KotlinSymbolProcessing.execute(KotlinSymbolProcessing.kt:533)
        at com.google.devtools.ksp.impl.KSPLoader$Companion.loadAndRunKSP(KSPLoader.kt:36)
        at com.google.devtools.ksp.impl.KSPLoader.loadAndRunKSP(KSPLoader.kt)

Kotlin 2.0.0 + KSP2.0.0-1.0.22 (ksp.useKSP2=true) + Dagger 2.51.1. Feels related to the other issue with classpath, but if it's an incompatibility that should be fixed on the Dagger side then I'll open an issue there.

BraisGabin commented 3 months ago

Feels related to the other issue with classpath

For reference, the other issue is this one #1911

aiyu-ayaan commented 2 months ago
FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':core:kspStudentReleaseKotlin'.
> A failure occurred while executing com.google.devtools.ksp.gradle.KspAAWorkerAction
   > failed to access class com.google.common.base.NullnessCasts from class com.google.common.base.Suppliers$NonSerializableMemoizingSupplier (com.google.common.base.NullnessCasts is in unnamed module of loader java.net.URLClassLoader @1da5a084; com.google.common.base.Suppliers$NonSerializableMemoizingSupplier is in unnamed module of loader java.net.URLClassLoader @6a396bab)

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

BUILD FAILED in 20s
ansman commented 2 months ago

So I did some digging and I found the issue. The problem happens when you have two KSP tasks in the same Gradle project. The tasks use a custom classloader for each run. But since the classes are loaded into the running VM, you end up in trouble.

So for example, say you have an Android project that has debug and release. You'll then have two separate tasks. The issue can happen like this for example:

  1. Task 1 runs
    • It uses classloader ABC
    • It loads class com.google.common.base.NullnessCasts
  2. Task 2 runs
    • It uses classloader XYZ
    • It loads com.google.common.base.Suppliers$NonSerializableMemoizingSupplier
    • com.google.common.base.Suppliers$NonSerializableMemoizingSupplier tries to access com.google.common.base.NullnessCasts

Now the issue happens because com.google.common.base.NullnessCasts is already loaded, but using a different classloader and since com.google.common.base.NullnessCasts is package private the JVM throws the illegal access error.

I'm not sure what the correct solution is. KSP could perhaps try and force the class loader to be GCed and thus unload the classes.

ZacSweers commented 2 months ago

The best solution here would be for KSP to shade its guava use, as it doesn't need to be a part of the public API anyway I think?

ansman commented 2 months ago

They already do. It's Daggers Guava that conflicts with Rooms guava I believe. But it's worth noting that it can happen with any dependency of course

ZacSweers commented 2 months ago

I'm not sure how that can be the case? I don't see the shading in those package names

ansman commented 2 months ago

https://github.com/google/ksp/blob/main/symbol-processing-aa-embeddable/build.gradle.kts#L40

ansman commented 2 months ago

I made a special build of KSP that logged the classloaders so I've confirmed that it's not KSP's guava that is causing issues, it's one from a processor

ZacSweers commented 2 months ago

Ahh, dagger should be shading then?

ansman commented 2 months ago

Dagger shading it would be an option, but other libraries can have issues too. Ideally this should be solved on the KSP side I think. If they could unload the CL and thus unload the classes that would be ideal I think. I don't know if there are other ways of isolating the VM that doesn't include forking.

ZacSweers commented 2 months ago

true, and dagger does expose guava in its public APIs in some places so it probably can't shade

ansman commented 2 months ago

The way I solved it was by removing ksp.useKSP2=true from my gradle.properties file. I hope this helps you.

That’s not solving it, that is disabling KSP2 thus circumventing the issue

ting-yuan commented 2 months ago

This is likely caused by the conflict between the bundled Guava in KSP and the one pulled as the dependency of Dagger. The KSP artifact is going to be changed from symbol-processing-aa to symbol-processing-aa-embeddable in 1.0.24. The latter renames all bundled dependencies to avoid conflicts.

Please reopen if KSP 1.0.24 doesn't work for you.