FIR: implements multi-module resolve, add tests #KT-24093 Fixed
This commit introduces FIR session provider, dependency provider, library session + other infrastructure necessary for multi-module resolve
This commit is contained in:
@@ -7,6 +7,7 @@ plugins {
|
||||
jvmTarget = "1.6"
|
||||
|
||||
dependencies {
|
||||
compile(project(":compiler:frontend.common"))
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(project(":compiler:fir:resolve"))
|
||||
|
||||
|
||||
@@ -6,21 +6,59 @@
|
||||
package org.jetbrains.kotlin.fir.java
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.fir.FirModuleBasedSession
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.resolve.FirProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.impl.FirCompositeSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.impl.FirDependenciesSymbolProviderImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.impl.FirLibrarySymbolProviderImpl
|
||||
import org.jetbrains.kotlin.fir.service
|
||||
|
||||
class FirJavaModuleBasedSession(moduleInfo: ModuleInfo, project: Project) : FirModuleBasedSession(moduleInfo) {
|
||||
class FirJavaModuleBasedSession(
|
||||
moduleInfo: ModuleInfo,
|
||||
override val sessionProvider: FirProjectSessionProvider,
|
||||
scope: GlobalSearchScope
|
||||
) : FirModuleBasedSession(moduleInfo) {
|
||||
init {
|
||||
sessionProvider.sessionCache[moduleInfo] = this
|
||||
registerComponent(
|
||||
FirSymbolProvider::class,
|
||||
FirCompositeSymbolProvider(
|
||||
listOf(service<FirProvider>(), JavaSymbolProvider(project), FirLibrarySymbolProviderImpl(this))
|
||||
listOf(
|
||||
service<FirProvider>(),
|
||||
JavaSymbolProvider(sessionProvider.project, scope),
|
||||
FirDependenciesSymbolProviderImpl(this)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FirLibrarySession(
|
||||
moduleInfo: ModuleInfo,
|
||||
override val sessionProvider: FirProjectSessionProvider,
|
||||
scope: GlobalSearchScope
|
||||
) : FirSessionBase() {
|
||||
init {
|
||||
sessionProvider.sessionCache[moduleInfo] = this
|
||||
registerComponent(
|
||||
FirSymbolProvider::class,
|
||||
FirCompositeSymbolProvider(
|
||||
listOf(
|
||||
FirLibrarySymbolProviderImpl(this),
|
||||
JavaSymbolProvider(sessionProvider.project, scope),
|
||||
FirDependenciesSymbolProviderImpl(this)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FirProjectSessionProvider(val project: Project) : FirSessionProvider {
|
||||
override fun getSession(moduleInfo: ModuleInfo): FirSession? {
|
||||
return sessionCache[moduleInfo]
|
||||
}
|
||||
|
||||
val sessionCache = mutableMapOf<ModuleInfo, FirSession>()
|
||||
}
|
||||
@@ -8,38 +8,22 @@ package org.jetbrains.kotlin.fir.java
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.fir.java.symbols.JavaClassSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.AbstractFirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeSymbol
|
||||
import org.jetbrains.kotlin.load.java.JavaClassFinder
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
|
||||
|
||||
class JavaSymbolProvider(val project: Project) : FirSymbolProvider {
|
||||
|
||||
override val doesLookupInFir: Boolean
|
||||
get() = false
|
||||
|
||||
// TODO: Concrete scope here
|
||||
private val allScope = GlobalSearchScope.allScope(project)
|
||||
|
||||
private val classCache = mutableMapOf<ClassId, ConeSymbol?>()
|
||||
private val packageCache = mutableMapOf<FqName, FqName?>()
|
||||
|
||||
private inline fun <K, V : Any?> MutableMap<K, V>.lookupCacheOrCalculate(key: K, l: (K) -> V): V? {
|
||||
return if (key in this.keys) {
|
||||
this[key]
|
||||
} else {
|
||||
val calculated = l(key)
|
||||
this[key] = calculated
|
||||
calculated
|
||||
}
|
||||
}
|
||||
class JavaSymbolProvider(
|
||||
val project: Project,
|
||||
private val searchScope: GlobalSearchScope
|
||||
) : AbstractFirSymbolProvider() {
|
||||
|
||||
override fun getSymbolByFqName(classId: ClassId): ConeSymbol? {
|
||||
return classCache.lookupCacheOrCalculate(classId) {
|
||||
val facade = KotlinJavaPsiFacade.getInstance(project)
|
||||
val foundClass = facade.findClass(JavaClassFinder.Request(classId), allScope)
|
||||
val foundClass = facade.findClass(JavaClassFinder.Request(classId), searchScope)
|
||||
foundClass?.let { javaClass -> JavaClassSymbol(javaClass) }
|
||||
}
|
||||
}
|
||||
@@ -47,7 +31,7 @@ class JavaSymbolProvider(val project: Project) : FirSymbolProvider {
|
||||
override fun getPackage(fqName: FqName): FqName? {
|
||||
return packageCache.lookupCacheOrCalculate(fqName) {
|
||||
val facade = KotlinJavaPsiFacade.getInstance(project)
|
||||
val javaPackage = facade.findPackage(fqName.asString(), allScope) ?: return@lookupCacheOrCalculate null
|
||||
val javaPackage = facade.findPackage(fqName.asString(), searchScope) ?: return@lookupCacheOrCalculate null
|
||||
FqName(javaPackage.qualifiedName)
|
||||
}
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve
|
||||
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeSymbol
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
abstract class AbstractFirSymbolProvider : FirSymbolProvider {
|
||||
protected val classCache = mutableMapOf<ClassId, ConeSymbol?>()
|
||||
protected val packageCache = mutableMapOf<FqName, FqName?>()
|
||||
|
||||
protected inline fun <K, V : Any?> MutableMap<K, V>.lookupCacheOrCalculate(key: K, crossinline l: (K) -> V): V? {
|
||||
return if (key in this.keys) {
|
||||
this[key]
|
||||
} else {
|
||||
val calculated = l(key)
|
||||
this[key] = calculated
|
||||
calculated
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,6 @@ import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
interface FirSymbolProvider {
|
||||
|
||||
val doesLookupInFir: Boolean
|
||||
|
||||
fun getSymbolByFqName(classId: ClassId): ConeSymbol?
|
||||
|
||||
fun getPackage(fqName: FqName): FqName? // TODO: Replace to symbol sometime
|
||||
|
||||
-3
@@ -13,9 +13,6 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
|
||||
class FirCompositeSymbolProvider(val providers: List<FirSymbolProvider>) : FirSymbolProvider {
|
||||
|
||||
override val doesLookupInFir: Boolean
|
||||
get() = providers.any(FirSymbolProvider::doesLookupInFir)
|
||||
|
||||
override fun getPackage(fqName: FqName): FqName? {
|
||||
return providers.firstNotNullResult { it.getPackage(fqName) }
|
||||
}
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve.impl
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.dependenciesWithoutSelf
|
||||
import org.jetbrains.kotlin.fir.resolve.AbstractFirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.service
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeSymbol
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
class FirDependenciesSymbolProviderImpl(val session: FirSession) : AbstractFirSymbolProvider() {
|
||||
|
||||
private val dependencyProviders by lazy {
|
||||
val moduleInfo = session.moduleInfo ?: return@lazy emptyList()
|
||||
moduleInfo.dependenciesWithoutSelf().mapNotNull {
|
||||
session.sessionProvider?.getSession(it)?.service<FirSymbolProvider>()
|
||||
}.toList()
|
||||
}
|
||||
|
||||
override fun getSymbolByFqName(classId: ClassId): ConeSymbol? {
|
||||
return classCache.lookupCacheOrCalculate(classId) {
|
||||
for (provider in dependencyProviders) {
|
||||
provider.getSymbolByFqName(classId)?.let {
|
||||
return@lookupCacheOrCalculate it
|
||||
}
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPackage(fqName: FqName): FqName? {
|
||||
return packageCache.lookupCacheOrCalculate(fqName) {
|
||||
for (provider in dependencyProviders) {
|
||||
provider.getPackage(fqName)?.let {
|
||||
return@lookupCacheOrCalculate it
|
||||
}
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -28,13 +28,10 @@ import java.io.InputStream
|
||||
|
||||
class FirLibrarySymbolProviderImpl(val session: FirSession) : FirSymbolProvider {
|
||||
|
||||
override val doesLookupInFir: Boolean
|
||||
get() = false
|
||||
|
||||
private class BuiltInsPackageFragment(stream: InputStream, val fqName: FqName) {
|
||||
lateinit var version: BuiltInsBinaryVersion
|
||||
|
||||
val packageProto = run {
|
||||
val packageProto: ProtoBuf.PackageFragment = run {
|
||||
|
||||
version = BuiltInsBinaryVersion.readFrom(stream)
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@ import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
class FirProviderImpl(val session: FirSession) : FirProvider {
|
||||
|
||||
override val doesLookupInFir: Boolean
|
||||
get() = true
|
||||
|
||||
override fun getFirClassifierBySymbol(symbol: ConeSymbol): FirNamedDeclaration? {
|
||||
return when (symbol) {
|
||||
is FirBasedSymbol<*> -> symbol.fir as? FirNamedDeclaration
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvedImport
|
||||
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.impl.FirCompositeSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.impl.FirDependenciesSymbolProviderImpl
|
||||
import org.jetbrains.kotlin.fir.scopes.FirPosition
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeSymbol
|
||||
@@ -21,7 +22,7 @@ abstract class FirAbstractStarImportingScope(val session: FirSession, lookupInFi
|
||||
|
||||
val provider = FirSymbolProvider.getInstance(session).let {
|
||||
when {
|
||||
it is FirCompositeSymbolProvider && !lookupInFir -> FirCompositeSymbolProvider(it.providers.filter { !it.doesLookupInFir })
|
||||
it is FirCompositeSymbolProvider && !lookupInFir -> it.providers.find { it is FirDependenciesSymbolProviderImpl }!!
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir
|
||||
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.FirTotalResolveTransformer
|
||||
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
@@ -21,13 +23,16 @@ abstract class AbstractFirResolveTestCase : AbstractFirResolveWithSessionTestCas
|
||||
return createEnvironmentWithMockJdk(ConfigurationKind.JDK_NO_RUNTIME)
|
||||
}
|
||||
|
||||
fun doCreateAndProcessFir(ktFiles: List<KtFile>): List<FirFile> {
|
||||
val session = createSession()
|
||||
private fun doCreateAndProcessFir(ktFiles: List<KtFile>): List<FirFile> {
|
||||
|
||||
val scope = GlobalSearchScope.filesScope(project, ktFiles.mapNotNull { it.virtualFile })
|
||||
.uniteWith(TopDownAnalyzerFacadeForJVM.AllJavaSourcesInProjectScope(project))
|
||||
val session = createSession(scope)
|
||||
|
||||
val builder = RawFirBuilder(session)
|
||||
|
||||
val transformer = FirTotalResolveTransformer()
|
||||
val firFiles = ktFiles.map {
|
||||
return ktFiles.map {
|
||||
val firFile = builder.buildFirFile(it)
|
||||
(session.service<FirProvider>() as FirProviderImpl).recordFile(firFile)
|
||||
firFile
|
||||
@@ -39,8 +44,6 @@ abstract class AbstractFirResolveTestCase : AbstractFirResolveWithSessionTestCas
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
return firFiles
|
||||
}
|
||||
|
||||
|
||||
|
||||
+18
-1
@@ -5,10 +5,27 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir
|
||||
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.fir.java.FirJavaModuleBasedSession
|
||||
import org.jetbrains.kotlin.fir.java.FirLibrarySession
|
||||
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
|
||||
import org.jetbrains.kotlin.test.KotlinTestWithEnvironment
|
||||
|
||||
abstract class AbstractFirResolveWithSessionTestCase : KotlinTestWithEnvironment() {
|
||||
|
||||
open fun createSession(): FirSession = FirJavaModuleBasedSession(FirTestModuleInfo(), project)
|
||||
open fun createSession(sourceScope: GlobalSearchScope): FirSession {
|
||||
val moduleInfo = FirTestModuleInfo()
|
||||
val provider = FirProjectSessionProvider(project)
|
||||
return FirJavaModuleBasedSession(moduleInfo, provider, sourceScope).also {
|
||||
createSessionForDependencies(provider, moduleInfo, sourceScope)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSessionForDependencies(
|
||||
provider: FirProjectSessionProvider, moduleInfo: FirTestModuleInfo, sourceScope: GlobalSearchScope
|
||||
) {
|
||||
val dependenciesInfo = FirTestModuleInfo()
|
||||
moduleInfo.dependencies.add(dependenciesInfo)
|
||||
FirLibrarySession(dependenciesInfo, provider, GlobalSearchScope.notScope(sourceScope))
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,17 @@ import kotlin.reflect.KClass
|
||||
interface FirSession {
|
||||
val moduleInfo: ModuleInfo?
|
||||
|
||||
val sessionProvider: FirSessionProvider? get() = null
|
||||
|
||||
val components: Map<KClass<*>, Any>
|
||||
|
||||
fun <T : Any> getService(kclass: KClass<T>): T =
|
||||
components[kclass] as T
|
||||
}
|
||||
|
||||
interface FirSessionProvider {
|
||||
fun getSession(moduleInfo: ModuleInfo): FirSession?
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> FirSession.service(): T =
|
||||
getService(T::class)
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir
|
||||
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
||||
|
||||
fun <T : FirElement, D> MutableList<T>.transformInplace(transformer: FirTransformer<D>, data: D) {
|
||||
@@ -31,4 +32,6 @@ fun <T : FirElement, D> MutableList<T>.transformInplace(transformer: FirTransfor
|
||||
|
||||
fun <T : FirElement, D> T.transformSingle(transformer: FirTransformer<D>, data: D): T {
|
||||
return this.transform<T, D>(transformer, data).single
|
||||
}
|
||||
}
|
||||
|
||||
fun ModuleInfo.dependenciesWithoutSelf(): Sequence<ModuleInfo> = dependencies().asSequence().filter { it != this }
|
||||
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||
|
||||
class FirTestModuleInfo(
|
||||
override val name: Name = Name.identifier("TestModule"),
|
||||
private val dependencies: List<ModuleInfo> = emptyList(),
|
||||
val dependencies: MutableList<ModuleInfo> = mutableListOf(),
|
||||
override val platform: TargetPlatform = JvmPlatform
|
||||
) : ModuleInfo {
|
||||
override fun dependencies(): List<ModuleInfo> = dependencies
|
||||
|
||||
Reference in New Issue
Block a user