Remove unneeded bunch files related to PsiJavaModule
PSI for modules and related classes are already available in AS3.1
This commit is contained in:
-332
@@ -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"
|
||||
}
|
||||
}
|
||||
-332
@@ -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"
|
||||
}
|
||||
}
|
||||
-291
@@ -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)) }
|
||||
-291
@@ -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)) }
|
||||
-471
@@ -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)
|
||||
}
|
||||
-471
@@ -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
|
||||
}
|
||||
}
|
||||
-89
@@ -1,89 +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.resolve.jvm.modules
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
//import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.PsiModifier
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.utils.compact
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor
|
||||
//import org.jetbrains.org.objectweb.asm.ModuleVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
//import org.jetbrains.org.objectweb.asm.Opcodes.ACC_TRANSITIVE
|
||||
import java.io.IOException
|
||||
|
||||
class JavaModuleInfo(
|
||||
val moduleName: String,
|
||||
val requires: List<Requires>,
|
||||
val exports: List<Exports>
|
||||
) {
|
||||
data class Requires(val moduleName: String, val isTransitive: Boolean)
|
||||
|
||||
data class Exports(val packageFqName: FqName, val toModules: List<String>)
|
||||
|
||||
override fun toString(): String =
|
||||
"Module $moduleName (${requires.size} requires, ${exports.size} exports)"
|
||||
|
||||
companion object {
|
||||
/*fun create(psiJavaModule: PsiJavaModule): JavaModuleInfo {
|
||||
return JavaModuleInfo(
|
||||
psiJavaModule.name,
|
||||
psiJavaModule.requires.mapNotNull { statement ->
|
||||
statement.moduleName?.let { moduleName ->
|
||||
JavaModuleInfo.Requires(moduleName, statement.hasModifierProperty(PsiModifier.TRANSITIVE))
|
||||
}
|
||||
},
|
||||
psiJavaModule.exports.mapNotNull { statement ->
|
||||
statement.packageName?.let { packageName ->
|
||||
JavaModuleInfo.Exports(FqName(packageName), statement.moduleNames)
|
||||
}
|
||||
}
|
||||
)
|
||||
}*/
|
||||
|
||||
/*fun read(file: VirtualFile): JavaModuleInfo? {
|
||||
val contents = try { file.contentsToByteArray() } catch (e: IOException) { return null }
|
||||
|
||||
var moduleName: String? = null
|
||||
val requires = arrayListOf<Requires>()
|
||||
val exports = arrayListOf<Exports>()
|
||||
|
||||
ClassReader(contents).accept(object : ClassVisitor(Opcodes.ASM6) {
|
||||
override fun visitModule(name: String, access: Int, version: String?): ModuleVisitor {
|
||||
moduleName = name
|
||||
|
||||
return object : ModuleVisitor(Opcodes.ASM6) {
|
||||
override fun visitRequire(module: String, access: Int, version: String?) {
|
||||
requires.add(Requires(module, (access and ACC_TRANSITIVE) != 0))
|
||||
}
|
||||
|
||||
override fun visitExport(packageFqName: String, access: Int, modules: Array<String>?) {
|
||||
// For some reason, '/' is the delimiter in packageFqName here
|
||||
exports.add(Exports(FqName(packageFqName.replace('/', '.')), modules?.toList().orEmpty()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}, ClassReader.SKIP_DEBUG or ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES)
|
||||
|
||||
return if (moduleName != null)
|
||||
JavaModuleInfo(moduleName!!, requires.compact(), exports.compact())
|
||||
else null
|
||||
}*/
|
||||
}
|
||||
}
|
||||
-89
@@ -1,89 +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.resolve.jvm.modules
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
//import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.PsiModifier
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.utils.compact
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor
|
||||
//import org.jetbrains.org.objectweb.asm.ModuleVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
//import org.jetbrains.org.objectweb.asm.Opcodes.ACC_TRANSITIVE
|
||||
import java.io.IOException
|
||||
|
||||
class JavaModuleInfo(
|
||||
val moduleName: String,
|
||||
val requires: List<Requires>,
|
||||
val exports: List<Exports>
|
||||
) {
|
||||
data class Requires(val moduleName: String, val isTransitive: Boolean)
|
||||
|
||||
data class Exports(val packageFqName: FqName, val toModules: List<String>)
|
||||
|
||||
override fun toString(): String =
|
||||
"Module $moduleName (${requires.size} requires, ${exports.size} exports)"
|
||||
|
||||
companion object {
|
||||
/*fun create(psiJavaModule: PsiJavaModule): JavaModuleInfo {
|
||||
return JavaModuleInfo(
|
||||
psiJavaModule.name,
|
||||
psiJavaModule.requires.mapNotNull { statement ->
|
||||
statement.moduleName?.let { moduleName ->
|
||||
JavaModuleInfo.Requires(moduleName, statement.hasModifierProperty(PsiModifier.TRANSITIVE))
|
||||
}
|
||||
},
|
||||
psiJavaModule.exports.mapNotNull { statement ->
|
||||
statement.packageName?.let { packageName ->
|
||||
JavaModuleInfo.Exports(FqName(packageName), statement.moduleNames)
|
||||
}
|
||||
}
|
||||
)
|
||||
}*/
|
||||
|
||||
/*fun read(file: VirtualFile): JavaModuleInfo? {
|
||||
val contents = try { file.contentsToByteArray() } catch (e: IOException) { return null }
|
||||
|
||||
var moduleName: String? = null
|
||||
val requires = arrayListOf<Requires>()
|
||||
val exports = arrayListOf<Exports>()
|
||||
|
||||
ClassReader(contents).accept(object : ClassVisitor(Opcodes.ASM6) {
|
||||
override fun visitModule(name: String, access: Int, version: String?): ModuleVisitor {
|
||||
moduleName = name
|
||||
|
||||
return object : ModuleVisitor(Opcodes.ASM6) {
|
||||
override fun visitRequire(module: String, access: Int, version: String?) {
|
||||
requires.add(Requires(module, (access and ACC_TRANSITIVE) != 0))
|
||||
}
|
||||
|
||||
override fun visitExport(packageFqName: String, access: Int, modules: Array<String>?) {
|
||||
// For some reason, '/' is the delimiter in packageFqName here
|
||||
exports.add(Exports(FqName(packageFqName.replace('/', '.')), modules?.toList().orEmpty()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}, ClassReader.SKIP_DEBUG or ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES)
|
||||
|
||||
return if (moduleName != null)
|
||||
JavaModuleInfo(moduleName!!, requires.compact(), exports.compact())
|
||||
else null
|
||||
}*/
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user