Use module path instead of class path for stdlib on Java 9

Also report the "named does not read unnamed" error, which was not
possible previously because we wouldn't be able to read anything from
kotlin-stdlib (because it was added to the unnamed module by default)
This commit is contained in:
Alexander Udalov
2017-07-07 18:25:06 +03:00
parent c94b21edd5
commit 03a6488464
16 changed files with 118 additions and 37 deletions
@@ -30,10 +30,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.CompileEnvironmentUtil
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.cli.jvm.config.*
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser
import org.jetbrains.kotlin.cli.jvm.repl.ReplFromTerminal
@@ -106,11 +103,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
}
}
val classpath = getClasspath(paths, arguments)
configuration.addJvmClasspathRoots(classpath)
for (modularRoot in arguments.javaModulePath?.split(File.pathSeparatorChar).orEmpty()) {
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(File(modularRoot)))
}
configureContentRoots(paths, arguments, configuration)
configuration.put(CommonConfigurationKeys.MODULE_NAME, arguments.moduleName ?: JvmAbi.DEFAULT_MODULE_NAME)
@@ -383,21 +376,35 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
arguments.declarationsOutputPath?.let { configuration.put(JVMConfigurationKeys.DECLARATIONS_JSON_PATH, it) }
}
private fun getClasspath(paths: KotlinPaths, arguments: K2JVMCompilerArguments): List<File> {
val classpath = arrayListOf<File>()
if (arguments.classpath != null) {
classpath.addAll(arguments.classpath.split(File.pathSeparatorChar).map(::File))
private fun configureContentRoots(paths: KotlinPaths, arguments: K2JVMCompilerArguments, configuration: CompilerConfiguration) {
for (path in arguments.classpath?.split(File.pathSeparatorChar).orEmpty()) {
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(File(path)))
}
for (modularRoot in arguments.javaModulePath?.split(File.pathSeparatorChar).orEmpty()) {
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(File(modularRoot)))
}
val isModularJava = configuration.get(JVMConfigurationKeys.JDK_HOME).let { it != null && CoreJrtFileSystem.isModularJdk(it) }
fun addRoot(moduleName: String, file: File) {
if (isModularJava) {
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(file))
configuration.add(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, moduleName)
}
else {
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(file))
}
}
if (!arguments.noStdlib) {
classpath.add(paths.runtimePath)
classpath.add(paths.scriptRuntimePath)
addRoot("kotlin.stdlib", paths.stdlibPath)
addRoot("kotlin.script.runtime", paths.scriptRuntimePath)
}
// "-no-stdlib" implies "-no-reflect": otherwise we would be able to transitively read stdlib classes through kotlin-reflect,
// which is likely not what user wants since s/he manually provided "-no-stdlib"
if (!arguments.noReflect && !arguments.noStdlib) {
classpath.add(paths.reflectPath)
addRoot("kotlin.reflect", paths.reflectPath)
}
return classpath
}
private fun setupJdkClasspathRoots(arguments: K2JVMCompilerArguments, configuration: CompilerConfiguration, messageCollector: MessageCollector): ExitCode {
@@ -52,7 +52,7 @@ internal class ClasspathRootsResolver(
private val additionalModules: List<String>,
private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?
) {
private val javaModuleFinder = CliJavaModuleFinder(VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL))
val javaModuleFinder = CliJavaModuleFinder(VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL))
val javaModuleGraph = JavaModuleGraph(javaModuleFinder)
data class RootsAndModules(val roots: List<JavaRoot>, val modules: List<JavaModule>)
@@ -229,7 +229,8 @@ class KotlinCoreEnvironment private constructor(
project.registerService(
JavaModuleResolver::class.java,
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules)
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules,
classpathRootsResolver.javaModuleFinder.systemModules.toList())
)
val finderFactory = CliVirtualFileFinderFactory(rootsIndex)
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.cli.jvm.modules
import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.ide.highlighter.JavaFileType
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.KotlinFileType
@@ -27,24 +28,31 @@ import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
class CliJavaModuleResolver(
private val moduleGraph: JavaModuleGraph,
private val javaModules: List<JavaModule>
private val userModules: List<JavaModule>,
private val systemModules: List<JavaModule.Explicit>
) : JavaModuleResolver {
init {
assert(javaModules.count { !it.isBinary } <= 1) {
"Modules computed by ClasspathRootsResolver cannot have more than one source module: $javaModules"
assert(userModules.count { !it.isBinary } <= 1) {
"Modules computed by ClasspathRootsResolver cannot have more than one source module: $userModules"
}
}
private val sourceModule = javaModules.firstOrNull { !it.isBinary }
private val sourceModule: JavaModule? = userModules.firstOrNull { !it.isBinary }
private fun findJavaModule(file: VirtualFile): JavaModule? =
when (file.fileType) {
KotlinFileType.INSTANCE, JavaFileType.INSTANCE -> sourceModule
JavaClassFileType.INSTANCE -> javaModules.firstOrNull { module ->
module.isBinary && VfsUtilCore.isAncestor(module.moduleRoot, file, false)
}
else -> null
}
private fun findJavaModule(file: VirtualFile): JavaModule? {
if (file.fileSystem.protocol == StandardFileSystems.JRT_PROTOCOL) {
return systemModules.firstOrNull { module -> file in module }
}
return when (file.fileType) {
KotlinFileType.INSTANCE, JavaFileType.INSTANCE -> sourceModule
JavaClassFileType.INSTANCE -> userModules.firstOrNull { module -> module.isBinary && file in module }
else -> null
}
}
private operator fun JavaModule.contains(file: VirtualFile): Boolean =
VfsUtilCore.isAncestor(moduleRoot, file, false)
override fun checkAccessibility(
fileFromOurModule: VirtualFile?, referencedFile: VirtualFile, referencedPackage: FqName?
@@ -68,9 +68,7 @@ class JvmModuleAccessibilityChecker(project: Project) : CallChecker {
return when (diagnostic) {
is ModuleDoesNotReadUnnamedModule ->
// TODO: report this error as soon as module path is used instead of class path for modular compilation
// JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE.on(reportOn)
null
JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE.on(reportOn)
is ModuleDoesNotReadModule ->
JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE.on(reportOn, diagnostic.dependencyModuleName)
is ModuleDoesNotExportPackage ->
@@ -74,7 +74,8 @@ class JavaModuleInfo(
}
override fun visitExport(packageFqName: String, access: Int, modules: Array<String>?) {
exports.add(Exports(FqName(packageFqName), modules?.toList().orEmpty()))
// For some reason, '/' is the delimiter in packageFqName here
exports.add(Exports(FqName(packageFqName.replace('/', '.')), modules?.toList().orEmpty()))
}
}
}
@@ -0,0 +1,3 @@
module namedWithExplicitDependency {
requires kotlin.stdlib;
}
@@ -0,0 +1,6 @@
import kotlin.text.Regex
fun f1(): List<String?> = emptyList()
fun f2(): Array<Lazy<Unit>> = arrayOf()
fun f3(map: Map<Int, Regex>): Collection<Regex> =
map.filterNot { (key, entry) -> "$key".equals(entry.toString(), ignoreCase = true) }.values
@@ -0,0 +1,34 @@
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:1:20: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
import kotlin.text.Regex
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:3:27: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
fun f1(): List<String?> = emptyList()
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:4:17: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
fun f2(): Array<Lazy<Unit>> = arrayOf()
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:4:22: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
fun f2(): Array<Lazy<Unit>> = arrayOf()
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:5:22: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
fun f3(map: Map<Int, Regex>): Collection<Regex> =
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:5:42: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
fun f3(map: Map<Int, Regex>): Collection<Regex> =
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:6:13: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
map.filterNot { (key, entry) -> "$key".equals(entry.toString(), ignoreCase = true) }.values
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:6:26: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
map.filterNot { (key, entry) -> "$key".equals(entry.toString(), ignoreCase = true) }.values
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:6:31: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
map.filterNot { (key, entry) -> "$key".equals(entry.toString(), ignoreCase = true) }.values
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:6:48: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
map.filterNot { (key, entry) -> "$key".equals(entry.toString(), ignoreCase = true) }.values
^
compiler/testData/javaModules/dependencyOnStdlib/namedWithoutExplicitDependency/test.kt:6:61: error: symbol is declared in module 'kotlin.stdlib' which current module does not depend on
map.filterNot { (key, entry) -> "$key".equals(entry.toString(), ignoreCase = true) }.values
^
COMPILATION_ERROR
@@ -0,0 +1,2 @@
module namedWithoutExplicitDependency {
}
@@ -0,0 +1,6 @@
import kotlin.text.Regex
fun f1(): List<String?> = emptyList()
fun f2(): Array<Lazy<Unit>> = arrayOf()
fun f3(map: Map<Int, Regex>): Collection<Regex> =
map.filterNot { (key, entry) -> "$key".equals(entry.toString(), ignoreCase = true) }.values
@@ -0,0 +1 @@
OK
@@ -0,0 +1,6 @@
import kotlin.text.Regex
fun f1(): List<String?> = emptyList()
fun f2(): Array<Lazy<Unit>> = arrayOf()
fun f3(map: Map<Int, Regex>): Collection<Regex> =
map.filterNot { (key, entry) -> "$key".equals(entry.toString(), ignoreCase = true) }.values
@@ -33,7 +33,7 @@ public class ForTestCompileRuntime {
@NotNull
public static File runtimeJarForTests() {
return assertExists(new File("dist/kotlinc/lib/kotlin-runtime.jar"));
return assertExists(new File("dist/kotlinc/lib/kotlin-stdlib.jar"));
}
@NotNull
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.jvm.compiler
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
import java.util.jar.Manifest
@@ -33,7 +34,7 @@ class Java9ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
): File {
val jdk9Home = KotlinTestUtils.getJdk9HomeIfPossible() ?: return File("<test-skipped>")
val paths = modulePath.joinToString(separator = File.pathSeparator) { it.path }
val paths = (modulePath + ForTestCompileRuntime.runtimeJarForTests()).joinToString(separator = File.pathSeparator) { it.path }
val kotlinOptions = mutableListOf(
"-jdk-home", jdk9Home.path,
@@ -245,4 +246,10 @@ class Java9ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
val d2 = module("dependency2")
module("main", listOf(d1, d2))
}
fun testDependencyOnStdlib() {
module("unnamed")
module("namedWithExplicitDependency")
module("namedWithoutExplicitDependency")
}
}