JpsUtils.isJps: fix NoClassDefFoundError
This commit fixes:
Caused by: java.lang.NoClassDefFoundError: com/intellij/openapi/components/ComponentManager
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:555)
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:458)
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:452)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:451)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3166)
at java.base/java.lang.Class.getMethodsRecursive(Class.java:3307)
at java.base/java.lang.Class.getMethod0(Class.java:3293)
at java.base/java.lang.Class.getMethod(Class.java:2106)
at org.jetbrains.kotlin.config.JpsUtilsKt$isJps$2.invoke(JpsUtils.kt:17)
In c1f2d66ed8 I replaced
`ApplicationManager.getApplication() == null` with
`Class.forName("ApplicationManager").getMethod("getApplication").invoke(null)`.
It turned out that those two are not equivalent.
`getMethod` tries to match queried signatures with existing signatures
=> it tries to load classes in signatures => it tries to load
`com.intellij.openapi.application.Application` (and fails because
`Application` supertype isn't in the classpath).
Contrary, `ApplicationManager.getApplication() == null` doesn't try to
load `com.intellij.openapi.application.Application` class, so it doesn't
fail.
I use MethodHandles API because it forces me to write `Class.forName`
myself (compared to implicit `Class.forName("Application")` inside
`getMethod`) which I find a nice safety feature.
This commit is contained in:
@@ -5,22 +5,34 @@
|
||||
|
||||
package org.jetbrains.kotlin.config
|
||||
|
||||
import java.lang.invoke.MethodHandles
|
||||
import java.lang.invoke.MethodType
|
||||
|
||||
const val APPLICATION_MANAGER_CLASS_NAME = "com.intellij.openapi.application.ApplicationManager"
|
||||
|
||||
val isJps: Boolean by lazy {
|
||||
val application = try {
|
||||
Class.forName("com.intellij.openapi.application.Application")
|
||||
} catch (ex: LinkageError) {
|
||||
// If any Application super class is not in the classpath
|
||||
return@lazy true
|
||||
} catch (ex: ClassNotFoundException) {
|
||||
return@lazy true
|
||||
}
|
||||
val applicationManager = try {
|
||||
Class.forName(APPLICATION_MANAGER_CLASS_NAME)
|
||||
} catch (ex: LinkageError) {
|
||||
// If any ApplicationManager super class is not in the classpath
|
||||
return@lazy true
|
||||
} catch (ex: ClassNotFoundException) {
|
||||
return@lazy true
|
||||
}
|
||||
/*
|
||||
Normally, JPS shouldn't have an ApplicationManager class in the classpath,
|
||||
but that's not true for JPS inside IDEA right now.
|
||||
Though Application is not properly initialized inside JPS so we can use it as a check.
|
||||
*/
|
||||
return@lazy if (doesClassExist(APPLICATION_MANAGER_CLASS_NAME)) {
|
||||
Class.forName(APPLICATION_MANAGER_CLASS_NAME).getMethod("getApplication").invoke(null) == null
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun doesClassExist(fqName: String): Boolean {
|
||||
val classPath = fqName.replace('.', '/') + ".class"
|
||||
return {}.javaClass.classLoader.getResource(classPath) != null
|
||||
return@lazy MethodHandles.lookup()
|
||||
.findStatic(applicationManager, "getApplication", MethodType.methodType(application))
|
||||
.invoke() == null
|
||||
}
|
||||
Reference in New Issue
Block a user