org.jetbrains.kotlin.analysis.decompiler.stub.clsStubBuilding.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.analysis.decompiler.stub
import com.intellij.psi.PsiElement
import com.intellij.psi.stubs.StubElement
import com.intellij.util.io.StringRef
import org.jetbrains.kotlin.analysis.decompiler.stub.flags.FlagsToModifiers
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.load.kotlin.FacadeClassSource
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.TypeTable
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.name.SpecialNames
import org.jetbrains.kotlin.protobuf.MessageLite
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.stubs.KotlinUserTypeStub
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
import org.jetbrains.kotlin.psi.stubs.impl.*
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.serialization.deserialization.AnnotatedCallableKind
import org.jetbrains.kotlin.serialization.deserialization.ProtoContainer
fun createTopLevelClassStub(
classId: ClassId,
classProto: ProtoBuf.Class,
source: SourceElement?,
context: ClsStubBuilderContext,
isScript: Boolean
): KotlinFileStubImpl {
val fileStub = createFileStub(classId.packageFqName, isScript)
createClassStub(fileStub, classProto, context.nameResolver, classId, source, context)
return fileStub
}
fun createPackageFacadeStub(
packageProto: ProtoBuf.Package,
packageFqName: FqName,
c: ClsStubBuilderContext
): KotlinFileStubImpl {
val fileStub = KotlinFileStubImpl.forFile(packageFqName, isScript = false)
setupFileStub(fileStub, packageFqName)
createPackageDeclarationsStubs(
fileStub, c, ProtoContainer.Package(packageFqName, c.nameResolver, c.typeTable, source = null), packageProto
)
return fileStub
}
fun createFileFacadeStub(
packageProto: ProtoBuf.Package,
facadeFqName: FqName,
c: ClsStubBuilderContext
): KotlinFileStubImpl {
val packageFqName = facadeFqName.parent()
val fileStub = KotlinFileStubImpl.forFileFacadeStub(facadeFqName)
setupFileStub(fileStub, packageFqName)
val container = ProtoContainer.Package(
packageFqName, c.nameResolver, c.typeTable,
JvmPackagePartSource(JvmClassName.byClassId(ClassId.topLevel(facadeFqName)), null, packageProto, c.nameResolver)
)
createPackageDeclarationsStubs(fileStub, c, container, packageProto)
return fileStub
}
fun createMultifileClassStub(
header: KotlinClassHeader,
partFiles: List,
facadeFqName: FqName,
components: ClsStubBuilderComponents
): KotlinFileStubImpl {
val packageFqName = header.packageName?.let { FqName(it) } ?: facadeFqName.parent()
val partNames = header.data?.asList()?.map { it.substringAfterLast('/') }
val fileStub = KotlinFileStubImpl.forMultifileClassStub(packageFqName, facadeFqName, partNames)
setupFileStub(fileStub, packageFqName)
for (partFile in partFiles) {
val partHeader = partFile.classHeader
val (nameResolver, packageProto) = JvmProtoBufUtil.readPackageDataFrom(partHeader.data!!, partHeader.strings!!)
val partContext = components.createContext(nameResolver, packageFqName, TypeTable(packageProto.typeTable))
val container = ProtoContainer.Package(
packageFqName, partContext.nameResolver, partContext.typeTable,
JvmPackagePartSource(partFile, packageProto, nameResolver)
)
createPackageDeclarationsStubs(fileStub, partContext, container, packageProto)
}
return fileStub
}
fun createIncompatibleAbiVersionFileStub() = createFileStub(FqName.ROOT, isScript = false)
fun createFileStub(packageFqName: FqName, isScript: Boolean): KotlinFileStubImpl {
val fileStub = KotlinFileStubImpl.forFile(packageFqName, isScript)
setupFileStub(fileStub, packageFqName)
return fileStub
}
private fun setupFileStub(fileStub: KotlinFileStubImpl, packageFqName: FqName) {
val packageDirectiveStub = KotlinPlaceHolderStubImpl(fileStub, KtStubElementTypes.PACKAGE_DIRECTIVE)
createStubForPackageName(packageDirectiveStub, packageFqName)
KotlinPlaceHolderStubImpl(fileStub, KtStubElementTypes.IMPORT_LIST)
}
fun createStubForPackageName(packageDirectiveStub: KotlinPlaceHolderStubImpl, packageFqName: FqName) {
val segments = packageFqName.pathSegments()
val iterator = segments.listIterator(segments.size)
fun recCreateStubForPackageName(current: StubElement) {
when (iterator.previousIndex()) {
-1 -> return
0 -> {
KotlinNameReferenceExpressionStubImpl(current, iterator.previous().ref())
return
}
else -> {
val lastSegment = iterator.previous()
val receiver = KotlinPlaceHolderStubImpl(current, KtStubElementTypes.DOT_QUALIFIED_EXPRESSION)
recCreateStubForPackageName(receiver)
KotlinNameReferenceExpressionStubImpl(receiver, lastSegment.ref())
}
}
}
recCreateStubForPackageName(packageDirectiveStub)
}
/**
* @param abbreviatedType The abbreviated type of the expanded type [typeClassId], if applicable. The abbreviated type always applies to the
* innermost type. For example, if we have `typealias Alias = Map.Entry`, `Alias` refers to `Entry` but not `Map`. It would be correct to
* refer back from `Entry` to `Alias`, but not from `Map` to `Alias`.
*
* Furthermore, an outer type in an already expanded type (e.g. `Map` in `Map.Entry`) cannot have originated from a type alias, because
* nested types cannot be accessed from type aliases. (Although this would change with KT-34281.)
*/
fun createStubForTypeName(
typeClassId: ClassId,
parent: StubElement,
abbreviatedType: KotlinClassTypeBean? = null,
upperBoundFun: ((Int) -> KotlinTypeBean?)? = null,
bindTypeArguments: (KotlinUserTypeStub, Int) -> Unit = { _, _ -> }
): KotlinUserTypeStub {
val substituteWithAny = typeClassId.isLocal
val fqName = if (substituteWithAny) StandardNames.FqNames.any
else typeClassId.asSingleFqName().toUnsafe()
val segments = fqName.pathSegments().asReversed()
assert(segments.isNotEmpty())
val classesNestedLevel = segments.size - if (substituteWithAny) 1 else typeClassId.packageFqName.pathSegments().size
fun recCreateStubForType(current: StubElement, level: Int): KotlinUserTypeStub {
val lastSegment = segments[level]
val userTypeStub = KotlinUserTypeStubImpl(current, upperBoundFun?.invoke(level), abbreviatedType.takeIf { level == 0 })
if (level + 1 < segments.size) {
recCreateStubForType(userTypeStub, level + 1)
}
KotlinNameReferenceExpressionStubImpl(userTypeStub, lastSegment.ref(), level < classesNestedLevel)
if (!substituteWithAny) {
bindTypeArguments(userTypeStub, level)
}
return userTypeStub
}
return recCreateStubForType(parent, level = 0)
}
fun createModifierListStubForDeclaration(
parent: StubElement,
flags: Int,
flagsToTranslate: List = listOf(),
additionalModifiers: List = listOf()
): KotlinModifierListStubImpl {
assert(flagsToTranslate.isNotEmpty())
val modifiers = flagsToTranslate.mapNotNull { it.getModifiers(flags) } + additionalModifiers
return createModifierListStub(parent, modifiers)!!
}
fun createModifierListStub(
parent: StubElement,
modifiers: Collection
): KotlinModifierListStubImpl? {
if (modifiers.isEmpty()) {
return null
}
return KotlinModifierListStubImpl(
parent,
ModifierMaskUtils.computeMask { it in modifiers },
KtStubElementTypes.MODIFIER_LIST
)
}
fun createEmptyModifierListStub(parent: KotlinStubBaseImpl<*>): KotlinModifierListStubImpl {
return KotlinModifierListStubImpl(
parent,
ModifierMaskUtils.computeMask { false },
KtStubElementTypes.MODIFIER_LIST
)
}
fun createAnnotationStubs(annotations: List, parent: KotlinStubBaseImpl<*>) {
return createTargetedAnnotationStubs(annotations.map { AnnotationWithTarget(it, null) }, parent)
}
fun createTargetedAnnotationStubs(
annotations: List,
parent: KotlinStubBaseImpl<*>
) {
if (annotations.isEmpty()) return
annotations.forEach { annotation ->
val (annotationWithArgs, target) = annotation
val annotationEntryStubImpl = KotlinAnnotationEntryStubImpl(
parent,
shortName = annotationWithArgs.classId.shortClassName.ref(),
hasValueArguments = false,
annotationWithArgs.args
)
if (target != null) {
KotlinAnnotationUseSiteTargetStubImpl(annotationEntryStubImpl, StringRef.fromString(target.name)!!)
}
val constructorCallee =
KotlinPlaceHolderStubImpl(annotationEntryStubImpl, KtStubElementTypes.CONSTRUCTOR_CALLEE)
val typeReference = KotlinPlaceHolderStubImpl(constructorCallee, KtStubElementTypes.TYPE_REFERENCE)
createStubForTypeName(annotationWithArgs.classId, typeReference)
}
}
internal fun createStubOrigin(protoContainer: ProtoContainer): KotlinStubOrigin? {
if (protoContainer is ProtoContainer.Package) {
val source = protoContainer.source
if (source is FacadeClassSource) {
val className = source.className.internalName
val facadeClassName = source.facadeClassName?.internalName
if (facadeClassName != null) {
return KotlinStubOrigin.MultiFileFacade(className, facadeClassName)
}
return KotlinStubOrigin.Facade(className)
}
}
return null
}
val MessageLite.annotatedCallableKind: AnnotatedCallableKind
get() = when (this) {
is ProtoBuf.Property -> AnnotatedCallableKind.PROPERTY
is ProtoBuf.Function, is ProtoBuf.Constructor -> AnnotatedCallableKind.FUNCTION
else -> throw IllegalStateException("Unsupported message: $this")
}
fun Name.ref() = StringRef.fromString(this.asString())!!
fun FqName.ref() = StringRef.fromString(this.asString())!!
fun computeParameterName(name: Name): Name {
return when {
name == SpecialNames.IMPLICIT_SET_PARAMETER -> StandardNames.DEFAULT_VALUE_PARAMETER
SpecialNames.isAnonymousParameterName(name) -> Name.identifier("_")
else -> name
}
}