[psi] don't load text for compiled code

use stubs when possible, when stub doesn't contain anything,
decompiled text won't contain it either,
no sense to load
This commit is contained in:
Anna Kozlova
2023-03-21 20:27:36 +01:00
committed by teamcity
parent e9c8be3b64
commit 956294821b
9 changed files with 344 additions and 86 deletions
@@ -31,12 +31,4 @@ open class KtDecompiledFile(
decompiledText.drop()
}
fun <T : Any> getDeclaration(indexer: DecompiledTextIndexer<T>, key: T): KtDeclaration? {
val range = decompiledText.get().index.getRange(indexer, key) ?: return null
return PsiTreeUtil.findElementOfClassAtRange(this@KtDecompiledFile, range.startOffset, range.endOffset, KtDeclaration::class.java)
}
fun <T : Any> hasDeclarationWithKey(indexer: DecompiledTextIndexer<T>, key: T): Boolean {
return decompiledText.get().index.getRange(indexer, key) != null
}
}
@@ -6,22 +6,26 @@
package org.jetbrains.kotlin.analysis.decompiler.psi.text
import com.intellij.openapi.diagnostic.Logger
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtDecompiledFile
import org.jetbrains.kotlin.analysis.decompiler.stub.COMPILED_DEFAULT_INITIALIZER
import org.jetbrains.kotlin.analysis.decompiler.stub.COMPILED_DEFAULT_PARAMETER_VALUE
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltInsSignatures
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtDeclarationContainer
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.hasSuspendModifier
import org.jetbrains.kotlin.psi.psiUtil.unwrapNullability
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.types.AbbreviatedType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isNullableAny
import org.jetbrains.kotlin.utils.addIfNotNull
object ByDescriptorIndexer : DecompiledTextIndexer<String> {
@@ -60,47 +64,117 @@ object ByDescriptorIndexer : DecompiledTextIndexer<String> {
return getDeclarationForDescriptor(original.containingDeclaration, file)
}
val descriptorKey = original.toStringKey()
if (original is MemberDescriptor) {
val declarationContainer: KtDeclarationContainer? = when {
DescriptorUtils.isTopLevelDeclaration(original) -> file
original.containingDeclaration is ClassDescriptor ->
getDeclarationForDescriptor(original.containingDeclaration as ClassDescriptor, file) as? KtClassOrObject
else -> null
}
if (!file.isContentsLoaded && original is MemberDescriptor) {
val hasDeclarationByKey = file.hasDeclarationWithKey(this, descriptorKey) || (getBuiltinsDescriptorKey(descriptor)?.let {
file.hasDeclarationWithKey(
this,
it
)
} ?: false)
if (hasDeclarationByKey) {
val declarationContainer: KtDeclarationContainer? = when {
DescriptorUtils.isTopLevelDeclaration(original) -> file
original.containingDeclaration is ClassDescriptor ->
getDeclarationForDescriptor(original.containingDeclaration as ClassDescriptor, file) as? KtClassOrObject
else -> null
if (declarationContainer != null) {
val descriptorName = original.name.asString()
val declarations = when {
original is ConstructorDescriptor && declarationContainer is KtClass -> declarationContainer.allConstructors
else -> declarationContainer.declarations.filter { it.name == descriptorName }
}
if (declarationContainer != null) {
val descriptorName = original.name.asString()
val singleOrNull = declarationContainer.declarations.singleOrNull { it.name == descriptorName }
if (singleOrNull != null) {
return singleOrNull
return declarations
.firstOrNull { declaration ->
if (original is CallableDescriptor) {
declaration is KtCallableDeclaration && isSameCallable(declaration, original)
} else declaration !is KtCallableDeclaration
}
}
}
}
return file.getDeclaration(this, descriptorKey) ?: run {
return getBuiltinsDescriptorKey(descriptor)?.let { file.getDeclaration(this, it) }
}
error("Should not be reachable")
}
fun getBuiltinsDescriptorKey(descriptor: DeclarationDescriptor): String? {
if (descriptor !is ClassDescriptor) return null
fun isSameCallable(
declaration: KtCallableDeclaration,
original: CallableDescriptor
): Boolean {
if (!receiverTypesMatch(declaration.receiverTypeReference, original.extensionReceiverParameter)) return false
val classFqName = descriptor.fqNameUnsafe
if (!JvmBuiltInsSignatures.isSerializableInJava(classFqName)) return null
if (!returnTypesMatch(declaration, original)) return false
if (!typeParametersMatch(declaration, original)) return false
val builtInDescriptor =
DefaultBuiltIns.Instance.builtInsModule.resolveTopLevelClass(classFqName.toSafe(), NoLookupLocation.FROM_IDE)
return builtInDescriptor?.toStringKey()
if (!parametersMatch(declaration, original)) return false
return true
}
private fun returnTypesMatch(declaration: KtCallableDeclaration, descriptor: CallableDescriptor): Boolean {
if (declaration is KtConstructor<*>) return true
return areTypesTheSame(descriptor.returnType!!, declaration.typeReference!!)
}
private fun typeParametersMatch(declaration: KtCallableDeclaration, descriptor: CallableDescriptor): Boolean {
if (declaration.typeParameters.size != declaration.typeParameters.size) return false
val boundsByName = declaration.typeConstraints.groupBy { it.subjectTypeParameterName?.getReferencedName() }
descriptor.typeParameters.zip(declaration.typeParameters) { descriptorTypeParam, psiTypeParameter ->
if (descriptorTypeParam.name.toString() != psiTypeParameter.name) return false
val psiBounds = mutableListOf<KtTypeReference>()
psiBounds.addIfNotNull(psiTypeParameter.extendsBound)
boundsByName[psiTypeParameter.name]?.forEach {
psiBounds.addIfNotNull(it.boundTypeReference)
}
val expectedBounds = descriptorTypeParam.upperBounds.filter { !it.isNullableAny() }
if (psiBounds.size != expectedBounds.size) return false
expectedBounds.zip(psiBounds) { expectedBound, candidateBound ->
if (!areTypesTheSame(expectedBound, candidateBound)) {
return false
}
}
}
return true
}
private fun parametersMatch(
declaration: KtCallableDeclaration,
original: CallableDescriptor
): Boolean {
if (declaration.valueParameters.size != original.valueParameters.size) {
return false
}
declaration.valueParameters.zip(original.valueParameters).forEach { (ktParam, paramDesc) ->
val isVarargs = ktParam.isVarArg
if (isVarargs != (paramDesc.varargElementType != null)) {
return false
}
if (!areTypesTheSame(if (isVarargs) paramDesc.varargElementType!! else paramDesc.type, ktParam.typeReference!!)) {
return false
}
}
return true
}
private fun receiverTypesMatch(
ktTypeReference: KtTypeReference?,
receiverParameter: ReceiverParameterDescriptor?,
): Boolean {
if (ktTypeReference != null) {
if (receiverParameter == null) return false
val receiverType = receiverParameter.type
if (!areTypesTheSame(receiverType, ktTypeReference)) {
return false
}
} else if (receiverParameter != null) return false
return true
}
private fun areTypesTheSame(
kotlinType: KotlinType,
ktTypeReference: KtTypeReference
): Boolean {
val qualifiedName = getQualifiedName(
ktTypeReference.typeElement,
ktTypeReference.getAllModifierLists().any { it.hasSuspendModifier() }) ?: return false
val declarationDescriptor =
((kotlinType as? AbbreviatedType)?.abbreviation ?: kotlinType).constructor.declarationDescriptor ?: return false
if (declarationDescriptor is TypeParameterDescriptor) {
return declarationDescriptor.name.asString() == qualifiedName
}
return declarationDescriptor.fqNameSafe.asString() == qualifiedName
}
private fun DeclarationDescriptor.toStringKey(): String {
@@ -118,3 +192,29 @@ object ByDescriptorIndexer : DecompiledTextIndexer<String> {
private val LOG = Logger.getInstance(this::class.java)
}
fun getQualifiedName(typeElement: KtTypeElement?, isSuspend: Boolean): String? {
val referencedName = when (typeElement) {
is KtUserType -> getQualifiedName(typeElement)
is KtFunctionType -> {
var parametersCount = typeElement.parameters.size
typeElement.receiverTypeReference?.let { parametersCount++ }
if (isSuspend) {
StandardNames.getSuspendFunctionClassId(parametersCount).asFqNameString()
} else {
StandardNames.getFunctionClassId(parametersCount).asFqNameString()
}
}
is KtNullableType -> getQualifiedName(typeElement.unwrapNullability(), isSuspend)
else -> null
}
return referencedName
}
private fun getQualifiedName(userType: KtUserType): String? {
val qualifier = userType.qualifier ?: return userType.referencedName
return getQualifiedName(qualifier) + "." + userType.referencedName
}
fun KtElementImplStub<*>.getAllModifierLists(): Array<out KtDeclarationModifierList> =
getStubOrPsiChildren(KtStubElementTypes.MODIFIER_LIST, KtStubElementTypes.MODIFIER_LIST.arrayFactory)