diff --git a/compiler/psi/cls-psi-stub-builder/build.gradle.kts b/compiler/psi/cls-psi-stub-builder/build.gradle.kts new file mode 100644 index 00000000000..31bca730316 --- /dev/null +++ b/compiler/psi/cls-psi-stub-builder/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + api(project(":compiler:psi")) + api(project(":core:deserialization.common")) + api(project(":core:deserialization.common.jvm")) + api(project(":core:deserialization")) + implementation(project(":core:compiler.common.jvm")) + + api(intellijCoreDep()) { includeJars("intellij-core", rootProject = rootProject) } + +} + +sourceSets { + "main" { projectDefault() } + "test" {} +} + + diff --git a/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/CallableClsStubBuilder.kt b/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/CallableClsStubBuilder.kt new file mode 100644 index 00000000000..021221504fb --- /dev/null +++ b/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/CallableClsStubBuilder.kt @@ -0,0 +1,286 @@ +// 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.psi.stubs.builder + +import com.intellij.psi.PsiElement +import com.intellij.psi.stubs.StubElement +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.ProtoBuf.MemberKind +import org.jetbrains.kotlin.metadata.ProtoBuf.Modality +import org.jetbrains.kotlin.metadata.deserialization.Flags +import org.jetbrains.kotlin.metadata.deserialization.hasReceiver +import org.jetbrains.kotlin.metadata.deserialization.receiverType +import org.jetbrains.kotlin.metadata.deserialization.returnType +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.stubs.builder.flags.* +import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes +import org.jetbrains.kotlin.psi.stubs.impl.KotlinFunctionStubImpl +import org.jetbrains.kotlin.psi.stubs.impl.KotlinPlaceHolderStubImpl +import org.jetbrains.kotlin.psi.stubs.impl.KotlinPropertyStubImpl +import org.jetbrains.kotlin.resolve.DataClassResolver +import org.jetbrains.kotlin.serialization.deserialization.AnnotatedCallableKind +import org.jetbrains.kotlin.serialization.deserialization.ProtoContainer +import org.jetbrains.kotlin.serialization.deserialization.getName + +fun createPackageDeclarationsStubs( + parentStub: StubElement, + outerContext: ClsStubBuilderContext, + protoContainer: ProtoContainer.Package, + packageProto: ProtoBuf.Package +) { + createDeclarationsStubs(parentStub, outerContext, protoContainer, packageProto.functionList, packageProto.propertyList) + createTypeAliasesStubs(parentStub, outerContext, protoContainer, packageProto.typeAliasList) +} + +fun createDeclarationsStubs( + parentStub: StubElement, + outerContext: ClsStubBuilderContext, + protoContainer: ProtoContainer, + functionProtos: List, + propertyProtos: List, +) { + for (propertyProto in propertyProtos) { + if (!shouldSkip(propertyProto.flags, outerContext.nameResolver.getName(propertyProto.name))) { + PropertyClsStubBuilder(parentStub, outerContext, protoContainer, propertyProto).build() + } + } + for (functionProto in functionProtos) { + if (!shouldSkip(functionProto.flags, outerContext.nameResolver.getName(functionProto.name))) { + FunctionClsStubBuilder(parentStub, outerContext, protoContainer, functionProto).build() + } + } +} + +fun createTypeAliasesStubs( + parentStub: StubElement, + outerContext: ClsStubBuilderContext, + protoContainer: ProtoContainer, + typeAliasesProtos: List +) { + for (typeAliasProto in typeAliasesProtos) { + createTypeAliasStub(parentStub, typeAliasProto, protoContainer, outerContext) + } +} + +fun createConstructorStub( + parentStub: StubElement, + constructorProto: ProtoBuf.Constructor, + outerContext: ClsStubBuilderContext, + protoContainer: ProtoContainer +) { + ConstructorClsStubBuilder(parentStub, outerContext, protoContainer, constructorProto).build() +} + +private fun shouldSkip(flags: Int, name: Name): Boolean { + return when (Flags.MEMBER_KIND.get(flags)) { + MemberKind.FAKE_OVERRIDE, MemberKind.DELEGATION -> true + //TODO: fix decompiler to use sane criteria + MemberKind.SYNTHESIZED -> !DataClassResolver.isComponentLike(name) + else -> false + } +} + +abstract class CallableClsStubBuilder( + parent: StubElement, + outerContext: ClsStubBuilderContext, + protected val protoContainer: ProtoContainer, + private val typeParameters: List +) { + protected val c = outerContext.child(typeParameters) + protected val typeStubBuilder = TypeClsStubBuilder(c) + protected val isTopLevel: Boolean get() = protoContainer is ProtoContainer.Package + protected val callableStub: StubElement by lazy(LazyThreadSafetyMode.NONE) { doCreateCallableStub(parent) } + + fun build() { + createModifierListStub() + val typeConstraintListData = typeStubBuilder.createTypeParameterListStub(callableStub, typeParameters) + createReceiverTypeReferenceStub() + createValueParameterList() + createReturnTypeStub() + typeStubBuilder.createTypeConstraintListStub(callableStub, typeConstraintListData) + } + + abstract val receiverType: ProtoBuf.Type? + abstract val receiverAnnotations: List + + abstract val returnType: ProtoBuf.Type? + + private fun createReceiverTypeReferenceStub() { + receiverType?.let { + typeStubBuilder.createTypeReferenceStub(callableStub, it, this::receiverAnnotations) + } + } + + private fun createReturnTypeStub() { + returnType?.let { + typeStubBuilder.createTypeReferenceStub(callableStub, it) + } + } + + abstract fun createModifierListStub() + + abstract fun createValueParameterList() + + abstract fun doCreateCallableStub(parent: StubElement): StubElement +} + +private class FunctionClsStubBuilder( + parent: StubElement, + outerContext: ClsStubBuilderContext, + protoContainer: ProtoContainer, + private val functionProto: ProtoBuf.Function +) : CallableClsStubBuilder(parent, outerContext, protoContainer, functionProto.typeParameterList) { + override val receiverType: ProtoBuf.Type? + get() = functionProto.receiverType(c.typeTable) + + override val receiverAnnotations: List + get() { + return c.components.annotationLoader + .loadExtensionReceiverParameterAnnotations(protoContainer, functionProto, AnnotatedCallableKind.FUNCTION) + .map { ClassIdWithTarget(it, AnnotationUseSiteTarget.RECEIVER) } + } + + override val returnType: ProtoBuf.Type + get() = functionProto.returnType(c.typeTable) + + override fun createValueParameterList() { + typeStubBuilder.createValueParameterListStub(callableStub, functionProto, functionProto.valueParameterList, protoContainer) + } + + override fun createModifierListStub() { + val modalityModifier = if (isTopLevel) listOf() else listOf(MODALITY) + val modifierListStubImpl = createModifierListStubForDeclaration( + callableStub, functionProto.flags, + listOf(VISIBILITY, OPERATOR, INFIX, EXTERNAL_FUN, INLINE, TAILREC, SUSPEND) + modalityModifier + ) + + // If function is marked as having no annotations, we don't create stubs for it + if (!Flags.HAS_ANNOTATIONS.get(functionProto.flags)) return + + val annotationIds = c.components.annotationLoader.loadCallableAnnotations( + protoContainer, functionProto, AnnotatedCallableKind.FUNCTION + ) + createAnnotationStubs(annotationIds, modifierListStubImpl) + } + + override fun doCreateCallableStub(parent: StubElement): StubElement { + val callableName = c.nameResolver.getName(functionProto.name) + + return KotlinFunctionStubImpl( + parent, + callableName.ref(), + isTopLevel, + c.containerFqName.child(callableName), + isExtension = functionProto.hasReceiver(), + hasBlockBody = true, + hasBody = Flags.MODALITY.get(functionProto.flags) != Modality.ABSTRACT, + hasTypeParameterListBeforeFunctionName = functionProto.typeParameterList.isNotEmpty(), + mayHaveContract = functionProto.hasContract() + ) + } +} + +private class PropertyClsStubBuilder( + parent: StubElement, + outerContext: ClsStubBuilderContext, + protoContainer: ProtoContainer, + private val propertyProto: ProtoBuf.Property +) : CallableClsStubBuilder(parent, outerContext, protoContainer, propertyProto.typeParameterList) { + private val isVar = Flags.IS_VAR.get(propertyProto.flags) + + override val receiverType: ProtoBuf.Type? + get() = propertyProto.receiverType(c.typeTable) + + override val receiverAnnotations: List + get() = c.components.annotationLoader + .loadExtensionReceiverParameterAnnotations(protoContainer, propertyProto, AnnotatedCallableKind.PROPERTY_GETTER) + .map { ClassIdWithTarget(it, AnnotationUseSiteTarget.RECEIVER) } + + override val returnType: ProtoBuf.Type + get() = propertyProto.returnType(c.typeTable) + + override fun createValueParameterList() { + } + + override fun createModifierListStub() { + val constModifier = if (isVar) listOf() else listOf(CONST) + val modalityModifier = if (isTopLevel) listOf() else listOf(MODALITY) + + val modifierListStubImpl = createModifierListStubForDeclaration( + callableStub, propertyProto.flags, + listOf(VISIBILITY, LATEINIT, EXTERNAL_PROPERTY) + constModifier + modalityModifier + ) + + // If field is marked as having no annotations, we don't create stubs for it + if (!Flags.HAS_ANNOTATIONS.get(propertyProto.flags)) return + + val propertyAnnotations = + c.components.annotationLoader.loadCallableAnnotations(protoContainer, propertyProto, AnnotatedCallableKind.PROPERTY) + val backingFieldAnnotations = + c.components.annotationLoader.loadPropertyBackingFieldAnnotations(protoContainer, propertyProto) + val delegateFieldAnnotations = + c.components.annotationLoader.loadPropertyDelegateFieldAnnotations(protoContainer, propertyProto) + val allAnnotations = + propertyAnnotations.map { ClassIdWithTarget(it, null) } + + backingFieldAnnotations.map { ClassIdWithTarget(it, AnnotationUseSiteTarget.FIELD) } + + delegateFieldAnnotations.map { ClassIdWithTarget(it, AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD) } + createTargetedAnnotationStubs(allAnnotations, modifierListStubImpl) + } + + override fun doCreateCallableStub(parent: StubElement): StubElement { + val callableName = c.nameResolver.getName(propertyProto.name) + + return KotlinPropertyStubImpl( + parent, + callableName.ref(), + isVar, + isTopLevel, + hasDelegate = false, + hasDelegateExpression = false, + hasInitializer = false, + isExtension = propertyProto.hasReceiver(), + hasReturnTypeRef = true, + fqName = c.containerFqName.child(callableName) + ) + } +} + +private class ConstructorClsStubBuilder( + parent: StubElement, + outerContext: ClsStubBuilderContext, + protoContainer: ProtoContainer, + private val constructorProto: ProtoBuf.Constructor +) : CallableClsStubBuilder(parent, outerContext, protoContainer, emptyList()) { + override val receiverType: ProtoBuf.Type? + get() = null + + override val receiverAnnotations: List + get() = emptyList() + + override val returnType: ProtoBuf.Type? + get() = null + + override fun createValueParameterList() { + typeStubBuilder.createValueParameterListStub(callableStub, constructorProto, constructorProto.valueParameterList, protoContainer) + } + + override fun createModifierListStub() { + val modifierListStubImpl = createModifierListStubForDeclaration(callableStub, constructorProto.flags, listOf(VISIBILITY)) + + // If constructor is marked as having no annotations, we don't create stubs for it + if (!Flags.HAS_ANNOTATIONS.get(constructorProto.flags)) return + + val annotationIds = c.components.annotationLoader.loadCallableAnnotations( + protoContainer, constructorProto, AnnotatedCallableKind.FUNCTION + ) + createAnnotationStubs(annotationIds, modifierListStubImpl) + } + + override fun doCreateCallableStub(parent: StubElement): StubElement { + return if (Flags.IS_SECONDARY.get(constructorProto.flags)) + KotlinPlaceHolderStubImpl(parent, KtStubElementTypes.SECONDARY_CONSTRUCTOR) + else + KotlinPlaceHolderStubImpl(parent, KtStubElementTypes.PRIMARY_CONSTRUCTOR) + } +} diff --git a/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/ClassClsStubBuilder.kt b/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/ClassClsStubBuilder.kt new file mode 100644 index 00000000000..5c7f9fadfe7 --- /dev/null +++ b/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/ClassClsStubBuilder.kt @@ -0,0 +1,283 @@ +// 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.psi.stubs.builder + +import com.intellij.openapi.diagnostic.Logger +import com.intellij.psi.PsiElement +import com.intellij.psi.stubs.StubElement +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.builtins.isNumberedFunctionClassFqName +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.lexer.KtModifierKeywordToken +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.Flags +import org.jetbrains.kotlin.metadata.deserialization.NameResolver +import org.jetbrains.kotlin.metadata.deserialization.TypeTable +import org.jetbrains.kotlin.metadata.deserialization.supertypes +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.psi.KtClassBody +import org.jetbrains.kotlin.psi.KtSuperTypeEntry +import org.jetbrains.kotlin.psi.KtSuperTypeList +import org.jetbrains.kotlin.psi.stubs.builder.flags.* +import org.jetbrains.kotlin.psi.stubs.elements.KtClassElementType +import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes +import org.jetbrains.kotlin.psi.stubs.impl.KotlinClassStubImpl +import org.jetbrains.kotlin.psi.stubs.impl.KotlinModifierListStubImpl +import org.jetbrains.kotlin.psi.stubs.impl.KotlinObjectStubImpl +import org.jetbrains.kotlin.psi.stubs.impl.KotlinPlaceHolderStubImpl +import org.jetbrains.kotlin.serialization.deserialization.ProtoContainer +import org.jetbrains.kotlin.serialization.deserialization.getClassId +import org.jetbrains.kotlin.serialization.deserialization.getName + +fun createClassStub( + parent: StubElement, + classProto: ProtoBuf.Class, + nameResolver: NameResolver, + classId: ClassId, + source: SourceElement?, + context: ClsStubBuilderContext +) { + ClassClsStubBuilder(parent, classProto, nameResolver, classId, source, context).build() +} + +private class ClassClsStubBuilder( + private val parentStub: StubElement, + private val classProto: ProtoBuf.Class, + nameResolver: NameResolver, + private val classId: ClassId, + source: SourceElement?, + outerContext: ClsStubBuilderContext +) { + private val thisAsProtoContainer = ProtoContainer.Class( + classProto, nameResolver, TypeTable(classProto.typeTable), source, outerContext.protoContainer + ) + private val classKind = thisAsProtoContainer.kind + + private val c = outerContext.child( + classProto.typeParameterList, classId.shortClassName, nameResolver, thisAsProtoContainer.typeTable, thisAsProtoContainer + ) + private val typeStubBuilder = TypeClsStubBuilder(c) + private val supertypeIds = run { + val supertypeIds = classProto.supertypes(c.typeTable).map { c.nameResolver.getClassId(it.className) } + //empty supertype list if single supertype is Any + if (supertypeIds.singleOrNull()?.let { StandardNames.FqNames.any == it.asSingleFqName().toUnsafe() } == true) { + listOf() + } else { + supertypeIds + } + } + + private val companionObjectName = if (classProto.hasCompanionObjectName()) + c.nameResolver.getName(classProto.companionObjectName) + else + null + + private val classOrObjectStub = createClassOrObjectStubAndModifierListStub() + + fun build() { + val typeConstraintListData = typeStubBuilder.createTypeParameterListStub(classOrObjectStub, classProto.typeParameterList) + createConstructorStub() + createDelegationSpecifierList() + typeStubBuilder.createTypeConstraintListStub(classOrObjectStub, typeConstraintListData) + createClassBodyAndMemberStubs() + } + + private fun createClassOrObjectStubAndModifierListStub(): StubElement { + val classOrObjectStub = doCreateClassOrObjectStub() + val modifierList = createModifierListForClass(classOrObjectStub) + if (Flags.HAS_ANNOTATIONS.get(classProto.flags)) { + createAnnotationStubs(c.components.annotationLoader.loadClassAnnotations(thisAsProtoContainer), modifierList) + } + return classOrObjectStub + } + + private fun createModifierListForClass(parent: StubElement): KotlinModifierListStubImpl { + val relevantFlags = arrayListOf(VISIBILITY) + relevantFlags.add(EXTERNAL_CLASS) + if (isClass()) { + relevantFlags.add(INNER) + relevantFlags.add(DATA) + relevantFlags.add(MODALITY) + relevantFlags.add(VALUE_CLASS) + } + if (isInterface()) { + relevantFlags.add(FUN_INTERFACE) + } + val additionalModifiers = when (classKind) { + ProtoBuf.Class.Kind.ENUM_CLASS -> listOf(KtTokens.ENUM_KEYWORD) + ProtoBuf.Class.Kind.COMPANION_OBJECT -> listOf(KtTokens.COMPANION_KEYWORD) + ProtoBuf.Class.Kind.ANNOTATION_CLASS -> listOf(KtTokens.ANNOTATION_KEYWORD) + else -> listOf() + } + return createModifierListStubForDeclaration(parent, classProto.flags, relevantFlags, additionalModifiers) + } + + private fun doCreateClassOrObjectStub(): StubElement { + val isCompanionObject = classKind == ProtoBuf.Class.Kind.COMPANION_OBJECT + val fqName = classId.asSingleFqName() + val shortName = fqName.shortName().ref() + val superTypeRefs = supertypeIds.filterNot { + //TODO: filtering function types should go away + isNumberedFunctionClassFqName(it.asSingleFqName().toUnsafe()) + }.map { it.shortClassName.ref() }.toTypedArray() + val classId = classId.takeUnless { it.isLocal } + return when (classKind) { + ProtoBuf.Class.Kind.OBJECT, ProtoBuf.Class.Kind.COMPANION_OBJECT -> { + KotlinObjectStubImpl( + parentStub, shortName, fqName, + classId = classId, + superTypeRefs, + isTopLevel = !this.classId.isNestedClass, + isDefault = isCompanionObject, + isLocal = false, + isObjectLiteral = false, + ) + } + else -> { + KotlinClassStubImpl( + KtClassElementType.getStubType(classKind == ProtoBuf.Class.Kind.ENUM_ENTRY), + parentStub, + fqName.ref(), + classId = classId, + shortName, + superTypeRefs, + isInterface = classKind == ProtoBuf.Class.Kind.INTERFACE, + isEnumEntry = classKind == ProtoBuf.Class.Kind.ENUM_ENTRY, + isLocal = false, + isTopLevel = !this.classId.isNestedClass, + ) + } + } + } + + private fun createConstructorStub() { + if (!isClass()) return + + val primaryConstructorProto = classProto.constructorList.find { !Flags.IS_SECONDARY.get(it.flags) } ?: return + + createConstructorStub(classOrObjectStub, primaryConstructorProto, c, thisAsProtoContainer) + } + + private fun createDelegationSpecifierList() { + // if single supertype is any then no delegation specifier list is needed + if (supertypeIds.isEmpty()) return + + val delegationSpecifierListStub = KotlinPlaceHolderStubImpl(classOrObjectStub, KtStubElementTypes.SUPER_TYPE_LIST) + + classProto.supertypes(c.typeTable).forEach { type -> + val superClassStub = KotlinPlaceHolderStubImpl( + delegationSpecifierListStub, KtStubElementTypes.SUPER_TYPE_ENTRY + ) + typeStubBuilder.createTypeReferenceStub(superClassStub, type) + } + } + + private fun createClassBodyAndMemberStubs() { + val classBody = KotlinPlaceHolderStubImpl(classOrObjectStub, KtStubElementTypes.CLASS_BODY) + createEnumEntryStubs(classBody) + createCompanionObjectStub(classBody) + createCallableMemberStubs(classBody) + createInnerAndNestedClasses(classBody) + createTypeAliasesStubs(classBody) + } + + private fun createCompanionObjectStub(classBody: KotlinPlaceHolderStubImpl) { + if (companionObjectName == null) { + return + } + + val companionObjectId = classId.createNestedClassId(companionObjectName) + createNestedClassStub(classBody, companionObjectId) + } + + private fun createEnumEntryStubs(classBody: KotlinPlaceHolderStubImpl) { + if (classKind != ProtoBuf.Class.Kind.ENUM_CLASS) return + + classProto.enumEntryList.forEach { entry -> + val name = c.nameResolver.getName(entry.name) + val annotations = c.components.annotationLoader.loadEnumEntryAnnotations(thisAsProtoContainer, entry) + val enumEntryStub = KotlinClassStubImpl( + KtStubElementTypes.ENUM_ENTRY, + classBody, + qualifiedName = c.containerFqName.child(name).ref(), + classId = null, // enum entry do not have class id + name = name.ref(), + superNames = arrayOf(), + isInterface = false, + isEnumEntry = true, + isLocal = false, + isTopLevel = false + ) + if (annotations.isNotEmpty()) { + createAnnotationStubs(annotations, createEmptyModifierListStub(enumEntryStub)) + } + } + } + + private fun createCallableMemberStubs(classBody: KotlinPlaceHolderStubImpl) { + for (secondaryConstructorProto in classProto.constructorList) { + if (Flags.IS_SECONDARY.get(secondaryConstructorProto.flags)) { + createConstructorStub(classBody, secondaryConstructorProto, c, thisAsProtoContainer) + } + } + + createDeclarationsStubs(classBody, c, thisAsProtoContainer, classProto.functionList, classProto.propertyList) + } + + private fun isClass(): Boolean { + return classKind == ProtoBuf.Class.Kind.CLASS || + classKind == ProtoBuf.Class.Kind.ENUM_CLASS || + classKind == ProtoBuf.Class.Kind.ANNOTATION_CLASS + } + + private fun isInterface(): Boolean { + return classKind == ProtoBuf.Class.Kind.INTERFACE + } + + private fun createInnerAndNestedClasses(classBody: KotlinPlaceHolderStubImpl) { + classProto.nestedClassNameList.forEach { id -> + val nestedClassName = c.nameResolver.getName(id) + if (nestedClassName != companionObjectName) { + val nestedClassId = classId.createNestedClassId(nestedClassName) + createNestedClassStub(classBody, nestedClassId) + } + } + } + + private fun createTypeAliasesStubs(classBody: KotlinPlaceHolderStubImpl) { + createTypeAliasesStubs(classBody, c, thisAsProtoContainer, classProto.typeAliasList) + } + + private fun createNestedClassStub(classBody: StubElement, nestedClassId: ClassId) { + val (nameResolver, classProto, _, sourceElement) = + c.components.classDataFinder.findClassData(nestedClassId) + ?: c.components.virtualFileForDebug.let { rootFile -> + val outerClassId = nestedClassId.outerClassId + val sortedChildren = rootFile.parent.children.sortedBy { it.name } + val msgPrefix = "Could not find data for nested class $nestedClassId of class $outerClassId\n" + val explanation = when { + outerClassId != null && sortedChildren.none { it.name.startsWith("${outerClassId.relativeClassName}\$a") } -> + // KT-29427: case with obfuscation + "Reason: obfuscation suspected (single-letter name)\n" + else -> + // General case + "" + } + val msg = msgPrefix + explanation + + "Root file: ${rootFile.canonicalPath}\n" + + "Dir: ${rootFile.parent.canonicalPath}\n" + + "Children:\n" + + sortedChildren.joinToString(separator = "\n") { + "${it.name} (valid: ${it.isValid})" + } + LOG.info(msg) + return + } + createClassStub(classBody, classProto, nameResolver, nestedClassId, sourceElement, c) + } + + companion object { + private val LOG = Logger.getInstance(ClassClsStubBuilder::class.java) + } +} diff --git a/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/ClsStubBuilderContext.kt b/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/ClsStubBuilderContext.kt new file mode 100644 index 00000000000..949967b24df --- /dev/null +++ b/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/ClsStubBuilderContext.kt @@ -0,0 +1,76 @@ +// 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.psi.stubs.builder + +import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.NameResolver +import org.jetbrains.kotlin.metadata.deserialization.TypeTable +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.serialization.deserialization.AnnotationAndConstantLoader +import org.jetbrains.kotlin.serialization.deserialization.ClassDataFinder +import org.jetbrains.kotlin.serialization.deserialization.ProtoContainer +import org.jetbrains.kotlin.serialization.deserialization.getName + +data class ClassIdWithTarget(val classId: ClassId, val target: AnnotationUseSiteTarget?) + +class ClsStubBuilderComponents( + val classDataFinder: ClassDataFinder, + val annotationLoader: AnnotationAndConstantLoader, + val virtualFileForDebug: VirtualFile +) { + fun createContext( + nameResolver: NameResolver, + packageFqName: FqName, + typeTable: TypeTable + ): ClsStubBuilderContext = + ClsStubBuilderContext(this, nameResolver, packageFqName, EmptyTypeParameters, typeTable, protoContainer = null) +} + +interface TypeParameters { + operator fun get(id: Int): Name + + fun child(nameResolver: NameResolver, innerTypeParameters: List) = + TypeParametersImpl(nameResolver, innerTypeParameters, parent = this) +} + +object EmptyTypeParameters : TypeParameters { + override fun get(id: Int): Name = throw IllegalStateException("Unknown type parameter with id = $id") +} + +class TypeParametersImpl( + nameResolver: NameResolver, + typeParameterProtos: Collection, + private val parent: TypeParameters +) : TypeParameters { + private val typeParametersById = typeParameterProtos.map { Pair(it.id, nameResolver.getName(it.name)) }.toMap() + + override fun get(id: Int): Name = typeParametersById[id] ?: parent[id] +} + +class ClsStubBuilderContext( + val components: ClsStubBuilderComponents, + val nameResolver: NameResolver, + val containerFqName: FqName, + val typeParameters: TypeParameters, + val typeTable: TypeTable, + val protoContainer: ProtoContainer.Class? +) + +internal fun ClsStubBuilderContext.child( + typeParameterList: List, + name: Name? = null, + nameResolver: NameResolver = this.nameResolver, + typeTable: TypeTable = this.typeTable, + protoContainer: ProtoContainer.Class? = this.protoContainer +): ClsStubBuilderContext = ClsStubBuilderContext( + this.components, + nameResolver, + if (name != null) this.containerFqName.child(name) else this.containerFqName, + this.typeParameters.child(nameResolver, typeParameterList), + typeTable, + protoContainer +) diff --git a/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/TypeClsStubBuilder.kt b/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/TypeClsStubBuilder.kt new file mode 100644 index 00000000000..7c6b96db24d --- /dev/null +++ b/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/TypeClsStubBuilder.kt @@ -0,0 +1,344 @@ +// 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.psi.stubs.builder + +import com.intellij.psi.PsiElement +import com.intellij.psi.stubs.StubElement +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.builtins.isBuiltinFunctionClass +import org.jetbrains.kotlin.lexer.KtModifierKeywordToken +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.ProtoBuf.Type +import org.jetbrains.kotlin.metadata.ProtoBuf.Type.Argument.Projection +import org.jetbrains.kotlin.metadata.ProtoBuf.TypeParameter.Variance +import org.jetbrains.kotlin.metadata.deserialization.* +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +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.serialization.deserialization.ProtoContainer +import org.jetbrains.kotlin.serialization.deserialization.getClassId +import org.jetbrains.kotlin.serialization.deserialization.getName +import org.jetbrains.kotlin.serialization.js.DynamicTypeDeserializer +import org.jetbrains.kotlin.utils.doNothing +import java.util.* + +// TODO: see DescriptorRendererOptions.excludedTypeAnnotationClasses for decompiler +private val ANNOTATIONS_NOT_LOADED_FOR_TYPES = setOf(StandardNames.FqNames.parameterName) + +class TypeClsStubBuilder(private val c: ClsStubBuilderContext) { + fun createTypeReferenceStub( + parent: StubElement, + type: Type, + additionalAnnotations: () -> List = { emptyList() } + ) { + val abbreviatedType = type.abbreviatedType(c.typeTable) + if (abbreviatedType != null) { + return createTypeReferenceStub(parent, abbreviatedType, additionalAnnotations) + } + + val typeReference = KotlinPlaceHolderStubImpl(parent, KtStubElementTypes.TYPE_REFERENCE) + + val annotations = c.components.annotationLoader.loadTypeAnnotations(type, c.nameResolver).filterNot { + val isTopLevelClass = !it.isNestedClass + isTopLevelClass && it.asSingleFqName() in ANNOTATIONS_NOT_LOADED_FOR_TYPES + } + + val allAnnotations = additionalAnnotations() + annotations.map { ClassIdWithTarget(it, null) } + + when { + type.hasClassName() || type.hasTypeAliasName() -> + createClassReferenceTypeStub(typeReference, type, allAnnotations) + type.hasTypeParameter() -> + createTypeParameterStub(typeReference, type, c.typeParameters[type.typeParameter], allAnnotations) + type.hasTypeParameterName() -> + createTypeParameterStub(typeReference, type, c.nameResolver.getName(type.typeParameterName), allAnnotations) + else -> { + doNothing() + } + } + } + + private fun nullableTypeParent(parent: KotlinStubBaseImpl<*>, type: Type): KotlinStubBaseImpl<*> = if (type.nullable) + KotlinPlaceHolderStubImpl(parent, KtStubElementTypes.NULLABLE_TYPE) + else + parent + + private fun createTypeParameterStub(parent: KotlinStubBaseImpl<*>, type: Type, name: Name, annotations: List) { + createTypeAnnotationStubs(parent, type, annotations) + val nullableParentWrapper = nullableTypeParent(parent, type) + createStubForTypeName(ClassId.topLevel(FqName.topLevel(name)), nullableParentWrapper) + } + + private fun createClassReferenceTypeStub(parent: KotlinStubBaseImpl<*>, type: Type, annotations: List) { + if (type.hasFlexibleTypeCapabilitiesId()) { + val id = c.nameResolver.getString(type.flexibleTypeCapabilitiesId) + + if (id == DynamicTypeDeserializer.id) { + KotlinPlaceHolderStubImpl(nullableTypeParent(parent, type), KtStubElementTypes.DYNAMIC_TYPE) + return + } + } + + assert(type.hasClassName() || type.hasTypeAliasName()) { + "Class reference stub must have either class or type alias name" + } + + val classId = c.nameResolver.getClassId(if (type.hasClassName()) type.className else type.typeAliasName) + val shouldBuildAsFunctionType = isBuiltinFunctionClass(classId) && type.argumentList.none { it.projection == Projection.STAR } + if (shouldBuildAsFunctionType) { + val (extensionAnnotations, notExtensionAnnotations) = annotations.partition { + it.classId.asSingleFqName() == StandardNames.FqNames.extensionFunctionType + } + + val isExtension = extensionAnnotations.isNotEmpty() + val isSuspend = Flags.SUSPEND_TYPE.get(type.flags) + + val nullableWrapper = if (isSuspend) { + val wrapper = nullableTypeParent(parent, type) + createTypeAnnotationStubs(wrapper, type, notExtensionAnnotations) + wrapper + } else { + createTypeAnnotationStubs(parent, type, notExtensionAnnotations) + nullableTypeParent(parent, type) + } + + createFunctionTypeStub(nullableWrapper, type, isExtension, isSuspend) + + return + } + + createTypeAnnotationStubs(parent, type, annotations) + + val outerTypeChain = generateSequence(type) { it.outerType(c.typeTable) }.toList() + + createStubForTypeName(classId, nullableTypeParent(parent, type)) { userTypeStub, index -> + outerTypeChain.getOrNull(index)?.let { createTypeArgumentListStub(userTypeStub, it.argumentList) } + } + } + + private fun createTypeAnnotationStubs(parent: KotlinStubBaseImpl<*>, type: Type, annotations: List) { + val typeModifiers = getTypeModifiersAsWritten(type) + if (annotations.isEmpty() && typeModifiers.isEmpty()) return + val typeModifiersMask = ModifierMaskUtils.computeMask { it in typeModifiers } + val modifiersList = KotlinModifierListStubImpl(parent, typeModifiersMask, KtStubElementTypes.MODIFIER_LIST) + createTargetedAnnotationStubs(annotations, modifiersList) + } + + private fun getTypeModifiersAsWritten(type: Type): Set { + if (!type.hasClassName() && !type.hasTypeAliasName()) return emptySet() + + val result = hashSetOf() + + if (Flags.SUSPEND_TYPE.get(type.flags)) { + result.add(KtTokens.SUSPEND_KEYWORD) + } + + return result + } + + private fun createTypeArgumentListStub(typeStub: KotlinUserTypeStub, typeArgumentProtoList: List) { + if (typeArgumentProtoList.isEmpty()) { + return + } + val typeArgumentsListStub = KotlinPlaceHolderStubImpl(typeStub, KtStubElementTypes.TYPE_ARGUMENT_LIST) + typeArgumentProtoList.forEach { typeArgumentProto -> + val projectionKind = typeArgumentProto.projection.toProjectionKind() + val typeProjection = KotlinTypeProjectionStubImpl(typeArgumentsListStub, projectionKind.ordinal) + if (projectionKind != KtProjectionKind.STAR) { + val modifierKeywordToken = projectionKind.token as? KtModifierKeywordToken + createModifierListStub(typeProjection, listOfNotNull(modifierKeywordToken)) + createTypeReferenceStub(typeProjection, typeArgumentProto.type(c.typeTable)!!) + } + } + } + + private fun Projection.toProjectionKind() = when (this) { + Projection.IN -> KtProjectionKind.IN + Projection.OUT -> KtProjectionKind.OUT + Projection.INV -> KtProjectionKind.NONE + Projection.STAR -> KtProjectionKind.STAR + } + + private fun createFunctionTypeStub( + parent: StubElement, + type: Type, + isExtensionFunctionType: Boolean, + isSuspend: Boolean + ) { + val typeArgumentList = type.argumentList + val functionType = KotlinPlaceHolderStubImpl(parent, KtStubElementTypes.FUNCTION_TYPE) + if (isExtensionFunctionType) { + val functionTypeReceiverStub = + KotlinPlaceHolderStubImpl(functionType, KtStubElementTypes.FUNCTION_TYPE_RECEIVER) + val receiverTypeProto = typeArgumentList.first().type(c.typeTable)!! + createTypeReferenceStub(functionTypeReceiverStub, receiverTypeProto) + } + + val parameterList = KotlinPlaceHolderStubImpl(functionType, KtStubElementTypes.VALUE_PARAMETER_LIST) + val typeArgumentsWithoutReceiverAndReturnType = + typeArgumentList.subList(if (isExtensionFunctionType) 1 else 0, typeArgumentList.size - 1) + var suspendParameterType: Type? = null + + for ((index, argument) in typeArgumentsWithoutReceiverAndReturnType.withIndex()) { + if (isSuspend && index == typeArgumentsWithoutReceiverAndReturnType.size - 1) { + val parameterType = argument.type(c.typeTable)!! + if (parameterType.hasClassName() && parameterType.argumentCount == 1) { + val classId = c.nameResolver.getClassId(parameterType.className) + val fqName = classId.asSingleFqName() + assert( + fqName == FqName("kotlin.coroutines.Continuation") || + fqName == FqName("kotlin.coroutines.experimental.Continuation") + ) { + "Last parameter type of suspend function must be Continuation, but it is $fqName" + } + suspendParameterType = parameterType + continue + } + } + val parameter = KotlinParameterStubImpl( + parameterList, fqName = null, name = null, isMutable = false, hasValOrVar = false, hasDefaultValue = false + ) + + createTypeReferenceStub(parameter, argument.type(c.typeTable)!!) + } + + + if (suspendParameterType == null) { + val returnType = typeArgumentList.last().type(c.typeTable)!! + createTypeReferenceStub(functionType, returnType) + } else { + val continuationArgumentType = suspendParameterType.getArgument(0).type(c.typeTable)!! + createTypeReferenceStub(functionType, continuationArgumentType) + } + } + + fun createValueParameterListStub( + parent: StubElement, + callableProto: MessageLite, + parameters: List, + container: ProtoContainer + ) { + val parameterListStub = KotlinPlaceHolderStubImpl(parent, KtStubElementTypes.VALUE_PARAMETER_LIST) + for ((index, valueParameterProto) in parameters.withIndex()) { + val name = c.nameResolver.getName(valueParameterProto.name) + val parameterStub = KotlinParameterStubImpl( + parameterListStub, + name = name.ref(), + fqName = null, + hasDefaultValue = false, + hasValOrVar = false, + isMutable = false + ) + val varargElementType = valueParameterProto.varargElementType(c.typeTable) + val typeProto = varargElementType ?: valueParameterProto.type(c.typeTable) + val modifiers = arrayListOf() + + if (varargElementType != null) { + modifiers.add(KtTokens.VARARG_KEYWORD) + } + if (Flags.IS_CROSSINLINE.get(valueParameterProto.flags)) { + modifiers.add(KtTokens.CROSSINLINE_KEYWORD) + } + if (Flags.IS_NOINLINE.get(valueParameterProto.flags)) { + modifiers.add(KtTokens.NOINLINE_KEYWORD) + } + + val modifierList = createModifierListStub(parameterStub, modifiers) + + if (Flags.HAS_ANNOTATIONS.get(valueParameterProto.flags)) { + val parameterAnnotations = c.components.annotationLoader.loadValueParameterAnnotations( + container, callableProto, callableProto.annotatedCallableKind, index, valueParameterProto + ) + if (parameterAnnotations.isNotEmpty()) { + createAnnotationStubs(parameterAnnotations, modifierList ?: createEmptyModifierListStub(parameterStub)) + } + } + + createTypeReferenceStub(parameterStub, typeProto) + } + } + + fun createTypeParameterListStub( + parent: StubElement, + typeParameterProtoList: List + ): List> { + if (typeParameterProtoList.isEmpty()) return listOf() + + val typeParameterListStub = KotlinPlaceHolderStubImpl(parent, KtStubElementTypes.TYPE_PARAMETER_LIST) + val protosForTypeConstraintList = arrayListOf>() + for (proto in typeParameterProtoList) { + val name = c.nameResolver.getName(proto.name) + val typeParameterStub = KotlinTypeParameterStubImpl( + typeParameterListStub, + name = name.ref(), + isInVariance = proto.variance == Variance.IN, + isOutVariance = proto.variance == Variance.OUT + ) + createTypeParameterModifierListStub(typeParameterStub, proto) + val upperBoundProtos = proto.upperBounds(c.typeTable) + if (upperBoundProtos.isNotEmpty()) { + val upperBound = upperBoundProtos.first() + if (!upperBound.isDefaultUpperBound()) { + createTypeReferenceStub(typeParameterStub, upperBound) + } + protosForTypeConstraintList.addAll(upperBoundProtos.drop(1).map { Pair(name, it) }) + } + } + return protosForTypeConstraintList + } + + fun createTypeConstraintListStub( + parent: StubElement, + protosForTypeConstraintList: List> + ) { + if (protosForTypeConstraintList.isEmpty()) { + return + } + val typeConstraintListStub = KotlinPlaceHolderStubImpl(parent, KtStubElementTypes.TYPE_CONSTRAINT_LIST) + for ((name, type) in protosForTypeConstraintList) { + val typeConstraintStub = KotlinPlaceHolderStubImpl(typeConstraintListStub, KtStubElementTypes.TYPE_CONSTRAINT) + KotlinNameReferenceExpressionStubImpl(typeConstraintStub, name.ref()) + createTypeReferenceStub(typeConstraintStub, type) + } + } + + private fun createTypeParameterModifierListStub( + typeParameterStub: KotlinTypeParameterStubImpl, + typeParameterProto: ProtoBuf.TypeParameter + ) { + val modifiers = ArrayList() + when (typeParameterProto.variance) { + Variance.IN -> modifiers.add(KtTokens.IN_KEYWORD) + Variance.OUT -> modifiers.add(KtTokens.OUT_KEYWORD) + Variance.INV -> { /* do nothing */ + } + null -> { /* do nothing */ + } + } + if (typeParameterProto.reified) { + modifiers.add(KtTokens.REIFIED_KEYWORD) + } + + val modifierList = createModifierListStub(typeParameterStub, modifiers) + + val annotations = c.components.annotationLoader.loadTypeParameterAnnotations(typeParameterProto, c.nameResolver) + if (annotations.isNotEmpty()) { + createAnnotationStubs( + annotations, + modifierList ?: createEmptyModifierListStub(typeParameterStub) + ) + } + } + + private fun Type.isDefaultUpperBound(): Boolean { + return this.hasClassName() && + c.nameResolver.getClassId(className).let { StandardNames.FqNames.any == it.asSingleFqName().toUnsafe() } && + this.nullable + } +} diff --git a/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/clsStubBuilding.kt b/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/clsStubBuilding.kt new file mode 100644 index 00000000000..972de379eb5 --- /dev/null +++ b/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/clsStubBuilding.kt @@ -0,0 +1,232 @@ +// 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.psi.stubs.builder + +import com.intellij.psi.PsiElement +import com.intellij.psi.stubs.StubElement +import com.intellij.util.io.StringRef +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.psi.stubs.builder.flags.FlagsToModifiers +import org.jetbrains.kotlin.idea.stubindex.KotlinFileStubForIde +import org.jetbrains.kotlin.lexer.KtModifierKeywordToken +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.protobuf.MessageLite +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.stubs.KotlinUserTypeStub +import org.jetbrains.kotlin.psi.stubs.builder.* +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 = KotlinFileStubForIde.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 = KotlinFileStubForIde.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 = KotlinFileStubForIde.forMultifileClassStub(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 = KotlinFileStubForIde.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) +} + +fun createStubForTypeName( + typeClassId: ClassId, + parent: StubElement, + 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()) + + fun recCreateStubForType(current: StubElement, level: Int): KotlinUserTypeStub { + val lastSegment = segments[level] + val userTypeStub = KotlinUserTypeStubImpl(current) + if (level + 1 < segments.size) { + recCreateStubForType(userTypeStub, level + 1) + } + KotlinNameReferenceExpressionStubImpl(userTypeStub, lastSegment.ref()) + 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(annotationIds: List, parent: KotlinStubBaseImpl<*>) { + return createTargetedAnnotationStubs(annotationIds.map { ClassIdWithTarget(it, null) }, parent) +} + +fun createTargetedAnnotationStubs( + annotationIds: List, + parent: KotlinStubBaseImpl<*> +) { + if (annotationIds.isEmpty()) return + + annotationIds.forEach { annotation -> + val (annotationClassId, target) = annotation + val annotationEntryStubImpl = KotlinAnnotationEntryStubImpl( + parent, + shortName = annotationClassId.shortClassName.ref(), + hasValueArguments = false + ) + if (target != null) { + KotlinAnnotationUseSiteTargetStubImpl(annotationEntryStubImpl, StringRef.fromString(target.name)!!) + } + val constructorCallee = + KotlinPlaceHolderStubImpl(annotationEntryStubImpl, KtStubElementTypes.CONSTRUCTOR_CALLEE) + val typeReference = KotlinPlaceHolderStubImpl(constructorCallee, KtStubElementTypes.TYPE_REFERENCE) + createStubForTypeName(annotationClassId, typeReference) + } +} + +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())!! diff --git a/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/flags/flags.kt b/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/flags/flags.kt new file mode 100644 index 00000000000..56ed427ded5 --- /dev/null +++ b/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/flags/flags.kt @@ -0,0 +1,65 @@ +// 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.psi.stubs.builder.flags + +import org.jetbrains.kotlin.lexer.KtModifierKeywordToken +import org.jetbrains.kotlin.lexer.KtToken +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.Flags + +abstract class FlagsToModifiers { + abstract fun getModifiers(flags: Int): KtModifierKeywordToken? +} + +val MODALITY: FlagsToModifiers = object : FlagsToModifiers() { + override fun getModifiers(flags: Int): KtModifierKeywordToken { + val modality = Flags.MODALITY.get(flags) + return when (modality) { + ProtoBuf.Modality.ABSTRACT -> KtTokens.ABSTRACT_KEYWORD + ProtoBuf.Modality.FINAL -> KtTokens.FINAL_KEYWORD + ProtoBuf.Modality.OPEN -> KtTokens.OPEN_KEYWORD + ProtoBuf.Modality.SEALED -> KtTokens.SEALED_KEYWORD + null -> throw IllegalStateException("Unexpected modality: null") + } + } +} + +val VISIBILITY: FlagsToModifiers = object : FlagsToModifiers() { + override fun getModifiers(flags: Int): KtModifierKeywordToken? { + val visibility = Flags.VISIBILITY.get(flags) + return when (visibility) { + ProtoBuf.Visibility.PRIVATE, ProtoBuf.Visibility.PRIVATE_TO_THIS -> KtTokens.PRIVATE_KEYWORD + ProtoBuf.Visibility.INTERNAL -> KtTokens.INTERNAL_KEYWORD + ProtoBuf.Visibility.PROTECTED -> KtTokens.PROTECTED_KEYWORD + ProtoBuf.Visibility.PUBLIC -> KtTokens.PUBLIC_KEYWORD + else -> throw IllegalStateException("Unexpected visibility: $visibility") + } + } +} + +val INNER = createBooleanFlagToModifier(Flags.IS_INNER, KtTokens.INNER_KEYWORD) +val CONST = createBooleanFlagToModifier(Flags.IS_CONST, KtTokens.CONST_KEYWORD) +val LATEINIT = createBooleanFlagToModifier(Flags.IS_LATEINIT, KtTokens.LATEINIT_KEYWORD) +val OPERATOR = createBooleanFlagToModifier(Flags.IS_OPERATOR, KtTokens.OPERATOR_KEYWORD) +val INFIX = createBooleanFlagToModifier(Flags.IS_INFIX, KtTokens.INFIX_KEYWORD) +val DATA = createBooleanFlagToModifier(Flags.IS_DATA, KtTokens.DATA_KEYWORD) +val EXTERNAL_FUN = createBooleanFlagToModifier(Flags.IS_EXTERNAL_FUNCTION, KtTokens.EXTERNAL_KEYWORD) +val EXTERNAL_PROPERTY = createBooleanFlagToModifier(Flags.IS_EXTERNAL_PROPERTY, KtTokens.EXTERNAL_KEYWORD) +val EXTERNAL_CLASS = createBooleanFlagToModifier(Flags.IS_EXTERNAL_CLASS, KtTokens.EXTERNAL_KEYWORD) +val INLINE = createBooleanFlagToModifier(Flags.IS_INLINE, KtTokens.INLINE_KEYWORD) +val VALUE_CLASS = createBooleanFlagToModifier(Flags.IS_INLINE_CLASS, KtTokens.VALUE_KEYWORD) +val FUN_INTERFACE = createBooleanFlagToModifier(Flags.IS_FUN_INTERFACE, KtTokens.FUN_KEYWORD) +val TAILREC = createBooleanFlagToModifier(Flags.IS_TAILREC, KtTokens.TAILREC_KEYWORD) +val SUSPEND = createBooleanFlagToModifier(Flags.IS_SUSPEND, KtTokens.SUSPEND_KEYWORD) + +private fun createBooleanFlagToModifier( + flagField: Flags.BooleanFlagField, ktModifierKeywordToken: KtModifierKeywordToken +): FlagsToModifiers = BooleanFlagToModifier(flagField, ktModifierKeywordToken) + +private class BooleanFlagToModifier( + private val flagField: Flags.BooleanFlagField, + private val ktModifierKeywordToken: KtModifierKeywordToken +) : FlagsToModifiers() { + override fun getModifiers(flags: Int): KtModifierKeywordToken? = if (flagField.get(flags)) ktModifierKeywordToken else null +} diff --git a/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/typeAliasClsStubBuilding.kt b/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/typeAliasClsStubBuilding.kt new file mode 100644 index 00000000000..84d8b6b02b1 --- /dev/null +++ b/compiler/psi/cls-psi-stub-builder/src/org/jetbrains/kotlin/psi/stubs/builder/typeAliasClsStubBuilding.kt @@ -0,0 +1,50 @@ +// 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.psi.stubs.builder + +import com.intellij.psi.PsiElement +import com.intellij.psi.stubs.StubElement +import org.jetbrains.kotlin.psi.stubs.builder.flags.VISIBILITY +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.Flags +import org.jetbrains.kotlin.metadata.deserialization.underlyingType +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.psi.stubs.impl.KotlinTypeAliasStubImpl +import org.jetbrains.kotlin.serialization.deserialization.ProtoContainer +import org.jetbrains.kotlin.serialization.deserialization.getClassId +import org.jetbrains.kotlin.serialization.deserialization.getName + +fun createTypeAliasStub( + parent: StubElement, + typeAliasProto: ProtoBuf.TypeAlias, + protoContainer: ProtoContainer, + outerContext: ClsStubBuilderContext +) { + val c = outerContext.child(typeAliasProto.typeParameterList) + val shortName = c.nameResolver.getName(typeAliasProto.name) + + val classId = when (protoContainer) { + is ProtoContainer.Class -> protoContainer.classId.createNestedClassId(shortName) + is ProtoContainer.Package -> ClassId.topLevel(protoContainer.fqName.child(shortName)) + } + + val typeAlias = KotlinTypeAliasStubImpl( + parent, classId.shortClassName.ref(), classId.asSingleFqName().ref(), classId, + isTopLevel = !classId.isNestedClass + ) + + val modifierList = createModifierListStubForDeclaration(typeAlias, typeAliasProto.flags, arrayListOf(VISIBILITY), listOf()) + + val typeStubBuilder = TypeClsStubBuilder(c) + val restConstraints = typeStubBuilder.createTypeParameterListStub(typeAlias, typeAliasProto.typeParameterList) + assert(restConstraints.isEmpty()) { + "'where' constraints are not allowed for type aliases" + } + + if (Flags.HAS_ANNOTATIONS.get(typeAliasProto.flags)) { + createAnnotationStubs(typeAliasProto.annotationList.map { c.nameResolver.getClassId(it.id) }, modifierList) + } + + val typeAliasUnderlyingType = typeAliasProto.underlyingType(c.typeTable) + typeStubBuilder.createTypeReferenceStub(typeAlias, typeAliasUnderlyingType) +} diff --git a/settings.gradle b/settings.gradle index e13618674fc..6c0765d5463 100644 --- a/settings.gradle +++ b/settings.gradle @@ -112,7 +112,6 @@ include ":benchmarks", ":compiler:resolution.common.jvm", ":compiler:resolution", ":compiler:serialization", - ":compiler:psi", ":compiler:visualizer", ":compiler:visualizer:common", ":compiler:visualizer:render-fir", @@ -292,6 +291,10 @@ include ":benchmarks", ":kotlin-serialization-unshaded", ":wasm:wasm.ir" +include ":compiler:psi", + ":compiler:psi:cls-psi-stub-builder" + + include ":kotlinx-atomicfu-compiler-plugin", ":kotlinx-atomicfu-runtime", ":atomicfu"