Load all resources in RuntimePackagePartProvider; optimize code
The issue was reproducible when the same package is present in different modules with the same -module-name (which is a popular case of src/test roots in simple IDEA projects). The problem was in the fact that several resource files containing package name mapping with the same name were present in the classpath, but RuntimePackagePartProvider only considered the first one. The fix is to use getResources instead of getResourceAsStream and handle each returned resource. Also, optimize internal representation to store the mapping in the form which is the most convenient for findPackageParts, which should be faster than registerModule because in theory, it's called more often. #KT-21973 Fixed #KT-24651
This commit is contained in:
+36
-14
@@ -21,27 +21,44 @@ import org.jetbrains.kotlin.load.kotlin.loadModuleMapping
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class RuntimePackagePartProvider(private val classLoader: ClassLoader) : PackagePartProvider {
|
||||
private val module2Mapping = ConcurrentHashMap<String, ModuleMapping>()
|
||||
// Names of modules which were registered with registerModule
|
||||
private val visitedModules = hashSetOf<String>()
|
||||
|
||||
// Package FQ name -> list of JVM internal names of package parts in that package across all registered modules
|
||||
private val packageParts = hashMapOf<String, LinkedHashSet<String>>()
|
||||
|
||||
@Synchronized
|
||||
fun registerModule(moduleName: String) {
|
||||
val mapping = try {
|
||||
val resourcePath = "META-INF/$moduleName.${ModuleMapping.MAPPING_FILE_EXT}"
|
||||
classLoader.getResourceAsStream(resourcePath)?.use { stream ->
|
||||
ModuleMapping.loadModuleMapping(stream.readBytes(), resourcePath, DeserializationConfiguration.Default)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// TODO: do not swallow this exception?
|
||||
null
|
||||
if (!visitedModules.add(moduleName)) return
|
||||
|
||||
val resourcePath = "META-INF/$moduleName.${ModuleMapping.MAPPING_FILE_EXT}"
|
||||
val resources = try {
|
||||
classLoader.getResources(resourcePath)
|
||||
} catch (e: IOException) {
|
||||
EmptyEnumeration
|
||||
}
|
||||
|
||||
for (resource in resources) {
|
||||
try {
|
||||
resource.openStream()?.use { stream ->
|
||||
val mapping = ModuleMapping.loadModuleMapping(stream.readBytes(), resourcePath, DeserializationConfiguration.Default)
|
||||
for ((packageFqName, parts) in mapping.packageFqName2Parts) {
|
||||
packageParts.getOrPut(packageFqName) { linkedSetOf() }.addAll(parts.parts)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// TODO: do not swallow this exception?
|
||||
}
|
||||
}
|
||||
module2Mapping.putIfAbsent(moduleName, mapping ?: ModuleMapping.EMPTY)
|
||||
}
|
||||
|
||||
override fun findPackageParts(packageFqName: String): List<String> {
|
||||
return module2Mapping.values.mapNotNull { it.findPackageParts(packageFqName) }.flatMap { it.parts }.distinct()
|
||||
}
|
||||
@Synchronized
|
||||
override fun findPackageParts(packageFqName: String): List<String> =
|
||||
packageParts[packageFqName]?.toList().orEmpty()
|
||||
|
||||
// TODO
|
||||
override fun findMetadataPackageParts(packageFqName: String): List<String> = TODO()
|
||||
@@ -50,4 +67,9 @@ class RuntimePackagePartProvider(private val classLoader: ClassLoader) : Package
|
||||
// TODO: load annotations from resource files
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
private object EmptyEnumeration : Enumeration<Nothing> {
|
||||
override fun hasMoreElements(): Boolean = false
|
||||
override fun nextElement(): Nothing = throw NoSuchElementException()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user