FIR: Support loading deserialized declarations from JVM class files
This commit is contained in:
@@ -8,10 +8,19 @@ 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.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.java.deserialization.KotlinDeserializedJvmSymbolsProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.FirProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.impl.*
|
||||
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.load.java.JavaClassFinder
|
||||
import org.jetbrains.kotlin.load.java.JavaClassFinderImpl
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
|
||||
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
|
||||
|
||||
class FirJavaModuleBasedSession(
|
||||
moduleInfo: ModuleInfo,
|
||||
@@ -34,10 +43,13 @@ class FirJavaModuleBasedSession(
|
||||
}
|
||||
}
|
||||
|
||||
class FirLibrarySession(
|
||||
class FirLibrarySession private constructor(
|
||||
moduleInfo: ModuleInfo,
|
||||
override val sessionProvider: FirProjectSessionProvider,
|
||||
scope: GlobalSearchScope
|
||||
scope: GlobalSearchScope,
|
||||
packagePartProvider: PackagePartProvider,
|
||||
kotlinClassFinder: KotlinClassFinder,
|
||||
javaClassFinder: JavaClassFinder
|
||||
) : FirSessionBase() {
|
||||
init {
|
||||
sessionProvider.sessionCache[moduleInfo] = this
|
||||
@@ -45,6 +57,11 @@ class FirLibrarySession(
|
||||
FirSymbolProvider::class,
|
||||
FirCompositeSymbolProvider(
|
||||
listOf(
|
||||
KotlinDeserializedJvmSymbolsProvider(
|
||||
this, sessionProvider.project,
|
||||
packagePartProvider, kotlinClassFinder,
|
||||
javaClassFinder
|
||||
),
|
||||
FirLibrarySymbolProviderImpl(this),
|
||||
JavaSymbolProvider(this, sessionProvider.project, scope),
|
||||
FirDependenciesSymbolProviderImpl(this)
|
||||
@@ -52,6 +69,38 @@ class FirLibrarySession(
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
moduleInfo: ModuleInfo,
|
||||
sessionProvider: FirProjectSessionProvider,
|
||||
scope: GlobalSearchScope,
|
||||
environment: KotlinCoreEnvironment
|
||||
): FirLibrarySession = create(
|
||||
moduleInfo, sessionProvider, scope, environment.project,
|
||||
environment.createPackagePartProvider(scope)
|
||||
)
|
||||
|
||||
fun create(
|
||||
moduleInfo: ModuleInfo,
|
||||
sessionProvider: FirProjectSessionProvider,
|
||||
scope: GlobalSearchScope,
|
||||
project: Project,
|
||||
packagePartProvider: PackagePartProvider
|
||||
): FirLibrarySession {
|
||||
val javaClassFinder = JavaClassFinderImpl().apply {
|
||||
this.setProjectInstance(project)
|
||||
this.setScope(scope)
|
||||
}
|
||||
|
||||
return FirLibrarySession(
|
||||
moduleInfo, sessionProvider, scope,
|
||||
packagePartProvider,
|
||||
VirtualFileFinderFactory.getInstance(project).create(scope),
|
||||
javaClassFinder
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FirProjectSessionProvider(val project: Project) : FirSessionProvider {
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.java.deserialization
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirNamedDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.deserialization.FirDeserializationContext
|
||||
import org.jetbrains.kotlin.fir.deserialization.deserializeClassToSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.AbstractFirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.getOrPut
|
||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.load.java.JavaClassFinder
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
|
||||
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
|
||||
import org.jetbrains.kotlin.load.kotlin.findKotlinClass
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import org.jetbrains.kotlin.serialization.deserialization.getName
|
||||
|
||||
class KotlinDeserializedJvmSymbolsProvider(
|
||||
val session: FirSession,
|
||||
val project: Project,
|
||||
private val packagePartProvider: PackagePartProvider,
|
||||
private val kotlinClassFinder: KotlinClassFinder,
|
||||
private val javaClassFinder: JavaClassFinder
|
||||
) : AbstractFirSymbolProvider() {
|
||||
|
||||
private val classesCache = mutableMapOf<ClassId, FirClassSymbol>()
|
||||
private val packagePartsCache = mutableMapOf<FqName, Collection<Pair<ProtoBuf.Package, FirDeserializationContext>>>()
|
||||
|
||||
private fun computePackagePartsInfos(packageFqName: FqName): List<Pair<ProtoBuf.Package, FirDeserializationContext>> {
|
||||
return packagePartProvider.findPackageParts(packageFqName.asString()).mapNotNull { partName ->
|
||||
val classId = ClassId.topLevel(JvmClassName.byInternalName(partName).fqNameForTopLevelClassMaybeWithDollars)
|
||||
val kotlinJvmBinaryClass = kotlinClassFinder.findKotlinClass(classId) ?: return@mapNotNull null
|
||||
|
||||
val data = kotlinJvmBinaryClass.classHeader.data ?: return@mapNotNull null
|
||||
val strings = kotlinJvmBinaryClass.classHeader.strings ?: return@mapNotNull null
|
||||
val (nameResolver, packageProto) = JvmProtoBufUtil.readPackageDataFrom(data, strings)
|
||||
|
||||
packageProto to FirDeserializationContext.createForPackage(packageFqName, packageProto, nameResolver, session)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getClassLikeSymbolByFqName(classId: ClassId): ConeClassLikeSymbol? {
|
||||
return findAndDeserializeClass(classId)
|
||||
}
|
||||
|
||||
private fun findAndDeserializeClass(
|
||||
classId: ClassId,
|
||||
parentContext: FirDeserializationContext? = null
|
||||
): FirClassSymbol? {
|
||||
val kotlinJvmBinaryClass = kotlinClassFinder.findKotlinClass(classId) ?: return null
|
||||
if (kotlinJvmBinaryClass.classHeader.kind != KotlinClassHeader.Kind.CLASS) return null
|
||||
|
||||
val data = kotlinJvmBinaryClass.classHeader.data ?: return null
|
||||
val strings = kotlinJvmBinaryClass.classHeader.strings ?: return null
|
||||
val (nameResolver, classProto) = JvmProtoBufUtil.readClassDataFrom(data, strings)
|
||||
|
||||
return classesCache.getOrPut(classId, { FirClassSymbol(classId) }) { symbol ->
|
||||
deserializeClassToSymbol(
|
||||
classId, classProto, symbol, nameResolver, session, parentContext,
|
||||
this::findAndDeserializeClass
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCallableSymbols(callableId: CallableId): List<ConeCallableSymbol> {
|
||||
if (callableId.classId != null) {
|
||||
return getClassDeclarations(callableId.classId!!).filterIsInstance<FirCallableMemberDeclaration>()
|
||||
.filter { it.name == callableId.callableName }
|
||||
.map { it.symbol }
|
||||
}
|
||||
|
||||
val packageFqName = callableId.packageName
|
||||
|
||||
return getPackageParts(packageFqName).flatMap { (packageProto, context) ->
|
||||
packageProto.functionList.map {
|
||||
context.memberDeserializer.loadFunction(it).symbol
|
||||
}.filter { callableSymbol -> callableSymbol.callableId.callableName == callableId.callableName }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPackageParts(packageFqName: FqName): Collection<Pair<ProtoBuf.Package, FirDeserializationContext>> {
|
||||
return packagePartsCache.getOrPut(packageFqName) {
|
||||
computePackagePartsInfos(packageFqName)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAllCallableNamesInPackage(fqName: FqName): Set<Name> {
|
||||
return getPackageParts(fqName).flatMapTo(mutableSetOf()) { (packageProto, context) ->
|
||||
packageProto.functionList.map { context.nameResolver.getName(it.name) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getClassNamesInPackage(fqName: FqName): Set<Name> =
|
||||
javaClassFinder.findPackage(fqName)
|
||||
?.getClasses { true }.orEmpty()
|
||||
.mapTo(sortedSetOf(), JavaClass::name)
|
||||
|
||||
private fun getClassDeclarations(classId: ClassId): List<FirDeclaration> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val classSymbol = getClassLikeSymbolByFqName(classId) as? FirBasedSymbol<FirRegularClass> ?: return emptyList()
|
||||
return classSymbol.fir.declarations
|
||||
}
|
||||
|
||||
override fun getAllCallableNamesInClass(classId: ClassId): Set<Name> =
|
||||
getClassDeclarations(classId)
|
||||
.filterIsInstance<FirNamedDeclaration>()
|
||||
.mapTo(mutableSetOf(), FirNamedDeclaration::name)
|
||||
|
||||
override fun getNestedClassesNamesInClass(classId: ClassId): Set<Name> {
|
||||
return getClassDeclarations(classId).filterIsInstance<FirRegularClass>().mapTo(mutableSetOf()) { it.name }
|
||||
}
|
||||
|
||||
override fun getPackage(fqName: FqName): FqName? = null
|
||||
}
|
||||
Reference in New Issue
Block a user