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

Bug: ClassNotFoundException, but it does exist in the project #1038

Open
juliocbcotta opened this issue Jul 9, 2022 · 19 comments
Open

Bug: ClassNotFoundException, but it does exist in the project #1038

juliocbcotta opened this issue Jul 9, 2022 · 19 comments

Comments

@juliocbcotta
Copy link

Hello there, I am trying to read my annotation using

classDeclaration.getAnnotationsByType(ActivityReference::class).first()

and getting this error

Caused by: com.google.devtools.ksp.KSTypeNotPresentException: java.lang.ClassNotFoundException: com.veepee.vpcore.route.BarImpl

This is a bit odd since the full qualified name in the exception seems to correct.

I tried this, but I still got the same error.

A project to reproduce it is attached here

To my specific needs I don't need to instantiate the annotation, but to get the String with the qualified name for ActivityReference.activityName, would it be possible with KSP ?

@juliocbcotta
Copy link
Author

Ahhh, it seems that one MR to fix this already exists, #951

Sorry for the issue report.

@juliocbcotta
Copy link
Author

Even after the merge of #1039 the code in my sample does not work well.

@juliocbcotta
Copy link
Author

here we have an updated project using a local build

./gradlew -PkspVersion=1.7.20-dev-2726-1.0.7-snapshot publishToMavenLocal

and the build.gradle pointing to it

kotlin.code.style=official

kotlinVersion=1.7.20-dev-2726
kspVersion=1.7.20-dev-2726-1.0.7-snapshot

ksp-problem.zip

@nima-flipp
Copy link

Did you resolve it?

@juliocbcotta
Copy link
Author

No. I am still waiting the ksp developers to take a look at the sample project. Cc: @neetopia

@neetopia
Copy link
Collaborator

The problem with your project is that you are trying to get BarImpl which is declared in the source code of the workload src, therefore at KSP's runtime, BarImpl is not present on the classpath. Can you try to avoid using reflection if any of the KClass you want to access is not on classpath?

@brizzbuzz
Copy link

Is this a planned feature for the future? IMO there are totally valid use cases would be very convenient to reflect on elements in the classpath while using KSP.

My particular use case is injecting permission policies defined by the developer into generated code.

@neetopia
Copy link
Collaborator

As I said, it is more like a limit of reflection itself (class has to be on classpath), is it possible to avoid using reflection for your use case? Or alternative way like having some interfaces in your library and reflect on the interface instead of the actual implementation generated?

@brizzbuzz
Copy link

Not really, in this case I have an interface, which is implemented
by the end user. I would like the generated code to be able to introspect that implementation.

But it sounds like I will need to rearchitect that, since it's a limitation of the language more than anything.

@neetopia
Copy link
Collaborator

By inspection do you mean generated code will need some logic that looks into the implementation provided by end users? It still sounds to me that reflection is not necessary, unless you need to access the object of the implementation at processing time. KSP already offers inspection into source code and generated code via KSP APIs, but if your use case is beyond the inspection provided by KSP, it might not work.

@brizzbuzz
Copy link

personally, i don't need to access the object, I just need to be able yoink the fully qualified name, but the processor errors out when trying to access any information of the class

@juliocbcotta
Copy link
Author

Me too, I just need to class name, I think this is super odd, because the ClassNotFoundException message that we receive contains the class name.

@JailedBird
Copy link

JailedBird commented Dec 15, 2023

This is a deprecated answer and a better way for this issue: #1038 (comment)

Me too, but I found that I could get the corresponding class qualifiedName from the Exception, howerve It's not a perfect solution;

import kotlin.reflect.KClass

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
annotation class AutoService(val target: KClass<*> = Unit::class)
val spi: AutoService = element.findAnnotationWithType()
  

val targetInterfaceClassName = try {
    spi.target.qualifiedName.toString()
} catch (e: Exception) {
    /**
     * Bug: ksp: com.google.devtools.ksp.KSTypeNotPresentException: java.lang.ClassNotFoundException: cn.jailedbird.arouter.ksp.test.TestInterface
     * Official document: https://github.com/google/ksp/issues?q=ClassNotFoundException++KClass%3C*%3E
     * temporary fix method as follows, but it is not perfect!!!
     * TODO completely fix it!
     * */
    ((e as? KSTypeNotPresentException)?.cause as? ClassNotFoundException)?.message.toString()
}

KSTypeNotPresentException is thrown from here

@KspExperimental
private fun KSType.asClass(proxyClass: Class<*>) = try {
    Class.forName(this.declaration.qualifiedName!!.asString(), true, proxyClass.classLoader)
} catch (e: Exception) {
    throw KSTypeNotPresentException(this, e)
}

@yuriisurzhykov
Copy link

Is there any updates for this issue?
I'm having the same problem even for Any::class, when trying to get KClass from annotation property, it throws exception.
My KSP version is: 1.9.21-1.0.15

@JailedBird
Copy link

JailedBird commented Feb 22, 2024

Recently, I adapted KSP for WMRouter project. During the adaptation process, I found a more suitable method to solve issue1038 (at least I think so)

We should adopt the solution proposed by bennyhuo issue694 to solve the problem of class parsing;

The operations of parsing the class object in the annotation and obtaining the fully qualified name of its class can be divided into the following types:

1、 Class[]:

Sample project:JailedBird/WMRouterKspCompiler

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface RouterService {
    Class[] interfaces();
}

solution:

val interfaceNames = WMRouterHelper.parseAnnotationClassParameter { service.interfaces.asList() }

fun parseAnnotationClassParameter(block: () -> List<KClass<*>>): List<String> {
    return try { // KSTypesNotPresentException will be thrown
        block.invoke().mapNotNull { it.qualifiedName }
    } catch (e: KSTypesNotPresentException) {
        val res = mutableListOf<String>()
        val ksTypes = e.ksTypes
        for (ksType in ksTypes) {
            val declaration = ksType.declaration
            if (declaration is KSClassDeclaration) {
                declaration.qualifiedName?.asString()?.let {
                    res.add(it)
                }
            }
        }
        res
    }
}

2、 Class (A little different from Class[])

Sample project: JailedBird/AutoService

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
annotation class AutoService(val target: KClass<*> = Unit::class)

solution:

val targetInterfaceClassName = parseAnnotationClassParameter { spi.target }

@OptIn(KspExperimental::class)
fun parseAnnotationClassParameter(block: () -> KClass<*>): String? {
    return try { // KSTypeNotPresentException will be thrown
        block.invoke().qualifiedName
    } catch (e: KSTypeNotPresentException) {
        var res: String? = null
        val declaration = e.ksType.declaration
        if (declaration is KSClassDeclaration) {
            declaration.qualifiedName?.asString()?.let {
                res = it
            }
        }
        res
    }
}

Maybe help🤞

@JailedBird
Copy link

Is there any updates for this issue? I'm having the same problem even for Any::class, when trying to get KClass from annotation property, it throws exception. My KSP version is: 1.9.21-1.0.15

Perhaps you can try this method:#1038 (comment)

@juliocbcotta
Copy link
Author

This looks like a work around, not a fix or a reliable solution.

@yuriisurzhykov
Copy link

Is there any updates for this issue? I'm having the same problem even for Any::class, when trying to get KClass from annotation property, it throws exception. My KSP version is: 1.9.21-1.0.15

Perhaps you can try this method:#1038 (comment)

@JailedBird Great explanation, thank you! At least this work around helped me!

@yuriisurzhykov
Copy link

In addition, here is adapted solution to work with KotlinPoet:

@OptIn(KspExperimental::class)
fun parseAnnotationClassParameter(block: () -> List<KClass<*>>): List<ClassName> {
    return try {
        block.invoke().map { ClassName(it.java.`package`.name, it.simpleName.orEmpty()) }
    } catch (e: KSTypesNotPresentException) {
        val res = mutableListOf<ClassName>()
        val ksTypes = e.ksTypes
        for (ksType in ksTypes) {
            val declaration = ksType.declaration
            if (declaration is KSClassDeclaration) {
                res.add(declaration.toClassName())
            }
        }
        res
    }
}

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

6 participants