Introduce JavaModule, refactor module graph construction

#KT-18598 In Progress
 #KT-18599 In Progress
This commit is contained in:
Alexander Udalov
2017-04-10 18:10:29 +03:00
parent 4f914fafca
commit 2275068c94
7 changed files with 228 additions and 48 deletions
@@ -19,9 +19,13 @@ package org.jetbrains.kotlin.cli.jvm.compiler
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiJavaModule
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageUtil
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot
@@ -30,10 +34,12 @@ import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder
import org.jetbrains.kotlin.config.ContentRoot
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.isValidJavaFqName
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleGraph
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
internal class ClasspathRootsResolver(
private val psiManager: PsiManager,
private val messageCollector: MessageCollector?,
private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?
) {
@@ -43,18 +49,26 @@ internal class ClasspathRootsResolver(
fun convertClasspathRoots(contentRoots: Iterable<ContentRoot>): List<JavaRoot> {
val result = mutableListOf<JavaRoot>()
val modules = ArrayList<JavaModule>()
for (contentRoot in contentRoots) {
if (contentRoot !is JvmContentRoot) continue
val root = contentRootToVirtualFile(contentRoot) ?: continue
when (contentRoot) {
is JavaSourceRoot -> {
result += JavaRoot(root, JavaRoot.RootType.SOURCE, contentRoot.packagePrefix?.let { prefix ->
if (isValidJavaFqName(prefix)) FqName(prefix)
else null.also {
report(STRONG_WARNING, "Invalid package prefix name is ignored: $prefix")
}
})
val modularRoot = modularSourceRoot(root)
if (modularRoot != null) {
modules += modularRoot
}
else {
result += JavaRoot(root, JavaRoot.RootType.SOURCE, contentRoot.packagePrefix?.let { prefix ->
if (isValidJavaFqName(prefix)) FqName(prefix)
else null.also {
report(STRONG_WARNING, "Invalid package prefix name is ignored: $prefix")
}
})
}
}
is JvmClasspathRoot -> {
result += JavaRoot(root, JavaRoot.RootType.BINARY)
@@ -63,43 +77,78 @@ internal class ClasspathRootsResolver(
}
}
addModularRoots(result)
addModularRoots(modules, result)
return result
}
private fun addModularRoots(result: MutableList<JavaRoot>) {
val jrtFileSystem = javaModuleFinder.jrtFileSystem ?: return
private fun modularSourceRoot(root: VirtualFile): JavaModule.Explicit? {
val moduleInfoFile =
when {
root.isDirectory -> root.findChild(PsiJavaModule.MODULE_INFO_FILE)
root.name == PsiJavaModule.MODULE_INFO_FILE -> root
else -> null
} ?: return null
val rootModules = computeRootModules()
val allDependencies = javaModuleGraph.getAllDependencies(rootModules).also { modules ->
report(LOGGING, "Loading modules: $modules")
val psiFile = psiManager.findFile(moduleInfoFile) ?: return null
val psiJavaModule = psiFile.children.singleOrNull { it is PsiJavaModule } as? PsiJavaModule ?: return null
return JavaModule.Explicit(JavaModuleInfo.create(psiJavaModule), root, moduleInfoFile, isBinary = false)
}
private fun addModularRoots(modules: List<JavaModule>, result: MutableList<JavaRoot>) {
val sourceModules = modules.filterIsInstance<JavaModule.Explicit>().filterNot(JavaModule::isBinary)
if (sourceModules.size > 1) {
for (module in sourceModules) {
report(ERROR, "Too many source module declarations found", module.moduleInfoFile)
}
return
}
for (module in modules) {
// TODO: report a diagnostic if a module with this name was already added
javaModuleFinder.addUserModule(module)
}
if (javaModuleFinder.allObservableModules.none()) return
val rootModules =
if (sourceModules.isNotEmpty()) {
// TODO: support an option similar to --add-modules
listOf(sourceModules.single().name)
}
else computeDefaultRootModules() // TODO: + everything from module path
// TODO: if at least one automatic module is added, add all automatic modules as per java.lang.module javadoc
val allDependencies = javaModuleGraph.getAllDependencies(rootModules).also { loadedModules ->
report(LOGGING, "Loading modules: $loadedModules")
}
for (moduleName in allDependencies) {
// TODO: support modules not only from Java runtime image, but from a separate module path
val root = jrtFileSystem.findFileByPath("/modules/$moduleName")
if (root == null) {
val module = javaModuleFinder.findModule(moduleName)
if (module == null) {
report(ERROR, "Module $moduleName cannot be found in the module graph")
}
else {
result.add(JavaRoot(root, JavaRoot.RootType.BINARY))
result.add(JavaRoot(
module.moduleRoot,
if (module.isBinary) JavaRoot.RootType.BINARY else JavaRoot.RootType.SOURCE
))
}
}
}
// See http://openjdk.java.net/jeps/261
private fun computeRootModules(): List<String> {
private fun computeDefaultRootModules(): List<String> {
val result = arrayListOf<String>()
val systemModules = javaModuleFinder.computeAllSystemModules()
val systemModules = javaModuleFinder.systemModules.associateBy(JavaModule::name)
val javaSeExists = "java.se" in systemModules
if (javaSeExists) {
// The java.se module is a root, if it exists.
result.add("java.se")
}
fun JavaModuleInfo.exportsAtLeastOnePackageUnqualified(): Boolean = exports.any { it.toModules.isEmpty() }
fun JavaModule.Explicit.exportsAtLeastOnePackageUnqualified(): Boolean = moduleInfo.exports.any { it.toModules.isEmpty() }
if (!javaSeExists) {
// If it does not exist then every java.* module on the upgrade module path or among the system modules
@@ -122,10 +171,13 @@ internal class ClasspathRootsResolver(
return result
}
private fun report(severity: CompilerMessageSeverity, message: String) {
private fun report(severity: CompilerMessageSeverity, message: String, file: VirtualFile? = null) {
if (messageCollector == null) {
throw IllegalStateException("$severity: $message (no MessageCollector configured)")
throw IllegalStateException("${if (file != null) file.path + ":" else ""}$severity: $message (no MessageCollector configured)")
}
messageCollector.report(severity, message)
messageCollector.report(
severity, message,
if (file == null) null else CompilerMessageLocation.create(MessageUtil.virtualFileToPath(file))
)
}
}
@@ -49,6 +49,7 @@ import com.intellij.openapi.vfs.VirtualFileSystem
import com.intellij.openapi.vfs.impl.ZipHandler
import com.intellij.psi.FileContextProvider
import com.intellij.psi.PsiElementFinder
import com.intellij.psi.PsiManager
import com.intellij.psi.augment.PsiAugmentProvider
import com.intellij.psi.augment.TypeAnnotationModifier
import com.intellij.psi.compiled.ClassFileDecompilers
@@ -191,7 +192,7 @@ class KotlinCoreEnvironment private constructor(
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
classpathRootsResolver = ClasspathRootsResolver(messageCollector, this::contentRootToVirtualFile)
classpathRootsResolver = ClasspathRootsResolver(PsiManager.getInstance(project), messageCollector, this::contentRootToVirtualFile)
initialRoots = classpathRootsResolver.convertClasspathRoots(configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS))
@@ -16,27 +16,38 @@
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(val jrtFileSystem: VirtualFileSystem?) : JavaModuleFinder {
internal fun computeAllSystemModules(): Map<String, JavaModuleInfo> {
return jrtFileSystem?.findFileByPath("/modules")?.children.orEmpty()
.mapNotNull { root -> root.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) }
.mapNotNull((JavaModuleInfo)::read)
.associateBy { moduleInfo -> moduleInfo.moduleName }
internal class CliJavaModuleFinder(private val jrtFileSystem: VirtualFileSystem?) : JavaModuleFinder {
private val userModules = linkedMapOf<String, JavaModule>()
/**
* @return true if the module was added successfully, false otherwise
*/
fun addUserModule(module: JavaModule): Boolean {
if (module.name in userModules) return false
userModules[module.name] = module
return true
}
override fun findModule(name: String): JavaModuleInfo? {
val file = jrtFileSystem?.findFileByPath("/modules/$name/${PsiJavaModule.MODULE_INFO_CLS_FILE}")
if (file != null) {
val moduleInfo = JavaModuleInfo.read(file)
if (moduleInfo != null) {
return moduleInfo
}
}
return null
val allObservableModules: Sequence<JavaModule>
get() = systemModules + userModules.values
val systemModules: Sequence<JavaModule.Explicit>
get() = jrtFileSystem?.findFileByPath("/modules")?.children.orEmpty().asSequence().mapNotNull(this::findSystemModule)
override fun findModule(name: String): JavaModule? =
jrtFileSystem?.findFileByPath("/modules/$name")?.let(this::findSystemModule)
?: userModules[name]
private fun findSystemModule(moduleRoot: VirtualFile): JavaModule.Explicit? {
val file = moduleRoot.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) ?: return null
val moduleInfo = JavaModuleInfo.read(file) ?: return null
return JavaModule.Explicit(moduleInfo, moduleRoot, file, isBinary = true)
}
}