Remove unneeded bunch files related to PsiJavaModule

PSI for modules and related classes are already available in AS3.1
This commit is contained in:
Alexander Udalov
2018-08-08 12:33:44 +02:00
parent 6c8e30eb05
commit cd12772bc5
10 changed files with 0 additions and 2462 deletions
@@ -1,332 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.compiler
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiJavaModule
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.light.LightJavaModule
import org.jetbrains.kotlin.cli.common.config.ContentRoot
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
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder
import org.jetbrains.kotlin.cli.jvm.modules.JavaModuleGraph
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.JavaModuleInfo
import org.jetbrains.kotlin.resolve.jvm.modules.KOTLIN_STDLIB_MODULE_NAME
import java.io.IOException
import java.util.jar.Attributes
import java.util.jar.Manifest
import kotlin.LazyThreadSafetyMode.NONE
class ClasspathRootsResolver(
private val psiManager: PsiManager,
private val messageCollector: MessageCollector?,
private val additionalModules: List<String>,
private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?,
private val javaModuleFinder: CliJavaModuleFinder,
private val requireStdlibModule: Boolean,
private val outputDirectory: VirtualFile?
) {
val javaModuleGraph = JavaModuleGraph(javaModuleFinder)
data class RootsAndModules(val roots: List<JavaRoot>, val modules: List<JavaModule>)
private data class RootWithPrefix(val root: VirtualFile, val packagePrefix: String?)
fun convertClasspathRoots(contentRoots: List<ContentRoot>): RootsAndModules {
val javaSourceRoots = mutableListOf<RootWithPrefix>()
val jvmClasspathRoots = mutableListOf<VirtualFile>()
val jvmModulePathRoots = mutableListOf<VirtualFile>()
for (contentRoot in contentRoots) {
if (contentRoot !is JvmContentRoot) continue
val root = contentRootToVirtualFile(contentRoot) ?: continue
when (contentRoot) {
is JavaSourceRoot -> javaSourceRoots += RootWithPrefix(root, contentRoot.packagePrefix)
is JvmClasspathRoot -> jvmClasspathRoots += root
is JvmModulePathRoot -> jvmModulePathRoots += root
else -> error("Unknown root type: $contentRoot")
}
}
return computeRoots(javaSourceRoots, jvmClasspathRoots, jvmModulePathRoots)
}
private fun computeRoots(
javaSourceRoots: List<RootWithPrefix>,
jvmClasspathRoots: List<VirtualFile>,
jvmModulePathRoots: List<VirtualFile>
): RootsAndModules {
val result = mutableListOf<JavaRoot>()
val modules = mutableListOf<JavaModule>()
val hasOutputDirectoryInClasspath = outputDirectory in jvmClasspathRoots || outputDirectory in jvmModulePathRoots
for ((root, packagePrefix) in javaSourceRoots) {
val modularRoot = modularSourceRoot(root, hasOutputDirectoryInClasspath)
if (modularRoot != null) {
modules += modularRoot
} else {
result += JavaRoot(root, JavaRoot.RootType.SOURCE, packagePrefix?.let { prefix ->
if (isValidJavaFqName(prefix)) FqName(prefix)
else null.also {
report(STRONG_WARNING, "Invalid package prefix name is ignored: $prefix")
}
})
}
}
for (root in jvmClasspathRoots) {
result += JavaRoot(root, JavaRoot.RootType.BINARY)
}
val outputDirectoryAddedAsPartOfModule = modules.any { module -> module.moduleRoots.any { it.file == outputDirectory } }
for (root in jvmModulePathRoots) {
// Do not add output directory as a separate module if we're compiling an explicit named module.
// It's going to be included as a root of our module in modularSourceRoot.
if (outputDirectoryAddedAsPartOfModule && root == outputDirectory) continue
val module = modularBinaryRoot(root)
if (module != null) {
modules += module
}
}
addModularRoots(modules, result)
return RootsAndModules(result, modules)
}
/*
private fun findSourceModuleInfo(root: VirtualFile): Pair<VirtualFile, PsiJavaModule>? {
val moduleInfoFile =
when {
root.isDirectory -> root.findChild(PsiJavaModule.MODULE_INFO_FILE)
root.name == PsiJavaModule.MODULE_INFO_FILE -> root
else -> null
} ?: return null
val psiFile = psiManager.findFile(moduleInfoFile) ?: return null
val psiJavaModule = psiFile.children.singleOrNull { it is PsiJavaModule } as? PsiJavaModule ?: return null
return moduleInfoFile to psiJavaModule
}
*/
private fun modularSourceRoot(root: VirtualFile, hasOutputDirectoryInClasspath: Boolean): JavaModule.Explicit? {
/*
val (moduleInfoFile, psiJavaModule) = findSourceModuleInfo(root) ?: return null
val sourceRoot = JavaModule.Root(root, isBinary = false)
val roots =
if (hasOutputDirectoryInClasspath)
listOf(sourceRoot, JavaModule.Root(outputDirectory!!, isBinary = true))
else listOf(sourceRoot)
return JavaModule.Explicit(JavaModuleInfo.create(psiJavaModule), roots, moduleInfoFile)
*/
return null
}
private fun modularBinaryRoot(root: VirtualFile): JavaModule? {
val isJar = root.fileSystem.protocol == StandardFileSystems.JAR_PROTOCOL
val manifest: Attributes? by lazy(NONE) { readManifestAttributes(root) }
/*
val moduleInfoFile =
root.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE)
?: root.takeIf { isJar }?.findFileByRelativePath(MULTI_RELEASE_MODULE_INFO_CLS_FILE)?.takeIf {
manifest?.getValue(IS_MULTI_RELEASE)?.equals("true", ignoreCase = true) == true
}
if (moduleInfoFile != null) {
val moduleInfo = JavaModuleInfo.read(moduleInfoFile) ?: return null
return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(root, isBinary = true)), moduleInfoFile)
}
*/
// Only .jar files can be automatic modules
if (isJar) {
val moduleRoot = listOf(JavaModule.Root(root, isBinary = true))
val automaticModuleName = manifest?.getValue(AUTOMATIC_MODULE_NAME)
if (automaticModuleName != null) {
return JavaModule.Automatic(automaticModuleName, moduleRoot)
}
val originalFile = VfsUtilCore.virtualToIoFile(root)
val moduleName = LightJavaModule.moduleName(originalFile.nameWithoutExtension)
if (moduleName.isEmpty()) {
report(ERROR, "Cannot infer automatic module name for the file", VfsUtilCore.getVirtualFileForJar(root) ?: root)
return null
}
return JavaModule.Automatic(moduleName, moduleRoot)
}
return null
}
private fun readManifestAttributes(jarRoot: VirtualFile): Attributes? {
val manifestFile = jarRoot.findChild("META-INF")?.findChild("MANIFEST.MF")
return try {
manifestFile?.inputStream?.let(::Manifest)?.mainAttributes
} catch (e: IOException) {
null
}
}
private fun addModularRoots(modules: List<JavaModule>, result: MutableList<JavaRoot>) {
// In current implementation, at most one source module is supported. This can be relaxed in the future if we support another
// compilation mode, similar to java's --module-source-path
val sourceModules = modules.filterIsInstance<JavaModule.Explicit>().filter(JavaModule::isSourceModule)
if (sourceModules.size > 1) {
for (module in sourceModules) {
report(ERROR, "Too many source module declarations found", module.moduleInfoFile)
}
return
}
for (module in modules) {
val existing = javaModuleFinder.findModule(module.name)
if (existing == null) {
javaModuleFinder.addUserModule(module)
} else if (module.moduleRoots != existing.moduleRoots) {
fun JavaModule.getRootFile() =
moduleRoots.firstOrNull()?.file?.let { VfsUtilCore.getVirtualFileForJar(it) ?: it }
val thisFile = module.getRootFile()
val existingFile = existing.getRootFile()
val atExistingPath = if (existingFile == null) "" else " at: ${existingFile.path}"
report(
STRONG_WARNING, "The root is ignored because a module with the same name '${module.name}' " +
"has been found earlier on the module path$atExistingPath", thisFile
)
}
}
if (javaModuleFinder.allObservableModules.none()) return
val sourceModule = sourceModules.singleOrNull()
val addAllModulePathToRoots = "ALL-MODULE-PATH" in additionalModules
if (addAllModulePathToRoots && sourceModule != null) {
report(ERROR, "-Xadd-modules=ALL-MODULE-PATH can only be used when compiling the unnamed module")
return
}
val rootModules = when {
sourceModule != null -> listOf(sourceModule.name) + additionalModules
addAllModulePathToRoots -> modules.map(JavaModule::name)
else -> computeDefaultRootModules() + additionalModules
}
val allDependencies = javaModuleGraph.getAllDependencies(rootModules)
if (allDependencies.any { moduleName -> javaModuleFinder.findModule(moduleName) is JavaModule.Automatic }) {
// According to java.lang.module javadoc, if at least one automatic module is added to the module graph,
// all observable automatic modules should be added.
// There are no automatic modules in the JDK, so we select all automatic modules out of user modules
for (module in modules) {
if (module is JavaModule.Automatic) {
allDependencies += module.name
}
}
}
report(LOGGING, "Loading modules: $allDependencies")
for (moduleName in allDependencies) {
val module = javaModuleFinder.findModule(moduleName)
if (module == null) {
report(ERROR, "Module $moduleName cannot be found in the module graph")
} else {
for ((root, isBinary) in module.moduleRoots) {
result.add(JavaRoot(root, if (isBinary) JavaRoot.RootType.BINARY else JavaRoot.RootType.SOURCE))
}
}
}
if (requireStdlibModule && sourceModule != null && !javaModuleGraph.reads(sourceModule.name, KOTLIN_STDLIB_MODULE_NAME)) {
report(
ERROR,
"The Kotlin standard library is not found in the module graph. " +
"Please ensure you have the 'requires $KOTLIN_STDLIB_MODULE_NAME' clause in your module definition",
sourceModule.moduleInfoFile
)
}
}
// See http://openjdk.java.net/jeps/261
private fun computeDefaultRootModules(): List<String> {
val result = arrayListOf<String>()
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 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
// that exports at least one package, without qualification, is a root.
for ((name, module) in systemModules) {
if (name.startsWith("java.") && module.exportsAtLeastOnePackageUnqualified()) {
result.add(name)
}
}
}
for ((name, module) in systemModules) {
// Every non-java.* module on the upgrade module path or among the system modules that exports at least one package,
// without qualification, is also a root.
if (!name.startsWith("java.") && module.exportsAtLeastOnePackageUnqualified()) {
result.add(name)
}
}
return result
}
private fun report(severity: CompilerMessageSeverity, message: String, file: VirtualFile? = null) {
if (messageCollector == null) {
throw IllegalStateException("${if (file != null) file.path + ":" else ""}$severity: $message (no MessageCollector configured)")
}
messageCollector.report(
severity, message,
if (file == null) null else CompilerMessageLocation.create(MessageUtil.virtualFileToPath(file))
)
}
private companion object {
const val MULTI_RELEASE_MODULE_INFO_CLS_FILE = "META-INF/versions/9/${PsiJavaModule.MODULE_INFO_CLS_FILE}"
const val AUTOMATIC_MODULE_NAME = "Automatic-Module-Name"
const val IS_MULTI_RELEASE = "Multi-Release"
}
}
@@ -1,332 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.compiler
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiJavaModule
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.light.LightJavaModule
import org.jetbrains.kotlin.cli.common.config.ContentRoot
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
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder
import org.jetbrains.kotlin.cli.jvm.modules.JavaModuleGraph
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.JavaModuleInfo
import org.jetbrains.kotlin.resolve.jvm.modules.KOTLIN_STDLIB_MODULE_NAME
import java.io.IOException
import java.util.jar.Attributes
import java.util.jar.Manifest
import kotlin.LazyThreadSafetyMode.NONE
class ClasspathRootsResolver(
private val psiManager: PsiManager,
private val messageCollector: MessageCollector?,
private val additionalModules: List<String>,
private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?,
private val javaModuleFinder: CliJavaModuleFinder,
private val requireStdlibModule: Boolean,
private val outputDirectory: VirtualFile?
) {
val javaModuleGraph = JavaModuleGraph(javaModuleFinder)
data class RootsAndModules(val roots: List<JavaRoot>, val modules: List<JavaModule>)
private data class RootWithPrefix(val root: VirtualFile, val packagePrefix: String?)
fun convertClasspathRoots(contentRoots: List<ContentRoot>): RootsAndModules {
val javaSourceRoots = mutableListOf<RootWithPrefix>()
val jvmClasspathRoots = mutableListOf<VirtualFile>()
val jvmModulePathRoots = mutableListOf<VirtualFile>()
for (contentRoot in contentRoots) {
if (contentRoot !is JvmContentRoot) continue
val root = contentRootToVirtualFile(contentRoot) ?: continue
when (contentRoot) {
is JavaSourceRoot -> javaSourceRoots += RootWithPrefix(root, contentRoot.packagePrefix)
is JvmClasspathRoot -> jvmClasspathRoots += root
is JvmModulePathRoot -> jvmModulePathRoots += root
else -> error("Unknown root type: $contentRoot")
}
}
return computeRoots(javaSourceRoots, jvmClasspathRoots, jvmModulePathRoots)
}
private fun computeRoots(
javaSourceRoots: List<RootWithPrefix>,
jvmClasspathRoots: List<VirtualFile>,
jvmModulePathRoots: List<VirtualFile>
): RootsAndModules {
val result = mutableListOf<JavaRoot>()
val modules = mutableListOf<JavaModule>()
val hasOutputDirectoryInClasspath = outputDirectory in jvmClasspathRoots || outputDirectory in jvmModulePathRoots
for ((root, packagePrefix) in javaSourceRoots) {
val modularRoot = modularSourceRoot(root, hasOutputDirectoryInClasspath)
if (modularRoot != null) {
modules += modularRoot
} else {
result += JavaRoot(root, JavaRoot.RootType.SOURCE, packagePrefix?.let { prefix ->
if (isValidJavaFqName(prefix)) FqName(prefix)
else null.also {
report(STRONG_WARNING, "Invalid package prefix name is ignored: $prefix")
}
})
}
}
for (root in jvmClasspathRoots) {
result += JavaRoot(root, JavaRoot.RootType.BINARY)
}
val outputDirectoryAddedAsPartOfModule = modules.any { module -> module.moduleRoots.any { it.file == outputDirectory } }
for (root in jvmModulePathRoots) {
// Do not add output directory as a separate module if we're compiling an explicit named module.
// It's going to be included as a root of our module in modularSourceRoot.
if (outputDirectoryAddedAsPartOfModule && root == outputDirectory) continue
val module = modularBinaryRoot(root)
if (module != null) {
modules += module
}
}
addModularRoots(modules, result)
return RootsAndModules(result, modules)
}
/*
private fun findSourceModuleInfo(root: VirtualFile): Pair<VirtualFile, PsiJavaModule>? {
val moduleInfoFile =
when {
root.isDirectory -> root.findChild(PsiJavaModule.MODULE_INFO_FILE)
root.name == PsiJavaModule.MODULE_INFO_FILE -> root
else -> null
} ?: return null
val psiFile = psiManager.findFile(moduleInfoFile) ?: return null
val psiJavaModule = psiFile.children.singleOrNull { it is PsiJavaModule } as? PsiJavaModule ?: return null
return moduleInfoFile to psiJavaModule
}
*/
private fun modularSourceRoot(root: VirtualFile, hasOutputDirectoryInClasspath: Boolean): JavaModule.Explicit? {
/*
val (moduleInfoFile, psiJavaModule) = findSourceModuleInfo(root) ?: return null
val sourceRoot = JavaModule.Root(root, isBinary = false)
val roots =
if (hasOutputDirectoryInClasspath)
listOf(sourceRoot, JavaModule.Root(outputDirectory!!, isBinary = true))
else listOf(sourceRoot)
return JavaModule.Explicit(JavaModuleInfo.create(psiJavaModule), roots, moduleInfoFile)
*/
return null
}
private fun modularBinaryRoot(root: VirtualFile): JavaModule? {
val isJar = root.fileSystem.protocol == StandardFileSystems.JAR_PROTOCOL
val manifest: Attributes? by lazy(NONE) { readManifestAttributes(root) }
/*
val moduleInfoFile =
root.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE)
?: root.takeIf { isJar }?.findFileByRelativePath(MULTI_RELEASE_MODULE_INFO_CLS_FILE)?.takeIf {
manifest?.getValue(IS_MULTI_RELEASE)?.equals("true", ignoreCase = true) == true
}
if (moduleInfoFile != null) {
val moduleInfo = JavaModuleInfo.read(moduleInfoFile) ?: return null
return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(root, isBinary = true)), moduleInfoFile)
}
*/
// Only .jar files can be automatic modules
if (isJar) {
val moduleRoot = listOf(JavaModule.Root(root, isBinary = true))
val automaticModuleName = manifest?.getValue(AUTOMATIC_MODULE_NAME)
if (automaticModuleName != null) {
return JavaModule.Automatic(automaticModuleName, moduleRoot)
}
val originalFile = VfsUtilCore.virtualToIoFile(root)
val moduleName = LightJavaModule.moduleName(originalFile.nameWithoutExtension)
if (moduleName.isEmpty()) {
report(ERROR, "Cannot infer automatic module name for the file", VfsUtilCore.getVirtualFileForJar(root) ?: root)
return null
}
return JavaModule.Automatic(moduleName, moduleRoot)
}
return null
}
private fun readManifestAttributes(jarRoot: VirtualFile): Attributes? {
val manifestFile = jarRoot.findChild("META-INF")?.findChild("MANIFEST.MF")
return try {
manifestFile?.inputStream?.let(::Manifest)?.mainAttributes
} catch (e: IOException) {
null
}
}
private fun addModularRoots(modules: List<JavaModule>, result: MutableList<JavaRoot>) {
// In current implementation, at most one source module is supported. This can be relaxed in the future if we support another
// compilation mode, similar to java's --module-source-path
val sourceModules = modules.filterIsInstance<JavaModule.Explicit>().filter(JavaModule::isSourceModule)
if (sourceModules.size > 1) {
for (module in sourceModules) {
report(ERROR, "Too many source module declarations found", module.moduleInfoFile)
}
return
}
for (module in modules) {
val existing = javaModuleFinder.findModule(module.name)
if (existing == null) {
javaModuleFinder.addUserModule(module)
} else if (module.moduleRoots != existing.moduleRoots) {
fun JavaModule.getRootFile() =
moduleRoots.firstOrNull()?.file?.let { VfsUtilCore.getVirtualFileForJar(it) ?: it }
val thisFile = module.getRootFile()
val existingFile = existing.getRootFile()
val atExistingPath = if (existingFile == null) "" else " at: ${existingFile.path}"
report(
STRONG_WARNING, "The root is ignored because a module with the same name '${module.name}' " +
"has been found earlier on the module path$atExistingPath", thisFile
)
}
}
if (javaModuleFinder.allObservableModules.none()) return
val sourceModule = sourceModules.singleOrNull()
val addAllModulePathToRoots = "ALL-MODULE-PATH" in additionalModules
if (addAllModulePathToRoots && sourceModule != null) {
report(ERROR, "-Xadd-modules=ALL-MODULE-PATH can only be used when compiling the unnamed module")
return
}
val rootModules = when {
sourceModule != null -> listOf(sourceModule.name) + additionalModules
addAllModulePathToRoots -> modules.map(JavaModule::name)
else -> computeDefaultRootModules() + additionalModules
}
val allDependencies = javaModuleGraph.getAllDependencies(rootModules)
if (allDependencies.any { moduleName -> javaModuleFinder.findModule(moduleName) is JavaModule.Automatic }) {
// According to java.lang.module javadoc, if at least one automatic module is added to the module graph,
// all observable automatic modules should be added.
// There are no automatic modules in the JDK, so we select all automatic modules out of user modules
for (module in modules) {
if (module is JavaModule.Automatic) {
allDependencies += module.name
}
}
}
report(LOGGING, "Loading modules: $allDependencies")
for (moduleName in allDependencies) {
val module = javaModuleFinder.findModule(moduleName)
if (module == null) {
report(ERROR, "Module $moduleName cannot be found in the module graph")
} else {
for ((root, isBinary) in module.moduleRoots) {
result.add(JavaRoot(root, if (isBinary) JavaRoot.RootType.BINARY else JavaRoot.RootType.SOURCE))
}
}
}
if (requireStdlibModule && sourceModule != null && !javaModuleGraph.reads(sourceModule.name, KOTLIN_STDLIB_MODULE_NAME)) {
report(
ERROR,
"The Kotlin standard library is not found in the module graph. " +
"Please ensure you have the 'requires $KOTLIN_STDLIB_MODULE_NAME' clause in your module definition",
sourceModule.moduleInfoFile
)
}
}
// See http://openjdk.java.net/jeps/261
private fun computeDefaultRootModules(): List<String> {
val result = arrayListOf<String>()
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 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
// that exports at least one package, without qualification, is a root.
for ((name, module) in systemModules) {
if (name.startsWith("java.") && module.exportsAtLeastOnePackageUnqualified()) {
result.add(name)
}
}
}
for ((name, module) in systemModules) {
// Every non-java.* module on the upgrade module path or among the system modules that exports at least one package,
// without qualification, is also a root.
if (!name.startsWith("java.") && module.exportsAtLeastOnePackageUnqualified()) {
result.add(name)
}
}
return result
}
private fun report(severity: CompilerMessageSeverity, message: String, file: VirtualFile? = null) {
if (messageCollector == null) {
throw IllegalStateException("${if (file != null) file.path + ":" else ""}$severity: $message (no MessageCollector configured)")
}
messageCollector.report(
severity, message,
if (file == null) null else CompilerMessageLocation.create(MessageUtil.virtualFileToPath(file))
)
}
private companion object {
const val MULTI_RELEASE_MODULE_INFO_CLS_FILE = "META-INF/versions/9/${PsiJavaModule.MODULE_INFO_CLS_FILE}"
const val AUTOMATIC_MODULE_NAME = "Automatic-Module-Name"
const val IS_MULTI_RELEASE = "Multi-Release"
}
}
@@ -1,291 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.compiler
import com.intellij.core.CoreJavaFileManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.*
import com.intellij.psi.impl.file.PsiPackageImpl
import com.intellij.psi.search.GlobalSearchScope
import gnu.trove.THashMap
import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndex
import org.jetbrains.kotlin.cli.jvm.index.SingleJavaFileRootsIndex
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryClassSignatureParser
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.ClassifierResolutionContext
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.isNotTopLevelClass
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.jvm.KotlinCliJavaFileManager
import org.jetbrains.kotlin.util.PerformanceCounter
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
// TODO: do not inherit from CoreJavaFileManager to avoid accidental usage of its methods which do not use caches/indices
// Currently, the only relevant usage of this class as CoreJavaFileManager is at CoreJavaDirectoryService.getPackage,
// which is indirectly invoked from PsiPackage.getSubPackages
class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJavaFileManager(myPsiManager), KotlinCliJavaFileManager {
private val perfCounter = PerformanceCounter.create("Find Java class")
private lateinit var index: JvmDependenciesIndex
private lateinit var singleJavaFileRootsIndex: SingleJavaFileRootsIndex
private lateinit var packagePartProviders: List<JvmPackagePartProvider>
private val topLevelClassesCache: MutableMap<FqName, VirtualFile?> = THashMap()
private val allScope = GlobalSearchScope.allScope(myPsiManager.project)
private var useFastClassFilesReading = false
fun initialize(
index: JvmDependenciesIndex,
packagePartProviders: List<JvmPackagePartProvider>,
singleJavaFileRootsIndex: SingleJavaFileRootsIndex,
useFastClassFilesReading: Boolean
) {
this.index = index
this.packagePartProviders = packagePartProviders
this.singleJavaFileRootsIndex = singleJavaFileRootsIndex
this.useFastClassFilesReading = useFastClassFilesReading
}
private fun findPsiClass(classId: ClassId, searchScope: GlobalSearchScope): PsiClass? = perfCounter.time {
findVirtualFileForTopLevelClass(classId, searchScope)?.findPsiClassInVirtualFile(classId.relativeClassName.asString())
}
private fun findVirtualFileForTopLevelClass(classId: ClassId, searchScope: GlobalSearchScope): VirtualFile? {
val relativeClassName = classId.relativeClassName.asString()
return topLevelClassesCache.getOrPut(classId.packageFqName.child(classId.relativeClassName.pathSegments().first())) {
index.findClass(classId) { dir, type ->
findVirtualFileGivenPackage(dir, relativeClassName, type)
} ?: singleJavaFileRootsIndex.findJavaSourceClass(classId)
}?.takeIf { it in searchScope }
}
private val binaryCache: MutableMap<ClassId, JavaClass?> = THashMap()
private val signatureParsingComponent = BinaryClassSignatureParser()
override fun findClass(classId: ClassId, searchScope: GlobalSearchScope): JavaClass? {
val virtualFile = findVirtualFileForTopLevelClass(classId, searchScope) ?: return null
if (useFastClassFilesReading && virtualFile.extension == "class") {
// We return all class files' names in the directory in knownClassNamesInPackage method, so one may request an inner class
return binaryCache.getOrPut(classId) {
// Note that currently we implicitly suppose that searchScope for binary classes is constant and we do not use it
// as a key in cache
// This is a true assumption by now since there are two search scopes in compiler: one for sources and another one for binary
// When it become wrong because we introduce the modules into CLI, it's worth to consider
// having different KotlinCliJavaFileManagerImpl's for different modules
val classContent = virtualFile.contentsToByteArray()
if (virtualFile.nameWithoutExtension.contains("$") && isNotTopLevelClass(classContent)) return@getOrPut null
classId.outerClassId?.let { outerClassId ->
val outerClass = findClass(outerClassId, searchScope)
return@getOrPut outerClass?.findInnerClass(classId.shortClassName)
}
val resolver = ClassifierResolutionContext { findClass(it, allScope) }
BinaryJavaClass(
virtualFile, classId.asSingleFqName(), resolver, signatureParsingComponent,
outerClass = null, classContent = classContent
)
}
}
return virtualFile.findPsiClassInVirtualFile(classId.relativeClassName.asString())?.let(::JavaClassImpl)
}
// this method is called from IDEA to resolve dependencies in Java code
// which supposedly shouldn't have errors so the dependencies exist in general
override fun findClass(qName: String, scope: GlobalSearchScope): PsiClass? {
// String cannot be reliably converted to ClassId because we don't know where the package name ends and class names begin.
// For example, if qName is "a.b.c.d.e", we should either look for a top level class "e" in the package "a.b.c.d",
// or, for example, for a nested class with the relative qualified name "c.d.e" in the package "a.b".
// Below, we start by looking for the top level class "e" in the package "a.b.c.d" first, then for the class "d.e" in the package
// "a.b.c", and so on, until we find something. Most classes are top level, so most of the times the search ends quickly
forEachClassId(qName) { classId ->
findPsiClass(classId, scope)?.let { return it }
}
return null
}
private inline fun forEachClassId(fqName: String, block: (ClassId) -> Unit) {
var classId = fqName.toSafeTopLevelClassId() ?: return
while (true) {
block(classId)
val packageFqName = classId.packageFqName
if (packageFqName.isRoot) break
classId = ClassId(
packageFqName.parent(),
FqName(packageFqName.shortName().asString() + "." + classId.relativeClassName.asString()),
false
)
}
}
override fun findClasses(qName: String, scope: GlobalSearchScope): Array<PsiClass> = perfCounter.time {
val result = ArrayList<PsiClass>(1)
forEachClassId(qName) { classId ->
val relativeClassName = classId.relativeClassName.asString()
index.traverseDirectoriesInPackage(classId.packageFqName) { dir, rootType ->
val psiClass =
findVirtualFileGivenPackage(dir, relativeClassName, rootType)
?.takeIf { it in scope }
?.findPsiClassInVirtualFile(relativeClassName)
if (psiClass != null) {
result.add(psiClass)
}
// traverse all
true
}
result.addIfNotNull(
singleJavaFileRootsIndex.findJavaSourceClass(classId)
?.takeIf { it in scope }
?.findPsiClassInVirtualFile(relativeClassName)
)
if (result.isNotEmpty()) {
return@time result.toTypedArray()
}
}
PsiClass.EMPTY_ARRAY
}
override fun findPackage(packageName: String): PsiPackage? {
var found = false
val packageFqName = packageName.toSafeFqName() ?: return null
index.traverseDirectoriesInPackage(packageFqName) { _, _ ->
found = true
//abort on first found
false
}
if (!found) {
found = packagePartProviders.any { it.findPackageParts(packageName).isNotEmpty() }
}
if (!found) {
found = singleJavaFileRootsIndex.findJavaSourceClasses(packageFqName).isNotEmpty()
}
return if (found) PsiPackageImpl(myPsiManager, packageName) else null
}
private fun findVirtualFileGivenPackage(
packageDir: VirtualFile,
classNameWithInnerClasses: String,
rootType: JavaRoot.RootType
): VirtualFile? {
val topLevelClassName = classNameWithInnerClasses.substringBefore('.')
val vFile = when (rootType) {
JavaRoot.RootType.BINARY -> packageDir.findChild("$topLevelClassName.class")
JavaRoot.RootType.SOURCE -> packageDir.findChild("$topLevelClassName.java")
} ?: return null
if (!vFile.isValid) {
LOG.error("Invalid child of valid parent: ${vFile.path}; ${packageDir.isValid} path=${packageDir.path}")
return null
}
return vFile
}
private fun VirtualFile.findPsiClassInVirtualFile(classNameWithInnerClasses: String): PsiClass? {
val file = myPsiManager.findFile(this) as? PsiClassOwner ?: return null
return findClassInPsiFile(classNameWithInnerClasses, file)
}
override fun knownClassNamesInPackage(packageFqName: FqName): Set<String> {
val result = hashSetOf<String>()
index.traverseDirectoriesInPackage(packageFqName, continueSearch = { dir, _ ->
for (child in dir.children) {
if (child.extension == "class" || child.extension == "java") {
result.add(child.nameWithoutExtension)
}
}
true
})
for (classId in singleJavaFileRootsIndex.findJavaSourceClasses(packageFqName)) {
assert(!classId.isNestedClass) { "ClassId of a single .java source class should not be nested: $classId" }
result.add(classId.shortClassName.asString())
}
return result
}
/*
override fun findModules(moduleName: String, scope: GlobalSearchScope): Collection<PsiJavaModule> {
// TODO
return emptySet()
}
*/
override fun getNonTrivialPackagePrefixes(): Collection<String> = emptyList()
companion object {
private val LOG = Logger.getInstance(KotlinCliJavaFileManagerImpl::class.java)
private fun findClassInPsiFile(classNameWithInnerClassesDotSeparated: String, file: PsiClassOwner): PsiClass? {
for (topLevelClass in file.classes) {
val candidate = findClassByTopLevelClass(classNameWithInnerClassesDotSeparated, topLevelClass)
if (candidate != null) {
return candidate
}
}
return null
}
private fun findClassByTopLevelClass(className: String, topLevelClass: PsiClass): PsiClass? {
if (className.indexOf('.') < 0) {
return if (className == topLevelClass.name) topLevelClass else null
}
val segments = StringUtil.split(className, ".").iterator()
if (!segments.hasNext() || segments.next() != topLevelClass.name) {
return null
}
var curClass = topLevelClass
while (segments.hasNext()) {
val innerClassName = segments.next()
val innerClass = curClass.findInnerClassByName(innerClassName, false) ?: return null
curClass = innerClass
}
return curClass
}
}
}
// a sad workaround to avoid throwing exception when called from inside IDEA code
private fun <T : Any> safely(compute: () -> T): T? =
try {
compute()
} catch (e: IllegalArgumentException) {
null
} catch (e: AssertionError) {
null
}
private fun String.toSafeFqName(): FqName? = safely { FqName(this) }
private fun String.toSafeTopLevelClassId(): ClassId? = safely { ClassId.topLevel(FqName(this)) }
@@ -1,291 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.compiler
import com.intellij.core.CoreJavaFileManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.*
import com.intellij.psi.impl.file.PsiPackageImpl
import com.intellij.psi.search.GlobalSearchScope
import gnu.trove.THashMap
import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndex
import org.jetbrains.kotlin.cli.jvm.index.SingleJavaFileRootsIndex
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryClassSignatureParser
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.ClassifierResolutionContext
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.isNotTopLevelClass
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.jvm.KotlinCliJavaFileManager
import org.jetbrains.kotlin.util.PerformanceCounter
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
// TODO: do not inherit from CoreJavaFileManager to avoid accidental usage of its methods which do not use caches/indices
// Currently, the only relevant usage of this class as CoreJavaFileManager is at CoreJavaDirectoryService.getPackage,
// which is indirectly invoked from PsiPackage.getSubPackages
class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJavaFileManager(myPsiManager), KotlinCliJavaFileManager {
private val perfCounter = PerformanceCounter.create("Find Java class")
private lateinit var index: JvmDependenciesIndex
private lateinit var singleJavaFileRootsIndex: SingleJavaFileRootsIndex
private lateinit var packagePartProviders: List<JvmPackagePartProvider>
private val topLevelClassesCache: MutableMap<FqName, VirtualFile?> = THashMap()
private val allScope = GlobalSearchScope.allScope(myPsiManager.project)
private var useFastClassFilesReading = false
fun initialize(
index: JvmDependenciesIndex,
packagePartProviders: List<JvmPackagePartProvider>,
singleJavaFileRootsIndex: SingleJavaFileRootsIndex,
useFastClassFilesReading: Boolean
) {
this.index = index
this.packagePartProviders = packagePartProviders
this.singleJavaFileRootsIndex = singleJavaFileRootsIndex
this.useFastClassFilesReading = useFastClassFilesReading
}
private fun findPsiClass(classId: ClassId, searchScope: GlobalSearchScope): PsiClass? = perfCounter.time {
findVirtualFileForTopLevelClass(classId, searchScope)?.findPsiClassInVirtualFile(classId.relativeClassName.asString())
}
private fun findVirtualFileForTopLevelClass(classId: ClassId, searchScope: GlobalSearchScope): VirtualFile? {
val relativeClassName = classId.relativeClassName.asString()
return topLevelClassesCache.getOrPut(classId.packageFqName.child(classId.relativeClassName.pathSegments().first())) {
index.findClass(classId) { dir, type ->
findVirtualFileGivenPackage(dir, relativeClassName, type)
} ?: singleJavaFileRootsIndex.findJavaSourceClass(classId)
}?.takeIf { it in searchScope }
}
private val binaryCache: MutableMap<ClassId, JavaClass?> = THashMap()
private val signatureParsingComponent = BinaryClassSignatureParser()
override fun findClass(classId: ClassId, searchScope: GlobalSearchScope): JavaClass? {
val virtualFile = findVirtualFileForTopLevelClass(classId, searchScope) ?: return null
if (useFastClassFilesReading && virtualFile.extension == "class") {
// We return all class files' names in the directory in knownClassNamesInPackage method, so one may request an inner class
return binaryCache.getOrPut(classId) {
// Note that currently we implicitly suppose that searchScope for binary classes is constant and we do not use it
// as a key in cache
// This is a true assumption by now since there are two search scopes in compiler: one for sources and another one for binary
// When it become wrong because we introduce the modules into CLI, it's worth to consider
// having different KotlinCliJavaFileManagerImpl's for different modules
val classContent = virtualFile.contentsToByteArray()
if (virtualFile.nameWithoutExtension.contains("$") && isNotTopLevelClass(classContent)) return@getOrPut null
classId.outerClassId?.let { outerClassId ->
val outerClass = findClass(outerClassId, searchScope)
return@getOrPut outerClass?.findInnerClass(classId.shortClassName)
}
val resolver = ClassifierResolutionContext { findClass(it, allScope) }
BinaryJavaClass(
virtualFile, classId.asSingleFqName(), resolver, signatureParsingComponent,
outerClass = null, classContent = classContent
)
}
}
return virtualFile.findPsiClassInVirtualFile(classId.relativeClassName.asString())?.let(::JavaClassImpl)
}
// this method is called from IDEA to resolve dependencies in Java code
// which supposedly shouldn't have errors so the dependencies exist in general
override fun findClass(qName: String, scope: GlobalSearchScope): PsiClass? {
// String cannot be reliably converted to ClassId because we don't know where the package name ends and class names begin.
// For example, if qName is "a.b.c.d.e", we should either look for a top level class "e" in the package "a.b.c.d",
// or, for example, for a nested class with the relative qualified name "c.d.e" in the package "a.b".
// Below, we start by looking for the top level class "e" in the package "a.b.c.d" first, then for the class "d.e" in the package
// "a.b.c", and so on, until we find something. Most classes are top level, so most of the times the search ends quickly
forEachClassId(qName) { classId ->
findPsiClass(classId, scope)?.let { return it }
}
return null
}
private inline fun forEachClassId(fqName: String, block: (ClassId) -> Unit) {
var classId = fqName.toSafeTopLevelClassId() ?: return
while (true) {
block(classId)
val packageFqName = classId.packageFqName
if (packageFqName.isRoot) break
classId = ClassId(
packageFqName.parent(),
FqName(packageFqName.shortName().asString() + "." + classId.relativeClassName.asString()),
false
)
}
}
override fun findClasses(qName: String, scope: GlobalSearchScope): Array<PsiClass> = perfCounter.time {
val result = ArrayList<PsiClass>(1)
forEachClassId(qName) { classId ->
val relativeClassName = classId.relativeClassName.asString()
index.traverseDirectoriesInPackage(classId.packageFqName) { dir, rootType ->
val psiClass =
findVirtualFileGivenPackage(dir, relativeClassName, rootType)
?.takeIf { it in scope }
?.findPsiClassInVirtualFile(relativeClassName)
if (psiClass != null) {
result.add(psiClass)
}
// traverse all
true
}
result.addIfNotNull(
singleJavaFileRootsIndex.findJavaSourceClass(classId)
?.takeIf { it in scope }
?.findPsiClassInVirtualFile(relativeClassName)
)
if (result.isNotEmpty()) {
return@time result.toTypedArray()
}
}
PsiClass.EMPTY_ARRAY
}
override fun findPackage(packageName: String): PsiPackage? {
var found = false
val packageFqName = packageName.toSafeFqName() ?: return null
index.traverseDirectoriesInPackage(packageFqName) { _, _ ->
found = true
//abort on first found
false
}
if (!found) {
found = packagePartProviders.any { it.findPackageParts(packageName).isNotEmpty() }
}
if (!found) {
found = singleJavaFileRootsIndex.findJavaSourceClasses(packageFqName).isNotEmpty()
}
return if (found) PsiPackageImpl(myPsiManager, packageName) else null
}
private fun findVirtualFileGivenPackage(
packageDir: VirtualFile,
classNameWithInnerClasses: String,
rootType: JavaRoot.RootType
): VirtualFile? {
val topLevelClassName = classNameWithInnerClasses.substringBefore('.')
val vFile = when (rootType) {
JavaRoot.RootType.BINARY -> packageDir.findChild("$topLevelClassName.class")
JavaRoot.RootType.SOURCE -> packageDir.findChild("$topLevelClassName.java")
} ?: return null
if (!vFile.isValid) {
LOG.error("Invalid child of valid parent: ${vFile.path}; ${packageDir.isValid} path=${packageDir.path}")
return null
}
return vFile
}
private fun VirtualFile.findPsiClassInVirtualFile(classNameWithInnerClasses: String): PsiClass? {
val file = myPsiManager.findFile(this) as? PsiClassOwner ?: return null
return findClassInPsiFile(classNameWithInnerClasses, file)
}
override fun knownClassNamesInPackage(packageFqName: FqName): Set<String> {
val result = hashSetOf<String>()
index.traverseDirectoriesInPackage(packageFqName, continueSearch = { dir, _ ->
for (child in dir.children) {
if (child.extension == "class" || child.extension == "java") {
result.add(child.nameWithoutExtension)
}
}
true
})
for (classId in singleJavaFileRootsIndex.findJavaSourceClasses(packageFqName)) {
assert(!classId.isNestedClass) { "ClassId of a single .java source class should not be nested: $classId" }
result.add(classId.shortClassName.asString())
}
return result
}
/*
override fun findModules(moduleName: String, scope: GlobalSearchScope): Collection<PsiJavaModule> {
// TODO
return emptySet()
}
*/
override fun getNonTrivialPackagePrefixes(): Collection<String> = emptyList()
companion object {
private val LOG = Logger.getInstance(KotlinCliJavaFileManagerImpl::class.java)
private fun findClassInPsiFile(classNameWithInnerClassesDotSeparated: String, file: PsiClassOwner): PsiClass? {
for (topLevelClass in file.classes) {
val candidate = findClassByTopLevelClass(classNameWithInnerClassesDotSeparated, topLevelClass)
if (candidate != null) {
return candidate
}
}
return null
}
private fun findClassByTopLevelClass(className: String, topLevelClass: PsiClass): PsiClass? {
if (className.indexOf('.') < 0) {
return if (className == topLevelClass.name) topLevelClass else null
}
val segments = StringUtil.split(className, ".").iterator()
if (!segments.hasNext() || segments.next() != topLevelClass.name) {
return null
}
var curClass = topLevelClass
while (segments.hasNext()) {
val innerClassName = segments.next()
val innerClass = curClass.findInnerClassByName(innerClassName, false) ?: return null
curClass = innerClass
}
return curClass
}
}
}
// a sad workaround to avoid throwing exception when called from inside IDEA code
private fun <T : Any> safely(compute: () -> T): T? =
try {
compute()
} catch (e: IllegalArgumentException) {
null
} catch (e: AssertionError) {
null
}
private fun String.toSafeFqName(): FqName? = safely { FqName(this) }
private fun String.toSafeTopLevelClassId(): ClassId? = safely { ClassId.topLevel(FqName(this)) }
@@ -1,471 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.compiler
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.PsiModificationTrackerImpl
import com.intellij.psi.search.DelegatingGlobalSearchScope
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.asJava.FilteredJvmDiagnostics
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.checkKotlinPackageUsage
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoots
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.OUTPUT
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.WARNING
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
import org.jetbrains.kotlin.cli.common.output.writeAll
import org.jetbrains.kotlin.cli.jvm.config.*
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.GenerationStateEventCallback
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.javac.JavacWrapper
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager
import org.jetbrains.kotlin.modules.Module
import org.jetbrains.kotlin.modules.TargetId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.script.tryConstructClassFromStringArgs
import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize
import java.io.File
import java.lang.reflect.InvocationTargetException
import java.net.URLClassLoader
object KotlinToJVMBytecodeCompiler {
private fun getAbsolutePaths(buildFile: File, module: Module): List<String> {
return module.getSourceFiles().map { sourceFile ->
val source = File(sourceFile)
if (!source.isAbsolute) {
File(buildFile.absoluteFile.parentFile, sourceFile).absolutePath
} else {
source.absolutePath
}
}
}
private fun writeOutput(
configuration: CompilerConfiguration,
outputFiles: OutputFileCollection,
mainClass: FqName?
) {
val reportOutputFiles = configuration.getBoolean(CommonConfigurationKeys.REPORT_OUTPUT_FILES)
val jarPath = configuration.get(JVMConfigurationKeys.OUTPUT_JAR)
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)
if (jarPath != null) {
val includeRuntime = configuration.get(JVMConfigurationKeys.INCLUDE_RUNTIME, false)
CompileEnvironmentUtil.writeToJar(jarPath, includeRuntime, mainClass, outputFiles)
if (reportOutputFiles) {
val message = OutputMessageUtil.formatOutputMessage(outputFiles.asList().flatMap { it.sourceFiles }.distinct(), jarPath)
messageCollector.report(OUTPUT, message)
}
return
}
val outputDir = configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY) ?: File(".")
outputFiles.writeAll(outputDir, messageCollector, reportOutputFiles)
}
private fun createOutputFilesFlushingCallbackIfPossible(configuration: CompilerConfiguration): GenerationStateEventCallback {
if (configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY) == null) {
return GenerationStateEventCallback.DO_NOTHING
}
return GenerationStateEventCallback { state ->
val currentOutput = SimpleOutputFileCollection(state.factory.currentOutput)
writeOutput(configuration, currentOutput, mainClass = null)
if (!configuration.get(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, false)) {
state.factory.releaseGeneratedOutput()
}
}
}
internal fun compileModules(environment: KotlinCoreEnvironment, buildFile: File, chunk: List<Module>): Boolean {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(environment.project)
val projectConfiguration = environment.configuration
for (module in chunk) {
moduleVisibilityManager.addModule(module)
}
val friendPaths = environment.configuration.getList(JVMConfigurationKeys.FRIEND_PATHS)
for (path in friendPaths) {
moduleVisibilityManager.addFriendPath(path)
}
val targetDescription = "in targets [" + chunk.joinToString { input -> input.getModuleName() + "-" + input.getModuleType() } + "]"
val result = repeatAnalysisIfNeeded(analyze(environment, targetDescription), environment, targetDescription)
if (result == null || !result.shouldGenerateCode) return false
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
result.throwIfError()
val outputs = newLinkedHashMapWithExpectedSize<Module, GenerationState>(chunk.size)
for (module in chunk) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val moduleSourcePaths = getAbsolutePaths(buildFile, module)
val ktFiles = environment.getSourceFiles().filter { file -> file.virtualFilePath in moduleSourcePaths }
if (!checkKotlinPackageUsage(environment, ktFiles)) return false
val moduleConfiguration = projectConfiguration.copy().apply {
put(JVMConfigurationKeys.OUTPUT_DIRECTORY, File(module.getOutputDirectory()))
}
outputs[module] = generate(environment, moduleConfiguration, result, ktFiles, module)
}
try {
for ((_, state) in outputs) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
writeOutput(state.configuration, state.factory, null)
}
if (projectConfiguration.getBoolean(JVMConfigurationKeys.COMPILE_JAVA)) {
val singleModule = chunk.singleOrNull()
if (singleModule != null) {
return JavacWrapper.getInstance(environment.project).use {
it.compile(File(singleModule.getOutputDirectory()))
}
} else {
projectConfiguration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
WARNING,
"A chunk contains multiple modules (${chunk.joinToString { it.getModuleName() }}). " +
"-Xuse-javac option couldn't be used to compile java files"
)
JavacWrapper.getInstance(environment.project).close()
}
}
return true
} finally {
outputs.values.forEach(GenerationState::destroy)
}
}
internal fun configureSourceRoots(configuration: CompilerConfiguration, chunk: List<Module>, buildFile: File) {
for (module in chunk) {
configuration.addKotlinSourceRoots(getAbsolutePaths(buildFile, module))
}
for (module in chunk) {
for ((path, packagePrefix) in module.getJavaSourceRoots()) {
configuration.addJavaSourceRoot(File(path), packagePrefix)
}
}
val isJava9Module = false /*chunk.any { module ->
module.getJavaSourceRoots().any { (path, packagePrefix) ->
val file = File(path)
packagePrefix == null &&
(file.name == PsiJavaModule.MODULE_INFO_FILE ||
(file.isDirectory && file.listFiles().any { it.name == PsiJavaModule.MODULE_INFO_FILE }))
}
}*/
for (module in chunk) {
for (classpathRoot in module.getClasspathRoots()) {
configuration.add(
CLIConfigurationKeys.CONTENT_ROOTS,
if (isJava9Module) JvmModulePathRoot(File(classpathRoot)) else JvmClasspathRoot(File(classpathRoot))
)
}
}
for (module in chunk) {
val modularJdkRoot = module.modularJdkRoot
if (modularJdkRoot != null) {
// We use the SDK of the first module in the chunk, which is not always correct because some other module in the chunk
// might depend on a different SDK
configuration.put(JVMConfigurationKeys.JDK_HOME, File(modularJdkRoot))
break
}
}
configuration.addAll(JVMConfigurationKeys.MODULES, chunk)
}
private fun findMainClass(generationState: GenerationState, files: List<KtFile>): FqName? {
val mainFunctionDetector = MainFunctionDetector(generationState.bindingContext)
return files.asSequence()
.map { file ->
if (mainFunctionDetector.hasMain(file.declarations))
JvmFileClassUtil.getFileClassInfoNoResolve(file).facadeClassFqName
else
null
}
.singleOrNull { it != null }
}
fun compileBunchOfSources(environment: KotlinCoreEnvironment): Boolean {
val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(environment.project)
val friendPaths = environment.configuration.getList(JVMConfigurationKeys.FRIEND_PATHS)
for (path in friendPaths) {
moduleVisibilityManager.addFriendPath(path)
}
if (!checkKotlinPackageUsage(environment, environment.getSourceFiles())) return false
val generationState = analyzeAndGenerate(environment) ?: return false
val mainClass = findMainClass(generationState, environment.getSourceFiles())
try {
writeOutput(environment.configuration, generationState.factory, mainClass)
return true
} finally {
generationState.destroy()
}
}
internal fun compileAndExecuteScript(environment: KotlinCoreEnvironment, scriptArgs: List<String>): ExitCode {
val scriptClass = compileScript(environment) ?: return ExitCode.COMPILATION_ERROR
try {
try {
tryConstructClassFromStringArgs(scriptClass, scriptArgs)
?: throw RuntimeException("unable to find appropriate constructor for class ${scriptClass.name} accepting arguments $scriptArgs\n")
} finally {
// NB: these lines are required (see KT-9546) but aren't covered by tests
System.out.flush()
System.err.flush()
}
} catch (e: Throwable) {
reportExceptionFromScript(e)
return ExitCode.SCRIPT_EXECUTION_ERROR
}
return ExitCode.OK
}
private fun repeatAnalysisIfNeeded(
result: AnalysisResult?,
environment: KotlinCoreEnvironment,
targetDescription: String?
): AnalysisResult? {
if (result is AnalysisResult.RetryWithAdditionalJavaRoots) {
val configuration = environment.configuration
val oldReadOnlyValue = configuration.isReadOnly
configuration.isReadOnly = false
configuration.addJavaSourceRoots(result.additionalJavaRoots)
configuration.isReadOnly = oldReadOnlyValue
if (result.addToEnvironment) {
environment.updateClasspath(result.additionalJavaRoots.map { JavaSourceRoot(it, null) })
}
// Clear package caches (see KotlinJavaPsiFacade)
ApplicationManager.getApplication().runWriteAction {
(PsiManager.getInstance(environment.project).modificationTracker as? PsiModificationTrackerImpl)?.incCounter()
}
// Clear all diagnostic messages
configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY]?.clear()
// Repeat analysis with additional Java roots (kapt generated sources)
return analyze(environment, targetDescription)
}
return result
}
private fun reportExceptionFromScript(exception: Throwable) {
// expecting InvocationTargetException from constructor invocation with cause that describes the actual cause
val stream = System.err
val cause = exception.cause
if (exception !is InvocationTargetException || cause == null) {
exception.printStackTrace(stream)
return
}
stream.println(cause)
val fullTrace = cause.stackTrace
for (i in 0 until fullTrace.size - exception.stackTrace.size) {
stream.println("\tat " + fullTrace[i])
}
}
fun compileScript(environment: KotlinCoreEnvironment, parentClassLoader: ClassLoader? = null): Class<*>? {
val state = analyzeAndGenerate(environment) ?: return null
try {
val urls = environment.configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS).mapNotNull { root ->
when (root) {
is JvmModulePathRoot -> root.file // TODO: only add required modules
is JvmClasspathRoot -> root.file
else -> null
}
}.map { it.toURI().toURL() }
val classLoader = GeneratedClassLoader(state.factory, parentClassLoader ?: URLClassLoader(urls.toTypedArray(), null))
val script = environment.getSourceFiles()[0].script ?: error("Script must be parsed")
return classLoader.loadClass(script.fqName.asString())
} catch (e: Exception) {
throw RuntimeException("Failed to evaluate script: $e", e)
}
}
private fun analyzeAndGenerate(environment: KotlinCoreEnvironment): GenerationState? {
val result = repeatAnalysisIfNeeded(analyze(environment, null), environment, null) ?: return null
if (!result.shouldGenerateCode) return null
result.throwIfError()
return generate(environment, environment.configuration, result, environment.getSourceFiles(), null)
}
private fun analyze(environment: KotlinCoreEnvironment, targetDescription: String?): AnalysisResult? {
val sourceFiles = environment.getSourceFiles()
val collector = environment.messageCollector
// Can be null for Scripts/REPL
val performanceManager = environment.configuration.get(CLIConfigurationKeys.PERF_MANAGER)
performanceManager?.notifyAnalysisStarted()
val analyzerWithCompilerReport = AnalyzerWithCompilerReport(collector, environment.configuration.languageVersionSettings)
analyzerWithCompilerReport.analyzeAndReport(sourceFiles) {
val project = environment.project
val moduleOutputs = environment.configuration.get(JVMConfigurationKeys.MODULES)?.mapNotNullTo(hashSetOf()) { module ->
environment.findLocalFile(module.getOutputDirectory())
}.orEmpty()
val sourcesOnly = TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, sourceFiles)
// To support partial and incremental compilation, we add the scope which contains binaries from output directories
// of the compiled modules (.class) to the list of scopes of the source module
val scope = if (moduleOutputs.isEmpty()) sourcesOnly else sourcesOnly.uniteWith(DirectoriesScope(project, moduleOutputs))
TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
project,
sourceFiles,
NoScopeRecordCliBindingTrace(),
environment.configuration,
environment::createPackagePartProvider,
sourceModuleSearchScope = scope
)
}
performanceManager?.notifyAnalysisFinished(sourceFiles.size, environment.countLinesOfCode(sourceFiles), targetDescription)
val analysisResult = analyzerWithCompilerReport.analysisResult
return if (!analyzerWithCompilerReport.hasErrors() || analysisResult is AnalysisResult.RetryWithAdditionalJavaRoots)
analysisResult
else
null
}
class DirectoriesScope(
project: Project,
private val directories: Set<VirtualFile>
) : DelegatingGlobalSearchScope(GlobalSearchScope.allScope(project)) {
private val fileSystems = directories.mapTo(hashSetOf(), VirtualFile::getFileSystem)
override fun contains(file: VirtualFile): Boolean {
if (file.fileSystem !in fileSystems) return false
var parent: VirtualFile = file
while (true) {
if (parent in directories) return true
parent = parent.parent ?: return false
}
}
override fun toString() = "All files under: $directories"
}
private fun GenerationState.Builder.withModule(module: Module?) =
apply {
targetId(module?.let { TargetId(it) })
moduleName(module?.getModuleName())
outDirectory(module?.let { File(it.getOutputDirectory()) })
}
private fun generate(
environment: KotlinCoreEnvironment,
configuration: CompilerConfiguration,
result: AnalysisResult,
sourceFiles: List<KtFile>,
module: Module?
): GenerationState {
val generationState = GenerationState.Builder(
environment.project,
ClassBuilderFactories.BINARIES,
result.moduleDescriptor,
result.bindingContext,
sourceFiles,
configuration
)
.codegenFactory(if (configuration.getBoolean(JVMConfigurationKeys.IR)) JvmIrCodegenFactory else DefaultCodegenFactory)
.withModule(module)
.onIndependentPartCompilationEnd(createOutputFilesFlushingCallbackIfPossible(configuration))
.build()
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val performanceManager = environment.configuration.get(CLIConfigurationKeys.PERF_MANAGER)
performanceManager?.notifyGenerationStarted()
KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION)
performanceManager?.notifyGenerationFinished(
sourceFiles.size,
environment.countLinesOfCode(sourceFiles),
additionalDescription = if (module != null) "target " + module.getModuleName() + "-" + module.getModuleType() + " " else ""
)
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
AnalyzerWithCompilerReport.reportDiagnostics(
FilteredJvmDiagnostics(
generationState.collectedExtraJvmDiagnostics,
result.bindingContext.diagnostics
),
environment.messageCollector
)
AnalyzerWithCompilerReport.reportBytecodeVersionErrors(
generationState.extraJvmDiagnosticsTrace.bindingContext, environment.messageCollector
)
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
return generationState
}
private val KotlinCoreEnvironment.messageCollector: MessageCollector
get() = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
}
@@ -1,471 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.compiler
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.PsiModificationTrackerImpl
import com.intellij.psi.search.DelegatingGlobalSearchScope
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.asJava.FilteredJvmDiagnostics
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.checkKotlinPackageUsage
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoots
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.OUTPUT
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.WARNING
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
import org.jetbrains.kotlin.cli.common.output.writeAll
import org.jetbrains.kotlin.cli.jvm.config.*
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.GenerationStateEventCallback
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.javac.JavacWrapper
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager
import org.jetbrains.kotlin.modules.Module
import org.jetbrains.kotlin.modules.TargetId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.script.tryConstructClassFromStringArgs
import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize
import java.io.File
import java.lang.reflect.InvocationTargetException
import java.net.URLClassLoader
object KotlinToJVMBytecodeCompiler {
private fun getAbsolutePaths(buildFile: File, module: Module): List<String> {
return module.getSourceFiles().map { sourceFile ->
val source = File(sourceFile)
if (!source.isAbsolute) {
File(buildFile.absoluteFile.parentFile, sourceFile).absolutePath
} else {
source.absolutePath
}
}
}
private fun writeOutput(
configuration: CompilerConfiguration,
outputFiles: OutputFileCollection,
mainClass: FqName?
) {
val reportOutputFiles = configuration.getBoolean(CommonConfigurationKeys.REPORT_OUTPUT_FILES)
val jarPath = configuration.get(JVMConfigurationKeys.OUTPUT_JAR)
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)
if (jarPath != null) {
val includeRuntime = configuration.get(JVMConfigurationKeys.INCLUDE_RUNTIME, false)
CompileEnvironmentUtil.writeToJar(jarPath, includeRuntime, mainClass, outputFiles)
if (reportOutputFiles) {
val message = OutputMessageUtil.formatOutputMessage(outputFiles.asList().flatMap { it.sourceFiles }.distinct(), jarPath)
messageCollector.report(OUTPUT, message)
}
return
}
val outputDir = configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY) ?: File(".")
outputFiles.writeAll(outputDir, messageCollector, reportOutputFiles)
}
private fun createOutputFilesFlushingCallbackIfPossible(configuration: CompilerConfiguration): GenerationStateEventCallback {
if (configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY) == null) {
return GenerationStateEventCallback.DO_NOTHING
}
return GenerationStateEventCallback { state ->
val currentOutput = SimpleOutputFileCollection(state.factory.currentOutput)
writeOutput(configuration, currentOutput, mainClass = null)
if (!configuration.get(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, false)) {
state.factory.releaseGeneratedOutput()
}
}
}
internal fun compileModules(environment: KotlinCoreEnvironment, buildFile: File, chunk: List<Module>): Boolean {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(environment.project)
val projectConfiguration = environment.configuration
for (module in chunk) {
moduleVisibilityManager.addModule(module)
}
val friendPaths = environment.configuration.getList(JVMConfigurationKeys.FRIEND_PATHS)
for (path in friendPaths) {
moduleVisibilityManager.addFriendPath(path)
}
val targetDescription = "in targets [" + chunk.joinToString { input -> input.getModuleName() + "-" + input.getModuleType() } + "]"
val result = repeatAnalysisIfNeeded(analyze(environment, targetDescription), environment, targetDescription)
if (result == null || !result.shouldGenerateCode) return false
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
result.throwIfError()
val outputs = newLinkedHashMapWithExpectedSize<Module, GenerationState>(chunk.size)
for (module in chunk) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val moduleSourcePaths = getAbsolutePaths(buildFile, module)
val ktFiles = environment.getSourceFiles().filter { file -> file.virtualFilePath in moduleSourcePaths }
if (!checkKotlinPackageUsage(environment, ktFiles)) return false
val moduleConfiguration = projectConfiguration.copy().apply {
put(JVMConfigurationKeys.OUTPUT_DIRECTORY, File(module.getOutputDirectory()))
}
outputs[module] = generate(environment, moduleConfiguration, result, ktFiles, module)
}
try {
for ((_, state) in outputs) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
writeOutput(state.configuration, state.factory, null)
}
if (projectConfiguration.getBoolean(JVMConfigurationKeys.COMPILE_JAVA)) {
val singleModule = chunk.singleOrNull()
if (singleModule != null) {
return JavacWrapper.getInstance(environment.project).use {
it.compile(File(singleModule.getOutputDirectory()))
}
} else {
projectConfiguration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
WARNING,
"A chunk contains multiple modules (${chunk.joinToString { it.getModuleName() }}). " +
"-Xuse-javac option couldn't be used to compile java files"
)
JavacWrapper.getInstance(environment.project).close()
}
}
return true
} finally {
outputs.values.forEach(GenerationState::destroy)
}
}
internal fun configureSourceRoots(configuration: CompilerConfiguration, chunk: List<Module>, buildFile: File) {
for (module in chunk) {
configuration.addKotlinSourceRoots(getAbsolutePaths(buildFile, module))
}
for (module in chunk) {
for ((path, packagePrefix) in module.getJavaSourceRoots()) {
configuration.addJavaSourceRoot(File(path), packagePrefix)
}
}
val isJava9Module = false /*chunk.any { module ->
module.getJavaSourceRoots().any { (path, packagePrefix) ->
val file = File(path)
packagePrefix == null &&
(file.name == PsiJavaModule.MODULE_INFO_FILE ||
(file.isDirectory && file.listFiles().any { it.name == PsiJavaModule.MODULE_INFO_FILE }))
}
}*/
for (module in chunk) {
for (classpathRoot in module.getClasspathRoots()) {
configuration.add(
CLIConfigurationKeys.CONTENT_ROOTS,
if (isJava9Module) JvmModulePathRoot(File(classpathRoot)) else JvmClasspathRoot(File(classpathRoot))
)
}
}
for (module in chunk) {
val modularJdkRoot = module.modularJdkRoot
if (modularJdkRoot != null) {
// We use the SDK of the first module in the chunk, which is not always correct because some other module in the chunk
// might depend on a different SDK
configuration.put(JVMConfigurationKeys.JDK_HOME, File(modularJdkRoot))
break
}
}
configuration.addAll(JVMConfigurationKeys.MODULES, chunk)
}
private fun findMainClass(generationState: GenerationState, files: List<KtFile>): FqName? {
val mainFunctionDetector = MainFunctionDetector(generationState.bindingContext)
return files.asSequence()
.map { file ->
if (mainFunctionDetector.hasMain(file.declarations))
JvmFileClassUtil.getFileClassInfoNoResolve(file).facadeClassFqName
else
null
}
.singleOrNull { it != null }
}
fun compileBunchOfSources(environment: KotlinCoreEnvironment): Boolean {
val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(environment.project)
val friendPaths = environment.configuration.getList(JVMConfigurationKeys.FRIEND_PATHS)
for (path in friendPaths) {
moduleVisibilityManager.addFriendPath(path)
}
if (!checkKotlinPackageUsage(environment, environment.getSourceFiles())) return false
val generationState = analyzeAndGenerate(environment) ?: return false
val mainClass = findMainClass(generationState, environment.getSourceFiles())
try {
writeOutput(environment.configuration, generationState.factory, mainClass)
return true
} finally {
generationState.destroy()
}
}
internal fun compileAndExecuteScript(environment: KotlinCoreEnvironment, scriptArgs: List<String>): ExitCode {
val scriptClass = compileScript(environment) ?: return ExitCode.COMPILATION_ERROR
try {
try {
tryConstructClassFromStringArgs(scriptClass, scriptArgs)
?: throw RuntimeException("unable to find appropriate constructor for class ${scriptClass.name} accepting arguments $scriptArgs\n")
} finally {
// NB: these lines are required (see KT-9546) but aren't covered by tests
System.out.flush()
System.err.flush()
}
} catch (e: Throwable) {
reportExceptionFromScript(e)
return ExitCode.SCRIPT_EXECUTION_ERROR
}
return ExitCode.OK
}
private fun repeatAnalysisIfNeeded(
result: AnalysisResult?,
environment: KotlinCoreEnvironment,
targetDescription: String?
): AnalysisResult? {
if (result is AnalysisResult.RetryWithAdditionalJavaRoots) {
val configuration = environment.configuration
val oldReadOnlyValue = configuration.isReadOnly
configuration.isReadOnly = false
configuration.addJavaSourceRoots(result.additionalJavaRoots)
configuration.isReadOnly = oldReadOnlyValue
if (result.addToEnvironment) {
environment.updateClasspath(result.additionalJavaRoots.map { JavaSourceRoot(it, null) })
}
// Clear package caches (see KotlinJavaPsiFacade)
ApplicationManager.getApplication().runWriteAction {
(PsiManager.getInstance(environment.project).modificationTracker as? PsiModificationTrackerImpl)?.incCounter()
}
// Clear all diagnostic messages
configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY]?.clear()
// Repeat analysis with additional Java roots (kapt generated sources)
return analyze(environment, targetDescription)
}
return result
}
private fun reportExceptionFromScript(exception: Throwable) {
// expecting InvocationTargetException from constructor invocation with cause that describes the actual cause
val stream = System.err
val cause = exception.cause
if (exception !is InvocationTargetException || cause == null) {
exception.printStackTrace(stream)
return
}
stream.println(cause)
val fullTrace = cause.stackTrace
for (i in 0 until fullTrace.size - exception.stackTrace.size) {
stream.println("\tat " + fullTrace[i])
}
}
fun compileScript(environment: KotlinCoreEnvironment, parentClassLoader: ClassLoader? = null): Class<*>? {
val state = analyzeAndGenerate(environment) ?: return null
try {
val urls = environment.configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS).mapNotNull { root ->
when (root) {
is JvmModulePathRoot -> root.file // TODO: only add required modules
is JvmClasspathRoot -> root.file
else -> null
}
}.map { it.toURI().toURL() }
val classLoader = GeneratedClassLoader(state.factory, parentClassLoader ?: URLClassLoader(urls.toTypedArray(), null))
val script = environment.getSourceFiles()[0].script ?: error("Script must be parsed")
return classLoader.loadClass(script.fqName.asString())
} catch (e: Exception) {
throw RuntimeException("Failed to evaluate script: $e", e)
}
}
private fun analyzeAndGenerate(environment: KotlinCoreEnvironment): GenerationState? {
val result = repeatAnalysisIfNeeded(analyze(environment, null), environment, null) ?: return null
if (!result.shouldGenerateCode) return null
result.throwIfError()
return generate(environment, environment.configuration, result, environment.getSourceFiles(), null)
}
private fun analyze(environment: KotlinCoreEnvironment, targetDescription: String?): AnalysisResult? {
val sourceFiles = environment.getSourceFiles()
val collector = environment.messageCollector
// Can be null for Scripts/REPL
val performanceManager = environment.configuration.get(CLIConfigurationKeys.PERF_MANAGER)
performanceManager?.notifyAnalysisStarted()
val analyzerWithCompilerReport = AnalyzerWithCompilerReport(collector, environment.configuration.languageVersionSettings)
analyzerWithCompilerReport.analyzeAndReport(sourceFiles) {
val project = environment.project
val moduleOutputs = environment.configuration.get(JVMConfigurationKeys.MODULES)?.mapNotNullTo(hashSetOf()) { module ->
environment.findLocalFile(module.getOutputDirectory())
}.orEmpty()
val sourcesOnly = TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, sourceFiles)
// To support partial and incremental compilation, we add the scope which contains binaries from output directories
// of the compiled modules (.class) to the list of scopes of the source module
val scope = if (moduleOutputs.isEmpty()) sourcesOnly else sourcesOnly.uniteWith(DirectoriesScope(project, moduleOutputs))
TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
project,
sourceFiles,
NoScopeRecordCliBindingTrace(),
environment.configuration,
environment::createPackagePartProvider,
sourceModuleSearchScope = scope
)
}
performanceManager?.notifyAnalysisFinished(sourceFiles.size, environment.countLinesOfCode(sourceFiles), targetDescription)
val analysisResult = analyzerWithCompilerReport.analysisResult
return if (!analyzerWithCompilerReport.hasErrors() || analysisResult is AnalysisResult.RetryWithAdditionalJavaRoots)
analysisResult
else
null
}
class DirectoriesScope(
project: Project,
private val directories: Set<VirtualFile>
) : DelegatingGlobalSearchScope(GlobalSearchScope.allScope(project)) {
private val fileSystems = directories.mapTo(hashSetOf(), VirtualFile::getFileSystem)
override fun contains(file: VirtualFile): Boolean {
if (file.fileSystem !in fileSystems) return false
var parent: VirtualFile = file
while (true) {
if (parent in directories) return true
parent = parent.parent ?: return false
}
}
override fun toString() = "All files under: $directories"
}
private fun GenerationState.Builder.withModule(module: Module?) =
apply {
targetId(module?.let { TargetId(it) })
moduleName(module?.getModuleName())
outDirectory(module?.let { File(it.getOutputDirectory()) })
}
private fun generate(
environment: KotlinCoreEnvironment,
configuration: CompilerConfiguration,
result: AnalysisResult,
sourceFiles: List<KtFile>,
module: Module?
): GenerationState {
val generationState = GenerationState.Builder(
environment.project,
ClassBuilderFactories.BINARIES,
result.moduleDescriptor,
result.bindingContext,
sourceFiles,
configuration
)
.codegenFactory(if (configuration.getBoolean(JVMConfigurationKeys.IR)) JvmIrCodegenFactory else DefaultCodegenFactory)
.withModule(module)
.onIndependentPartCompilationEnd(createOutputFilesFlushingCallbackIfPossible(configuration))
.build()
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val performanceManager = environment.configuration.get(CLIConfigurationKeys.PERF_MANAGER)
performanceManager?.notifyGenerationStarted()
KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION)
performanceManager?.notifyGenerationFinished(
sourceFiles.size,
environment.countLinesOfCode(sourceFiles),
additionalDescription = if (module != null) "target " + module.getModuleName() + "-" + module.getModuleType() + " " else ""
)
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
AnalyzerWithCompilerReport.reportDiagnostics(
FilteredJvmDiagnostics(
generationState.collectedExtraJvmDiagnostics,
result.bindingContext.diagnostics
),
environment.messageCollector
)
AnalyzerWithCompilerReport.reportBytecodeVersionErrors(
generationState.extraJvmDiagnosticsTrace.bindingContext, environment.messageCollector
)
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
return generationState
}
private val KotlinCoreEnvironment.messageCollector: MessageCollector
get() = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
}
@@ -1,48 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.modules
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder
class CliJavaModuleFinder(jrtFileSystemRoot: VirtualFile?) : JavaModuleFinder {
private val modulesRoot = jrtFileSystemRoot?.findChild("modules")
private val userModules = linkedMapOf<String, JavaModule>()
fun addUserModule(module: JavaModule) {
userModules.putIfAbsent(module.name, module)
}
val allObservableModules: Sequence<JavaModule>
get() = systemModules + userModules.values
val systemModules: Sequence<JavaModule.Explicit>
get() = modulesRoot?.children.orEmpty().asSequence().mapNotNull(this::findSystemModule)
override fun findModule(name: String): JavaModule? =
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
val moduleInfo = JavaModuleInfo.read(file) ?: return null
return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(moduleRoot, isBinary = true)), file)
*/
return null
}
}
@@ -1,48 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.modules
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder
class CliJavaModuleFinder(jrtFileSystemRoot: VirtualFile?) : JavaModuleFinder {
private val modulesRoot = jrtFileSystemRoot?.findChild("modules")
private val userModules = linkedMapOf<String, JavaModule>()
fun addUserModule(module: JavaModule) {
userModules.putIfAbsent(module.name, module)
}
val allObservableModules: Sequence<JavaModule>
get() = systemModules + userModules.values
val systemModules: Sequence<JavaModule.Explicit>
get() = modulesRoot?.children.orEmpty().asSequence().mapNotNull(this::findSystemModule)
override fun findModule(name: String): JavaModule? =
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
val moduleInfo = JavaModuleInfo.read(file) ?: return null
return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(moduleRoot, isBinary = true)), file)
*/
return null
}
}