Migrate LightMemberOriginForCompiledElement from IJ repository

This commit is contained in:
Ilya Kirillov
2021-12-20 13:30:13 +03:00
parent 764ad8b9fa
commit b563824b35
8 changed files with 382 additions and 7 deletions
@@ -8,10 +8,6 @@ dependencies {
implementation(project(":compiler:frontend.java"))
implementation(project(":core:compiler.common"))
implementation(project(":compiler:light-classes"))
implementation(project(":analysis:analysis-api-providers"))
implementation(project(":analysis:analysis-api"))
implementation(project(":analysis:analysis-internal-utils"))
implementation(project(":analysis:project-structure"))
implementation(project(":compiler:psi:cls-psi-stub-builder"))
implementation(project(":compiler:psi:cls-psi-file-stub-builder"))
implementation(intellijCore())
@@ -0,0 +1,109 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.analysis.decompiler.psi.text
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtDecompiledFile
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltInsSignatures
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.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass
object ByDescriptorIndexer : DecompiledTextIndexer<String> {
override fun indexDescriptor(descriptor: DeclarationDescriptor): Collection<String> {
return listOf(descriptor.toStringKey())
}
internal fun getDeclarationForDescriptor(descriptor: DeclarationDescriptor, file: KtDecompiledFile): KtDeclaration? {
val original = descriptor.original
if (original is TypeAliasConstructorDescriptor) {
return getDeclarationForDescriptor(original.typeAliasDescriptor, file)
}
if (original is ValueParameterDescriptor) {
val callable = original.containingDeclaration
val callableDeclaration = getDeclarationForDescriptor(callable, file) as? KtCallableDeclaration ?: return null
if (original.index >= callableDeclaration.valueParameters.size) {
LOG.error(
"Parameter count mismatch for ${DescriptorRenderer.DEBUG_TEXT.render(callable)}[${original.index}] vs " +
callableDeclaration.valueParameterList?.text
)
return null
}
return callableDeclaration.valueParameters[original.index]
}
if (original is ConstructorDescriptor && original.isPrimary) {
val classOrObject = getDeclarationForDescriptor(original.containingDeclaration, file) as? KtClassOrObject
return classOrObject?.primaryConstructor ?: classOrObject
}
val descriptorKey = original.toStringKey()
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 singleOrNull = declarationContainer.declarations.singleOrNull { it.name == descriptorName }
if (singleOrNull != null) {
return singleOrNull
}
}
}
}
return file.getDeclaration(this, descriptorKey) ?: run {
return getBuiltinsDescriptorKey(descriptor)?.let { file.getDeclaration(this, it) }
}
}
fun getBuiltinsDescriptorKey(descriptor: DeclarationDescriptor): String? {
if (descriptor !is ClassDescriptor) return null
val classFqName = descriptor.fqNameUnsafe
if (!JvmBuiltInsSignatures.isSerializableInJava(classFqName)) return null
val builtInDescriptor =
DefaultBuiltIns.Instance.builtInsModule.resolveTopLevelClass(classFqName.toSafe(), NoLookupLocation.FROM_IDE)
return builtInDescriptor?.toStringKey()
}
private fun DeclarationDescriptor.toStringKey(): String {
return descriptorRendererForKeys.render(this)
}
private val descriptorRendererForKeys = DescriptorRenderer.withOptions {
defaultDecompilerRendererOptions()
withDefinedIn = true
renderUnabbreviatedType = false
}
private val LOG = Logger.getInstance(this::class.java)
}
@@ -0,0 +1,184 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.analysis.decompiler.psi.text
import com.intellij.psi.PsiMember
import com.intellij.psi.impl.compiled.SignatureParsing
import com.intellij.util.cls.ClsFormatException
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.kotlin.MemberSignature
import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmOverloadsAnnotation
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassConstructorDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor
import java.text.CharacterIterator
import java.text.StringCharacterIterator
private object ByJvmSignatureIndexer : DecompiledTextIndexer<ClassNameAndSignature> {
override fun indexDescriptor(descriptor: DeclarationDescriptor): Collection<ClassNameAndSignature> {
val signatures = arrayListOf<ClassNameAndSignature>()
fun save(id: List<Name>, signature: MemberSignature) {
signatures.add(ClassNameAndSignature(id, signature))
}
fun ClassDescriptor.apply() {
when (kind) {
ClassKind.ENUM_ENTRY -> {
val enumClass = containingDeclaration as ClassDescriptor
val signature = MemberSignature.fromFieldNameAndDesc(name.asString(), enumClass.desc())
save(enumClass.relativeClassName(), signature)
}
ClassKind.OBJECT -> {
val instanceFieldSignature = MemberSignature.fromFieldNameAndDesc(JvmAbi.INSTANCE_FIELD, desc())
save(relativeClassName(), instanceFieldSignature)
if (isCompanionObject) {
val signature = MemberSignature.fromFieldNameAndDesc(name.asString(), desc())
save((containingDeclaration as? ClassDescriptor)?.relativeClassName().orEmpty(), signature)
}
}
else -> {
}
}
}
fun DeserializedClassConstructorDescriptor.apply() {
JvmProtoBufUtil.getJvmConstructorSignature(proto, nameResolver, typeTable)?.let {
val id = (containingDeclaration as? ClassDescriptor)?.relativeClassName().orEmpty()
val signature = MemberSignature.fromJvmMemberSignature(it)
save(id, signature)
}
}
fun DeserializedSimpleFunctionDescriptor.apply() {
JvmProtoBufUtil.getJvmMethodSignature(proto, nameResolver, typeTable)?.let {
val id = (containingDeclaration as? ClassDescriptor)?.relativeClassName().orEmpty()
val signature = MemberSignature.fromJvmMemberSignature(it)
save(id, signature)
if (findJvmOverloadsAnnotation() == null) return
val extensionShift = if (isExtension) 1 else 0
val omittedList = mutableListOf<Int>()
valueParameters.asReversed().forEach { parameter ->
if (parameter.hasDefaultValue()) {
omittedList.add(parameter.index + extensionShift)
val newDescriptor = excludeParametersFromDescriptor(it.desc, omittedList)
if (newDescriptor != null) {
val overloadedSignature = MemberSignature.fromMethodNameAndDesc(it.name, newDescriptor)
save(id, overloadedSignature)
}
}
}
}
}
fun DeserializedPropertyDescriptor.apply() {
val className = (containingDeclaration as? ClassDescriptor)?.relativeClassName().orEmpty()
val signature = proto.getExtensionOrNull(JvmProtoBuf.propertySignature)
if (signature != null) {
val fieldSignature = JvmProtoBufUtil.getJvmFieldSignature(proto, nameResolver, typeTable)
if (fieldSignature != null) {
save(className, MemberSignature.fromJvmMemberSignature(fieldSignature))
}
if (signature.hasGetter()) {
save(className, MemberSignature.fromMethod(nameResolver, signature.getter))
}
if (signature.hasSetter()) {
save(className, MemberSignature.fromMethod(nameResolver, signature.setter))
}
}
}
when (descriptor) {
is ClassDescriptor -> descriptor.apply()
is DeserializedClassConstructorDescriptor -> descriptor.apply()
is DeserializedSimpleFunctionDescriptor -> descriptor.apply()
is DeserializedPropertyDescriptor -> descriptor.apply()
}
return signatures
}
}
private fun excludeParametersFromDescriptor(descriptor: String, omittedParameters: List<Int>): String? {
fun tryParseParametersAndReturnType(): Pair<List<String>, String>? {
val iterator = StringCharacterIterator(descriptor)
fun parseTypeString(): String? {
val begin = iterator.index
try {
SignatureParsing.parseTypeString(iterator) { it }
} catch (e: ClsFormatException) {
return null
}
val end = iterator.index
return descriptor.substring(begin, end)
}
if (iterator.current() != '(') return null
iterator.next()
if (iterator.current() == ')') {
iterator.next()
val returnType = parseTypeString() ?: return null
return emptyList<String>() to returnType
}
val parameterTypes = mutableListOf<String>()
while (iterator.current() != ')' && iterator.current() != CharacterIterator.DONE) {
parameterTypes += parseTypeString() ?: return null
}
if (iterator.current() != ')') return null
iterator.next()
val returnType = parseTypeString() ?: return null
return parameterTypes to returnType
}
val (parameterTypes, returnType) = tryParseParametersAndReturnType() ?: return null
val parametersList = parameterTypes
.filterIndexed { index, _ -> index !in omittedParameters }
.joinToString("")
return "($parametersList)$returnType"
}
private fun ClassDescriptor.desc(): String = "L" + JvmClassName.byClassId(classId!!).internalName + ";"
fun PsiMember.relativeClassName(): List<Name> {
return generateSequence(this.containingClass) { it.containingClass }.toList().dropLast(1).reversed().map { Name.identifier(it.name!!) }
}
private fun ClassDescriptor.relativeClassName(): List<Name> {
return classId!!.relativeClassName.pathSegments().drop(1)
}
// every member is represented by its jvm signature and relative class name (which is easy to obtain from descriptors or cls psi)
// relative class name is a path containing inner/nested class names from top level class to the class containing this member (excluding top level class name)
// Examples: for top level function or function in a top level class relativeClassName is empty
// For: class TopLevel { class A { class B { fun f() } } }
// relativeClassName for function 'f' will be [A, B]
data class ClassNameAndSignature(val relativeClassName: List<Name>, val memberSignature: MemberSignature)
// expose with different type
val BySignatureIndexer: DecompiledTextIndexer<*> = ByJvmSignatureIndexer
@@ -0,0 +1,17 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
api(project(":compiler:psi"))
api(project(":analysis:decompiled:decompiler-to-psi"))
api(project(":analysis:decompiled:light-classes-for-decompiled"))
api(project(":compiler:light-classes"))
implementation(intellijCore())
}
sourceSets {
"main" { projectDefault() }
"test" { none() }
}
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.analysis.decompiled.light.classes.fe10
import com.intellij.psi.PsiMember
import org.jetbrains.kotlin.analysis.decompiled.light.classes.origin.KotlinDeclarationInCompiledFileSearcher
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtClsFile
import org.jetbrains.kotlin.analysis.decompiler.psi.text.BySignatureIndexer
import org.jetbrains.kotlin.analysis.decompiler.psi.text.ClassNameAndSignature
import org.jetbrains.kotlin.analysis.decompiler.psi.text.relativeClassName
import org.jetbrains.kotlin.load.kotlin.MemberSignature
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtDeclarationContainer
class KotlinDeclarationInCompiledFileSearcherFE10Impl: KotlinDeclarationInCompiledFileSearcher() {
override fun findDeclarationInCompiledFile(file: KtClsFile, member: PsiMember, signature: MemberSignature): KtDeclaration? {
val relativeClassName = member.relativeClassName()
val key = ClassNameAndSignature(relativeClassName, signature)
val memberName = member.name
if (memberName != null && !file.isContentsLoaded && file.hasDeclarationWithKey(BySignatureIndexer, key)) {
val container: KtDeclarationContainer? = if (relativeClassName.isEmpty())
file
else {
val topClassOrObject = file.declarations.singleOrNull() as? KtClassOrObject
relativeClassName.fold(topClassOrObject) { classOrObject, name ->
classOrObject?.declarations?.singleOrNull { it.name == name.asString() } as? KtClassOrObject
}
}
val declaration = container?.declarations?.singleOrNull {
it.name == memberName
}
if (declaration != null) {
return declaration
}
}
return file.getDeclaration(BySignatureIndexer, key)
}
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.analysis.decompiled.light.classes.origin
import com.intellij.openapi.application.ApplicationManager
import com.intellij.psi.PsiMember
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtClsFile
import org.jetbrains.kotlin.load.kotlin.MemberSignature
import org.jetbrains.kotlin.psi.KtDeclaration
abstract class KotlinDeclarationInCompiledFileSearcher {
abstract fun findDeclarationInCompiledFile(file: KtClsFile, member: PsiMember, signature: MemberSignature): KtDeclaration?
companion object {
fun getInstance(): KotlinDeclarationInCompiledFileSearcher =
ApplicationManager.getApplication().getService(KotlinDeclarationInCompiledFileSearcher::class.java)
}
}
@@ -47,7 +47,7 @@ data class LightMemberOriginForCompiledField(val psiField: PsiField, val file: K
override val originalElement: KtDeclaration? by lazyPub {
val desc = MapPsiToAsmDesc.typeDesc(psiField.type)
val signature = MemberSignature.fromFieldNameAndDesc(psiField.name, desc)
findDeclarationInCompiledFile(file, psiField, signature)
KotlinDeclarationInCompiledFileSearcher.getInstance().findDeclarationInCompiledFile(file, psiField, signature)
}
}
@@ -70,6 +70,6 @@ data class LightMemberOriginForCompiledMethod(val psiMethod: PsiMethod, val file
val desc = MapPsiToAsmDesc.methodDesc(psiMethod)
val name = if (psiMethod.isConstructor) "<init>" else psiMethod.name
val signature = MemberSignature.fromMethodNameAndDesc(name, desc)
findDeclarationInCompiledFile(file, psiMethod, signature)
KotlinDeclarationInCompiledFileSearcher.getInstance().findDeclarationInCompiledFile(file, psiMethod, signature)
}
}
+2 -1
View File
@@ -500,7 +500,8 @@ include ":generators:analysis-api-generator",
":analysis:project-structure",
":analysis:analysis-api-fe10",
":analysis:decompiled:decompiler-to-psi",
":analysis:decompiled:light-classes-for-decompiled"
":analysis:decompiled:light-classes-for-decompiled",
":analysis:decompiled:light-classes-for-decompiled-fe10"
if (buildProperties.inJpsBuildIdeaSync) {