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 psiManager: PsiManager,
|
||||||
private val messageCollector: MessageCollector?,
|
private val messageCollector: MessageCollector?,
|
||||||
private val additionalModules: List<String>,
|
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)
|
val javaModuleGraph = JavaModuleGraph(javaModuleFinder)
|
||||||
|
|
||||||
data class RootsAndModules(val roots: List<JavaRoot>, val modules: List<JavaModule>)
|
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.FileUtil
|
||||||
import com.intellij.openapi.util.io.FileUtilRt
|
import com.intellij.openapi.util.io.FileUtilRt
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
import com.intellij.openapi.vfs.PersistentFSConstants
|
import com.intellij.openapi.vfs.*
|
||||||
import com.intellij.openapi.vfs.VfsUtilCore
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
|
||||||
import com.intellij.openapi.vfs.VirtualFileSystem
|
|
||||||
import com.intellij.openapi.vfs.impl.ZipHandler
|
import com.intellij.openapi.vfs.impl.ZipHandler
|
||||||
import com.intellij.psi.FileContextProvider
|
import com.intellij.psi.FileContextProvider
|
||||||
import com.intellij.psi.PsiElementFinder
|
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.ERROR
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
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.CliScriptDependenciesProvider
|
||||||
|
import org.jetbrains.kotlin.cli.common.script.CliScriptReportSink
|
||||||
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||||
import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker
|
import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.*
|
import org.jetbrains.kotlin.cli.jvm.config.*
|
||||||
import org.jetbrains.kotlin.cli.jvm.index.*
|
import org.jetbrains.kotlin.cli.jvm.index.*
|
||||||
import org.jetbrains.kotlin.cli.jvm.javac.JavacWrapperRegistrar
|
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.CliJavaModuleResolver
|
||||||
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
|
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
|
||||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
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(
|
classpathRootsResolver = ClasspathRootsResolver(
|
||||||
PsiManager.getInstance(project), messageCollector,
|
PsiManager.getInstance(project),
|
||||||
|
messageCollector,
|
||||||
configuration.getList(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES),
|
configuration.getList(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES),
|
||||||
this::contentRootToVirtualFile
|
this::contentRootToVirtualFile,
|
||||||
|
javaModuleFinder
|
||||||
)
|
)
|
||||||
|
|
||||||
val (initialRoots, javaModules) =
|
val (initialRoots, javaModules) =
|
||||||
@@ -232,8 +237,7 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
|
|
||||||
project.registerService(
|
project.registerService(
|
||||||
JavaModuleResolver::class.java,
|
JavaModuleResolver::class.java,
|
||||||
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules,
|
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules, javaModuleFinder.systemModules.toList())
|
||||||
classpathRootsResolver.javaModuleFinder.systemModules.toList())
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val finderFactory = CliVirtualFileFinderFactory(rootsIndex)
|
val finderFactory = CliVirtualFileFinderFactory(rootsIndex)
|
||||||
@@ -441,10 +445,7 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
Extensions.cleanRootArea(parentDisposable)
|
Extensions.cleanRootArea(parentDisposable)
|
||||||
registerAppExtensionPoints()
|
registerAppExtensionPoints()
|
||||||
val applicationEnvironment = object : JavaCoreApplicationEnvironment(parentDisposable) {
|
val applicationEnvironment = object : JavaCoreApplicationEnvironment(parentDisposable) {
|
||||||
override fun createJrtFileSystem(): VirtualFileSystem? {
|
override fun createJrtFileSystem(): VirtualFileSystem? = CoreJrtFileSystem()
|
||||||
val jdkHome = configuration[JVMConfigurationKeys.JDK_HOME] ?: return null
|
|
||||||
return CoreJrtFileSystem.create(jdkHome)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (configPath in configFilePaths) {
|
for (configPath in configFilePaths) {
|
||||||
|
|||||||
@@ -17,13 +17,13 @@
|
|||||||
package org.jetbrains.kotlin.cli.jvm.modules
|
package org.jetbrains.kotlin.cli.jvm.modules
|
||||||
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.openapi.vfs.VirtualFileSystem
|
|
||||||
import com.intellij.psi.PsiJavaModule
|
import com.intellij.psi.PsiJavaModule
|
||||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder
|
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder
|
||||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
|
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>()
|
private val userModules = linkedMapOf<String, JavaModule>()
|
||||||
|
|
||||||
fun addUserModule(module: JavaModule) {
|
fun addUserModule(module: JavaModule) {
|
||||||
@@ -34,11 +34,10 @@ internal class CliJavaModuleFinder(private val jrtFileSystem: VirtualFileSystem?
|
|||||||
get() = systemModules + userModules.values
|
get() = systemModules + userModules.values
|
||||||
|
|
||||||
val systemModules: Sequence<JavaModule.Explicit>
|
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? =
|
override fun findModule(name: String): JavaModule? =
|
||||||
jrtFileSystem?.findFileByPath("/modules/$name")?.let(this::findSystemModule)
|
modulesRoot?.findChild(name)?.let(this::findSystemModule) ?: userModules[name]
|
||||||
?: userModules[name]
|
|
||||||
|
|
||||||
private fun findSystemModule(moduleRoot: VirtualFile): JavaModule.Explicit? {
|
private fun findSystemModule(moduleRoot: VirtualFile): JavaModule.Explicit? {
|
||||||
val file = moduleRoot.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) ?: return null
|
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.DeprecatedVirtualFileSystem
|
||||||
import com.intellij.openapi.vfs.StandardFileSystems
|
import com.intellij.openapi.vfs.StandardFileSystems
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.util.containers.ConcurrentFactoryMap
|
||||||
|
import com.intellij.util.io.URLUtil
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.nio.file.FileSystem
|
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
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
|
// 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() {
|
class CoreJrtFileSystem : DeprecatedVirtualFileSystem() {
|
||||||
override fun getProtocol(): String = StandardFileSystems.JRT_PROTOCOL
|
private val handlers = object : ConcurrentFactoryMap<String, CoreJrtHandler?>() {
|
||||||
|
override fun create(jdkHomePath: String): CoreJrtHandler? {
|
||||||
private fun findFileByPath(path: Path): VirtualFile? =
|
val jdkHome = File(jdkHomePath)
|
||||||
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? {
|
|
||||||
val rootUri = URI.create(StandardFileSystems.JRT_PROTOCOL + ":/")
|
val rootUri = URI.create(StandardFileSystems.JRT_PROTOCOL + ":/")
|
||||||
val jrtFsJar = loadJrtFsJar(jdkHome) ?: return null
|
val jrtFsJar = loadJrtFsJar(jdkHome) ?: return null
|
||||||
val fileSystem =
|
val fileSystem =
|
||||||
@@ -54,9 +44,43 @@ class CoreJrtFileSystem(private val fileSystem: FileSystem) : DeprecatedVirtualF
|
|||||||
val classLoader = URLClassLoader(arrayOf(jrtFsJar.toURI().toURL()), null)
|
val classLoader = URLClassLoader(arrayOf(jrtFsJar.toURI().toURL()), null)
|
||||||
FileSystems.newFileSystem(rootUri, emptyMap<String, Nothing>(), classLoader)
|
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? =
|
private fun loadJrtFsJar(jdkHome: File): File? =
|
||||||
File(jdkHome, "lib/jrt-fs.jar").takeIf(File::exists)
|
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.VfsUtilCore
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.openapi.vfs.VirtualFileSystem
|
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.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
@@ -27,17 +29,17 @@ import java.nio.file.Files
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.attribute.BasicFileAttributes
|
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?
|
// TODO: catch IOException?
|
||||||
private val attributes: BasicFileAttributes get() = Files.readAttributes(path, BasicFileAttributes::class.java)
|
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 =
|
override fun getName(): String =
|
||||||
path.fileName.toString()
|
path.fileName.toString()
|
||||||
|
|
||||||
override fun getPath(): String =
|
override fun getPath(): String =
|
||||||
FileUtil.toSystemIndependentName(path.toString())
|
FileUtil.toSystemIndependentName(handler.jdkHomePath + URLUtil.JAR_SEPARATOR + path)
|
||||||
|
|
||||||
override fun isWritable(): Boolean = false
|
override fun isWritable(): Boolean = false
|
||||||
|
|
||||||
@@ -47,7 +49,7 @@ internal class CoreJrtVirtualFile(private val fileSystem: VirtualFileSystem, pri
|
|||||||
|
|
||||||
override fun getParent(): VirtualFile? {
|
override fun getParent(): VirtualFile? {
|
||||||
val parentPath = path.parent
|
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> {
|
override fun getChildren(): Array<out VirtualFile> {
|
||||||
@@ -59,7 +61,7 @@ internal class CoreJrtVirtualFile(private val fileSystem: VirtualFileSystem, pri
|
|||||||
}
|
}
|
||||||
return when {
|
return when {
|
||||||
paths.isEmpty() -> VirtualFile.EMPTY_ARRAY
|
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