diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt index 95e4f3ca703..757edcd7a90 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt @@ -50,9 +50,9 @@ internal class ClasspathRootsResolver( private val psiManager: PsiManager, private val messageCollector: MessageCollector?, private val additionalModules: List, - 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, val modules: List) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt index d0b476abf08..3e6bd7c793f 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt @@ -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) { diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleFinder.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleFinder.kt index 88348732f6f..2c2f8a81834 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleFinder.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleFinder.kt @@ -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() fun addUserModule(module: JavaModule) { @@ -34,11 +34,10 @@ internal class CliJavaModuleFinder(private val jrtFileSystem: VirtualFileSystem? get() = systemModules + userModules.values val systemModules: Sequence - 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 diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CoreJrtFileSystem.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CoreJrtFileSystem.kt index 9fc2039ef54..fceed586d61 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CoreJrtFileSystem.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CoreJrtFileSystem.kt @@ -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() { + 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(), 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 { + 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) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CoreJrtVirtualFile.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CoreJrtVirtualFile.kt index 4cdf15bec68..6ec10452e11 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CoreJrtVirtualFile.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CoreJrtVirtualFile.kt @@ -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 { @@ -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() } }