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",
valueDescription = "Supported versions depend on used JDK",
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)
@@ -239,15 +239,10 @@ class KotlinCoreEnvironment private constructor(
val javaFileManager = ServiceManager.getService(project, CoreJavaFileManager::class.java) as KotlinCliJavaFileManagerImpl
val jdkHome = configuration.get(JVMConfigurationKeys.JDK_HOME)
val jrtFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL)
val releaseTarget = configuration.get(JVMConfigurationKeys.RELEASE, 0)
val javaModuleFinder = CliJavaModuleFinder(
jdkHome?.path?.let { path ->
VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL).findFileByPath(path)
},
jdkHome?.path?.let { path ->
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
},
jdkHome,
messageCollector,
javaFileManager,
project,
releaseTarget
@@ -73,17 +73,26 @@ fun CompilerConfiguration.setupJvmSpecificArguments(arguments: K2JVMCompilerArgu
}
}
val release = arguments.release
if (release != null) {
val value = release.toIntOrNull()
val releaseTarget = arguments.release
if (releaseTarget != null) {
val value = releaseTarget.toIntOrNull()
if (value == null) {
messageCollector.report(
ERROR,
"Can't parse value passed for `-Xrelease`: $release."
"Can't parse value passed for `-Xrelease`: $releaseTarget."
)
} else {
// TODO: check
put(JVMConfigurationKeys.RELEASE, value)
if (value < 6) {
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
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.search.GlobalSearchScope
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.modules.JavaModule
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
import java.io.File
class CliJavaModuleFinder(
jdkRootFile: VirtualFile?,
jrtFileSystemRoot: VirtualFile?,
private val jdkHome: File?,
private val messageCollector: MessageCollector?,
private val javaFileManager: KotlinCliJavaFileManager,
project: Project,
private val releaseTarget: Int
) : 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 ctSymFile = jdkRootFile?.findChild("lib")?.findChild("ct.sym")
private val userModules = linkedMapOf<String, JavaModule>()
private val allScope = GlobalSearchScope.allScope(project)
public val compilationJdkVersion by lazy {
//TODO: add test with -jdk-home
private val ctSymFile: VirtualFile? by lazy {
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,
// there should be maximal one corresponding to used compilation JDK
ctSymRootFolder()?.children?.maxOf {
ctSymRootFolder?.children?.maxOf {
if (it.name == "META-INF") -1
else it.name.substringBeforeLast("-modules").maxOf { char ->
char.toString().toIntOrNull(36) ?: -1
@@ -53,7 +85,11 @@ class CliJavaModuleFinder(
} ?: -1
}
private val isJDK12OrLater
val ctSymModules by lazy {
collectModuleRoots()
}
private val isCompilationJDK12OrLater
get() = compilationJdkVersion >= 12
private val useLastJdkApi: Boolean
@@ -66,10 +102,6 @@ class CliJavaModuleFinder(
val allObservableModules: Sequence<JavaModule>
get() = systemModules + userModules.values
val ctSymModules by lazy {
collectModuleRoots(releaseTarget)
}
val systemModules: Sequence<JavaModule.Explicit>
get() = if (useLastJdkApi) modulesRoot?.children.orEmpty().asSequence().mapNotNull(this::findSystemModule) else
ctSymModules.values.asSequence().mapNotNull { findSystemModule(it, true) }
@@ -101,44 +133,41 @@ class CliJavaModuleFinder(
private fun matchesRelease(fileName: String, release: Int) =
!fileName.contains("-") && fileName.contains(codeFor(release)) // skip `*-modules`
private fun hasCtSymFile() = ctSymFile != null && ctSymFile.isValid
fun listFoldersForRelease(): List<VirtualFile> {
if (!hasCtSymFile()) return emptyList()
val root = ctSymRootFolder() ?: return emptyList()
return root.children.filter { matchesRelease(it.name, releaseTarget) }.flatMap {
if (isJDK12OrLater)
if (ctSymRootFolder == null) return emptyList()
return ctSymRootFolder!!.children.filter { matchesRelease(it.name, releaseTarget) }.flatMap {
if (isCompilationJDK12OrLater)
it.children.toList()
else {
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> {
if (!hasCtSymFile()) return emptyMap()
private fun collectModuleRoots(): Map<String, VirtualFile> {
val result = mutableMapOf<String, VirtualFile>()
val root = ctSymRootFolder() ?: return emptyMap()
if (isJDK12OrLater) {
if (isCompilationJDK12OrLater) {
listFoldersForRelease().forEach { modulesRoot ->
modulesRoot.findChild("module-info.sig")?.let {
result[modulesRoot.name] = modulesRoot
}
}
} else {
if (releaseTarget > 8) {
val moduleSigs = root.findChild(codeFor(release) + if (!isJDK12OrLater) "-modules" else "")
?: error("Can't find modules signatures in `ct.sym` file for `-release $release` in ${ctSymFile?.path}")
moduleSigs.children.forEach {
result[it.name] = it
}
if (this.releaseTarget > 8 && ctSymRootFolder != null) {
ctSymRootFolder!!.findChild(codeFor(releaseTarget) + if (!isCompilationJDK12OrLater) "-modules" else "")?.apply {
children.forEach {
result[it.name] = it
}
} ?: reportError("Can't find modules signatures in `ct.sym` file for `-Xrelease=$releaseTarget` in ${ctSymFile!!.path}")
}
}
return result
}
private fun ctSymRootFolder() = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JAR_PROTOCOL)
?.findFileByPath(ctSymFile!!.path + URLUtil.JAR_SEPARATOR)
private fun reportError(message: String): VirtualFile? {
messageCollector?.report(CompilerMessageSeverity.ERROR, message)
return null
}
}
@@ -54,7 +54,8 @@ class CliJavaModuleResolver(
private val sourceModule: JavaModule? = userModules.firstOrNull(JavaModule::isSourceModule)
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 }
}
+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 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.test.KotlinTestUtils
import org.jetbrains.kotlin.test.util.KtTestUtil
@@ -59,7 +61,10 @@ class Java11ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
}
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)
@@ -144,6 +149,21 @@ class Java11ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
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() {
val a = module("moduleA")
val b = module("moduleB", listOf(a))