Release flag. Add diagnostics

This commit is contained in:
Mikhael Bogdanov
2021-11-10 10:58:03 +01:00
committed by Space
parent e5e3e9d326
commit c2d17ded33
20 changed files with 160 additions and 47 deletions
@@ -392,7 +392,7 @@ default: `indy-with-constants` for JVM target 9 or greater, `inline` otherwise""
value = "-Xrelease", value = "-Xrelease",
valueDescription = "Supported versions depend on used JDK", valueDescription = "Supported versions depend on used JDK",
description = """ description = """
Compile against specified JDK API. Supported versions depend on used JDK Compile against specified JDK API. Supported versions depend on used JDK.
""" """
) )
var release: String? by NullableStringFreezableVar(null) var release: String? by NullableStringFreezableVar(null)
@@ -239,15 +239,10 @@ class KotlinCoreEnvironment private constructor(
val javaFileManager = ServiceManager.getService(project, CoreJavaFileManager::class.java) as KotlinCliJavaFileManagerImpl val javaFileManager = ServiceManager.getService(project, CoreJavaFileManager::class.java) as KotlinCliJavaFileManagerImpl
val jdkHome = configuration.get(JVMConfigurationKeys.JDK_HOME) val jdkHome = configuration.get(JVMConfigurationKeys.JDK_HOME)
val jrtFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL)
val releaseTarget = configuration.get(JVMConfigurationKeys.RELEASE, 0) val releaseTarget = configuration.get(JVMConfigurationKeys.RELEASE, 0)
val javaModuleFinder = CliJavaModuleFinder( val javaModuleFinder = CliJavaModuleFinder(
jdkHome?.path?.let { path -> jdkHome,
VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL).findFileByPath(path) messageCollector,
},
jdkHome?.path?.let { path ->
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
},
javaFileManager, javaFileManager,
project, project,
releaseTarget releaseTarget
@@ -73,17 +73,26 @@ fun CompilerConfiguration.setupJvmSpecificArguments(arguments: K2JVMCompilerArgu
} }
} }
val release = arguments.release val releaseTarget = arguments.release
if (release != null) { if (releaseTarget != null) {
val value = release.toIntOrNull() val value = releaseTarget.toIntOrNull()
if (value == null) { if (value == null) {
messageCollector.report( messageCollector.report(
ERROR, ERROR,
"Can't parse value passed for `-Xrelease`: $release." "Can't parse value passed for `-Xrelease`: $releaseTarget."
) )
} else { } else {
// TODO: check if (value < 6) {
put(JVMConfigurationKeys.RELEASE, value) messageCollector.report(
ERROR,
"`$value` is not valid value for `-Xrelease` flag."
)
} else {
//don't use release flag if it equals to compilation JDK version
if (value != getJavaVersion() || arguments.jdkHome != null) {
put(JVMConfigurationKeys.RELEASE, value)
}
}
} }
} }
@@ -321,3 +330,6 @@ fun CompilerConfiguration.configureKlibPaths(arguments: K2JVMCompilerArguments)
private val CompilerConfiguration.messageCollector: MessageCollector private val CompilerConfiguration.messageCollector: MessageCollector
get() = getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) get() = getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
private fun getJavaVersion(): Int =
System.getProperty("java.specification.version")?.substringAfter('.')?.toIntOrNull() ?: 6
@@ -23,29 +23,61 @@ import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiJavaModule import com.intellij.psi.PsiJavaModule
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.io.URLUtil import com.intellij.util.io.URLUtil
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.resolve.jvm.KotlinCliJavaFileManager import org.jetbrains.kotlin.resolve.jvm.KotlinCliJavaFileManager
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
import java.io.File
class CliJavaModuleFinder( class CliJavaModuleFinder(
jdkRootFile: VirtualFile?, private val jdkHome: File?,
jrtFileSystemRoot: VirtualFile?, private val messageCollector: MessageCollector?,
private val javaFileManager: KotlinCliJavaFileManager, private val javaFileManager: KotlinCliJavaFileManager,
project: Project, project: Project,
private val releaseTarget: Int private val releaseTarget: Int
) : JavaModuleFinder { ) : JavaModuleFinder {
private val jrtFileSystemRoot = jdkHome?.path?.let { path ->
VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL)?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
}
private val modulesRoot = jrtFileSystemRoot?.findChild("modules") private val modulesRoot = jrtFileSystemRoot?.findChild("modules")
private val ctSymFile = jdkRootFile?.findChild("lib")?.findChild("ct.sym")
private val userModules = linkedMapOf<String, JavaModule>() private val userModules = linkedMapOf<String, JavaModule>()
private val allScope = GlobalSearchScope.allScope(project) private val allScope = GlobalSearchScope.allScope(project)
public val compilationJdkVersion by lazy { private val ctSymFile: VirtualFile? by lazy {
//TODO: add test with -jdk-home if (jdkHome == null) return@lazy reportError("JDK_HOME path is not specified in compiler configuration")
val jdkRootFile = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL).findFileByPath(jdkHome.path)
?: return@lazy reportError("Can't create virtual file for JDK root under ${jdkHome.path}")
val lib = jdkRootFile.findChild("lib") ?: return@lazy reportError("Can't find `lib` folder under JDK root: ${jdkHome.path}")
val ctSym = lib.findChild("ct.sym") ?: return@lazy reportError("Can't find `ct.sym` file in ${jdkHome.path}")
if (!ctSym.isValid) return@lazy reportError("`ct.sym` is invalid: ${ctSym.path}")
ctSym
}
private val ctSymRootFolder: VirtualFile? by lazy {
if (ctSymFile != null) {
VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JAR_PROTOCOL)
?.findFileByPath(ctSymFile?.path + URLUtil.JAR_SEPARATOR)
?: reportError("Can't open `ct.sym` as jar file, file path: ${ctSymFile?.path} ")
} else {
null
}
}
private val compilationJdkVersion by lazy {
// Observe all JDK codes from folder name chars in ct.sym file, // Observe all JDK codes from folder name chars in ct.sym file,
// there should be maximal one corresponding to used compilation JDK // there should be maximal one corresponding to used compilation JDK
ctSymRootFolder()?.children?.maxOf { ctSymRootFolder?.children?.maxOf {
if (it.name == "META-INF") -1 if (it.name == "META-INF") -1
else it.name.substringBeforeLast("-modules").maxOf { char -> else it.name.substringBeforeLast("-modules").maxOf { char ->
char.toString().toIntOrNull(36) ?: -1 char.toString().toIntOrNull(36) ?: -1
@@ -53,7 +85,11 @@ class CliJavaModuleFinder(
} ?: -1 } ?: -1
} }
private val isJDK12OrLater val ctSymModules by lazy {
collectModuleRoots()
}
private val isCompilationJDK12OrLater
get() = compilationJdkVersion >= 12 get() = compilationJdkVersion >= 12
private val useLastJdkApi: Boolean private val useLastJdkApi: Boolean
@@ -66,10 +102,6 @@ class CliJavaModuleFinder(
val allObservableModules: Sequence<JavaModule> val allObservableModules: Sequence<JavaModule>
get() = systemModules + userModules.values get() = systemModules + userModules.values
val ctSymModules by lazy {
collectModuleRoots(releaseTarget)
}
val systemModules: Sequence<JavaModule.Explicit> val systemModules: Sequence<JavaModule.Explicit>
get() = if (useLastJdkApi) modulesRoot?.children.orEmpty().asSequence().mapNotNull(this::findSystemModule) else get() = if (useLastJdkApi) modulesRoot?.children.orEmpty().asSequence().mapNotNull(this::findSystemModule) else
ctSymModules.values.asSequence().mapNotNull { findSystemModule(it, true) } ctSymModules.values.asSequence().mapNotNull { findSystemModule(it, true) }
@@ -101,44 +133,41 @@ class CliJavaModuleFinder(
private fun matchesRelease(fileName: String, release: Int) = private fun matchesRelease(fileName: String, release: Int) =
!fileName.contains("-") && fileName.contains(codeFor(release)) // skip `*-modules` !fileName.contains("-") && fileName.contains(codeFor(release)) // skip `*-modules`
private fun hasCtSymFile() = ctSymFile != null && ctSymFile.isValid
fun listFoldersForRelease(): List<VirtualFile> { fun listFoldersForRelease(): List<VirtualFile> {
if (!hasCtSymFile()) return emptyList() if (ctSymRootFolder == null) return emptyList()
val root = ctSymRootFolder() ?: return emptyList() return ctSymRootFolder!!.children.filter { matchesRelease(it.name, releaseTarget) }.flatMap {
return root.children.filter { matchesRelease(it.name, releaseTarget) }.flatMap { if (isCompilationJDK12OrLater)
if (isJDK12OrLater)
it.children.toList() it.children.toList()
else { else {
listOf(it) listOf(it)
} }
}.apply {
if (isEmpty()) reportError("'-Xrelease=${releaseTarget}' option is not supported by used JDK: ${jdkHome?.path}")
} }
} }
private fun collectModuleRoots(release: Int): Map<String, VirtualFile> { private fun collectModuleRoots(): Map<String, VirtualFile> {
if (!hasCtSymFile()) return emptyMap()
val result = mutableMapOf<String, VirtualFile>() val result = mutableMapOf<String, VirtualFile>()
val root = ctSymRootFolder() ?: return emptyMap() if (isCompilationJDK12OrLater) {
if (isJDK12OrLater) {
listFoldersForRelease().forEach { modulesRoot -> listFoldersForRelease().forEach { modulesRoot ->
modulesRoot.findChild("module-info.sig")?.let { modulesRoot.findChild("module-info.sig")?.let {
result[modulesRoot.name] = modulesRoot result[modulesRoot.name] = modulesRoot
} }
} }
} else { } else {
if (releaseTarget > 8) { if (this.releaseTarget > 8 && ctSymRootFolder != null) {
val moduleSigs = root.findChild(codeFor(release) + if (!isJDK12OrLater) "-modules" else "") ctSymRootFolder!!.findChild(codeFor(releaseTarget) + if (!isCompilationJDK12OrLater) "-modules" else "")?.apply {
?: error("Can't find modules signatures in `ct.sym` file for `-release $release` in ${ctSymFile?.path}") children.forEach {
moduleSigs.children.forEach { result[it.name] = it
result[it.name] = it }
} } ?: reportError("Can't find modules signatures in `ct.sym` file for `-Xrelease=$releaseTarget` in ${ctSymFile!!.path}")
} }
} }
return result return result
} }
private fun ctSymRootFolder() = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JAR_PROTOCOL) private fun reportError(message: String): VirtualFile? {
?.findFileByPath(ctSymFile!!.path + URLUtil.JAR_SEPARATOR) messageCollector?.report(CompilerMessageSeverity.ERROR, message)
return null
}
} }
@@ -54,7 +54,8 @@ class CliJavaModuleResolver(
private val sourceModule: JavaModule? = userModules.firstOrNull(JavaModule::isSourceModule) private val sourceModule: JavaModule? = userModules.firstOrNull(JavaModule::isSourceModule)
private fun findJavaModule(file: VirtualFile): JavaModule? { private fun findJavaModule(file: VirtualFile): JavaModule? {
if (file.fileSystem.protocol == StandardFileSystems.JRT_PROTOCOL) { //TODO: proper root filtering is required
if (file.fileSystem.protocol == StandardFileSystems.JRT_PROTOCOL /*|| file.extension == "sig"*/) {
return systemModules.firstOrNull { module -> file in module } return systemModules.firstOrNull { module -> file in module }
} }
+1
View File
@@ -0,0 +1 @@
OK
@@ -0,0 +1,5 @@
package foo;
public class Foo {
val z: java.nio.ByteBuffer? = null
}
@@ -0,0 +1,5 @@
module module {
exports foo;
requires kotlin.stdlib;
}
@@ -0,0 +1 @@
OK
@@ -0,0 +1,5 @@
package foo;
public class Foo {
val z: java.nio.ByteBuffer? = null
}
@@ -0,0 +1,5 @@
module module11 {
exports foo;
requires kotlin.stdlib;
}
@@ -0,0 +1,4 @@
compiler/testData/javaModules/releaseFlag/moduleSwing/foo/Foo.kt:5:24: error: symbol is declared in module 'javafx.media' which current module does not depend on
val z: javax.swing.JFrame? = null
^
COMPILATION_ERROR
@@ -0,0 +1,6 @@
package foo;
public class Foo {
//no requirement
val z: javax.swing.JFrame? = null
}
@@ -0,0 +1,3 @@
package foo;
public class Foo2 {}
@@ -0,0 +1,5 @@
module moduleSwing {
exports foo;
requires kotlin.stdlib;
}
@@ -0,0 +1,3 @@
error: can't find modules signatures in `ct.sym` file for `-Xrelease=12` in $JDK11/lib/ct.sym
error: module java.base cannot be found in the module graph
COMPILATION_ERROR
@@ -0,0 +1,6 @@
package foo;
public class Foo {
val z: java.nio.ByteBuffer? = null
}
@@ -0,0 +1,2 @@
error: `5` is not valid value for `-Xrelease` flag.
COMPILATION_ERROR
@@ -0,0 +1,5 @@
package foo;
public class Foo {
val z: java.nio.ByteBuffer? = null
}
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.jvm.compiler
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.cli.AbstractCliTest import org.jetbrains.kotlin.cli.AbstractCliTest
import org.jetbrains.kotlin.cli.AbstractCliTest.getNormalizedCompilerOutput
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.util.KtTestUtil import org.jetbrains.kotlin.test.util.KtTestUtil
@@ -59,7 +61,10 @@ class Java11ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
} }
private fun checkKotlinOutput(moduleName: String): (String) -> Unit = { actual -> private fun checkKotlinOutput(moduleName: String): (String) -> Unit = { actual ->
KotlinTestUtils.assertEqualsToFile(File(testDataDirectory, "$moduleName.txt"), actual) KotlinTestUtils.assertEqualsToFile(
File(testDataDirectory, "$moduleName.txt"),
getNormalizedCompilerOutput(actual, null, testDataPath).replace(System.getenv("JDK_11"), "\$JDK11")
)
} }
private data class ModuleRunResult(val stdout: String, val stderr: String) private data class ModuleRunResult(val stdout: String, val stderr: String)
@@ -144,6 +149,21 @@ class Java11ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
module("moduleB", listOf(module("moduleA")), addModules = emptyList()) module("moduleB", listOf(module("moduleA")), addModules = emptyList())
} }
fun testReleaseFlagWrongValue() {
// Test that although we have moduleA in the module path, it's not in the module graph
// because we did not provide -Xadd-modules=moduleA
module("module5", additionalKotlinArguments = listOf("-Xrelease=5"))
module("module12", additionalKotlinArguments = listOf("-Xrelease=12"))
}
fun testReleaseFlag() {
// Test that although we have moduleA in the module path, it's not in the module graph
// because we did not provide -Xadd-modules=moduleA
module("module")
module("module11", additionalKotlinArguments = listOf("-Xrelease=11"))
module("moduleSwing", additionalKotlinArguments = listOf("-Xrelease=9"))
}
fun testNamedReadsTransitive() { fun testNamedReadsTransitive() {
val a = module("moduleA") val a = module("moduleA")
val b = module("moduleB", listOf(a)) val b = module("moduleB", listOf(a))