Introduce JavaModule, refactor module graph construction
#KT-18598 In Progress #KT-18599 In Progress
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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 org.jetbrains.kotlin.name.FqName
|
||||
|
||||
interface JavaModule {
|
||||
/**
|
||||
* The name of the module. For explicit modules, this is the name specified in the module-info file.
|
||||
* For automatic modules, this is the name in the Automatic-Module-Name attribute in the manifest,
|
||||
* or an automatically inferred name in case of absence of such attribute.
|
||||
*/
|
||||
val name: String
|
||||
|
||||
/**
|
||||
* A directory or the root of a .jar file on the module path. In case of an explicit module,
|
||||
* module-info.class file can be found in its children.
|
||||
*/
|
||||
val moduleRoot: VirtualFile
|
||||
|
||||
/**
|
||||
* A module-info.class or module-info.java file where this module was loaded from.
|
||||
*/
|
||||
val moduleInfoFile: VirtualFile?
|
||||
|
||||
/**
|
||||
* `true` if this module is either an automatic module on the module path, or an explicit module loaded from module-info.class.
|
||||
* `false` if this module is an explicit module loaded from module-info.java.
|
||||
*/
|
||||
val isBinary: Boolean
|
||||
|
||||
/**
|
||||
* `true` if this module exports the package with the given FQ name to all dependent modules.
|
||||
* For explicit modules, this means that there's an _unqualified_ (without the 'to' clause) exports statement in the module-info file.
|
||||
* For automatic modules, this is always `true`.
|
||||
*
|
||||
* Note that it's assumed that the module contains this package.
|
||||
*/
|
||||
fun exports(packageFqName: FqName): Boolean
|
||||
|
||||
/**
|
||||
* `true` if this module exports the package with the given FQ name to a module with the given name.
|
||||
* For explicit modules, this means that there's either an unqualified exports statement (without the 'to' clause)
|
||||
* in the module-info file, or a _qualified_ statement (with the 'to' clause) with the given module name at the right hand side.
|
||||
* For automatic modules, this is always `true`.
|
||||
*
|
||||
* Note that it's assumed that the module contains this package.
|
||||
*/
|
||||
fun exportsTo(packageFqName: FqName, moduleName: String): Boolean
|
||||
|
||||
class Automatic(override val name: String, override val moduleRoot: VirtualFile) : JavaModule {
|
||||
override val moduleInfoFile: VirtualFile? get() = null
|
||||
|
||||
override val isBinary: Boolean get() = true
|
||||
|
||||
override fun exports(packageFqName: FqName): Boolean = true
|
||||
|
||||
override fun exportsTo(packageFqName: FqName, moduleName: String): Boolean = true
|
||||
|
||||
override fun toString(): String = name
|
||||
}
|
||||
|
||||
class Explicit(
|
||||
val moduleInfo: JavaModuleInfo,
|
||||
override val moduleRoot: VirtualFile,
|
||||
override val moduleInfoFile: VirtualFile,
|
||||
override val isBinary: Boolean
|
||||
) : JavaModule {
|
||||
override val name: String
|
||||
get() = moduleInfo.moduleName
|
||||
|
||||
override fun exports(packageFqName: FqName): Boolean {
|
||||
return moduleInfo.exports.any { (fqName, toModules) ->
|
||||
fqName == packageFqName && toModules.isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
override fun exportsTo(packageFqName: FqName, moduleName: String): Boolean {
|
||||
return moduleInfo.exports.any { (fqName, toModules) ->
|
||||
fqName == packageFqName && (toModules.isEmpty() || moduleName in toModules)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = name
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -17,5 +17,5 @@
|
||||
package org.jetbrains.kotlin.resolve.jvm.modules
|
||||
|
||||
interface JavaModuleFinder {
|
||||
fun findModule(name: String): JavaModuleInfo?
|
||||
fun findModule(name: String): JavaModule?
|
||||
}
|
||||
|
||||
+10
-9
@@ -19,19 +19,20 @@ package org.jetbrains.kotlin.resolve.jvm.modules
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
|
||||
class JavaModuleGraph(finder: JavaModuleFinder) {
|
||||
private val moduleInfo: (String) -> JavaModuleInfo? =
|
||||
private val module: (String) -> JavaModule? =
|
||||
LockBasedStorageManager.NO_LOCKS.createMemoizedFunctionWithNullableValues(finder::findModule)
|
||||
|
||||
fun getAllDependencies(moduleNames: List<String>): List<String> {
|
||||
// Every module implicitly depends on java.base
|
||||
val visited = linkedSetOf("java.base")
|
||||
val visited = moduleNames.toMutableSet()
|
||||
|
||||
fun dfs(module: String) {
|
||||
if (!visited.add(module)) return
|
||||
val moduleInfo = moduleInfo(module) ?: return
|
||||
for ((moduleName, isTransitive) in moduleInfo.requires) {
|
||||
if (isTransitive) {
|
||||
dfs(moduleName)
|
||||
// Every module implicitly depends on java.base
|
||||
visited += "java.base"
|
||||
|
||||
fun dfs(moduleName: String) {
|
||||
val moduleInfo = (module(moduleName) as? JavaModule.Explicit)?.moduleInfo ?: return
|
||||
for ((dependencyModuleName, isTransitive) in moduleInfo.requires) {
|
||||
if (!visited.add(dependencyModuleName) && isTransitive) {
|
||||
dfs(dependencyModuleName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
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.compactIfPossible
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||
@@ -39,6 +41,18 @@ class JavaModuleInfo(
|
||||
"Module $moduleName (${requires.size} requires, ${exports.size} exports)"
|
||||
|
||||
companion object {
|
||||
fun create(psiJavaModule: PsiJavaModule): JavaModuleInfo {
|
||||
return JavaModuleInfo(
|
||||
psiJavaModule.name,
|
||||
psiJavaModule.requires.map { statement ->
|
||||
JavaModuleInfo.Requires(statement.moduleName!!, statement.hasModifierProperty(PsiModifier.TRANSITIVE))
|
||||
},
|
||||
psiJavaModule.exports.map { statement ->
|
||||
JavaModuleInfo.Exports(FqName(statement.packageName!!), statement.moduleNames)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun read(file: VirtualFile): JavaModuleInfo? {
|
||||
val contents = try { file.contentsToByteArray() } catch (e: IOException) { return null }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user