Skip to content

Commit

Permalink
HMPP. Rewrite LibraryDependenciesCache on using LibraryInfo/SdkInfo
Browse files Browse the repository at this point in the history
... instead of pure IJ Library/SDK.
  • Loading branch information
ddolovov committed Apr 14, 2020
1 parent 6d0e731 commit b67a48f
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class CommonPlatformKindResolution : IdePlatformKindResolution {
}
} else {
// No klib files <=> old metadata-library <=> create usual LibraryInfo
listOf(LibraryInfo(project, library))
listOf(CommonMetadataLibraryInfo(project, library))
}
}

Expand Down Expand Up @@ -115,9 +115,16 @@ class CommonPlatformKindResolution : IdePlatformKindResolution {
}
}

class CommonKlibLibraryInfo(project: Project, library: Library, libraryRoot: String) :
AbstractKlibLibraryInfo(project, library, libraryRoot) {
class CommonKlibLibraryInfo(
project: Project,
library: Library,
libraryRoot: String
) : AbstractKlibLibraryInfo(project, library, libraryRoot) {
override val platform: TargetPlatform
get() = CommonPlatforms.defaultCommonPlatform
}

class CommonMetadataLibraryInfo(project: Project, library: Library) : LibraryInfo(project, library) {
override val platform: TargetPlatform
get() = CommonPlatforms.defaultCommonPlatform
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ interface IdePlatformKindResolution {

val libraryKind: PersistentLibraryKind<*>?

fun createLibraryInfo(project: Project, library: Library): List<LibraryInfo> = listOf(LibraryInfo(project, library))
fun createLibraryInfo(project: Project, library: Library): List<LibraryInfo>

companion object : ApplicationExtensionDescriptor<IdePlatformKindResolution>(
"org.jetbrains.kotlin.idePlatformKindResolution", IdePlatformKindResolution::class.java
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class JsPlatformKindResolution : IdePlatformKindResolution {
JsKlibLibraryInfo(project, library, path)
}
} else {
super.createLibraryInfo(project, library)
listOf(JsMetadataLibraryInfo(project, library))
}
}

Expand Down Expand Up @@ -112,3 +112,8 @@ class JsKlibLibraryInfo(project: Project, library: Library, libraryRoot: String)
override val platform: TargetPlatform
get() = JsPlatforms.defaultJsPlatform
}

class JsMetadataLibraryInfo(project: Project, library: Library) : LibraryInfo(project, library) {
override val platform: TargetPlatform
get() = JsPlatforms.defaultJsPlatform
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

package org.jetbrains.kotlin.caches.resolve

import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.analyzer.ModuleInfo
Expand All @@ -15,10 +17,12 @@ import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.idea.caches.project.LibraryInfo
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.impl.JvmIdePlatformKind
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.resolve.TargetEnvironment
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
import org.jetbrains.kotlin.resolve.jvm.JvmResolverForModuleFactory
Expand All @@ -39,6 +43,9 @@ class JvmPlatformKindResolution : IdePlatformKindResolution {
override val libraryKind: PersistentLibraryKind<*>?
get() = null

override fun createLibraryInfo(project: Project, library: Library): List<LibraryInfo> =
listOf(JvmLibraryInfo(project, library))

override val kind get() = JvmIdePlatformKind

override fun getKeyForBuiltIns(moduleInfo: ModuleInfo, sdkInfo: SdkInfo?): BuiltInsCacheKey {
Expand All @@ -54,3 +61,8 @@ class JvmPlatformKindResolution : IdePlatformKindResolution {

data class CacheKeyBySdk(val sdk: Sdk) : BuiltInsCacheKey
}

class JvmLibraryInfo(project: Project, library: Library) : LibraryInfo(project, library) {
override val platform: TargetPlatform
get() = JvmPlatforms.defaultJvmPlatform
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ import org.jetbrains.kotlin.idea.caches.trackers.KotlinModuleOutOfCodeBlockModif
import org.jetbrains.kotlin.idea.configuration.BuildSystemType
import org.jetbrains.kotlin.idea.configuration.getBuildSystemType
import org.jetbrains.kotlin.idea.core.isInTestSourceContentKotlinAware
import org.jetbrains.kotlin.idea.framework.getLibraryPlatform
import org.jetbrains.kotlin.idea.framework.effectiveKind
import org.jetbrains.kotlin.idea.framework.platform
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.project.findAnalyzerServices
import org.jetbrains.kotlin.idea.project.getStableName
Expand Down Expand Up @@ -109,16 +110,20 @@ private fun orderEntryToModuleInfo(project: Project, orderEntry: OrderEntry, for
}
}

val Project.libraryInfoCache: MutableMap<Library, List<LibraryInfo>>
private val Project.libraryInfoCache: MutableMap<Library, List<LibraryInfo>>
get() = cacheInvalidatingOnRootModifications { ContainerUtil.createConcurrentWeakMap() }

fun createLibraryInfo(project: Project, library: Library): List<LibraryInfo> {
val cache = project.libraryInfoCache
fun createLibraryInfo(project: Project, library: Library): List<LibraryInfo> =
project.libraryInfoCache.getOrPut(library) {
val approximatePlatform = if (library is LibraryEx && !library.isDisposed) {
// for Native returns 'unspecifiedNativePlatform', thus "approximate"
library.effectiveKind(project).platform
} else {
DefaultIdeTargetPlatformKindProvider.defaultPlatform
}

return cache.getOrPut(library) {
getLibraryPlatform(project, library).idePlatformKind.resolution.createLibraryInfo(project, library)
approximatePlatform.idePlatformKind.resolution.createLibraryInfo(project, library)
}
}

private fun OrderEntry.acceptAsDependency(forProduction: Boolean): Boolean {
return this !is ExportableOrderEntry
Expand Down Expand Up @@ -300,7 +305,7 @@ private class ModuleTestSourceScope(module: Module) : ModuleSourceScope(module)
override fun toString() = "ModuleTestSourceScope($module)"
}

open class LibraryInfo(override val project: Project, val library: Library) : IdeaModuleInfo, LibraryModuleInfo, BinaryModuleInfo {
abstract class LibraryInfo(override val project: Project, val library: Library) : IdeaModuleInfo, LibraryModuleInfo, BinaryModuleInfo {
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.LIBRARY

Expand All @@ -315,18 +320,15 @@ open class LibraryInfo(override val project: Project, val library: Library) : Id
val result = LinkedHashSet<IdeaModuleInfo>()
result.add(this)

val (libraries, sdks) = LibraryDependenciesCache.getInstance(project).getLibrariesAndSdksUsedWith(library)
val (libraries, sdks) = LibraryDependenciesCache.getInstance(project).getLibrariesAndSdksUsedWith(this)

sdks.mapTo(result) { SdkInfo(project, it) }
libraries.filter { it is LibraryEx && !it.isDisposed }.flatMapTo(result) {
createLibraryInfo(project, it)
}
result.addAll(sdks)
result.addAll(libraries)

return result.toList()
}

override val platform: TargetPlatform
get() = getLibraryPlatform(project, library)
abstract override val platform: TargetPlatform // must override

override val analyzerServices: PlatformDependentAnalyzerServices
get() = platform.findAnalyzerServices(project)
Expand All @@ -337,15 +339,14 @@ open class LibraryInfo(override val project: Project, val library: Library) : Id
override fun getLibraryRoots(): Collection<String> =
library.getFiles(OrderRootType.CLASSES).mapNotNull(PathUtil::getLocalPath)

override fun toString() = "LibraryInfo(libraryName=${library.name})"
override fun toString() = "${this::class.simpleName}(libraryName=${library.name}, libraryRoots=${getLibraryRoots()})"

override fun equals(other: Any?): Boolean {
if (this === other) return true
return (other is LibraryInfo && library == other.library)
}

override fun hashCode(): Int = 43 * library.hashCode()

}

data class LibrarySourceInfo(override val project: Project, val library: Library, override val binariesModuleInfo: BinaryModuleInfo) :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.*
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.util.Condition
import com.intellij.psi.util.CachedValueProvider
Expand All @@ -19,36 +19,34 @@ import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.idea.core.util.CachedValue
import org.jetbrains.kotlin.idea.core.util.getValue
import org.jetbrains.kotlin.idea.framework.getLibraryPlatform
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*

internal typealias LibrariesAndSdks = Pair<List<Library>, List<Sdk>>
internal typealias LibrariesAndSdks = Pair<List<LibraryInfo>, List<SdkInfo>>

interface LibraryDependenciesCache {
companion object {
fun getInstance(project: Project) = ServiceManager.getService(project, LibraryDependenciesCache::class.java)!!
}

fun getLibrariesAndSdksUsedWith(library: Library): LibrariesAndSdks
fun getLibrariesAndSdksUsedWith(libraryInfo: LibraryInfo): LibrariesAndSdks
}

class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDependenciesCache {
val cache by CachedValue(project) {
private val cache by CachedValue(project) {
CachedValueProvider.Result(
ContainerUtil.createConcurrentWeakMap<Library, LibrariesAndSdks>(),
ContainerUtil.createConcurrentWeakMap<LibraryInfo, LibrariesAndSdks>(),
ProjectRootManager.getInstance(project)
)
}

override fun getLibrariesAndSdksUsedWith(library: Library): LibrariesAndSdks =
cache.getOrPut(library) { computeLibrariesAndSdksUsedWith(library) }
override fun getLibrariesAndSdksUsedWith(libraryInfo: LibraryInfo): LibrariesAndSdks =
cache.getOrPut(libraryInfo) { computeLibrariesAndSdksUsedWith(libraryInfo) }


//NOTE: used LibraryRuntimeClasspathScope as reference
private fun computeLibrariesAndSdksUsedWith(library: Library): LibrariesAndSdks {
private fun computeLibrariesAndSdksUsedWith(libraryInfo: LibraryInfo): LibrariesAndSdks {
val processedModules = HashSet<Module>()
val condition = Condition<OrderEntry> { orderEntry ->
if (orderEntry is ModuleOrderEntry) {
Expand All @@ -59,12 +57,12 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
}
}

val libraries = LinkedHashSet<Library>()
val sdks = LinkedHashSet<Sdk>()
val libraries = LinkedHashSet<LibraryInfo>()
val sdks = LinkedHashSet<SdkInfo>()

val platform = getLibraryPlatform(project, library)
val platform = libraryInfo.platform

for (module in getLibraryUsageIndex().modulesLibraryIsUsedIn[library]) {
for (module in getLibraryUsageIndex().modulesLibraryIsUsedIn[libraryInfo.library]) {
if (!processedModules.add(module)) continue

ModuleRootManager.getInstance(module).orderEntries().recursively().satisfying(condition).process(object : RootPolicy<Unit>() {
Expand All @@ -74,13 +72,19 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend

override fun visitLibraryOrderEntry(libraryOrderEntry: LibraryOrderEntry, value: Unit) {
val otherLibrary = libraryOrderEntry.library
if (otherLibrary != null && compatiblePlatforms(platform, getLibraryPlatform(project, otherLibrary))) {
libraries.add(otherLibrary)
if (otherLibrary is LibraryEx && !otherLibrary.isDisposed) {
val otherLibraryInfos = createLibraryInfo(project, otherLibrary)
otherLibraryInfos.firstOrNull()?.platform?.let { otherLibraryPlatform ->
if (compatiblePlatforms(platform, otherLibraryPlatform)) {
libraries.addAll(otherLibraryInfos)
}
}
}
}

override fun visitJdkOrderEntry(jdkOrderEntry: JdkOrderEntry, value: Unit) {
sdks.addIfNotNull(jdkOrderEntry.jdk)
val jdk = jdkOrderEntry.jdk ?: return
sdks += SdkInfo(project, jdk)
}
}, Unit)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@

package org.jetbrains.kotlin.idea.framework

import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.DummyLibraryProperties
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
Expand Down Expand Up @@ -61,13 +59,6 @@ val PersistentLibraryKind<*>?.platform: TargetPlatform
else -> DefaultIdeTargetPlatformKindProvider.defaultPlatform
}

fun getLibraryPlatform(project: Project, library: Library): TargetPlatform {
if (library !is LibraryEx) return DefaultIdeTargetPlatformKindProvider.defaultPlatform
if (library.isDisposed) return DefaultIdeTargetPlatformKindProvider.defaultPlatform

return library.effectiveKind(project).platform
}

fun detectLibraryKind(roots: Array<VirtualFile>): PersistentLibraryKind<*>? {
val jarFile = roots.firstOrNull() ?: return null
if (jarFile.fileSystem is JarFileSystem) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ abstract class AbstractKlibLibraryInfo(project: Project, library: Library, val l

final override fun getLibraryRoots() = listOf(libraryRoot)

final override fun toString() = "${this::class.simpleName}(libraryName=${library.name}, libraryRoot=$libraryRoot)"

abstract override val platform: TargetPlatform // must override

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
Expand All @@ -25,9 +25,11 @@ import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.project.ModuleTestSourceInfo
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
import org.jetbrains.kotlin.idea.framework.platform
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase.*
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.idea.util.getProjectJdkTableSafe
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.util.addDependency
Expand Down Expand Up @@ -437,8 +439,11 @@ class IdeaModuleInfoTest : ModuleTestCase() {
private val Module.test: ModuleTestSourceInfo
get() = testSourceInfo()!!

private val Library.classes: LibraryInfo
get() = LibraryInfo(project!!, this)
private val LibraryEx.classes: LibraryInfo
get() = object : LibraryInfo(project!!, this) {
override val platform: TargetPlatform
get() = kind.platform
}

private fun module(name: String, hasProductionRoot: Boolean = true, hasTestRoot: Boolean = true): Module {
return createModuleFromTestData(createTempDirectory().absolutePath, name, StdModuleTypes.JAVA, false).apply {
Expand Down Expand Up @@ -467,15 +472,15 @@ class IdeaModuleInfoTest : ModuleTestCase() {
UsefulTestCase.assertSameElements(this.getDependentModules(), expected.toList())
}

private fun stdlibCommon(): Library = projectLibrary(
private fun stdlibCommon(): LibraryEx = projectLibrary(
"kotlin-stdlib-common",
ForTestCompileRuntime.stdlibCommonForTests().jarRoot,
kind = CommonLibraryKind
)

private fun stdlibJvm(): Library = projectLibrary("kotlin-stdlib", ForTestCompileRuntime.runtimeJarForTests().jarRoot)
private fun stdlibJvm(): LibraryEx = projectLibrary("kotlin-stdlib", ForTestCompileRuntime.runtimeJarForTests().jarRoot)

private fun stdlibJs(): Library = projectLibrary(
private fun stdlibJs(): LibraryEx = projectLibrary(
"kotlin-stdlib-js",
ForTestCompileRuntime.runtimeJarForTests().jarRoot,
kind = JSLibraryKind
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
Expand All @@ -24,15 +25,15 @@ fun HeavyPlatformTestCase.projectLibrary(
classesRoot: VirtualFile? = null,
sourcesRoot: VirtualFile? = null,
kind: PersistentLibraryKind<*>? = null
): Library {
): LibraryEx {
return runWriteAction {
val modifiableModel = ProjectLibraryTable.getInstance(project).modifiableModel
val library = try {
modifiableModel.createLibrary(libraryName, kind)
modifiableModel.createLibrary(libraryName, kind) as LibraryEx
} finally {
modifiableModel.commit()
}
with (library.modifiableModel) {
with(library.modifiableModel) {
classesRoot?.let { addRoot(it, OrderRootType.CLASSES) }
sourcesRoot?.let { addRoot(it, OrderRootType.SOURCES) }
commit()
Expand Down
Loading

0 comments on commit b67a48f

Please sign in to comment.