FIR: Support loading deserialized declarations from JVM class files

This commit is contained in:
Denis Zharkov
2019-03-25 18:10:40 +03:00
parent 0f9d54c4dc
commit 1426341e9f
482 changed files with 6225 additions and 27 deletions
@@ -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 {
@@ -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
}