Include JDK home path into virtual file paths in CoreJrtFileSystem
The problem in KT-19833 is caused by the fact that the application environment instance is shared between consecutive runs of the compiler in one Make action (KotlinCoreEnvironment.ourApplicationEnvironment). If the JDK 8 module is compiled first, the created application environment has no JRT file system, and once the JDK 9 module is compiled later, that environment is not recreated and thus classes from JDK 9 are unresolved. To mitigate this, split the CoreJrtFileSystem implementation into the file system itself which is global per application, and CoreJrtHandler which is bound to a particular JDK home location. CoreJrtVirtualFile paths now consist of the path to the JDK home, the "!/" separator, and the path to the file itself, e.g. "/usr/lib/jvm/java9!/modules/java.base/java/lang/Object.class". The implementation is inspired by CoreJarFileSystem & CoreJarHandler. No tests added because the application environment is _not_ shared in tests. Also, a JDK 9 module is going to be added to the Kotlin project soon, and that will serve as a test #KT-19833 Fixed
This commit is contained in:
@@ -50,9 +50,9 @@ internal class ClasspathRootsResolver(
|
||||
private val psiManager: PsiManager,
|
||||
private val messageCollector: MessageCollector?,
|
||||
private val additionalModules: List<String>,
|
||||
private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?
|
||||
private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?,
|
||||
private val javaModuleFinder: CliJavaModuleFinder
|
||||
) {
|
||||
val javaModuleFinder = CliJavaModuleFinder(VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL))
|
||||
val javaModuleGraph = JavaModuleGraph(javaModuleFinder)
|
||||
|
||||
data class RootsAndModules(val roots: List<JavaRoot>, val modules: List<JavaModule>)
|
||||
|
||||
@@ -42,10 +42,7 @@ import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.PersistentFSConstants
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileSystem
|
||||
import com.intellij.openapi.vfs.*
|
||||
import com.intellij.openapi.vfs.impl.ZipHandler
|
||||
import com.intellij.psi.FileContextProvider
|
||||
import com.intellij.psi.PsiElementFinder
|
||||
@@ -74,13 +71,14 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.script.CliScriptReportSink
|
||||
import org.jetbrains.kotlin.cli.common.script.CliScriptDependenciesProvider
|
||||
import org.jetbrains.kotlin.cli.common.script.CliScriptReportSink
|
||||
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||
import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker
|
||||
import org.jetbrains.kotlin.cli.jvm.config.*
|
||||
import org.jetbrains.kotlin.cli.jvm.index.*
|
||||
import org.jetbrains.kotlin.cli.jvm.javac.JavacWrapperRegistrar
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleResolver
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||
@@ -197,10 +195,17 @@ class KotlinCoreEnvironment private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
val jdkHome = configuration.get(JVMConfigurationKeys.JDK_HOME)
|
||||
val jrtFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL)
|
||||
val javaModuleFinder = CliJavaModuleFinder(jdkHome?.path?.let { path ->
|
||||
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
|
||||
})
|
||||
classpathRootsResolver = ClasspathRootsResolver(
|
||||
PsiManager.getInstance(project), messageCollector,
|
||||
PsiManager.getInstance(project),
|
||||
messageCollector,
|
||||
configuration.getList(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES),
|
||||
this::contentRootToVirtualFile
|
||||
this::contentRootToVirtualFile,
|
||||
javaModuleFinder
|
||||
)
|
||||
|
||||
val (initialRoots, javaModules) =
|
||||
@@ -232,8 +237,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
|
||||
project.registerService(
|
||||
JavaModuleResolver::class.java,
|
||||
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules,
|
||||
classpathRootsResolver.javaModuleFinder.systemModules.toList())
|
||||
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules, javaModuleFinder.systemModules.toList())
|
||||
)
|
||||
|
||||
val finderFactory = CliVirtualFileFinderFactory(rootsIndex)
|
||||
@@ -441,10 +445,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
Extensions.cleanRootArea(parentDisposable)
|
||||
registerAppExtensionPoints()
|
||||
val applicationEnvironment = object : JavaCoreApplicationEnvironment(parentDisposable) {
|
||||
override fun createJrtFileSystem(): VirtualFileSystem? {
|
||||
val jdkHome = configuration[JVMConfigurationKeys.JDK_HOME] ?: return null
|
||||
return CoreJrtFileSystem.create(jdkHome)
|
||||
}
|
||||
override fun createJrtFileSystem(): VirtualFileSystem? = CoreJrtFileSystem()
|
||||
}
|
||||
|
||||
for (configPath in configFilePaths) {
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
package org.jetbrains.kotlin.cli.jvm.modules
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileSystem
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
|
||||
|
||||
internal class CliJavaModuleFinder(private val jrtFileSystem: VirtualFileSystem?) : JavaModuleFinder {
|
||||
internal class CliJavaModuleFinder(jrtFileSystemRoot: VirtualFile?) : JavaModuleFinder {
|
||||
private val modulesRoot = jrtFileSystemRoot?.findChild("modules")
|
||||
private val userModules = linkedMapOf<String, JavaModule>()
|
||||
|
||||
fun addUserModule(module: JavaModule) {
|
||||
@@ -34,11 +34,10 @@ internal class CliJavaModuleFinder(private val jrtFileSystem: VirtualFileSystem?
|
||||
get() = systemModules + userModules.values
|
||||
|
||||
val systemModules: Sequence<JavaModule.Explicit>
|
||||
get() = jrtFileSystem?.findFileByPath("/modules")?.children.orEmpty().asSequence().mapNotNull(this::findSystemModule)
|
||||
get() = modulesRoot?.children.orEmpty().asSequence().mapNotNull(this::findSystemModule)
|
||||
|
||||
override fun findModule(name: String): JavaModule? =
|
||||
jrtFileSystem?.findFileByPath("/modules/$name")?.let(this::findSystemModule)
|
||||
?: userModules[name]
|
||||
modulesRoot?.findChild(name)?.let(this::findSystemModule) ?: userModules[name]
|
||||
|
||||
private fun findSystemModule(moduleRoot: VirtualFile): JavaModule.Explicit? {
|
||||
val file = moduleRoot.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) ?: return null
|
||||
|
||||
@@ -20,30 +20,20 @@ import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.vfs.DeprecatedVirtualFileSystem
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.containers.ConcurrentFactoryMap
|
||||
import com.intellij.util.io.URLUtil
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.net.URLClassLoader
|
||||
import java.nio.file.FileSystem
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
// There's JrtFileSystem in idea-full which we can't use in the compiler because it depends on NewVirtualFileSystem, absent in intellij-core
|
||||
class CoreJrtFileSystem(private val fileSystem: FileSystem) : DeprecatedVirtualFileSystem() {
|
||||
override fun getProtocol(): String = StandardFileSystems.JRT_PROTOCOL
|
||||
|
||||
private fun findFileByPath(path: Path): VirtualFile? =
|
||||
if (Files.exists(path)) CoreJrtVirtualFile(this, path) else null
|
||||
|
||||
override fun findFileByPath(path: String): VirtualFile? =
|
||||
findFileByPath(fileSystem.getPath(path))
|
||||
|
||||
override fun refresh(asynchronous: Boolean) {}
|
||||
|
||||
override fun refreshAndFindFileByPath(path: String): VirtualFile? = findFileByPath(path)
|
||||
|
||||
companion object {
|
||||
fun create(jdkHome: File): CoreJrtFileSystem? {
|
||||
class CoreJrtFileSystem : DeprecatedVirtualFileSystem() {
|
||||
private val handlers = object : ConcurrentFactoryMap<String, CoreJrtHandler?>() {
|
||||
override fun create(jdkHomePath: String): CoreJrtHandler? {
|
||||
val jdkHome = File(jdkHomePath)
|
||||
val rootUri = URI.create(StandardFileSystems.JRT_PROTOCOL + ":/")
|
||||
val jrtFsJar = loadJrtFsJar(jdkHome) ?: return null
|
||||
val fileSystem =
|
||||
@@ -54,9 +44,43 @@ class CoreJrtFileSystem(private val fileSystem: FileSystem) : DeprecatedVirtualF
|
||||
val classLoader = URLClassLoader(arrayOf(jrtFsJar.toURI().toURL()), null)
|
||||
FileSystems.newFileSystem(rootUri, emptyMap<String, Nothing>(), classLoader)
|
||||
}
|
||||
return CoreJrtFileSystem(fileSystem)
|
||||
return CoreJrtHandler(this@CoreJrtFileSystem, jdkHomePath, fileSystem.getPath(""))
|
||||
}
|
||||
}
|
||||
|
||||
internal class CoreJrtHandler(
|
||||
val virtualFileSystem: CoreJrtFileSystem,
|
||||
val jdkHomePath: String,
|
||||
private val root: Path
|
||||
) {
|
||||
fun findFile(fileName: String): VirtualFile? {
|
||||
val path = root.resolve(fileName)
|
||||
return if (Files.exists(path)) CoreJrtVirtualFile(this, path) else null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getProtocol(): String = StandardFileSystems.JRT_PROTOCOL
|
||||
|
||||
override fun findFileByPath(path: String): VirtualFile? {
|
||||
val (jdkHomePath, pathInImage) = splitPath(path)
|
||||
return handlers[jdkHomePath]?.findFile(pathInImage)
|
||||
}
|
||||
|
||||
private fun splitPath(path: String): Pair<String, String> {
|
||||
val separator = path.indexOf(URLUtil.JAR_SEPARATOR)
|
||||
if (separator < 0) {
|
||||
throw IllegalArgumentException("Path in CoreJrtFileSystem must contain a separator: $path")
|
||||
}
|
||||
val localPath = path.substring(0, separator)
|
||||
val pathInJar = path.substring(separator + 2)
|
||||
return Pair(localPath, pathInJar)
|
||||
}
|
||||
|
||||
override fun refresh(asynchronous: Boolean) {}
|
||||
|
||||
override fun refreshAndFindFileByPath(path: String): VirtualFile? = findFileByPath(path)
|
||||
|
||||
companion object {
|
||||
private fun loadJrtFsJar(jdkHome: File): File? =
|
||||
File(jdkHome, "lib/jrt-fs.jar").takeIf(File::exists)
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileSystem
|
||||
import com.intellij.util.io.URLUtil
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem.CoreJrtHandler
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
@@ -27,17 +29,17 @@ import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.attribute.BasicFileAttributes
|
||||
|
||||
internal class CoreJrtVirtualFile(private val fileSystem: VirtualFileSystem, private val path: Path) : VirtualFile() {
|
||||
internal class CoreJrtVirtualFile(private val handler: CoreJrtHandler, private val path: Path) : VirtualFile() {
|
||||
// TODO: catch IOException?
|
||||
private val attributes: BasicFileAttributes get() = Files.readAttributes(path, BasicFileAttributes::class.java)
|
||||
|
||||
override fun getFileSystem(): VirtualFileSystem = fileSystem
|
||||
override fun getFileSystem(): VirtualFileSystem = handler.virtualFileSystem
|
||||
|
||||
override fun getName(): String =
|
||||
path.fileName.toString()
|
||||
|
||||
override fun getPath(): String =
|
||||
FileUtil.toSystemIndependentName(path.toString())
|
||||
FileUtil.toSystemIndependentName(handler.jdkHomePath + URLUtil.JAR_SEPARATOR + path)
|
||||
|
||||
override fun isWritable(): Boolean = false
|
||||
|
||||
@@ -47,7 +49,7 @@ internal class CoreJrtVirtualFile(private val fileSystem: VirtualFileSystem, pri
|
||||
|
||||
override fun getParent(): VirtualFile? {
|
||||
val parentPath = path.parent
|
||||
return if (parentPath != null) CoreJrtVirtualFile(fileSystem, parentPath) else null
|
||||
return if (parentPath != null) CoreJrtVirtualFile(handler, parentPath) else null
|
||||
}
|
||||
|
||||
override fun getChildren(): Array<out VirtualFile> {
|
||||
@@ -59,7 +61,7 @@ internal class CoreJrtVirtualFile(private val fileSystem: VirtualFileSystem, pri
|
||||
}
|
||||
return when {
|
||||
paths.isEmpty() -> VirtualFile.EMPTY_ARRAY
|
||||
else -> paths.map { path -> CoreJrtVirtualFile(fileSystem, path) }.toTypedArray()
|
||||
else -> paths.map { path -> CoreJrtVirtualFile(handler, path) }.toTypedArray()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user